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
|
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
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
@@ -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()
|
||||||
|
Reference in New Issue
Block a user