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
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

View File

@@ -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

View File

@@ -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)

View File

@@ -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()