commit 1b34a0bc4690256fa3a4f42244856bc6c81c2461
parent 436ebdb358c0c907ac0253c9ad42a2b98244d73a
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Sun, 16 Oct 2022 04:12:49 +0200
Inheritance. Done
Diffstat:
10 files changed, 133 insertions(+), 27 deletions(-)
diff --git a/boston.lox b/boston.lox
@@ -0,0 +1,9 @@
+class Doughnut {
+ cook() {
+ print "Fry until golden brown.";
+ }
+}
+
+class BostonCream < Doughnut {}
+
+BostonCream().cook();
diff --git a/gaze.lox b/gaze.lox
@@ -0,0 +1,28 @@
+class A {
+ method() {
+ print "A method";
+ }
+}
+
+class B < A {
+ method() {
+ print "B method";
+ }
+
+ test() {
+ print "B";
+ super.method();
+ this.ola()
+ }
+}
+
+class C < B {
+}
+
+class D < C {
+ ola() {
+ print "OLA!";
+ }
+}
+
+D().test();
diff --git a/source/ast.d b/source/ast.d
@@ -13,10 +13,11 @@ static immutable string[][] expressions = [
["Logical", "Expr left", "TokenI operator", "Expr right"],
["Call", "Expr callee", "TokenI paren", "Expr[] arguments"],
["Function", "TokenI[] params", "Stmt[] body"],
- ["Class", "Var[] methods", "Var[] classmethods"],
+ ["Class", "Var[] methods", "Var[] classmethods", "Variable superclass"],
["Get", "Expr object", "TokenI name"],
["Set", "Expr object", "TokenI name", "Expr value"],
["This", "TokenI keyword"],
+ ["Super", "TokenI keyword"],
];
static immutable string[][] statements = [
diff --git a/source/astprinter.d b/source/astprinter.d
@@ -56,6 +56,9 @@ class AstPrinter : ExprVisitor {
void visit(This th) {
printed = th.keyword.lexeme;
}
+ void visit(Super th) {
+ printed = th.keyword.lexeme;
+ }
private string parenthesize(string name, Expr[] exprs ...) {
return "(" ~ name ~ " " ~ exprs.map!(e => print(e)).join(" ") ~ ")";
}
diff --git a/source/cls.d b/source/cls.d
@@ -5,14 +5,17 @@ import instance;
import ast;
import fun;
import std.array;
+import std.range;
class Cls : Instance, Callable {
private Var[] props;
private ulong _arity;
- private Cls meta;
+ private Cls metaclass;
+ private Cls superclass;
- this(Var[] props, Var[] classprops = null, Interpreter interpreter = null) {
+ this(Var[] props, Var[] classprops = null, Interpreter interpreter = null, Cls superclass = null) {
this.props = props;
+ this.superclass = superclass;
_arity = 0;
foreach(prop; props) {
if (prop.name.lexeme == "init") {
@@ -22,25 +25,37 @@ class Cls : Instance, Callable {
}
}
if (classprops !is null) {
- this.meta = new Cls(classprops);
- super(meta.evalProps(interpreter));
+ metaclass = new Cls(classprops);
+ super(metaclass.evalProps(interpreter));
+ super.bindMethods("this");
super.construct([], interpreter);
} else {
- this.meta = null;
+ metaclass = null;
super();
}
}
+ Instance instatiate(Interpreter interpreter) {
+ Instance[] instances = [new Instance(evalProps(interpreter))];
+ for(Cls cls = this; cls.superclass; cls = cls.superclass) {
+ instances ~= new Instance(cls.superclass.evalProps(interpreter));
+ }
+ foreach_reverse(i, instance; instances[0..$-1].enumerate()) {
+ instance.bindMethods("super", instances[i+1]);
+ instance.addFields(instances[i+1].getFields());
+ }
+ return instances[0];
+ }
+
Variant call(Interpreter interpreter, Variant[] arguments) {
- Variant[string] fields = evalProps(interpreter);
- Instance instance = new Instance(fields);
+ Instance instance = instatiate(interpreter);
+ instance.bindMethods("this");
instance.addFields(super.getFields());
instance.construct(arguments, interpreter);
return Variant(instance);
}
- private Variant[string] evalProps(Interpreter interpreter) {
- Variant[string] fields = null;
+ private Variant[string] evalProps(Interpreter interpreter, Variant[string] fields = null) {
foreach(prop; props) {
fields[prop.name.lexeme] = interpreter.evaluate(prop.initializer);
}
diff --git a/source/fun.d b/source/fun.d
@@ -38,14 +38,14 @@ class Fun : Callable {
}
Variant retv(Variant value) {
- if (isInitializer) return Variant(new Instance(closure.getAt(
- TokenI(TokenType.THIS, "this", null, -1), 0).get!(Instance)));
+ if (isInitializer) return Variant(new Instance(closure.get(
+ TokenI(TokenType.THIS, "this", null, -1)).get!(Instance)));
return value;
}
- Fun bind(Instance instance) {
+ Fun bind(string name, Instance instance) {
Environment environment = new Environment(closure);
- environment.define("this", Variant(instance));
+ environment.define(name, Variant(instance));
return new Fun(fun, environment, isInitializer);
}
diff --git a/source/instance.d b/source/instance.d
@@ -5,6 +5,7 @@ import fun;
import interpreter;
import std.range;
import std.algorithm;
+import std.stdio;
class Instance {
private Variant[string] fields;
@@ -13,13 +14,10 @@ class Instance {
this(Variant[string] fields = null) {
this.fields = fields;
this.constructor = null;
- if (this.fields) bindMethods();
}
this(Instance inst) {
- this.fields = inst.fields.dup;
- this.constructor = null;
- if (this.fields) bindMethods();
+ this(inst.fields.dup);
}
Variant get(TokenI name) {
@@ -44,14 +42,23 @@ class Instance {
}
}
+ void updateFields(Variant[string] newf) {
+ writeln("ASD");
+ foreach(name, value; newf.byPair) {
+ fields[name] = value;
+ writeln(name);
+ }
+ }
+
Variant[string] getFields() {
return fields;
}
- private void bindMethods() {
+ void bindMethods(string token, Instance instance = null) {
foreach(name, field; fields.byPair) {
if (field.convertsTo!(Fun)) {
- auto ifun = field.get!(Fun).bind(this);
+ auto ifun = field.get!(Fun).bind(token,
+ instance ? instance : this);
if (name == "init") {
ifun.setInitializer();
constructor = ifun;
diff --git a/source/interpreter.d b/source/interpreter.d
@@ -144,7 +144,16 @@ class Interpreter : StmtVisitor, ExprVisitor {
}
void visit(Class expr) {
- value = new Cls(expr.methods, expr.classmethods, this);
+ Cls superclass = null;
+ if (expr.superclass) {
+ auto var = evaluate(expr.superclass);
+ if (var.convertsTo!(Cls))
+ superclass = var.get!(Cls);
+ else
+ throw new RuntimeError(expr.superclass.name,
+ "Attempt to subclass non-class");
+ }
+ value = new Cls(expr.methods, expr.classmethods, this, superclass);
}
void visit(Get expr) {
@@ -171,6 +180,10 @@ class Interpreter : StmtVisitor, ExprVisitor {
value = lookUpVariable(expr.keyword, expr);
}
+ void visit(Super expr) {
+ value = lookUpVariable(expr.keyword, expr);
+ }
+
void visit(Call expr) {
Variant callee = evaluate(expr.callee);
Variant[] arguments = [];
diff --git a/source/parser.d b/source/parser.d
@@ -24,7 +24,7 @@ returnStmt → "return" expression? ";" ;
funDecl → "fun" IDENTIFIER function ;
function → "(" parameters? ")" block ;
classDecl → "class" IDENTIFIER class ;
-class → "{" ( "class"? IDENTIFIER ( function | "=" expression ) )* "}" ;
+class → ( "<" IDENTIFIER )? "{" ( "class"? IDENTIFIER ( function | "=" expression ) )* "}" ;
parameters → IDENTIFIER ( "," IDENTIFIER )* ;
forStmt → "for" "(" ( varDecl | exprStmt | ";" )
expression? ";"
@@ -51,7 +51,7 @@ unary → ( "!" | "-" | "ast" ) unary
call → funExpr ( "(" arguments? ")" | "." IDENTIFIER )* ;
funExpr → "fun" function | classExpr ;
classExpr → "class" class | primary ;
-primary → NUMBER | STRING | "true" | "false" | "nil" | "this"
+primary → NUMBER | STRING | "true" | "false" | "nil" | "this" | "super"
| "(" expression ")" | IDENTIFIER ;
arguments → expression ( "," expression )* ;
*/
@@ -116,6 +116,12 @@ class Parser {
}
private Expr _class() {
+ Variable superclass = null;
+ if (match(TokenType.LESS)) {
+ consume(TokenType.IDENTIFIER, "Expect superclass name.");
+ superclass = new Variable(previous());
+ }
+
consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
Var[] fields;
@@ -136,7 +142,7 @@ class Parser {
consume(TokenType.RIGHT_BRACE, "Expect '}' after class.");
- return new Class(fields, classfields);
+ return new Class(fields, classfields, superclass);
}
private Stmt varDeclaration() {
@@ -408,6 +414,8 @@ class Parser {
return new Literal(previous().literal);
if (match(THIS))
return new This(previous());
+ if (match(SUPER))
+ return new Super(previous());
if (match(LEFT_PAREN)) {
Expr expr = expression();
diff --git a/source/resolver.d b/source/resolver.d
@@ -38,7 +38,8 @@ class Resolver : StmtVisitor, ExprVisitor {
private enum ClassType {
NONE,
- CLASS
+ CLASS,
+ SUBCLASS
}
this(Interpreter interpreter) {
@@ -214,10 +215,20 @@ class Resolver : StmtVisitor, ExprVisitor {
void visit(Class cl) {
ClassType enclosingClass = currentClass;
currentClass = ClassType.CLASS;
+
+ if(cl.superclass) {
+ resolve(cl.superclass);
+ currentClass = ClassType.SUBCLASS;
+ }
+
+ if(cl.superclass) {
+ beginScope();
+ scopes.front()["super"] = VarRef(VarState.REFERENCED, 0);
+ }
beginScope();
+ scopes.front()["this"] = VarRef(VarState.REFERENCED, 0);
foreach(i, method; chain(cl.methods, cl.classmethods).enumerate()) {
if (auto fun = cast(Function) method.initializer) {
- scopes.front()["this"] = VarRef(VarState.REFERENCED, 0);
FunctionType declaration = FunctionType.METHOD;
if (method.name.lexeme == "init") {
declaration = FunctionType.INITIALIZER;
@@ -231,6 +242,7 @@ class Resolver : StmtVisitor, ExprVisitor {
}
}
endScope();
+ if(cl.superclass) endScope();
currentClass = enclosingClass;
}
@@ -247,7 +259,17 @@ class Resolver : StmtVisitor, ExprVisitor {
if (currentClass == ClassType.NONE
|| currentFunction == FunctionType.FUN
|| currentFunction == FunctionType.NONE) {
- Lox.error(th.keyword, format("'This' used in invalid function type: %s", currentFunction));
+ Lox.error(th.keyword, "'This' not allowed here");
+ return;
+ }
+ resolveLocal(th, th.keyword);
+ }
+
+ void visit(Super th) {
+ if (currentClass != ClassType.SUBCLASS
+ || currentFunction == FunctionType.FUN
+ || currentFunction == FunctionType.NONE) {
+ Lox.error(th.keyword, "'Super' not allowed here");
return;
}
resolveLocal(th, th.keyword);