chapter 13

This commit is contained in:
2018-07-27 18:35:39 +02:00
parent 580795cd2b
commit 668248bd84
6 changed files with 115 additions and 7 deletions

9
examples/inheritance.lox Normal file
View 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
View 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();

View File

@@ -34,9 +34,10 @@ class LoxInstance
end end
class LoxClass class LoxClass
def initialize(name, methods) def initialize(name, methods, superclass)
@name = name @name = name
@methods = methods @methods = methods
@super = superclass
end end
def to_s() def to_s()
@@ -78,5 +79,9 @@ class LoxClass
if @methods.has_key? name.lexeme if @methods.has_key? name.lexeme
return @methods[name.lexeme].bind(instance) return @methods[name.lexeme].bind(instance)
end end
if @super
return @super.find_method(instance, name)
end
end end
end end

View File

@@ -2,9 +2,9 @@ require './rlox/environment'
require './rlox/parse' require './rlox/parse'
require './rlox/scan' require './rlox/scan'
Env = Struct.new(:map, :scopes, :vars, :fn, :cls) do Env = Struct.new(:map, :scopes, :vars, :fn, :cls, :parent) do
def child() def child()
Env.new(map, scopes, Environment.new(vars), fn) Env.new(map, scopes, Environment.new(vars), fn, cls, self)
end end
def lookup(expr, name) def lookup(expr, name)
@@ -19,7 +19,7 @@ end
class Executor class Executor
def initialize() def initialize()
@env = Env.new({}, [], Environment.global, nil, nil) @env = Env.new({}, [], Environment.global, nil, nil, nil)
@scanner = Scanner.new @scanner = Scanner.new
@parser = Parser.new @parser = Parser.new
end end

View File

@@ -319,13 +319,27 @@ Set = Struct.new(:object, :name, :value) do
val val
end end
end end
Klass = Struct.new(:name, :methods) do Klass = Struct.new(:name, :methods, :superclass) do
def resolve(env) def resolve(env)
enclosing = env.cls enclosing = env.cls
env.cls = :class env.cls = :class
declare(env.scopes, name) declare(env.scopes, name)
if superclass != nil
env.cls = :subclass
superclass.resolve(env)
end
define(env.scopes, name) 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) begin_scope(env.scopes)
dummy = Token.new(:id, "self", nil, 0) dummy = Token.new(:id, "self", nil, 0)
declare(env.scopes, dummy) declare(env.scopes, dummy)
@@ -336,14 +350,33 @@ Klass = Struct.new(:name, :methods) do
} }
end_scope(env.scopes) end_scope(env.scopes)
if superclass
end_scope(env.scopes)
end
env.cls = enclosing env.cls = enclosing
end end
def eval(env) 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 if name
env.vars.define(name.lexeme, nil) env.vars.define(name.lexeme, nil)
end end
if superclass
env = env.child
env.vars.define("super", supcls)
end
methods_dict = {} methods_dict = {}
methods.each { | method | methods.each { | method |
fn = Callable.new(method.name.lexeme, method.params, method.body, env) { 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 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 if name
env.vars.assign(name, klass) env.vars.assign(name, klass)
@@ -378,6 +415,37 @@ Self = Struct.new(:keyword) do
env.lookup(self, keyword) env.lookup(self, keyword)
end end
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 Expression = Struct.new(:expression) do
def resolve(env) def resolve(env)

View File

@@ -226,6 +226,11 @@ class Parser
Literal.new(nil) Literal.new(nil)
elsif match(:self) elsif match(:self)
Self.new(previous) 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) elsif match(:number, :string)
Literal.new(previous.literal) Literal.new(previous.literal)
elsif match(:left_paren) elsif match(:left_paren)
@@ -426,6 +431,13 @@ class Parser
consume(:class, "Expect 'class' keyword.") consume(:class, "Expect 'class' keyword.")
name = consume(:id, "Expect class name.") 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}.'") consume(:left_brace, "Expect '{' before body of class '#{name.lexeme}.'")
methods = [] methods = []
@@ -436,7 +448,7 @@ class Parser
consume(:right_brace, "Expect '}' after class body.") consume(:right_brace, "Expect '}' after class body.")
Klass.new(name, methods) Klass.new(name, methods, superclass)
end end
def declaration() def declaration()