]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
added symbol table to correctly handle scoping
authorMichael D. Lowis <mike@mdlowis.com>
Sat, 27 Jun 2020 01:53:26 +0000 (21:53 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Sat, 27 Jun 2020 01:53:26 +0000 (21:53 -0400)
dyn.rb
dyn.src

diff --git a/dyn.rb b/dyn.rb
index 56c07617eaf70c4afab1713ebeeb0bdc795534b3..f286d3f22a46ace73157661e7c6dcaec1bb35981 100755 (executable)
--- a/dyn.rb
+++ b/dyn.rb
@@ -4,14 +4,53 @@ require 'strscan'
 
 $debug = true
 
+def error(loc, msg)
+  lines = File.read(loc[0])[0..(loc[1])].split("\n")
+  $stderr.puts "#{loc[0]}:#{lines.length}: #{msg}"
+#  raise "" if $debug
+#    $stderr.puts "#{lines.last}"
+#    $stderr.puts (" " * lines.last.length) + "^"
+  exit 1
+end
+
 class SymTable < Hash
   def initialize(parent = nil)
-    @parent = parent
+    @parent = (parent || {})
   end
 
   def clone
     SymTable.new(self)
   end
+
+  def [](key)
+    (super(key) || @parent[key])
+  end
+
+  def []=(key, value)
+    existing = method(:[]).super_method.call(key)
+    if (not existing.nil?) and existing[:set!]
+      error(value[:loc], "symbol '#{key}' is multiply defined in scope")
+    end
+    super(key,value)
+  end
+
+  def local?(key)
+    (not method(:[]).super_method.call(key).nil?)
+  end
+
+  def global?(key)
+    if @parent.class == Hash
+      local?(key)
+    elsif local? key
+      false
+    else
+      @parent.global? key
+    end
+  end
+
+  def merge!(env)
+    env.each {|k,v| self[k] = v }
+  end
 end
 
 class Lexer
@@ -409,15 +448,6 @@ module TypeChecker
     "|"  => { int: [:int, :int, :int] },
   }
 
-  def self.error(loc, msg)
-    lines = File.read(loc[0])[0..(loc[1])].split("\n")
-    $stderr.puts "#{loc[0]}:#{lines.length}: #{msg}"
-    raise "" if $debug
-#    $stderr.puts "#{lines.last}"
-#    $stderr.puts (" " * lines.last.length) + "^"
-    exit 1
-  end
-
   def self.check(env, expr, type)
     if (expr.is_a? Parser::IfExpr)
       check_ifexpr(env, expr, type)
@@ -480,11 +510,11 @@ module TypeChecker
   end
 
   def self.infer_annotation(env, expr)
-      check(env, expr.expr, expr.type)
+    check(env, expr.expr, expr.type)
   end
 
   def self.infer_block(env, expr)
-    env = env.merge(expr.syms)
+    env.merge!(expr.syms)
     types = expr.exprs.map {|e| infer(env, e) }
     expr.type = types.last
   end
@@ -535,13 +565,10 @@ module TypeChecker
 end
 
 block = Parser.new("dyn.src").toplevel
-pp TypeChecker.infer({}, block)
+pp TypeChecker.infer(SymTable.new, block)
 
 # TODO:
 #   * Check for correct usage of void on function types and elsewhere...
-#   * refine rules for use of variables before definition and mutually recursive procedures...
-#       * Function args need to be defined in scope
-#       * Shadowing of arguments/globals is broken
 #   * Add support for structs
 #   * Add support for checked unions
 #   * Add support for arrays
diff --git a/dyn.src b/dyn.src
index 88c4e10cb231d53b272fe640a02768f8f0f4f0ca..2292eed6f13b7779b83b8a2aa64aa7251290bb55 100644 (file)
--- a/dyn.src
+++ b/dyn.src
@@ -22,15 +22,11 @@ if (true) {
   3
 }
 
-z = 42
 foo1 : int -> int
-foo1 = fun(y) {
-#  a : int
-#  a = 42
-  b = z
-#  z =123
-
-#  a = 43
+foo1 = fun(z) {
+  a= 42
+#  z = 1
+#  b = z
   42
 }