DzLox

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

commit fbc500e66a03bed8c5210fe009ab02a7235ca276
parent 80437b724f906283996193ab6c834446600e502f
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Mon,  3 Oct 2022 23:30:29 +0200

Control flow finished

Diffstat:
Msource/app.d | 7++++++-
Msource/astgen.d | 12+++++++++---
Msource/interpreter.d | 23+++++++++++++++++++++--
Msource/keywords.d | 1+
Msource/parser.d | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msource/stmt.d | 2++
Msource/tokentype.d | 1+
7 files changed, 120 insertions(+), 9 deletions(-)

diff --git a/source/app.d b/source/app.d @@ -32,6 +32,12 @@ class Lox { static bool hadError = false; static bool hadRuntimeError = false; + Interpreter interpreter; + + this() { + interpreter = new Interpreter(); + } + extern(C) static void completion(const char *buf, linenoiseCompletions *lc) { auto bufs = fromStringz(buf); auto lastsp = 1 + lastIndexOfAny(bufs, [ @@ -77,7 +83,6 @@ class Lox { auto expression = parser.parse(); if(hadError) return; - Interpreter interpreter = new Interpreter(); auto result = interpreter.interpret(expression); if(hadError) return; diff --git a/source/astgen.d b/source/astgen.d @@ -7,9 +7,15 @@ import std.algorithm.iteration; template GenVisitee(immutable string basename, immutable string[][] names) { const char[] GenVisitee = format("interface %s { void accept(Visitor visitor); }", basename) ~ names.map!(name => format( - "class %s:%s{%s;this(%s){%s;}void accept(Visitor visitor){visitor.visit(this);}}", - name[0], basename, name[1..$].join(";"), name[1..$].join(","), - name[1..$].map!(s => "this." ~ [s.split(" ")[$-1]].replicate(2).join("=")).join(";"))).join(); + "class %s:%s{%sthis(%s){%s}void accept(Visitor visitor){visitor.visit(this);}}", + name[0], + basename, + name[1..$].join(";") ~ (name.length > 1 ? ";" : ""), + name[1..$].join(","), + name[1..$].map!(s => + "this." ~ [s.split(" ")[$-1]].replicate(2).join("=") + ).join(";") ~ (name.length > 1 ? ";" : "") + )).join(); } template GenVisitor(immutable string[][] names) { diff --git a/source/interpreter.d b/source/interpreter.d @@ -14,9 +14,15 @@ import environment; class Interpreter : stmt.Visitor, expr.Visitor { Variant value; - static Environment environment; + Environment environment; - static this() { + private class BreakCalled : Exception { + this() { + super("", "", 0); + } + } + + this() { environment = new Environment(); } @@ -74,6 +80,19 @@ class Interpreter : stmt.Visitor, expr.Visitor { } } + void visit(Break _) { + throw new BreakCalled(); + } + + void visit(While stmt) { + while (isTruthy(evaluate(stmt.condition))) { + try { + execute(stmt.bod); + } catch (BreakCalled _) break; + } + value = null; + } + void executeBlock(Stmt[] statements, Environment environment) { Environment previous = this.environment; try { diff --git a/source/keywords.d b/source/keywords.d @@ -21,6 +21,7 @@ shared static this() { "true": TRUE, "var": VAR, "while": WHILE, + "break": BREAK, "ast": AST, ]; } diff --git a/source/parser.d b/source/parser.d @@ -12,9 +12,16 @@ declaration → varDecl | statement ; varDecl → "var" IDENTIFIER ( "=" expression )? ";" ; statement → exprStmt - | printStmt + | forStmt | ifStmt + | printStmt + | whileStmt + | breakStmt | block ; +forStmt → "for" "(" ( varDecl | exprStmt | ";" ) + expression? ";" + expression? ")" statement ; +whileStmt → "while" "(" expression ")" statement ; ifStmt → "if" "(" expression ")" statement ( "else" statement )? ; block → "{" declaration* "}" ; @@ -46,6 +53,7 @@ class Parser { private Array!TokenI tokens; private int current = 0; + private bool breakAllowed = false; this(Array!TokenI tokens) { this.tokens = tokens; @@ -79,14 +87,72 @@ class Parser { } private Stmt matchStatement() { + if (match(TokenType.FOR)) + return forStatement(); + if (match(TokenType.IF)) + return ifStatement(); if (match(TokenType.PRINT)) return statement!(Print)(expression()); + if (match(TokenType.WHILE)) + return whileStatement(); if (match(TokenType.LEFT_BRACE)) return statement!(Block)(block()); - if (match(TokenType.IF)) - return ifStatement(); + if (match(TokenType.BREAK)) + return breakStatement(); return statement!(Expression)(expression()); } + + private Stmt breakStatement() { + if (breakAllowed) { + return statement!(Break)(); + } else { + throw error(previous(), "Break not allowed here."); + } + } + + private Stmt forStatement() { + consume(TokenType.LEFT_PAREN, "Expect '(' after 'for'."); + + Stmt initializer; + + if (match(TokenType.SEMICOLON)) { + initializer = null; + } else if (match(TokenType.VAR)) { + initializer = varDeclaration(); + } else { + initializer = statement!(Expression)(expression()); + } + + Expr condition = null; + if (!check(TokenType.SEMICOLON)) { + condition = expression(); + } + consume(TokenType.SEMICOLON, "Expect ';' after loop condition."); + + Expr increment = null; + if (!check(TokenType.RIGHT_PAREN)) { + increment = expression(); + } + consume(TokenType.RIGHT_PAREN, "Expect ')' after for clauses."); + + breakAllowed = true; + Stmt bod = matchStatement(); + breakAllowed = false; + + if (increment !is null) { + bod = new Block([bod, new Expression(increment)]); + } + if (condition is null) { + condition = new Literal(Variant(true)); + } + + bod = new While(condition, bod); + if (initializer !is null) { + bod = new Block([initializer, bod]); + } + + return bod; + } private Stmt ifStatement() { consume(TokenType.LEFT_PAREN, "Expect '(' after 'if'."); @@ -103,6 +169,17 @@ class Parser { return statement!(If)(condition, thenBranch, elseBranch); } + private Stmt whileStatement() { + consume(TokenType.LEFT_PAREN, "Expect '(' after 'while'."); + Expr condition = expression(); + consume(TokenType.RIGHT_PAREN, "Expect ')' after condition."); + breakAllowed = true; + Stmt bod = matchStatement(); + breakAllowed = false; + + return statement!(While)(condition, bod); + } + private Stmt[] block() { Stmt[] statements; diff --git a/source/stmt.d b/source/stmt.d @@ -9,6 +9,8 @@ static immutable string[][] statements = [ ["Var", "TokenI name", "Expr initializer"], ["Block", "Stmt[] statements"], ["If", "Expr condition", "Stmt thenBranch", "Stmt elseBranch"], + ["While", "Expr condition", "Stmt bod"], + ["Break"] ]; mixin(GenVisitor!(statements) ~ GenVisitee!("Stmt", statements)); diff --git a/source/tokentype.d b/source/tokentype.d @@ -16,6 +16,7 @@ enum TokenType { // Keywords. AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NIL, OR, PRINT, RETURN, SUPER, THIS, TRUE, VAR, WHILE, AST, + BREAK, EOF }