From: Michael D. Lowis Date: Thu, 8 Oct 2020 20:44:55 +0000 (-0400) Subject: fixed free variable detection and started working on code generation X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=973dce22175ba1f0635b0f0cb92b3346bfcd40bc;p=proto%2Fsclpl-rb.git fixed free variable detection and started working on code generation --- diff --git a/lib/dyn.rb b/lib/dyn.rb index 2529218..2c4bc79 100755 --- a/lib/dyn.rb +++ b/lib/dyn.rb @@ -1,12 +1,20 @@ #!/usr/bin/env ruby # TODO: -# * Implement detection of free variables in lambdas # * Implement expression block syntax # * Add support for structs # * Add support for checked unions # * Add support for arrays # * Implement module system for namespacing # * Implement generic/template system or parametric polymorphism +# +# Atomic Types: +# bool X +# char +# int X +# float / +# byte +# intset + require 'strscan' $debug = false @@ -25,17 +33,18 @@ def error(loc, msg) end class SymTable < Hash - attr_accessor :is_func + attr_accessor :type attr_accessor :freevars def initialize(parent = nil) @parent = (parent || {}) - @is_func = false @freevars = [] end - def clone - SymTable.new(self) + def clone(type = :block) + s = SymTable.new(self) + s.type = type + s end def [](key) @@ -46,7 +55,7 @@ class SymTable < Hash method(:[]).super_method.call(key) end - def defined?(key) + def defined_ever?(key) (not (self[key] || {})[:type].nil?) end @@ -71,7 +80,7 @@ class SymTable < Hash def local?(key) if @parent.class == Hash false - elsif is_func + elsif @type == :func block_local? key else @parent.local? key @@ -79,7 +88,7 @@ class SymTable < Hash end def free?(key) - (defined? key) and (not local? key) and (not global? key) + (defined_ever? key) and (not local? key) and (not global? key) end def merge!(env) @@ -117,7 +126,7 @@ class Lexer "else" => :else, "end" => :end, "let" => :let, - "fun" => :fun, + "func" => :fun, } attr_accessor :file @@ -561,14 +570,13 @@ module TypeChecker end def self.check_func(env, expr, type) - env = env.clone + env = env.clone(:func) error(expr.loc, "wrong number of arguments for function definition. expected #{type.length-1}, got #{expr.args.length}") if type.length-1 != expr.args.length type[0..-2].each_with_index do |t, i| env[expr.args[i].name] = { :loc => expr.loc, :type => t } end itype = infer_block(env, expr.body, false) expr.freevars = env.freevars - pp expr error(expr.loc, "expected #{type}, received #{itype}") if itype != type.last type.last end @@ -607,7 +615,7 @@ module TypeChecker end def self.infer_symbol(env, expr) - error(expr.loc, "undefined symbol '#{expr.name}'") if not env.defined? expr.name + error(expr.loc, "undefined symbol '#{expr.name}'") if not env.defined_ever? expr.name env.freevars << expr.name if env.free? expr.name expr.type = env[expr.name][:type] end @@ -644,8 +652,6 @@ module TypeChecker end end env.freevars = (env.freevars + newenv.freevars).uniq - pp "free", env.freevars - pp env.stack types.last end @@ -731,23 +737,38 @@ class Codegen name end + +# Ident = Struct.new(:loc, :type, :name) +# Val = Struct.new(:loc, :type, :value) +# Def = Struct.new(:loc, :type, :name, :value) +# Func = Struct.new(:loc, :type, :args, :body, :freevars) +# Call = Struct.new(:loc, :type, :func, :args) +# IfExpr = Struct.new(:loc, :type, :cond, :br1, :br2) +# Ann = Struct.new(:loc, :type, :expr) +# Block = Struct.new(:loc, :type, :exprs) + + def emit(out, e, var = nil) val = nil type = e.type +# puts 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::Def + val = emit_def(out, e) + type = e.value.type +# 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) +# 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 @@ -762,11 +783,20 @@ class Codegen e.value.to_s when :int e.value.to_s + when :float + e.value.to_s when :string e.value.to_s + else + raise "unknown value type: #{e.value.to_s}" end end + def emit_def(out, e) + emit_var(out, e.name.name, e.value.type, emit(out, e.value)) + end + + def emit_call(out, e) if e.func.is_a? String emit_opcall(out, e) @@ -784,66 +814,72 @@ class Codegen 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 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 end STRING = <<-eos - +123 +123.0 # Fix emit +"" +true +false a = 123 - -foo : int -> int -foo = fun(b){ - d = 123 - bar : int -> int - bar = fun(c){ - a + b + c - } - bar(1) - d - e = 1 - e - d -} +b = (123 < 321) +#a = 123 +# +#foo : int -> int +#foo = func(b){ +# d = 123 +# bar : int -> int +# bar = func(c){ +# a + b + c +# } +# bar(1) +# d +# e = 1 +# e +# d +#} eos @@ -853,4 +889,5 @@ eos tree = Parser.new.parse_string(STRING) TypeChecker.infer_block(SymTable.new, tree, false) +puts Codegen.new(tree)