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:
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;