12.6
change zwkmzsxsouwmlkkmppmsswqkvolttwll
commit f058bee510fd918e0ab96b0949fc7de7a55213cf
author Alpha Chen <alpha@kejadlen.dev>
date
parent wuklnrwt
diff --git a/ruby/lib/lox/expr.rb b/ruby/lib/lox/expr.rb
index 52e2a82..1c1e7a8 100644
--- a/ruby/lib/lox/expr.rb
+++ b/ruby/lib/lox/expr.rb
@@ -18,6 +18,7 @@ module Lox
     expr :Literal, :value
     expr :Logical, :left, :op, :right
     expr :Set, :object, :name, :value
+    expr :This, :keyword
     expr :Unary, :op, :right
     expr :Variable, :name
   end
diff --git a/ruby/lib/lox/function.rb b/ruby/lib/lox/function.rb
index cc92c2f..f1f5475 100644
--- a/ruby/lib/lox/function.rb
+++ b/ruby/lib/lox/function.rb
@@ -6,6 +6,12 @@ module Lox
       @decl, @closure = decl, closure
     end
 
+    def bind(instance)
+      env = Environment.new(@closure)
+      env.define("this", instance)
+      Function.new(@decl, env)
+    end
+
     def arity = @decl.params.size
 
     def call(interpreter, args)
diff --git a/ruby/lib/lox/instance.rb b/ruby/lib/lox/instance.rb
index d6bd711..d149152 100644
--- a/ruby/lib/lox/instance.rb
+++ b/ruby/lib/lox/instance.rb
@@ -13,7 +13,7 @@ module Lox
       return @fields.fetch(name.lexeme) if @fields.has_key?(name.lexeme)
 
       method = @klass.find_method(name.lexeme)
-      return method unless method.nil?
+      return method.bind(self) unless method.nil?
 
       raise RuntimeError.new(name, "Undefined property '#{name.lexeme}'.")
     end
diff --git a/ruby/lib/lox/interpreter.rb b/ruby/lib/lox/interpreter.rb
index 39ee6b8..1774d51 100644
--- a/ruby/lib/lox/interpreter.rb
+++ b/ruby/lib/lox/interpreter.rb
@@ -136,6 +136,8 @@ module Lox
       value
     end
 
+    def visit_this(expr) = lookup_var(expr.keyword, expr)
+
     def visit_unary(expr)
       right = evaluate(expr.right)
 
diff --git a/ruby/lib/lox/parser.rb b/ruby/lib/lox/parser.rb
index 29f58b1..10e42b0 100644
--- a/ruby/lib/lox/parser.rb
+++ b/ruby/lib/lox/parser.rb
@@ -308,6 +308,7 @@ module Lox
       return Expr::Literal.new(true) if match?(:TRUE)
       return Expr::Literal.new(nil) if match?(:NIL)
       return Expr::Literal.new(prev.literal) if match?(:NUMBER, :STRING)
+      return Expr::This.new(prev) if match?(:THIS)
       return Expr::Variable.new(prev) if match?(:IDENTIFIER)
 
       if match?(:LEFT_PAREN)
diff --git a/ruby/lib/lox/resolver.rb b/ruby/lib/lox/resolver.rb
index 2740876..7cdf3b6 100644
--- a/ruby/lib/lox/resolver.rb
+++ b/ruby/lib/lox/resolver.rb
@@ -27,11 +27,16 @@ module Lox
     def visit_class(stmt)
       declare(stmt.name)
 
-      stmt.methods.each do |method|
-        resolve_function(method, :METHOD)
+      with_scope do
+        @scopes.last["this"] = true
+
+        stmt.methods.each do |method|
+          resolve_function(method, :METHOD)
+        end
+
+        define(stmt.name)
       end
 
-      define(stmt.name)
       nil
     end
 
@@ -98,6 +103,11 @@ module Lox
       nil
     end
 
+    def visit_this(expr)
+      resolve_local(expr, expr.keyword)
+      nil
+    end
+
     def visit_unary(expr) = resolve(expr.right)
 
     private
diff --git a/ruby/test/lox/test_interpreter.rb b/ruby/test/lox/test_interpreter.rb
index 439bd5d..6463058 100644
--- a/ruby/test/lox/test_interpreter.rb
+++ b/ruby/test/lox/test_interpreter.rb
@@ -356,22 +356,50 @@ class TestInterpreter < Lox::Test
       Bacon().eat(); // Prints "Crunch crunch crunch!".
     SRC
 
-    # assert_interpreted "", <<~SRC
-    #   class Person {
-    #     sayName() {
-    #       print this.name;
-    #     }
-    #   }
-
-    #   var jane = Person();
-    #   jane.name = "Jane";
-
-    #   var bill = Person();
-    #   bill.name = "Bill";
-
-    #   bill.sayName = jane.sayName;
-    #   bill.sayName(); // "Jane"
-    # SRC
+    assert_interpreted "The German chocolate cake is delicious!", <<~SRC
+      class Cake {
+        taste() {
+          var adjective = "delicious";
+          print "The " + this.flavor + " cake is " + adjective + "!";
+        }
+      }
+
+      var cake = Cake();
+      cake.flavor = "German chocolate";
+      cake.taste(); // Prints "The German chocolate cake is delicious!".
+    SRC
+
+    assert_interpreted "Thing instance", <<~SRC
+      class Thing {
+        getCallback() {
+          fun localFunction() {
+            print this;
+          }
+
+          return localFunction;
+        }
+      }
+
+      var callback = Thing().getCallback();
+      callback();
+    SRC
+
+    assert_interpreted "Jane", <<~SRC
+      class Person {
+        sayName() {
+          print this.name;
+        }
+      }
+
+      var jane = Person();
+      jane.name = "Jane";
+
+      var bill = Person();
+      bill.name = "Bill";
+
+      bill.sayName = jane.sayName;
+      bill.sayName(); // "Jane"
+    SRC
   end
 
   private