From: Michael D. Lowis Date: Tue, 29 Oct 2024 04:10:10 +0000 (-0400) Subject: started implementing records X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=218f38592544a30346faed2e3a5198bc604fecf4;p=proto%2Fcerise-c.git started implementing records --- diff --git a/Int.c b/Int.c index 1e8de55..af75d41 100644 --- a/Int.c +++ b/Int.c @@ -2,7 +2,8 @@ int Foo_Bar(); -int Int_SumInts(int a, int b); +typedef int Int_T; +int Int_SumInts(int, int); int Int_SumInts(int a, int b) { int _t0 = (a + b); diff --git a/Int.m b/Int.m index 12ae3f6..71cd364 100644 --- a/Int.m +++ b/Int.m @@ -6,6 +6,8 @@ imports ( exports (SumInts) +T is int + SumInts(a : int, b : int) : int { return a + b + Foo.Bar() diff --git a/Records.c b/Records.c new file mode 100644 index 0000000..9ca090e --- /dev/null +++ b/Records.c @@ -0,0 +1,12 @@ +#include + +typedef struct Records_Rec1 Records_Rec1; +typedef struct Records_Rec2 Records_Rec2; + +struct Records_Rec1 { + int foo; +}; +struct Records_Rec2 { + Records_Rec1 _base; + int bar; +}; diff --git a/Records.m b/Records.m new file mode 100644 index 0000000..31efe66 --- /dev/null +++ b/Records.m @@ -0,0 +1,9 @@ +module Records + +Rec1 is record { + foo = int +} + +Rec2 is record (Rec1) { + bar = int +} \ No newline at end of file diff --git a/cerise-c.m b/cerise-c.m index 1c1afd4..710527a 100644 --- a/cerise-c.m +++ b/cerise-c.m @@ -1,11 +1,14 @@ module TestSuite imports ( - "Int.m" + "Int.m", + "Records.m" ) exports (Main) +Foo is record {} + TestBoolOps() { assert true == true @@ -90,10 +93,10 @@ TestArrayOps() { def array = [1,2,3] def item = 42 -# set array[0] = item -# assert array[0] == 42 -# assert array[1] == 2 -# assert array[2] == 3 + set array[0] = item + assert array[0] == 42 + assert array[1] == 2 + assert array[2] == 3 # assert Length(array) == 3 } diff --git a/cerise-c.rb b/cerise-c.rb index 6e2a3c0..4bf7db7 100755 --- a/cerise-c.rb +++ b/cerise-c.rb @@ -18,6 +18,8 @@ # * Add optimization of constant operators # * Add increment/decrement optimization # * Implement tail call optimization? +# +# * add type checking for empty array literals require 'stringio' require 'strscan' diff --git a/lib/codegen.rb b/lib/codegen.rb index c5e67ee..45a4fab 100644 --- a/lib/codegen.rb +++ b/lib/codegen.rb @@ -19,6 +19,8 @@ class Codegen "_string" elsif type.is_a? Symbol type + elsif type.is_a? IR::Var + "#{type.module || @parser.module}_#{type.name}" elsif type.form == :array "_array" elsif type.form == :hash @@ -28,38 +30,90 @@ class Codegen end end + def declare_sym(sym) + if sym.kind == :type + type = sym.value + if type.form == :alias + puts "typedef #{type_to_s(type.base)} #{symname(sym)};" + elsif type.form == :record + puts "typedef struct #{symname(sym)} #{symname(sym)};" + else + raise "unimplemented" + end + elsif sym.kind == :func + args = sym.type[0..-2].map{|e| type_to_s(e) }.join(", ") + puts "#{type_to_s(sym.type.last)} #{symname(sym)}(#{args});" + else + puts "// #{sym.name}" + end + end + + def define_sym(sym) + if sym.kind == :type + define_type(sym) + elsif sym.kind == :func + define_func(sym) + else + puts "// #{sym.name}" + end + end + + def define_type(sym) + type = sym.value + if type.form == :record + puts "struct #{symname(sym)} {" + if (type.base) + puts " #{type_to_s(type.base)} _base;" + end + type.fields.each do |k,v| + puts " #{type_to_s(v)} #{k};" + end + puts "};" + end + end + + def define_func(sym) + args = sym.value.args.map{|e| "#{type_to_s(e.type)} #{e.name}" }.join(", ") + puts "#{type_to_s(sym.type.last)} #{symname(sym)}(#{args}) {" + @syms.open_scope + @locals = sym.value.locals + sym.value.args.each_with_index do |name, idx| + @syms.add_sym(name.name, name.loc, :param, name.type, idx) + end + sym.value.body.each do |stmnt| + emit(stmnt) + end + puts "}\n\n" + @syms.close_scope + end + + def output(outpath) @output = File.open(outpath, "wb") puts "#include \n\n" - (@parser.imported_modules || {}).values.each do |mod| + + # output imported symbol declarations + imported_mods = (@parser.imported_modules || {}).values + imported_mods.each do |mod| @syms[mod[:name]].type.exports.each do |name, val| - args = val.type[0..-2].map{|e| type_to_s(e) }.join(", ") - puts "#{type_to_s(val.type.last)} #{symname(val)}(#{args});" + declare_sym(val) end puts "" end - @syms.each do |name, val| - args = val.value.args.map{|e| "#{type_to_s(e.type)} #{e.name}" }.join(", ") - puts "#{type_to_s(val.type.last)} #{symname(val)}(#{args});" - end - puts "" +# # output imported record declarations +# imported_mods.each do |mod| +# @syms[mod[:name]].type.exports.each do |name, val| +# define_sym(val) if val.kind == :type +# end +# puts "" +# end - @syms.each do |name, val| - args = val.value.args.map{|e| "#{type_to_s(e.type)} #{e.name}" }.join(", ") - puts "#{type_to_s(val.type.last)} #{symname(val)}(#{args}) {" - @syms.open_scope - @locals = val.value.locals - val.value.args.each_with_index do |name, idx| - @syms.add_sym(name.name, name.loc, :param, name.type, idx) - end - val.value.body.each do |stmnt| - emit(stmnt) - end - puts "}\n\n" - @syms.close_scope - end + # output this modules symbol declarations + @syms.each { |name, val| declare_sym(val) } + puts "" + @syms.each {|name, val| define_sym(val) } @output.close end diff --git a/lib/compiler.rb b/lib/compiler.rb index 55d2cb4..1c1308a 100644 --- a/lib/compiler.rb +++ b/lib/compiler.rb @@ -29,7 +29,7 @@ module Compiler end def self.dependency_list(imports) - imports.map {|k,v| dependency_list(v[:deps]) + [v[:out_path]] } + (imports || {}).map {|k,v| dependency_list(v[:deps]) + [v[:out_path]] } end def self.compile_and_link(path) diff --git a/lib/lexer.rb b/lib/lexer.rb index 5505280..34b076c 100644 --- a/lib/lexer.rb +++ b/lib/lexer.rb @@ -33,17 +33,19 @@ class Lexer STRING = /"(\\"|[^"])*"/ CHAR = /'(\\'|[^'])'/ ID_TYPES = { - "true" => :bool, - "false" => :bool, - "if" => :if, - "else" => :else, - "def" => :def, - "set" => :set, - "return" => :return, - "assert" => :assert, - "module" => :module, + "true" => :bool, + "false" => :bool, + "if" => :if, + "is" => :is, + "else" => :else, + "def" => :def, + "set" => :set, + "return" => :return, + "assert" => :assert, + "module" => :module, "imports" => :imports, - "exports" => :exports + "exports" => :exports, + "record" => :record } def next diff --git a/lib/parser.rb b/lib/parser.rb index 3ded9bf..a33731b 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -19,7 +19,7 @@ class Parser syms = SymTable.new syms.module_name = @module @syms.globals.each do |k,v| - syms.add_sym(k, v.loc, v.kind, v.type, nil) + syms.add_sym(k, v.loc, v.kind, v.type, (v.kind == :func ? nil : v.value)) end syms end @@ -122,8 +122,8 @@ class Parser kind = :var elsif mod_def.value.is_a? IR::Func kind = :func -# elsif mod_def.value.is_a? IR::Type -# kind = :type + elsif mod_def.value.is_a? Value::Type + kind = :type else raise "invalid toplevel definition #{mod_def.value.class}" end @@ -182,6 +182,8 @@ class Parser ident = identifier() if matches("(") then val = function_definition(ident) + elsif accept(:is) + val = type_definition(ident) elsif matches("=") then val = constant_definition(ident) else @@ -196,6 +198,8 @@ class Parser defs = [] if accept(":") func.type = type_specifier() + else + func.type = :void end expect("{") while matches(:def) @@ -210,6 +214,32 @@ class Parser func end + def type_definition(name) + if accept(:record) + base_type = nil + if accept("(") + base_type = identifier() + expect(")") + end + type = Value::Type.new(:record, {}, base_type, 0) + expect("{") + while not matches("}") + name = expect(:ident).text.to_sym + if type.fields[name] + error("field #{name} already defined in record") + end + expect("=") + type.fields[name] = type_specifier() + end + # parse fields here + expect("}") + else + type = type_specifier() + type = Value::Type.new(:alias, nil, type, 0) + end + type + end + def constant_definition(name) error("constant definitions not yet supported") end @@ -219,9 +249,8 @@ class Parser expect("(") while !matches(")") id = identifier() - if accept(":") - id.type = type_specifier() - end + expect(":") + id.type = type_specifier() args << id expect(",") if !matches(")") end @@ -235,10 +264,16 @@ class Parser end def type_specifier() - expect(:ident).text.to_sym + if accept("[") + base_type = type_specifier() + type = Value::Type.new(:array, nil, base_type, 0) + expect("]") + else + type = expect(:ident).text.to_sym + end + type end - ## # Statements ## diff --git a/lib/type_checker.rb b/lib/type_checker.rb index 6a55031..a4eba9d 100644 --- a/lib/type_checker.rb +++ b/lib/type_checker.rb @@ -94,6 +94,7 @@ class TypeChecker @parser = parser env = parser.syms parser.symbols.each do |sym, data| + next if env[sym].kind == :type env[sym].type = infer(env, env[sym].value) end end