Inheritance 13
This commit is contained in:
parent
d8ac625429
commit
848c846e09
12 changed files with 117 additions and 7 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue