Control Flow 9

This commit is contained in:
nazrin 2025-06-01 21:08:11 +00:00
parent e749367886
commit f0ff14e5b5
6 changed files with 133 additions and 14 deletions

View file

@ -16,6 +16,7 @@ abstract class Expr{
R visit(Binary expr); R visit(Binary expr);
R visit(Grouping expr); R visit(Grouping expr);
R visit(Literal expr); R visit(Literal expr);
R visit(Logical expr);
R visit(Unary expr); R visit(Unary expr);
R visit(Variable expr); R visit(Variable expr);
} }
@ -47,6 +48,12 @@ abstract class Expr{
TValue value; TValue value;
mixin defCtorAndAccept; mixin defCtorAndAccept;
} }
static class Logical : typeof(this){
Expr left;
Token operator;
Expr right;
mixin defCtorAndAccept;
}
static class Unary : typeof(this){ static class Unary : typeof(this){
Token operator; Token operator;
Expr right; Expr right;

View file

@ -75,6 +75,12 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
void visit(Stmt.Expression stmt){ void visit(Stmt.Expression stmt){
evaluate(stmt.expression); evaluate(stmt.expression);
} }
void visit(Stmt.If stmt){
if(isTruthy(evaluate(stmt.condition)))
execute(stmt.thenBranch);
else if(stmt.elseBranch)
execute(stmt.elseBranch);
}
void visit(Stmt.Print stmt){ void visit(Stmt.Print stmt){
TValue value = evaluate(stmt.expression); TValue value = evaluate(stmt.expression);
writeln(tvalueToString(value)); writeln(tvalueToString(value));
@ -82,6 +88,10 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
void visit(Stmt.Var stmt){ void visit(Stmt.Var stmt){
environment.define(stmt.name.lexeme, stmt.initialiser is null ? TValue.nil(0) : evaluate(stmt.initialiser)); environment.define(stmt.name.lexeme, stmt.initialiser is null ? TValue.nil(0) : evaluate(stmt.initialiser));
} }
void visit(Stmt.While stmt){
while(isTruthy(evaluate(stmt.condition)))
execute(stmt.body);
}
TValue visit(Expr.Literal expr){ TValue visit(Expr.Literal expr){
return expr.value; return expr.value;
@ -101,6 +111,17 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
assert(0); assert(0);
} }
} }
TValue visit(Expr.Logical expr){
TValue left = evaluate(expr.left);
if(expr.operator.type == TokenType.OR){
if(isTruthy(left))
return left;
} else {
if(!isTruthy(left))
return left;
}
return evaluate(expr.right);
}
TValue visit(Expr.Binary expr){ TValue visit(Expr.Binary expr){
TValue left = evaluate(expr.left); TValue left = evaluate(expr.left);
TValue right = evaluate(expr.right); TValue right = evaluate(expr.right);

View file

@ -15,6 +15,12 @@ import jlox.interpreter;
import jlox.expr; import jlox.expr;
import jlox.stmt; import jlox.stmt;
private class MainException : Exception{
this(){
super(null);
}
}
static class Lox{ static class Lox{
private static Interpreter interpreter; private static Interpreter interpreter;
static this(){ static this(){
@ -56,7 +62,7 @@ static class Lox{
static void runFile(string path){ static void runFile(string path){
string bytes = readText(path); string bytes = readText(path);
run(bytes); run(bytes);
enforce(!hadError && !hadRuntimeError); enforce(!hadError && !hadRuntimeError, new MainException);
} }
static void runPrompt(){ static void runPrompt(){
while(true){ while(true){
@ -75,10 +81,14 @@ int main(string[] argv){
auto args = new Program("lox") auto args = new Program("lox")
.add(new Argument("path").optional.acceptsFiles) .add(new Argument("path").optional.acceptsFiles)
.parse(argv); .parse(argv);
try{
if(args.arg("path")) if(args.arg("path"))
Lox.runFile(args.arg("path")); Lox.runFile(args.arg("path"));
else else
Lox.runPrompt(); Lox.runPrompt();
} catch(MainException rte){
return Lox.hadError ? 1 : 2;
}
return 0; return 0;
} }

View file

@ -57,7 +57,7 @@ class Parser{
Lox.error(token, message); Lox.error(token, message);
return new ParseError("hello"); return new ParseError("hello");
} }
private void synchronize(){ private void synchronise(){
advance(); advance();
with(TokenType) while(!isAtEnd){ with(TokenType) while(!isAtEnd){
if(previous().type == SEMICOLON) if(previous().type == SEMICOLON)
@ -89,11 +89,61 @@ class Parser{
consume(TokenType.SEMICOLON, "Expect ';' after expression."); consume(TokenType.SEMICOLON, "Expect ';' after expression.");
return new Stmt.Expression(expr); return new Stmt.Expression(expr);
} }
private Stmt ifStatement(){
consume(TokenType.LEFT_PAREN, "Expect '(' after 'if'.");
Expr condition = expression();
consume(TokenType.RIGHT_PAREN, "Expect ')' after if condition.");
Stmt thenBranch = statement();
Stmt elseBranch = null;
if(match(TokenType.ELSE))
elseBranch = statement();
return new Stmt.If(condition, thenBranch, elseBranch);
}
private Stmt forStatement(){
consume(TokenType.LEFT_PAREN, "Expect '(' after 'for'.");
Stmt initialiser;
if(match(TokenType.SEMICOLON))
initialiser = null;
else if(match(TokenType.VAR))
initialiser = varDeclaration();
else
initialiser = expressionStatement();
Expr condition = check(TokenType.SEMICOLON) ? new Expr.Literal(TValue.bln(true)) : expression();
consume(TokenType.SEMICOLON, "Expect ';' after loop condition.");
Expr increment;
if(!check(TokenType.RIGHT_PAREN))
increment = expression();
consume(TokenType.RIGHT_PAREN, "Expect ')' after for clauses.");
Stmt body = statement();
if(increment !is null)
body = new Stmt.Block([ body, new Stmt.Expression(increment) ]);
body = new Stmt.While(condition, body);
if(initialiser !is null)
body = new Stmt.Block([ initialiser, body ]);
return body;
}
private Stmt whileStatement(){
consume(TokenType.LEFT_PAREN, "Expect '(' after 'while'.");
Expr condition = expression();
consume(TokenType.RIGHT_PAREN, "Expect ')' after condition.");
Stmt body = statement();
return new Stmt.While(condition, body);
}
private Stmt statement(){ private Stmt statement(){
if(match(TokenType.LEFT_BRACE)) if(match(TokenType.IF))
return new Stmt.Block(block()); return ifStatement();
if(match(TokenType.FOR))
return forStatement();
if(match(TokenType.WHILE))
return whileStatement();
if(match(TokenType.PRINT)) if(match(TokenType.PRINT))
return printStatement(); return printStatement();
if(match(TokenType.LEFT_BRACE))
return new Stmt.Block(block());
return expressionStatement(); return expressionStatement();
} }
private Stmt[] block(){ private Stmt[] block(){
@ -107,7 +157,7 @@ class Parser{
return assignment(); return assignment();
} }
private Expr assignment(){ private Expr assignment(){
Expr expr = equality(); Expr expr = or();
if(match(TokenType.EQUAL)){ if(match(TokenType.EQUAL)){
Token equals = previous(); Token equals = previous();
Expr value = assignment(); Expr value = assignment();
@ -117,6 +167,24 @@ class Parser{
} }
return expr; return expr;
} }
private Expr or(){
Expr expr = and();
while(match(TokenType.OR)){
Token operator = previous();
Expr right = and();
expr = new Expr.Logical(expr, operator, right);
}
return expr;
}
private Expr and(){
Expr expr = equality();
while(match(TokenType.AND)){
Token operator = previous();
Expr right = equality();
expr = new Expr.Logical(expr, operator, right);
}
return expr;
}
private Expr equality(){ private Expr equality(){
Expr expr = comparison(); Expr expr = comparison();
while(match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL)){ while(match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL)){
@ -181,11 +249,11 @@ class Parser{
} }
private Stmt varDeclaration(){ private Stmt varDeclaration(){
Token name = consume(TokenType.IDENTIFIER, "Expect variable name."); Token name = consume(TokenType.IDENTIFIER, "Expect variable name.");
Expr initializer = null; Expr initialiser = null;
if(match(TokenType.EQUAL)) if(match(TokenType.EQUAL))
initializer = expression(); initialiser = expression();
consume(TokenType.SEMICOLON, "Expect ';' after variable declaration."); consume(TokenType.SEMICOLON, "Expect ';' after variable declaration.");
return new Stmt.Var(name, initializer); return new Stmt.Var(name, initialiser);
} }
private Stmt declaration(){ private Stmt declaration(){
try { try {
@ -193,7 +261,7 @@ class Parser{
return varDeclaration(); return varDeclaration();
return statement(); return statement();
} catch(ParseError error){ } catch(ParseError error){
synchronize(); synchronise();
return null; return null;
} }
} }

View file

@ -72,7 +72,7 @@ class Scanner {
case "class": return CLASS; case "class": return CLASS;
case "else": return ELSE; case "else": return ELSE;
case "false": return FALSE; case "false": return FALSE;
case ":or": return FOR; case "for": return FOR;
case "fun": return FUN; case "fun": return FUN;
case "if": return IF; case "if": return IF;
case "nil": return NIL; case "nil": return NIL;

View file

@ -13,8 +13,10 @@ abstract class Stmt{
interface Visitor(R){ interface Visitor(R){
R visit(Block expr); R visit(Block expr);
R visit(Expression expr); R visit(Expression expr);
R visit(If expr);
R visit(Print expr); R visit(Print expr);
R visit(Var expr); R visit(Var expr);
R visit(While expr);
} }
private alias rTypes = AliasSeq!(void); private alias rTypes = AliasSeq!(void);
static foreach(T; rTypes) static foreach(T; rTypes)
@ -33,6 +35,12 @@ abstract class Stmt{
Expr expression; Expr expression;
mixin defCtorAndAccept; mixin defCtorAndAccept;
} }
static class If : typeof(this){
Expr condition;
Stmt thenBranch;
Stmt elseBranch;
mixin defCtorAndAccept;
}
static class Print : typeof(this){ static class Print : typeof(this){
Expr expression; Expr expression;
mixin defCtorAndAccept; mixin defCtorAndAccept;
@ -42,5 +50,10 @@ abstract class Stmt{
Expr initialiser; Expr initialiser;
mixin defCtorAndAccept; mixin defCtorAndAccept;
} }
static class While : typeof(this){
Expr condition;
Stmt body;
mixin defCtorAndAccept;
}
} }