]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
fixed free variable detection and started working on code generation
authorMichael D. Lowis <mike.lowis@gentex.com>
Thu, 8 Oct 2020 20:44:55 +0000 (16:44 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Thu, 8 Oct 2020 20:44:55 +0000 (16:44 -0400)
lib/dyn.rb

index 2529218193980ea83361b942fc05120c09e63272..2c4bc799a4fcfed3c6478d406dad020e3e23e683 100755 (executable)
@@ -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)