]> git.mdlowis.com Git - proto/cerise-c.git/commitdiff
fleshed out more type checking rules. Need to flesh out how type inference for functi...
authorMike Lowis <mike.lowis@gentex.com>
Thu, 17 Oct 2024 19:17:02 +0000 (15:17 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Thu, 17 Oct 2024 19:17:02 +0000 (15:17 -0400)
cerise-c.m
lib/parser.rb
lib/type_checker.rb
lib/value.rb

index 03e5f7fbaeef84898a26cf5f13e4e43fb3241997..40081a626d1a2e26ffc1ad611364913a9a4c7796 100644 (file)
@@ -103,30 +103,35 @@ TestHashOps()
 {
     def hash = {foo: "bar", "baz": "boo"}
     def item = 42
-    set hash["foo"] = item
-    assert hash["foo"] == 42
+#    set hash["foo"] = item
+#    assert hash["foo"] == 42
     assert hash["baz"] == "boo"
 #    assert Length(hash) == 2
 }
 
 TestEqOps() {
     assert ("" == "") == true
-    assert ("" == 1) == false
-    assert ("" == 1.0) == false
-    assert ("" == true) == false
-    assert ("" == false) == false
-    assert ("" == []) == false
-    assert ("" == {}) == false
+    assert ("" == "a") == true
+    assert (1 == 1) == true
+    assert (1 == 2) == false
+    assert (1.0 == 1.0) == true
+    assert (1.0 == 2.0) == false
+    assert (true == true) == true
+    assert (true == false) == false
+#    assert ([1] == [1]) == true
+#    assert ([1] == [2]) == false
+#    assert ({"foo": "bar"} == {"foo": "bar"}) == true
+#    assert ({"foo": "bar"} == {"foo": "baz"}) == false
 }
 
 TestNeqOps() {
-    assert ("" != "") == false
-    assert ("" != 1) == true
-    assert ("" != 1.0) == true
-    assert ("" != true) == true
-    assert ("" != false) == true
-    assert ("" != []) == true
-    assert ("" != {}) == true
+#    assert ("" != "") == false
+#    assert ("" != 1) == true
+#    assert ("" != 1.0) == true
+#    assert ("" != true) == true
+#    assert ("" != false) == true
+#    assert ("" != []) == true
+#    assert ("" != {}) == true
 }
 
 TestLtOps(){
index c844812591e3edcdae210a4946e0afdbd4a4f142..2ab631c140105cf85c60e919962b281353bf9a99 100644 (file)
@@ -454,9 +454,9 @@ class Parser
   def string_or_ident()
     tok = consume()
     if tok.type == :string
-      IR::Const.new(tok.pos, Value::String, tok.text)
+      IR::Const.new(tok.pos, :string, tok.text)
     elsif tok.type == :ident
-      IR::Const.new(tok.pos, Value::String, "\"#{tok.text}\"")
+      IR::Const.new(tok.pos, :string, "\"#{tok.text}\"")
     else
       error("not a string or identifier: #{tok}")
     end
index 17588764c1c269b95346645b09bc31056e520809..a05df208521dff8d0410a8e64060f19549d95455 100644 (file)
@@ -113,8 +113,6 @@ class TypeChecker
 #      check_let(env, expr, type)
 #    elsif expr.is_a? IR::If
 #      check_ifexpr(env, expr, type)
-#    elsif expr.is_a? IR::Set
-#      check_set(env, expr, type)
 #    elsif expr.is_a? IR::Func
 #      check_func(env, expr, type)
 #    elsif expr.is_a? IR::Apply
@@ -194,7 +192,34 @@ class TypeChecker
   end
 
   def infer_const(env, expr)
-    raise "inferring const"
+    if expr.type
+      check(env, expr, expr.type)
+    elsif expr.value.is_a? Array
+      types = expr.value.map{|v| infer(env, v) }.uniq
+      if types.length > 1
+        error(expr.loc, "array literal contains more than one type of value")
+      end
+      if types.length == 0
+        error(expr.loc, "cannot infer type of empty array literal")
+      end
+      base_type = (expr.value.first ? expr.value.first.type : nil)
+      if base_type.nil?
+        error(expr.loc, "could not determine base type of array literal")
+      end
+      expr.type = Value::Type.new(:array, nil, base_type, -1)
+    elsif expr.value.is_a? Hash
+      types = expr.value.map{|k,v| [infer(env, k), infer(env, v)] }.uniq
+      if types.length > 1
+        error(expr.loc, "hash literal contains more than one type of key/value")
+      end
+      if types.length == 0
+        error(expr.loc, "cannot infer type of empty hash literal")
+      end
+      expr.type = Value::Type.new(:hash, nil, [types.first[0], types.first[1]] )
+    else
+      raise "could not infer const"
+    end
+    pp "TYPED", expr
     expr.type
   end
 
@@ -206,30 +231,22 @@ class TypeChecker
   end
 
   def infer_def(env, expr)
-#    raise "unimplemented"
-#    error(expr.loc, "unimplemented")
-
-    pp expr
     name = expr.name.name
     type = expr.value.type
     if type
-      puts "check"
       check(env, expr.value, type)
     else
-      puts "infer"
       type = infer(env, expr.value)
     end
-    pp type
-#    env[name] = { loc: expr.loc, type: type }
-    expr.type = :void
+    env.add_sym(name, expr.loc, :local, type, expr.value)
     expr.value.type = type
-    pp expr
-    raise "unimplemented"
+    expr.type = :void
   end
 
   def infer_set(env, expr)
-    raise "unimplemented"
-    error(expr.loc, "unimplemented")
+    type = infer(env, expr.name)
+    check(env, expr.value, type)
+    expr.type = :void
   end
 
   def infer_op(env, expr)
@@ -248,8 +265,10 @@ class TypeChecker
   end
 
   def infer_if(env, expr)
-    raise "unimplemented"
-    error(expr.loc, "unimplemented")
+    check(env, expr.cond, :bool)
+    expr.type = :void
+    expr.then.each {|e| infer(env, e) }
+    (expr.else || []).each {|e| infer(env, e) }
   end
 
   def infer_assert(env, expr)
@@ -261,7 +280,6 @@ class TypeChecker
   # TYPE CHECKING
   ##
 
-
   def check_func(env, expr, type)
     raise "unimplemented"
     error(expr.loc, "unimplemented")
@@ -290,16 +308,6 @@ class TypeChecker
     type
   end
 
-  def check_def(env, expr, type)
-    raise "unimplemented"
-    error(expr.loc, "unimplemented")
-  end
-
-  def check_set(env, expr, type)
-    raise "unimplemented"
-    error(expr.loc, "unimplemented")
-  end
-
   def check_op(env, expr, type)
     expr.type = expr.type || infer(env, expr)
     if expr.type != type
@@ -309,13 +317,12 @@ class TypeChecker
   end
 
   def check_call(env, expr, type)
-#    pp expr
+    pp expr
     error(expr.loc, "object being applied is not a function (has type: #{type.to_s})") if not type.is_a? Array
     error(expr.loc, "wrong number of arguments to function call") if (type.length - 1) != expr.args.length
     type[0..-2].each_with_index do |t,i|
       check(env, expr.args[i], t)
     end
-    pp type
     expr.type = type.last
   end
 
@@ -433,16 +440,6 @@ class TypeChecker
 #    error(expr.loc, "infer_set unimplemented")
 #  end
 #
-#
-#  def infer_apply(env, expr)
-#    if expr.func.is_a? String
-#      expr.type = infer_opcall(env, expr)
-#    else
-#      type = infer(env, expr.func)
-#      check_apply(env, expr, type)
-#    end
-#  end
-#
 #  def assign_type(env, var, type)
 #    if var.class == IR::Var and (var.type.nil? or var.type == String) then
 #      var.type = type
@@ -460,10 +457,27 @@ class TypeChecker
   end
 
   def check_binary(env, expr, vtype)
-    optype = BinaryOps[expr.op][vtype]
-    error(expr.loc, "unknown binary operation '#{expr.op}' for operand type #{vtype}") if optype.nil?
-    check(env, expr.left,  optype[0])
-    check(env, expr.right, optype[1])
-    expr.type = optype.last
+    if expr.op == "["
+      left_type = infer(env, expr.left)
+      if (left_type.is_a? Value::Type) and left_type.form == :array
+        check(env, expr.right, :int)
+        expr.type = left_type.base
+      elsif (left_type.is_a? Value::Type) and left_type.form == :hash
+        check(env, expr.right, left_type.base[0])
+        expr.type = left_type.base[1]
+      else
+        error(expr.loc, "don't know how to index into type: #{left_type}")
+      end
+#      pp left_type
+#      raise "array access"
+    else
+      optype = BinaryOps[expr.op][vtype]
+      error(expr.loc, "unknown binary operation '#{expr.op}' for operand type #{vtype}") if optype.nil?
+      check(env, expr.left,  optype[0])
+      check(env, expr.right, optype[1])
+      expr.type = optype.last
+    end
+    pp expr
+    expr.type
   end
 end
index 5fb84945954392274766c26c614573e6407b787b..15e6560b39a68165efe6055e1e6c185f3534b8b8 100644 (file)
@@ -2,7 +2,7 @@
 # Data Type Representation Module
 ##
 module Value
-  # Forms: :bool, :int, :float, :void, :array, :record, :string, :proc
+  # Forms: :bool, :int, :float, :void, :array, :hash, :record, :string, :proc
   Type  = Struct.new(:form, :fields, :base, :size)
   Field = Struct.new(:type, :offset)