DzLox

:)
git clone https://git.sr.ht/~ashymad/DzLox
Log | Files | Refs | Submodules | LICENSE

commit 1b34a0bc4690256fa3a4f42244856bc6c81c2461
parent 436ebdb358c0c907ac0253c9ad42a2b98244d73a
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Sun, 16 Oct 2022 04:12:49 +0200

Inheritance. Done

Diffstat:
Aboston.lox | 9+++++++++
Agaze.lox | 28++++++++++++++++++++++++++++
Msource/ast.d | 3++-
Msource/astprinter.d | 3+++
Msource/cls.d | 33++++++++++++++++++++++++---------
Msource/fun.d | 8++++----
Msource/instance.d | 19+++++++++++++------
Msource/interpreter.d | 15++++++++++++++-
Msource/parser.d | 14+++++++++++---
Msource/resolver.d | 28+++++++++++++++++++++++++---
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);