13.3.2
change zrrrlyrtmyrrsknztzzyrkqlosvknxlv
commit f71f15710fca8633ff655a0512bca78a504ef075
author Alpha Chen <alpha@kejadlen.dev>
date
parent wvnloqzw
diff --git a/ruby/lib/lox/interpreter.rb b/ruby/lib/lox/interpreter.rb
index 50939ab..5b76ef4 100644
--- a/ruby/lib/lox/interpreter.rb
+++ b/ruby/lib/lox/interpreter.rb
@@ -55,11 +55,18 @@ module Lox
 
       @env.define(stmt.name.lexeme, nil)
 
+      if superclass
+        @env = Environment.new(@env)
+        @env.define("super", superclass)
+      end
+
       methods = stmt.methods.to_h {|method|
         [method.name.lexeme, Function.new(method, @env, method.name.lexeme == "init")]
       }
 
       klass = LoxClass.new(stmt.name.lexeme, superclass, methods)
+      @env = @env.enclosing if superclass
+
       @env.assign(stmt.name, klass)
       nil
     end
@@ -144,6 +151,16 @@ module Lox
       value
     end
 
+    def visit_super(expr)
+      distance = @locals.fetch(expr)
+      superclass = @env.get_at(distance, "super")
+      object = @env.get_at(distance-1, "this")
+
+      method = superclass.find_method(expr.method.lexeme)
+      raise RuntimeError.new(expr.method, "Undefined property '#{expr.method.lexeme}'.") if method.nil?
+      method.bind(object)
+    end
+
     def visit_this(expr) = lookup_var(expr.keyword, expr)
 
     def visit_unary(expr)
diff --git a/ruby/lib/lox/resolver.rb b/ruby/lib/lox/resolver.rb
index b1c3f07..0683335 100644
--- a/ruby/lib/lox/resolver.rb
+++ b/ruby/lib/lox/resolver.rb
@@ -30,17 +30,20 @@ module Lox
         declare(stmt.name)
         define(stmt.name)
 
-        if stmt.superclass
-          raise ResolverError.new(stmt.superclass.name, "A class can't inherit from itself.") if stmt.name.lexeme == stmt.superclass.name.lexeme
-          resolve(stmt.superclass)
-        end
-
-        with_scope do
-          @scopes.last["this"] = true
-
-          stmt.methods.each do |method|
-            decl = method.name.lexeme == "init" ? :INIT : :METHOD
-            resolve_function(method, decl)
+        with_superclass_scope = if stmt.superclass
+                                  raise ResolverError.new(stmt.superclass.name, "A class can't inherit from itself.") if stmt.name.lexeme == stmt.superclass.name.lexeme
+                                  resolve(stmt.superclass)
+                                  ->(&block) { with_scope(super: true) { block.call } }
+                                else
+                                  ->(&block) { block.call }
+                                end
+
+        with_superclass_scope.call do
+          with_scope(this: true) do
+            stmt.methods.each do |method|
+              decl = method.name.lexeme == "init" ? :INIT : :METHOD
+              resolve_function(method, decl)
+            end
           end
         end
       end
@@ -117,6 +120,11 @@ module Lox
       nil
     end
 
+    def visit_super(expr)
+      resolve_local(expr, expr.keyword)
+      nil
+    end
+
     def visit_this(expr)
       raise ResolverError.new(expr.keyword, "Can't use 'this' outside of a class.") if @current_class == :NONE
 
@@ -128,8 +136,8 @@ module Lox
 
     private
 
-    def with_scope
-      @scopes.push({})
+    def with_scope(scope={})
+      @scopes.push(scope.transform_keys(&:to_s))
       yield
       @scopes.pop
     end
diff --git a/ruby/test/lox/test_interpreter.rb b/ruby/test/lox/test_interpreter.rb
index 4c5d21d..446af71 100644
--- a/ruby/test/lox/test_interpreter.rb
+++ b/ruby/test/lox/test_interpreter.rb
@@ -485,7 +485,7 @@ class TestInterpreter < Lox::Test
   end
 
   def test_calling_superclass_methods
-    assert_interpreted <<~OUT, <<~SRC
+    assert_interpreted <<~OUT.chomp, <<~SRC
       Fry until golden brown.
       Pipe full of custard and coat with chocolate.
     OUT
@@ -504,6 +504,28 @@ class TestInterpreter < Lox::Test
 
       BostonCream().cook();
     SRC
+
+    assert_interpreted "A method", <<~SRC
+      class A {
+        method() {
+          print "A method";
+        }
+      }
+
+      class B < A {
+        method() {
+          print "B method";
+        }
+
+        test() {
+          super.method();
+        }
+      }
+
+      class C < B {}
+
+      C().test();
+    SRC
   end
 
   private