Inheritance 13
This commit is contained in:
parent
d8ac625429
commit
848c846e09
12 changed files with 117 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -16,4 +16,5 @@ lox-test-*
|
|||
*.lst
|
||||
|
||||
.msc/
|
||||
test/test.lox
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ abstract class Expr{
|
|||
R visit(Literal expr);
|
||||
R visit(Logical expr);
|
||||
R visit(Set expr);
|
||||
R visit(Super expr);
|
||||
R visit(This expr);
|
||||
R visit(Unary expr);
|
||||
R visit(Variable expr);
|
||||
|
|
@ -73,6 +74,11 @@ abstract class Expr{
|
|||
Expr value;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class Super : typeof(this){
|
||||
Token keyword;
|
||||
Token method;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class This : typeof(this){
|
||||
Token keyword;
|
||||
mixin defCtorAndAccept;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
module jlox.interpreter;
|
||||
|
||||
import std.conv;
|
||||
import std.exception : enforce;
|
||||
import std.stdio;
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
|
|
@ -109,13 +110,24 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!LoxValue {
|
|||
executeBlock(stmt.statements, new Environment(environment));
|
||||
}
|
||||
void visit(Stmt.Class stmt){
|
||||
LoxValue superclass;
|
||||
if(stmt.superclass !is null){
|
||||
superclass = evaluate(stmt.superclass);
|
||||
enforce(cast(LoxClass)superclass, new RuntimeError(stmt.superclass.name, "Superclass must be a class."));
|
||||
}
|
||||
environment.define(stmt.name.lexeme, new LoxNil());
|
||||
if(stmt.superclass !is null){
|
||||
environment = new Environment(environment);
|
||||
environment.define("super", superclass);
|
||||
}
|
||||
LoxFunction[string] methods;
|
||||
foreach(Stmt.Function method; stmt.methods){
|
||||
LoxFunction func = new LoxFunction(method, environment, method.name.lexeme == "init");
|
||||
methods[method.name.lexeme] = func;
|
||||
}
|
||||
LoxClass cls = new LoxClass(stmt.name.lexeme, methods);
|
||||
LoxClass cls = new LoxClass(stmt.name.lexeme, cast(LoxClass)superclass, methods);
|
||||
if(superclass !is null)
|
||||
environment = environment.enclosing;
|
||||
environment.assign(stmt.name, cls);
|
||||
}
|
||||
void visit(Stmt.Expression stmt){
|
||||
|
|
@ -188,6 +200,15 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!LoxValue {
|
|||
(cast(LoxInstance)object).set(expr.name, value);
|
||||
return value;
|
||||
}
|
||||
LoxValue visit(Expr.Super expr){
|
||||
uint distance = locals[expr];
|
||||
LoxClass superclass = cast(LoxClass)environment.getAt(distance, "super");
|
||||
LoxInstance object = cast(LoxInstance)environment.getAt(distance - 1, "this");
|
||||
LoxFunction method = superclass.findMethod(expr.method.lexeme);
|
||||
if(method is null)
|
||||
throw new RuntimeError(expr.method, "Undefined property '" ~ expr.method.lexeme ~ "'.");
|
||||
return method.bind(object);
|
||||
}
|
||||
LoxValue visit(Expr.This expr){
|
||||
return lookUpVariable(expr.keyword, expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,15 @@ import common.util;
|
|||
|
||||
class LoxClass : LoxCallable{
|
||||
package const string name;
|
||||
private const LoxClass superclass;
|
||||
private const LoxFunction[string] methods;
|
||||
mixin defaultCtor;
|
||||
|
||||
LoxFunction findMethod(string name){
|
||||
LoxFunction findMethod(string name) const{
|
||||
if(auto method = name in methods)
|
||||
return cast(LoxFunction)*method;
|
||||
if(superclass !is null)
|
||||
return superclass.findMethod(name);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -312,6 +312,12 @@ class Parser{
|
|||
consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
|
||||
return new Expr.Grouping(expr);
|
||||
}
|
||||
if(match(TokenType.SUPER)){
|
||||
Token keyword = previous();
|
||||
consume(TokenType.DOT, "Expect '.' after 'super'.");
|
||||
Token method = consume(TokenType.IDENTIFIER, "Expect superclass method name.");
|
||||
return new Expr.Super(keyword, method);
|
||||
}
|
||||
throw error(peek(), "Expect expression.");
|
||||
}
|
||||
private Stmt varDeclaration(){
|
||||
|
|
@ -324,12 +330,17 @@ class Parser{
|
|||
}
|
||||
private Stmt classDeclaration() {
|
||||
Token name = consume(TokenType.IDENTIFIER, "Expect class name.");
|
||||
Expr.Variable superclass;
|
||||
if(match(TokenType.LESS)){
|
||||
consume(TokenType.IDENTIFIER, "Expect superlcass name.");
|
||||
superclass = new Expr.Variable(previous);
|
||||
}
|
||||
consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
|
||||
Stmt.Function[] methods;
|
||||
while(!check(TokenType.RIGHT_BRACE) && !isAtEnd)
|
||||
methods ~= fun("method");
|
||||
consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");
|
||||
return new Stmt.Class(name, methods);
|
||||
return new Stmt.Class(name, superclass, methods);
|
||||
}
|
||||
private Stmt declaration(){
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import common.util;
|
|||
|
||||
class Resolver : Stmt.Visitor!void, Expr.Visitor!void {
|
||||
private enum FunctionType{ NONE, FUNCTION, INITIALISER, METHOD }
|
||||
private enum ClassType{ NONE, CLASS }
|
||||
private enum ClassType{ NONE, CLASS, SUBCLASS }
|
||||
private Interpreter interpreter;
|
||||
this(Interpreter interpreter){
|
||||
this.interpreter = interpreter;
|
||||
|
|
@ -80,6 +80,16 @@ class Resolver : Stmt.Visitor!void, Expr.Visitor!void {
|
|||
currentClass = enclosingClass;
|
||||
declare(stmt.name);
|
||||
define(stmt.name);
|
||||
if(stmt.superclass !is null){
|
||||
currentClass = ClassType.SUBCLASS;
|
||||
if(stmt.name.lexeme == stmt.superclass.name.lexeme)
|
||||
Lox.error(stmt.superclass.name, "A class can't inherit from itself.");
|
||||
resolve(stmt.superclass);
|
||||
}
|
||||
if(stmt.superclass !is null){
|
||||
beginScope();
|
||||
scopes.front["super"] = true;
|
||||
}
|
||||
beginScope();
|
||||
scopes.front["this"] = true;
|
||||
foreach(method; stmt.methods){
|
||||
|
|
@ -89,6 +99,8 @@ class Resolver : Stmt.Visitor!void, Expr.Visitor!void {
|
|||
resolveFunction(method, declaration);
|
||||
}
|
||||
endScope();
|
||||
if(stmt.superclass !is null)
|
||||
endScope();
|
||||
}
|
||||
void visit(Stmt.Var stmt){
|
||||
declare(stmt.name);
|
||||
|
|
@ -165,6 +177,13 @@ class Resolver : Stmt.Visitor!void, Expr.Visitor!void {
|
|||
resolve(expr.value);
|
||||
resolve(expr.object);
|
||||
}
|
||||
void visit(Expr.Super expr){
|
||||
if(currentClass == ClassType.NONE)
|
||||
Lox.error(expr.keyword, "Can't use 'super' outside of a class.");
|
||||
else if (currentClass != ClassType.SUBCLASS)
|
||||
Lox.error(expr.keyword, "Can't use 'super' in a class with no superclass.");
|
||||
resolveLocal(expr, expr.keyword);
|
||||
}
|
||||
void visit(Expr.This expr){
|
||||
if(currentClass == ClassType.NONE){
|
||||
Lox.error(expr.keyword, "Can't use 'this' outside of a class.");
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ abstract class Stmt{
|
|||
}
|
||||
static class Class : typeof(this){
|
||||
Token name;
|
||||
Expr.Variable superclass;
|
||||
Function[] methods;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,15 @@ void main(){
|
|||
"./test/fib_recursive.lox".match(fib(34));
|
||||
"./test/fib_closure.lox".match(fib(34));
|
||||
"./test/class.lox".match("The German chocolate cake is delicious!\n");
|
||||
"./test/super.lox".match("Fry until golden brown.\nPipe full of custard and coat with chocolate.\nA method\n");
|
||||
|
||||
"./test/err/invalid_syntax.lox".shouldFail(RetVal.other);
|
||||
"./test/err/already_defined.lox".shouldFail(RetVal.other, "Already a variable with this name");
|
||||
"./test/err/undefined_var.lox".shouldFail(RetVal.runtime, "Undefined variable");
|
||||
"./test/err/self_ref_vardecl.lox".shouldFail(RetVal.runtime, "Undefined variable");
|
||||
"./test/err/invalid_syntax.lox".shouldFail(RetVal.other);
|
||||
"./test/err/global_scope_return.lox".shouldFail(RetVal.other, "Can't return from top-level code");
|
||||
"./test/err/super_outside_class.lox".shouldFail(RetVal.other, "Can't use 'super' outside of a class");
|
||||
"./test/err/super_without_superclass.lox".shouldFail(RetVal.other, "Can't use 'super' in a class with no superclass");
|
||||
}
|
||||
|
||||
enum RetVal{
|
||||
|
|
|
|||
3
test/err/super_outside_class.lox
Normal file
3
test/err/super_outside_class.lox
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
super.notEvenInAClass();
|
||||
|
||||
8
test/err/super_without_superclass.lox
Normal file
8
test/err/super_without_superclass.lox
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
class Eclair{
|
||||
cook(){
|
||||
super.cook();
|
||||
print "Pipe full of crème pâtissière.";
|
||||
}
|
||||
}
|
||||
|
||||
36
test/super.lox
Normal file
36
test/super.lox
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
class Doughnut{
|
||||
cook(){
|
||||
print "Fry until golden brown.";
|
||||
}
|
||||
}
|
||||
|
||||
class BostonCream < Doughnut{
|
||||
cook(){
|
||||
super.cook();
|
||||
print "Pipe full of custard and coat with chocolate.";
|
||||
}
|
||||
}
|
||||
|
||||
BostonCream().cook();
|
||||
|
||||
|
||||
class A{
|
||||
method(){
|
||||
print "A method";
|
||||
}
|
||||
}
|
||||
|
||||
class B < A{
|
||||
method(){
|
||||
print "B method";
|
||||
}
|
||||
test(){
|
||||
super.method();
|
||||
}
|
||||
}
|
||||
|
||||
class C < B{}
|
||||
|
||||
C().test();
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue