main
Alpha Chen 2 years ago
parent dade99a51f
commit 009c22fa27

@ -55,11 +55,18 @@ module Lox
@env.define(stmt.name.lexeme, nil) @env.define(stmt.name.lexeme, nil)
if superclass
@env = Environment.new(@env)
@env.define("super", superclass)
end
methods = stmt.methods.to_h {|method| methods = stmt.methods.to_h {|method|
[method.name.lexeme, Function.new(method, @env, method.name.lexeme == "init")] [method.name.lexeme, Function.new(method, @env, method.name.lexeme == "init")]
} }
klass = LoxClass.new(stmt.name.lexeme, superclass, methods) klass = LoxClass.new(stmt.name.lexeme, superclass, methods)
@env = @env.enclosing if superclass
@env.assign(stmt.name, klass) @env.assign(stmt.name, klass)
nil nil
end end
@ -144,6 +151,16 @@ module Lox
value value
end 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_this(expr) = lookup_var(expr.keyword, expr)
def visit_unary(expr) def visit_unary(expr)

@ -30,20 +30,23 @@ module Lox
declare(stmt.name) declare(stmt.name)
define(stmt.name) define(stmt.name)
if stmt.superclass 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 raise ResolverError.new(stmt.superclass.name, "A class can't inherit from itself.") if stmt.name.lexeme == stmt.superclass.name.lexeme
resolve(stmt.superclass) resolve(stmt.superclass)
->(&block) { with_scope(super: true) { block.call } }
else
->(&block) { block.call }
end end
with_scope do with_superclass_scope.call do
@scopes.last["this"] = true with_scope(this: true) do
stmt.methods.each do |method| stmt.methods.each do |method|
decl = method.name.lexeme == "init" ? :INIT : :METHOD decl = method.name.lexeme == "init" ? :INIT : :METHOD
resolve_function(method, decl) resolve_function(method, decl)
end end
end end
end end
end
nil nil
end end
@ -117,6 +120,11 @@ module Lox
nil nil
end end
def visit_super(expr)
resolve_local(expr, expr.keyword)
nil
end
def visit_this(expr) def visit_this(expr)
raise ResolverError.new(expr.keyword, "Can't use 'this' outside of a class.") if @current_class == :NONE raise ResolverError.new(expr.keyword, "Can't use 'this' outside of a class.") if @current_class == :NONE
@ -128,8 +136,8 @@ module Lox
private private
def with_scope def with_scope(scope={})
@scopes.push({}) @scopes.push(scope.transform_keys(&:to_s))
yield yield
@scopes.pop @scopes.pop
end end

@ -485,7 +485,7 @@ class TestInterpreter < Lox::Test
end end
def test_calling_superclass_methods def test_calling_superclass_methods
assert_interpreted <<~OUT, <<~SRC assert_interpreted <<~OUT.chomp, <<~SRC
Fry until golden brown. Fry until golden brown.
Pipe full of custard and coat with chocolate. Pipe full of custard and coat with chocolate.
OUT OUT
@ -504,6 +504,28 @@ class TestInterpreter < Lox::Test
BostonCream().cook(); BostonCream().cook();
SRC 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 end
private private

Loading…
Cancel
Save