From e5b72abe00757066e461def268815268e80005d3 Mon Sep 17 00:00:00 2001 From: Mike Lowis Date: Thu, 21 Nov 2024 15:50:39 -0500 Subject: [PATCH] added records and record constructors. Code generation for record construction still needs to be sorted. Next up is record field access and assignment --- Int.m | 2 +- Records.c | 1 + Records.m | 1 + cerise-c.m | 9 ++++++++- lib/codegen.rb | 24 ++++++++++++++++++++---- lib/lexer.rb | 3 ++- lib/parser.rb | 22 ++++++++++++++++++++-- lib/type_checker.rb | 40 +++++++++++++++++++++++++++++++++++----- 8 files changed, 88 insertions(+), 14 deletions(-) diff --git a/Int.m b/Int.m index 71cd364..22b553d 100644 --- a/Int.m +++ b/Int.m @@ -10,5 +10,5 @@ T is int SumInts(a : int, b : int) : int { - return a + b + Foo.Bar() + return a + b + Foo:Bar() } diff --git a/Records.c b/Records.c index 9ca090e..8838061 100644 --- a/Records.c +++ b/Records.c @@ -9,4 +9,5 @@ struct Records_Rec1 { struct Records_Rec2 { Records_Rec1 _base; int bar; + Records_Rec2* baz; }; diff --git a/Records.m b/Records.m index 31efe66..614fe02 100644 --- a/Records.m +++ b/Records.m @@ -6,4 +6,5 @@ Rec1 is record { Rec2 is record (Rec1) { bar = int + baz = Rec2 } \ No newline at end of file diff --git a/cerise-c.m b/cerise-c.m index 710527a..05d842f 100644 --- a/cerise-c.m +++ b/cerise-c.m @@ -188,6 +188,13 @@ TestIfBlocks(){ } } +TestRecords(){ + def foo = new Records:Rec1 { foo = 42 } +# assert foo.foo == 42 +# set foo.foo = 43 +# assert foo.foo == 43 +} + AddTwoNums(a : int, b : int) { return a + b @@ -209,5 +216,5 @@ Main() TestGtEqOps() TestNeqOps() TestIfBlocks() - Int.SumInts(1,2) + Int:SumInts(1,2) } diff --git a/lib/codegen.rb b/lib/codegen.rb index aa259d0..ec09759 100644 --- a/lib/codegen.rb +++ b/lib/codegen.rb @@ -15,9 +15,6 @@ class Codegen end def type_to_s(type) - # TODO: lookup fully qualified type here or make sure it's a var? - - if type == :string "_string" elsif type.is_a? Symbol @@ -63,6 +60,24 @@ class Codegen end end + def reference_type?(type) + if type == :string + true + elsif type.is_a? Symbol + false + elsif type.is_a? IR::Var + if (not type.module) || (type.module == @parser.module) + reference_type?(@syms[type.name].value || @syms[type.name].type) + else + reference_type?(@syms[type.module].exports[env.name].type) + end + elsif [:array, :hash, :record].include? type.form + true + else + false + end + end + def define_type(sym) type = sym.value if type.form == :record @@ -71,7 +86,8 @@ class Codegen puts " #{type_to_s(type.base)} _base;" end type.fields.each do |k,v| - puts " #{type_to_s(v)} #{k};" + is_ref = reference_type?(v) + puts " #{type_to_s(v)}#{is_ref ? "*" : ""} #{k};" end puts "};" end diff --git a/lib/lexer.rb b/lib/lexer.rb index 34b076c..8f2f746 100644 --- a/lib/lexer.rb +++ b/lib/lexer.rb @@ -45,7 +45,8 @@ class Lexer "module" => :module, "imports" => :imports, "exports" => :exports, - "record" => :record + "record" => :record, + "new" => :new, } def next diff --git a/lib/parser.rb b/lib/parser.rb index af77572..6d4e31d 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -262,7 +262,7 @@ class Parser def qualified_identifier() tok = expect(:ident) varname = IR::Var.new(tok.pos, nil, tok.text.to_sym, nil) - if accept(".") + if accept(":") mod = syms[varname.name] if mod.nil? error("no such module: '#{varname.name}'") @@ -488,7 +488,9 @@ class Parser end def const_or_ident() - if matches("[") + if matches(:new) + record_literal() + elsif matches("[") array_literal() elsif matches("{") hash_literal() @@ -518,6 +520,22 @@ class Parser end end + def record_literal() + expect(:new) + name = qualified_identifier() + op = IR::Op.new(name.loc, nil, :new, name, {}) + expect("{") + while !matches("}") + fname = identifier() + expect("=") + value = expression() + op.right[fname.name] = value + expect(",") if not matches("}") + end + expect("}") + op + end + def array_literal() exprs = [] expect("[") diff --git a/lib/type_checker.rb b/lib/type_checker.rb index 899be5d..69f3085 100644 --- a/lib/type_checker.rb +++ b/lib/type_checker.rb @@ -143,6 +143,17 @@ class TypeChecker end end + def lookup_type(env, type) + if type.is_a? IR::Var + if type.module.nil? + type = env[type.name].value + else + type = env[type.module].type.exports[type.name].value + end + end + type + end + def check(env, expr, type) # if type.is_a? IR::Var # type = typename(type) @@ -307,12 +318,32 @@ class TypeChecker def infer_op(env, expr) # infer the operand type first - vtype = infer(env, expr.left) - if (expr.left and expr.right) - check_binary(env, expr, vtype) + if expr.op == :new + infer_new(env, expr) else - check_unary(env, expr, vtype) + vtype = infer(env, expr.left) + if (expr.left and expr.right) + check_binary(env, expr, vtype) + else + check_unary(env, expr, vtype) + end + end + end + + def infer_new(env, expr) + type = lookup_type(env, expr.left) + if type.nil? + error(expr.loc, "unknown record type: #{expr.left}") + end + expr.right.each do |field, value| + field_type = type.fields[field] + if field_type.nil? + error(expr.loc, "record type #{expr.left} has no such field '#{k}'") + end + check(env, value, field_type) + value.type = field_type end + expr.type = expr.left end def infer_call(env, expr) @@ -356,7 +387,6 @@ class TypeChecker end end - def check_func(env, expr, type) raise "unimplemented" error(expr.loc, "unimplemented") -- 2.54.0