]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
corrected generation of globals
authorMichael D. Lowis <mike@mdlowis.com>
Thu, 15 Oct 2020 02:49:46 +0000 (22:49 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Thu, 15 Oct 2020 02:49:46 +0000 (22:49 -0400)
lib/dyn.rb

index b7f9699fe586c7bb58623f02d440b61de193a641..34d06ec4733448c22443ded39c638d64d7f52f23 100755 (executable)
@@ -1,6 +1,5 @@
 #!/usr/bin/env ruby
 # TODO:
-#   * Implement expression block syntax
 #   * Add support for structs
 #   * Add support for checked unions
 #   * Add support for arrays
@@ -38,7 +37,7 @@ class SymTable < Hash
 
   def initialize(parent = nil)
     @parent = (parent || {})
-    @freevars = []
+    @freevars = {}
   end
 
   def clone(type = :block)
@@ -245,7 +244,7 @@ class Parser
     :byte   => { prefix: :constant,   infix: nil,         level: :none    },
   }
 
-  Ident = Struct.new(:loc, :type, :name)
+  Ident = Struct.new(:loc, :type, :name, :isfree)
   Val    = Struct.new(:loc, :type, :value)
   Def    = Struct.new(:loc, :type, :name, :value)
   Func   = Struct.new(:loc, :type, :args, :body, :freevars)
@@ -329,8 +328,8 @@ class Parser
     LVLNAMES[index + 1]
   end
 
-  def variable()
-    Ident.new(location(), nil, @prev.text.to_sym)
+  def variable(isfree = false)
+    Ident.new(location(), nil, @prev.text.to_sym, isfree)
   end
 
   def definition(name)
@@ -346,7 +345,7 @@ class Parser
     exprs = []
     expect("(")
     while (!matches(")"))
-      args << Ident.new(loc, nil, expect(:ident).text.to_sym)
+      args << Ident.new(loc, nil, expect(:ident).text.to_sym, false)
       expect(",") if not matches(")")
     end
     expect(")")
@@ -616,7 +615,10 @@ module TypeChecker
 
   def self.infer_symbol(env, expr)
     error(expr.loc, "undefined symbol '#{expr.name}'") if not env.defined_ever? expr.name
-    env.freevars << expr.name if env.free? expr.name
+    if env.free? expr.name
+      env.freevars[expr.name] = expr.type
+      expr.isfree = true
+    end
     expr.type = env[expr.name][:type]
   end
 
@@ -651,7 +653,7 @@ module TypeChecker
         infer(newenv, e)
       end
     end
-    env.freevars = (env.freevars + newenv.freevars).uniq
+    env.freevars = env.freevars.merge(newenv.freevars)
     types.last
   end
 
@@ -713,13 +715,32 @@ class Codegen
     end
   end
 
+  def emit_global(out, name, type, val = nil)
+    if type.is_a? Symbol or type.is_a? String
+      val = emit(out, val)
+      @protos.puts "#{type.to_s} #{name};"
+      out.puts "#{name}" +(val ? " = #{val}" : "") + ";"
+    elsif type.is_a? Array
+      @protos.puts "#{type.last} #{name}(#{type[0..-2].join(",")});"
+      emit_func(@funcs, val, name)
+    else
+      raise "unsupported var"
+    end
+    name
+  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) unless e.is_a? Parser::Ann
+      if e.is_a? Parser::Def
+        lastexpr = emit_global(out, e.name.name, e.value.type, e.value)
+      elsif not e.is_a? Parser::Ann
+        lastexpr = emit(out, e)
+      end
     end
     out.puts "}"
     out.string
@@ -754,7 +775,7 @@ class Codegen
     elsif e.is_a? Parser::Func
       val = emit_func(out, e)
     elsif e.is_a? Parser::Ident
-      val = e.name
+      val = (e.isfree ? "__env->" : "") + e.name.to_s
     else
       raise "unsupported expression"
     end
@@ -825,15 +846,22 @@ class Codegen
     var
   end
 
-  def emit_func(out, e)
+  def emit_func(out, e, var = nil)
     newout = StringIO.new
-    var = mkfunc()
+    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(", ")}) {"
+    if e.freevars.length > 0
+      newout.puts "struct {"
+      e.freevars.each do |k,v|
+        newout.puts type_to_s(v, k) + ";"
+      end
+      newout.puts "}* __env = Env;"
+    end
     exprs = e.body.exprs.map do |e|
       emit(newout, e) unless e.is_a? Parser::Ann
     end
@@ -841,7 +869,7 @@ class Codegen
     newout.puts "}\n\n"
     @funcs.puts newout.string
     if e.freevars.length > 0 then
-      "MKCLOSURE(#{var},#{e.freevars.length},#{e.freevars.join(", ")})"
+      "MKCLOSURE(#{var},#{e.freevars.length},#{e.freevars.keys.join(", ")})"
     else
       var
     end
@@ -900,7 +928,7 @@ puts <<-eos
 #include <stdbool.h>
 
 typedef struct string {
-    size_t offset; \
+    size_t offset;
     size_t nelems;
     char* elems;
 }* string;
@@ -910,9 +938,18 @@ static inline void* MKCLOSURE(void* fn, int nfree, ...)
     return NULL;
 }
 
-#define STRINGLIT(str) \
+#define STRINGLIT(str) \\
     &(struct string){ .offset = 0, .nelems = sizeof(str)-1, .elems = str }
 
+void* Env;
+
+int main(int argc, char** argv)
+{
+    extern void toplevel(void);
+    toplevel();
+    return 0;
+}
+
 eos
 puts Codegen.new(tree)