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