DzLox

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

commit fccd5ccafaeed8f7692998ca6ddf74e4598c93c1
parent 7f9410d7da372919957061986600b2e10624898c
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Fri, 14 Oct 2022 13:26:23 +0200

Check if variables are referenced

Diffstat:
Msource/interpreter.d | 2+-
Msource/resolver.d | 41++++++++++++++++++++++++++++++++---------
2 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/source/interpreter.d b/source/interpreter.d @@ -86,7 +86,7 @@ class Interpreter : StmtVisitor, ExprVisitor { } void visit(Var stmt) { - Variant variant; + Variant variant = Variant(null); if (stmt.initializer !is null) { variant = evaluate(stmt.initializer); } diff --git a/source/resolver.d b/source/resolver.d @@ -4,13 +4,24 @@ import std.container; import std.range; import token; import app; +import std.format; +import std.typecons : Tuple; class Resolver : StmtVisitor, ExprVisitor { + + private alias VarRef = Tuple!(VarState, "state", int, "line"); + private Interpreter interpreter; - private SList!(bool[string]) scopes; + private SList!(VarRef[string]) scopes; private FunctionType currentFunction = FunctionType.NONE; private LoopType currentLoop = LoopType.NONE; + private enum VarState { + DECLARED, + DEFINED, + REFERENCED + } + private enum FunctionType { NONE, FUN @@ -23,7 +34,7 @@ class Resolver : StmtVisitor, ExprVisitor { this(Interpreter interpreter) { this.interpreter = interpreter; - this.scopes = SList!(bool[string])(); + this.scopes = SList!(VarRef[string])(); } void resolve(T)(T[] statements...) { @@ -37,23 +48,27 @@ class Resolver : StmtVisitor, ExprVisitor { } private void endScope() { + foreach(sco; scopes.front().byPair) { + if(sco.value.state != VarState.REFERENCED) { + Lox.error(sco.value.line, format("Variable '%s' declared but never referenced", sco.key)); + } + } scopes.removeFront(); } private void declare(TokenI name) { if(scopes.empty()) return; - auto sco = scopes.front(); - if(name.lexeme in sco) { + if(name.lexeme in scopes.front()) { Lox.error(name, "Already a variable with this name in this scope."); } - sco[name.lexeme] = false; + scopes.front()[name.lexeme] = VarRef(VarState.DECLARED, name.line); } private void define(TokenI name) { if(scopes.empty()) return; - scopes.front()[name.lexeme] = true; + scopes.front()[name.lexeme].state = VarState.DEFINED; } private void resolveLocal(Expr expr, TokenI name) { @@ -76,8 +91,8 @@ class Resolver : StmtVisitor, ExprVisitor { declare(stmt.name); if (stmt.initializer !is null) { resolve(stmt.initializer); + define(stmt.name); } - define(stmt.name); } void visit(Block stmt) { @@ -135,15 +150,23 @@ class Resolver : StmtVisitor, ExprVisitor { } void visit(Variable expr) { - if (!scopes.empty() && !scopes.front().get(expr.name.lexeme, true)) { - Lox.error(expr.name, "Can't read local variable in its own initializer."); + if (!scopes.empty() && + scopes.front().get(expr.name.lexeme, VarRef(VarState.DEFINED, 0)).state == VarState.DECLARED) { + Lox.error(expr.name, "Attempt to resolve undefined local variable."); } resolveLocal(expr, expr.name); + + if(!scopes.empty() && expr.name.lexeme in scopes.front()) { + scopes.front()[expr.name.lexeme].state = VarState.REFERENCED; + } } 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; + } } void visit(Logical _logical) {