]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
complete type inferrer/checker but buggy code generation and symbol table implementation
authorMichael D. Lowis <mike@mdlowis.com>
Fri, 3 Jul 2020 19:27:34 +0000 (15:27 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Fri, 3 Jul 2020 19:27:34 +0000 (15:27 -0400)
dyn.rb
dyn.src

diff --git a/dyn.rb b/dyn.rb
index f286d3f22a46ace73157661e7c6dcaec1bb35981..92cd2bebb77d32c4420214e2985ae5a8cdddda69 100755 (executable)
--- 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 2292eed6f13b7779b83b8a2aa64aa7251290bb55..534152660f7c92f3d5c251ccce53f4d77d8b21db 100644 (file)
--- 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