From: Michael D. Lowis Date: Fri, 3 Jul 2020 19:27:34 +0000 (-0400) Subject: complete type inferrer/checker but buggy code generation and symbol table implementation X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=43e85a81ce487c18fefe3fad8229fe7d391720be;p=proto%2Fsclpl-rb.git complete type inferrer/checker but buggy code generation and symbol table implementation --- diff --git a/dyn.rb b/dyn.rb index f286d3f..92cd2be 100755 --- a/dyn.rb +++ b/dyn.rb @@ -2,12 +2,12 @@ require 'strscan' -$debug = true +$debug = false def error(loc, msg) lines = File.read(loc[0])[0..(loc[1])].split("\n") $stderr.puts "#{loc[0]}:#{lines.length}: #{msg}" -# raise "" if $debug + raise "" if $debug # $stderr.puts "#{lines.last}" # $stderr.puts (" " * lines.last.length) + "^" exit 1 @@ -292,6 +292,7 @@ class Parser end def type_name() + loc = location() type = expect(:ident).text.to_sym if peek().text == "->" then type = [type] @@ -300,9 +301,16 @@ class Parser type << expect(:ident).text.to_sym end end + verify_type(loc, type) type end + def verify_type(loc, type) + if type.is_a? Array and (type.length > 2 and type[0..-2].include? :void) + error("invalid use of void type", loc) + end + end + def unary() Call.new(location(), nil, @prev.type, [parseLevel(:unary)]) end @@ -457,7 +465,7 @@ module TypeChecker etype = infer(env, expr) error(expr.loc, "expected #{type}, recieved #{etype}") if type != etype end - type + expr.type = type end def self.check_ifexpr(env, expr, type) @@ -475,10 +483,11 @@ module TypeChecker end def self.check_call(env, expr, type) + error(expr.loc, "object being applied is not a function (has type: #{type.to_s})") if not type.is_a? Array type[0..-2].each_with_index do |t,i| check(env, expr.args[i], t) end - type.last + expr.type = type.last end def self.infer(env, expr) @@ -506,7 +515,7 @@ module TypeChecker def self.infer_symbol(env, expr) error(expr.loc, "undefined symbol '#{expr.name}'") if env[expr.name].nil? or not env[expr.name][:set!] error(expr.loc, "symbol '#{expr.name}' has unknown type") if env[expr.name][:type].nil? - env[expr.name][:type] + expr.type = env[expr.name][:type] end def self.infer_annotation(env, expr) @@ -516,7 +525,7 @@ module TypeChecker def self.infer_block(env, expr) env.merge!(expr.syms) types = expr.exprs.map {|e| infer(env, e) } - expr.type = types.last + types.last end def self.infer_call(env, expr) @@ -532,16 +541,16 @@ module TypeChecker def self.infer_def(env, expr) name = expr.args[0].name - expr.type = env[name][:type] - if (expr.type) + type = env[name][:type] + if (type) error(expr.loc, "symbol '#{name}' is multiply defined in scope") if env[name][:set!] - check(env, expr.args[1], expr.type) + check(env, expr.args[1], type) else - expr.type = infer(env, expr.args[1]) - env[name][:type] = expr.type + type = infer(env, expr.args[1]) + env[name][:type] = type end env[name][:set!] = true - :void + expr.type = :void end def self.infer_opcall(env, expr) @@ -564,14 +573,194 @@ module TypeChecker end end +class Codegen + def initialize(block) + @temp = 0 + @func = 0 + @protos = StringIO.new + @funcs = StringIO.new +# gen_protos(block.syms) + @funcs.puts emit_toplevel(block) + end + + def to_s() + @protos.string + "\n" + @funcs.string + end + + def tempvar() + "__tmp#{@temp +=1}" + end + + def mkfunc() + "__func#{@temp +=1}" + end + + def type_to_s(type, name) + if type.is_a? Array + raise "unsupported" + else + "#{type.to_s} #{name}" + end + end + + def emit_toplevel(block) + out = StringIO.new + out.puts "void toplevel(void)" + lastexpr = nil + out.puts "{" + block.exprs.each do |e| + lastexpr = emit(out, e) + end + out.puts "}" + out.string + end + + def emit_var(out, name, type, val = nil) + if type.is_a? Symbol or type.is_a? String + out.print "#{type.to_s} #{name}" + out.puts (val ? " = #{val}" : "") + ";" + elsif type.is_a? Array + out.puts "#{type.last} (*#{name})(#{type[0..-2].join(",")}) = #{val};" + else + raise "unsupported" + end + name + end + + def emit(out, e, var = nil) + val = nil + type = e.type + if e.is_a? Parser::Val + val = emit_val(out, e) + elsif e.is_a? Parser::Call and e.func == :set! + var = e.args[0].name + type = e.args[1].type + val = emit(out, e.args[1]) + elsif e.is_a? Parser::Call + val = emit_call(out, e) + elsif e.is_a? Parser::IfExpr + val = emit_ifexpr(out, e) + elsif e.is_a? Parser::Block + val = emit_block(out, e) + elsif e.is_a? Parser::Func + val = emit_func(out, e) + else + raise "unsupported" + end + var ||= tempvar() + emit_var(out, var, type, val) + var + end + + def emit_val(out, e) + case e.type + when :bool + e.value.to_s + when :int + e.value.to_s + when :string + e.value.to_s + end + end + + def emit_call(out, e) + if e.func.is_a? String + emit_opcall(out, e) + else + raise "unsupported" + end + end + + def emit_opcall(out, e) + args = e.args.map {|a| emit(out, a) } + if args.length == 2 + "#{args[0]} #{e.func} #{args[1]}" + else + "#{e.func}#{args[0]}" + end + end + + def emit_ifexpr(out, e) + cond = emit(out, e.cond) + var = tempvar() + emit_var(out, var, e.type) + out.puts "if (#{cond}) {" + out.puts "#{var} = #{emit(out, e.br1)};" + out.puts "} else {" + out.puts "#{var} = #{emit(out, e.br2)};" + out.puts "}" + var + end + + def emit_block(out, e) + var = tempvar() + emit_var(out, var, e.type) + out.puts "{" + exprs = e.exprs.map do |e| + emit(out, e) + end + out.puts "#{var} = #{exprs.last};" + out.puts "}" + var + end + + def emit_func(out, e) + newout = StringIO.new + var = mkfunc() + args = [] + e.type[0..-2].each_with_index do |t,i| + args << type_to_s(t, e.args[i].name) + end + @protos.puts "#{e.type.last} #{var}(#{args.join(", ")});" + newout.puts "#{e.type.last} #{var}(#{args.join(", ")}) {" + exprs = e.body.exprs.map do |e| + emit(newout, e) + end + newout.puts "return #{exprs.last};" if (e.type.last != :void) + newout.puts "}\n\n" + @funcs.puts newout.string + var + end + +# def get_vars(bsyms) +# syms = {} +# bsyms.each do |k,v| +# syms[v[:type]] ||= [] +# syms[v[:type]] << k +# end +# syms +# end +# +# def gen_protos(syms) +# get_vars(syms).each do |k,v| +# if k.is_a? Symbol +# @protos.puts "#{k.to_s} #{v.join(", ")};" +# else +# v.each do |name| +# @protos.puts "#{k.last} #{name}(#{k[0..-2].join(", ")});" +# end +# end +# end +# end +end + block = Parser.new("dyn.src").toplevel -pp TypeChecker.infer(SymTable.new, block) +block.type = TypeChecker.infer(SymTable.new, block) +puts Codegen.new(block) +#gen = Codegen.new +#gen << block +#pp block + +syms = {} +block.syms.each do |k,v| + syms[v[:type]] ||= [] + syms[v[:type]] << k +end +#pp syms # TODO: -# * Check for correct usage of void on function types and elsewhere... # * Add support for structs # * Add support for checked unions # * Add support for arrays -# * Implement proper scoping and shadowing of identifiers # * Implement module system for namespacing # * Implement generic/template system for polymorphism \ No newline at end of file diff --git a/dyn.src b/dyn.src index 2292eed..5341526 100644 --- a/dyn.src +++ b/dyn.src @@ -1,39 +1,45 @@ -# constants -true -false -123 -123.0 -"foo" +## constants +#true +#false +#123 +#123.0 +#"foo" +# +## operators +#a = (+1) +#b = (-1) +#1 + 1 +#2 - 2 +#3 * 3 +#4 / 4 +##5 % 5 +# +## conditionals +#if (true) { +# 1 +#} else { +# 2 +#} +# +#if (true) 1 else 2 + -# operators -a = (456 + 1) -b = (456 - 1) -c = (456 * 1) -d = (456 - 1) -e = (-1) -f = (+1) -# conditionals -if (true) { - 1 -} else if (true) { - 2 -} else { - 3 -} -foo1 : int -> int -foo1 = fun(z) { - a= 42 -# z = 1 -# b = z - 42 -} + +#foo1 : int -> int +#foo1 = fun(z) { +# a = 42 +## z = 1 +## b = z +# 42 +#} #add : int -> int -#add = fun(a,b) { +#add = fun(a, b) { # a + b #} + ## OR: #add : int -> int #add = fun(a,b) -> a + b