Alpha Chen 2 years ago
parent 7c4aaa5e3c
commit 6a9ef7dde1

@ -50,7 +50,8 @@ module Lox
end end
def visit_class(stmt) def visit_class(stmt)
parenthesize("class #{stmt.name.lexeme}", *stmt.methods) exprs = [stmt.superclass, *stmt.methods].compact
parenthesize("class #{stmt.name.lexeme}", *exprs)
end end
def visit_function(stmt) def visit_function(stmt)

@ -45,13 +45,21 @@ module Lox
end end
def visit_class(stmt) def visit_class(stmt)
superclass = if stmt.superclass
superclass = evaluate(stmt.superclass)
raise RuntimeError.new(stmt.superclass.name, "Superclass must be a class.") unless superclass.is_a?(LoxClass)
superclass
else
nil
end
@env.define(stmt.name.lexeme, nil) @env.define(stmt.name.lexeme, nil)
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, methods) klass = LoxClass.new(stmt.name.lexeme, superclass, methods)
@env.assign(stmt.name, klass) @env.assign(stmt.name, klass)
nil nil
end end

@ -5,8 +5,8 @@ module Lox
attr_reader :name attr_reader :name
def initialize(name, methods) def initialize(name, superclass, methods)
@name, @methods = name, methods @name, @superclass, @methods = name, superclass, methods
end end
def find_method(name) = @methods[name] def find_method(name) = @methods[name]

@ -33,6 +33,14 @@ module Lox
def class_decl def class_decl
name = consume!(:IDENTIFIER, "Expect class name.") name = consume!(:IDENTIFIER, "Expect class name.")
superclass = if match?(:LESS)
consume!(:IDENTIFIER, "Expect superclass name.")
Expr::Variable.new(prev)
else
nil
end
consume!(:LEFT_BRACE, "Expect '{' before class body.") consume!(:LEFT_BRACE, "Expect '{' before class body.")
methods = [] methods = []
@ -42,7 +50,7 @@ module Lox
consume!(:RIGHT_BRACE, "Expect '}' after class body.") consume!(:RIGHT_BRACE, "Expect '}' after class body.")
Stmt::Class.new(name, methods) Stmt::Class.new(name, superclass, methods)
end end
def var_declaration def var_declaration

@ -28,6 +28,12 @@ module Lox
def visit_class(stmt) def visit_class(stmt)
with_current_class(:CLASS) do with_current_class(:CLASS) do
declare(stmt.name) 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 with_scope do
@scopes.last["this"] = true @scopes.last["this"] = true
@ -36,8 +42,6 @@ module Lox
decl = method.name.lexeme == "init" ? :INIT : :METHOD decl = method.name.lexeme == "init" ? :INIT : :METHOD
resolve_function(method, decl) resolve_function(method, decl)
end end
define(stmt.name)
end end
end end

@ -13,7 +13,7 @@ module Lox
end end
stmt :Block, :stmts stmt :Block, :stmts
stmt :Class, :name, :methods stmt :Class, :name, :superclass, :methods
stmt :Expr, :expr stmt :Expr, :expr
stmt :Function, :name, :params, :body stmt :Function, :name, :params, :body
stmt :If, :cond, :then, :else stmt :If, :cond, :then, :else

@ -451,6 +451,25 @@ class TestInterpreter < Lox::Test
SRC SRC
end end
def test_inheritance
assert_interpreted "", <<~SRC
class Doughnut {}
class BostonCream < Doughnut {}
SRC
assert_raises Lox::ResolverError do
interpret("class Oops < Oops {}")
end
assert_raises Lox::RuntimeError do
interpret(<<~SRC)
var NotAClass = "I am totally not a class";
class Subclass < NotAClass {} // ?!
SRC
end
end
private private
def assert_interpreted(expected, src) def assert_interpreted(expected, src)

Loading…
Cancel
Save