From 009c22fa2743f2ed79f1800603fe118b61871110 Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Wed, 31 Aug 2022 09:06:34 -0700 Subject: [PATCH] 13.3.2 --- ruby/lib/lox/interpreter.rb | 17 ++++++++++++++++ ruby/lib/lox/resolver.rb | 34 +++++++++++++++++++------------ ruby/test/lox/test_interpreter.rb | 24 +++++++++++++++++++++- 3 files changed, 61 insertions(+), 14 deletions(-) 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