chapter 13
This commit is contained in:
9
examples/inheritance.lox
Normal file
9
examples/inheritance.lox
Normal file
@@ -0,0 +1,9 @@
|
||||
class Doughnut {
|
||||
fn cook() {
|
||||
print("Fry until golden brown.");
|
||||
}
|
||||
}
|
||||
|
||||
class BostonCream < Doughnut {}
|
||||
|
||||
BostonCream().cook();
|
14
examples/super.lox
Normal file
14
examples/super.lox
Normal file
@@ -0,0 +1,14 @@
|
||||
class Doughnut {
|
||||
fn cook() {
|
||||
print("Fry until golden brown.");
|
||||
}
|
||||
}
|
||||
|
||||
class BostonCream < Doughnut {
|
||||
fn cook() {
|
||||
super.cook();
|
||||
print("Pipe full of custard and coat with chocolate.");
|
||||
}
|
||||
}
|
||||
|
||||
BostonCream().cook();
|
@@ -34,9 +34,10 @@ class LoxInstance
|
||||
end
|
||||
|
||||
class LoxClass
|
||||
def initialize(name, methods)
|
||||
def initialize(name, methods, superclass)
|
||||
@name = name
|
||||
@methods = methods
|
||||
@super = superclass
|
||||
end
|
||||
|
||||
def to_s()
|
||||
@@ -78,5 +79,9 @@ class LoxClass
|
||||
if @methods.has_key? name.lexeme
|
||||
return @methods[name.lexeme].bind(instance)
|
||||
end
|
||||
|
||||
if @super
|
||||
return @super.find_method(instance, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -2,9 +2,9 @@ require './rlox/environment'
|
||||
require './rlox/parse'
|
||||
require './rlox/scan'
|
||||
|
||||
Env = Struct.new(:map, :scopes, :vars, :fn, :cls) do
|
||||
Env = Struct.new(:map, :scopes, :vars, :fn, :cls, :parent) do
|
||||
def child()
|
||||
Env.new(map, scopes, Environment.new(vars), fn)
|
||||
Env.new(map, scopes, Environment.new(vars), fn, cls, self)
|
||||
end
|
||||
|
||||
def lookup(expr, name)
|
||||
@@ -19,7 +19,7 @@ end
|
||||
|
||||
class Executor
|
||||
def initialize()
|
||||
@env = Env.new({}, [], Environment.global, nil, nil)
|
||||
@env = Env.new({}, [], Environment.global, nil, nil, nil)
|
||||
@scanner = Scanner.new
|
||||
@parser = Parser.new
|
||||
end
|
||||
|
@@ -319,13 +319,27 @@ Set = Struct.new(:object, :name, :value) do
|
||||
val
|
||||
end
|
||||
end
|
||||
Klass = Struct.new(:name, :methods) do
|
||||
Klass = Struct.new(:name, :methods, :superclass) do
|
||||
def resolve(env)
|
||||
enclosing = env.cls
|
||||
env.cls = :class
|
||||
declare(env.scopes, name)
|
||||
|
||||
if superclass != nil
|
||||
env.cls = :subclass
|
||||
superclass.resolve(env)
|
||||
end
|
||||
|
||||
define(env.scopes, name)
|
||||
|
||||
if superclass
|
||||
begin_scope(env.scopes)
|
||||
|
||||
dummy = Token.new(:id, "super", nil, 0)
|
||||
declare(env.scopes, dummy)
|
||||
define(env.scopes, dummy)
|
||||
end
|
||||
|
||||
begin_scope(env.scopes)
|
||||
dummy = Token.new(:id, "self", nil, 0)
|
||||
declare(env.scopes, dummy)
|
||||
@@ -336,14 +350,33 @@ Klass = Struct.new(:name, :methods) do
|
||||
}
|
||||
|
||||
end_scope(env.scopes)
|
||||
|
||||
if superclass
|
||||
end_scope(env.scopes)
|
||||
end
|
||||
|
||||
env.cls = enclosing
|
||||
end
|
||||
|
||||
def eval(env)
|
||||
supcls = nil
|
||||
if superclass
|
||||
supcls = superclass.eval(env)
|
||||
|
||||
if not supcls.is_a? LoxClass
|
||||
raise ExecError.new(name.line, "Superclass must be a class.")
|
||||
end
|
||||
end
|
||||
|
||||
if name
|
||||
env.vars.define(name.lexeme, nil)
|
||||
end
|
||||
|
||||
if superclass
|
||||
env = env.child
|
||||
env.vars.define("super", supcls)
|
||||
end
|
||||
|
||||
methods_dict = {}
|
||||
methods.each { | method |
|
||||
fn = Callable.new(method.name.lexeme, method.params, method.body, env) {
|
||||
@@ -357,7 +390,11 @@ Klass = Struct.new(:name, :methods) do
|
||||
methods_dict[method.name.lexeme] = fn
|
||||
}
|
||||
|
||||
klass = LoxClass.new(name ? name.lexeme : nil, methods_dict)
|
||||
klass = LoxClass.new(name ? name.lexeme : nil, methods_dict, supcls)
|
||||
|
||||
if superclass
|
||||
env = env.parent
|
||||
end
|
||||
|
||||
if name
|
||||
env.vars.assign(name, klass)
|
||||
@@ -378,6 +415,37 @@ Self = Struct.new(:keyword) do
|
||||
env.lookup(self, keyword)
|
||||
end
|
||||
end
|
||||
Super = Struct.new(:keyword, :method) do
|
||||
def resolve(env)
|
||||
if not env.cls
|
||||
raise VarError.new(keyword.line, "Cannot use 'super' outside of a class.")
|
||||
elsif env.cls != :subclass
|
||||
raise VarError.new(
|
||||
keyword.line,
|
||||
"Cannot use 'super' in a class without superclass."
|
||||
)
|
||||
end
|
||||
|
||||
resolve_local(env, self, keyword)
|
||||
end
|
||||
|
||||
def eval(env)
|
||||
distance = env.map[self]
|
||||
superclass = env.vars.get_at(keyword, distance)
|
||||
dummy = Token.new(:id, "self", nil, 0)
|
||||
obj = env.vars.get_at(dummy, distance-1)
|
||||
m = superclass.find_method(obj, method)
|
||||
|
||||
if not m
|
||||
raise ExecError.new(
|
||||
method.line,
|
||||
"Undefined 'super' property '#{method.lexeme}'."
|
||||
)
|
||||
end
|
||||
|
||||
m
|
||||
end
|
||||
end
|
||||
|
||||
Expression = Struct.new(:expression) do
|
||||
def resolve(env)
|
||||
|
@@ -226,6 +226,11 @@ class Parser
|
||||
Literal.new(nil)
|
||||
elsif match(:self)
|
||||
Self.new(previous)
|
||||
elsif match(:super)
|
||||
kw = previous
|
||||
consume(:dot, "Expect '.' after 'super'.")
|
||||
method = consume(:id, "Expect superclass method name.")
|
||||
Super.new(kw, method)
|
||||
elsif match(:number, :string)
|
||||
Literal.new(previous.literal)
|
||||
elsif match(:left_paren)
|
||||
@@ -426,6 +431,13 @@ class Parser
|
||||
consume(:class, "Expect 'class' keyword.")
|
||||
name = consume(:id, "Expect class name.")
|
||||
|
||||
superclass = nil
|
||||
|
||||
if match(:lt)
|
||||
consume(:id, "Expect superclass name.")
|
||||
superclass = Var.new(previous)
|
||||
end
|
||||
|
||||
consume(:left_brace, "Expect '{' before body of class '#{name.lexeme}.'")
|
||||
|
||||
methods = []
|
||||
@@ -436,7 +448,7 @@ class Parser
|
||||
|
||||
consume(:right_brace, "Expect '}' after class body.")
|
||||
|
||||
Klass.new(name, methods)
|
||||
Klass.new(name, methods, superclass)
|
||||
end
|
||||
|
||||
def declaration()
|
||||
|
Reference in New Issue
Block a user