DzLox

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

commit e135a269eda05077d26a9875de5dcfc2081fb3e9
parent a5a57b943bcf842e45dbf99f72e5621e757594e9
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Sun, 23 Oct 2022 18:18:42 +0200

Fix bugs discovered by running the testsuite

Diffstat:
A.gitmodules | 3+++
Acraftinginterpreters | 1+
Mdlox/source/app.d | 2+-
Mdlox/source/cls.d | 11+++++++----
Mdlox/source/environment.d | 9++++++++-
Mdlox/source/instance.d | 2--
Mdlox/source/interpreter.d | 5+++--
Mdlox/source/parser.d | 2+-
Mdlox/source/resolver.d | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
9 files changed, 81 insertions(+), 35 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "craftinginterpreters"] + path = craftinginterpreters + url = https://github.com/munificent/craftinginterpreters diff --git a/craftinginterpreters b/craftinginterpreters @@ -0,0 +1 @@ +Subproject commit 01e6f5b8f3e5dfa65674c2f9cf4700d73ab41cf8 diff --git a/dlox/source/app.d b/dlox/source/app.d @@ -123,7 +123,7 @@ class Lox { static void report(int line, string where, string msg, bool warning = false) { - writefln("[line %s] %s%s: %s", line, warning ? "Warning" : "Error", where, msg); + stderr.writefln("[line %s] %s%s: %s", line, warning ? "Warning" : "Error", where, msg); if (!warning) this.hadError = true; } } diff --git a/dlox/source/cls.d b/dlox/source/cls.d @@ -40,16 +40,19 @@ class Cls : Instance, Callable { 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()); + instances[$-1].bindMethods("this", instances[0]); + if (instances.length > 1) { + foreach_reverse(ins; instances.slide(2)) { + ins[0].bindMethods("super", ins[1]); + ins[0].bindMethods("this", instances[0]); + ins[0].addFields(ins[1].getFields()); + } } return instances[0]; } Variant call(Interpreter interpreter, Variant[] arguments) { Instance instance = instatiate(interpreter); - instance.bindMethods("this"); instance.addFields(super.getFields()); instance.construct(arguments, interpreter); return Variant(instance); diff --git a/dlox/source/environment.d b/dlox/source/environment.d @@ -1,5 +1,7 @@ import std.variant; +import core.exception; import token; +import std.stdio; import error; class Environment { @@ -46,7 +48,12 @@ class Environment { } Variant getAt(TokenI name, size_t distance) { - return ancestor(distance).values[name.lexeme]; + try { + return ancestor(distance).values[name.lexeme]; + } catch (RangeError err) { + stderr.writefln("Resolved variable %s not at distance %s!", name.lexeme, distance); + throw err; + } } void assignAt(TokenI name, Variant value, size_t distance) { diff --git a/dlox/source/instance.d b/dlox/source/instance.d @@ -43,10 +43,8 @@ class Instance { } void updateFields(Variant[string] newf) { - writeln("ASD"); foreach(name, value; newf.byPair) { fields[name] = value; - writeln(name); } } diff --git a/dlox/source/interpreter.d b/dlox/source/interpreter.d @@ -40,6 +40,7 @@ class Interpreter : StmtVisitor, ExprVisitor { globals = new Environment(); environment = globals; locals = null; + value = Variant(null); globals.define("clock", Variant(new class Callable { ulong arity() { @@ -280,10 +281,10 @@ class Interpreter : StmtVisitor, ExprVisitor { value = left.get!(double) <= right.get!(double); break; case EQUAL_EQUAL: - value = left == right; + value = left.type() == right.type() && left == right; break; case BANG_EQUAL: - value = left != right; + value = left.type() != right.type() || left != right; break; case COMMA: value = right; diff --git a/dlox/source/parser.d b/dlox/source/parser.d @@ -340,7 +340,7 @@ class Parser { private Expr unary() { with (TokenType) if (match(BANG, MINUS, AST)) { TokenI operator = previous(); - Expr right = primary(); + Expr right = unary(); if (operator.type == AST) { if(Grouping gr = cast(Grouping)right) { right = gr.expression; diff --git a/dlox/source/resolver.d b/dlox/source/resolver.d @@ -14,9 +14,11 @@ class Resolver : StmtVisitor, ExprVisitor { private Interpreter interpreter; private SList!(VarRef[string]) scopes; + private SList!(BlockType) blocks; private FunctionType currentFunction = FunctionType.NONE; private LoopType currentLoop = LoopType.NONE; private ClassType currentClass = ClassType.NONE; + private BlockType currentBlock = BlockType.EXEC; private enum VarState { DECLARED, @@ -39,12 +41,19 @@ class Resolver : StmtVisitor, ExprVisitor { private enum ClassType { NONE, CLASS, + METACLASS, SUBCLASS } + private enum BlockType { + EXEC, + CALL + } + this(Interpreter interpreter) { this.interpreter = interpreter; this.scopes = SList!(VarRef[string])(); + this.blocks = SList!(BlockType)(); } void resolve(T)(T[] statements...) { @@ -53,8 +62,9 @@ class Resolver : StmtVisitor, ExprVisitor { } } - void beginScope() { + void beginScope(BlockType type = BlockType.EXEC) { scopes.insertFront(null); + blocks.insertFront(type); } private void endScope() { @@ -65,6 +75,7 @@ class Resolver : StmtVisitor, ExprVisitor { } } scopes.removeFront(); + blocks.removeFront(); } private TokenI mkToken(string name, VarRef vref) { @@ -86,14 +97,24 @@ class Resolver : StmtVisitor, ExprVisitor { scopes.front()[name.lexeme].state = VarState.DEFINED; } - private void resolveLocal(Expr expr, TokenI name) { + private void resolveLocal(Expr expr, TokenI name, bool lvalue = false) { foreach(i, sco; scopes[].enumerate()) { if(auto local = name.lexeme in sco) { - if ((*local).state == VarState.DECLARED) { - Lox.error(name, "Attempt to reference undefined local variable"); + if (!lvalue && (*local).state == VarState.DECLARED) { + bool call = false; + foreach(bl; blocks[].take(i)) { + if (bl == BlockType.CALL) { + call = true; + break; + } + } + if (!call) Lox.error(name, "Attempt to reference undefined local variable"); } interpreter.resolve(expr, i); - (*local).state = VarState.REFERENCED; + if ((*local).state != VarState.REFERENCED) { + (*local).state = lvalue ? VarState.DEFINED : VarState.REFERENCED; + } + break; } } } @@ -144,9 +165,11 @@ class Resolver : StmtVisitor, ExprVisitor { if (currentFunction == FunctionType.NONE) { Lox.error(_return.keyword, "Can't return from top-level code."); } - if (_return.value !is null) resolve(_return.value); - else if (currentFunction == FunctionType.INITIALIZER) { - Lox.error(_return.keyword, "Can't return value from initializer method"); + if (_return.value !is null) { + if (currentFunction == FunctionType.INITIALIZER) { + Lox.error(_return.keyword, "Can't return value from initializer method"); + } + resolve(_return.value); } } @@ -176,10 +199,7 @@ class Resolver : StmtVisitor, ExprVisitor { } void visit(Assign expr) { resolve(expr.value); - resolveLocal(expr, expr.name); - if(!scopes.empty() && expr.name.lexeme in scopes.front()) { - scopes.front()[expr.name.lexeme].state = VarState.DEFINED; - } + resolveLocal(expr, expr.name, true); } void visit(Logical _logical) { @@ -202,7 +222,7 @@ class Resolver : StmtVisitor, ExprVisitor { private void resolveFunction(Function expr, FunctionType type) { FunctionType enclosingFunction = currentFunction; currentFunction = type; - beginScope(); + beginScope(BlockType.CALL); foreach (param; expr.params) { declare(param); define(param); @@ -214,25 +234,42 @@ class Resolver : StmtVisitor, ExprVisitor { void visit(Class cl) { ClassType enclosingClass = currentClass; + currentClass = ClassType.METACLASS; + + beginScope(); + scopes.front()["this"] = VarRef(VarState.REFERENCED, 0); + + resolveMethods(cl.classmethods); + + endScope(); + currentClass = ClassType.CLASS; if(cl.superclass) { resolve(cl.superclass); currentClass = ClassType.SUBCLASS; - } - - if(cl.superclass) { beginScope(); scopes.front()["super"] = VarRef(VarState.REFERENCED, 0); } - beginScope(); + + beginScope(BlockType.CALL); scopes.front()["this"] = VarRef(VarState.REFERENCED, 0); - foreach(i, method; chain(cl.methods, cl.classmethods).enumerate()) { + + resolveMethods(cl.methods); + + endScope(); + + if(cl.superclass) endScope(); + currentClass = enclosingClass; + } + + void resolveMethods(Var[] methods) { + foreach(method; methods) { if (auto fun = cast(Function) method.initializer) { FunctionType declaration = FunctionType.METHOD; if (method.name.lexeme == "init") { declaration = FunctionType.INITIALIZER; - if (i >= cl.methods.length && fun.params.length > 0) { + if (currentClass == ClassType.METACLASS && fun.params.length > 0) { Lox.error(fun.params[0], "class initializer cannot have arguments"); } } @@ -241,9 +278,7 @@ class Resolver : StmtVisitor, ExprVisitor { resolve(method.initializer); } } - endScope(); - if(cl.superclass) endScope(); - currentClass = enclosingClass; + } void visit(Get get) { @@ -257,7 +292,6 @@ class Resolver : StmtVisitor, ExprVisitor { void visit(This th) { if (currentClass == ClassType.NONE - || currentFunction == FunctionType.FUN || currentFunction == FunctionType.NONE) { Lox.error(th.keyword, "'This' not allowed here"); return; @@ -267,7 +301,6 @@ class Resolver : StmtVisitor, ExprVisitor { void visit(Super th) { if (currentClass != ClassType.SUBCLASS - || currentFunction == FunctionType.FUN || currentFunction == FunctionType.NONE) { Lox.error(th.keyword, "'Super' not allowed here"); return;