From 1a485ebcb10269870ccb19b9a98e954f07e33f4c Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Wed, 12 Feb 2025 11:07:13 +0100 Subject: [PATCH] chapter 9 in java --- .../java/ch/vanwa/lox_interpreter/Expr.java | 19 ++++ .../ch/vanwa/lox_interpreter/Interpreter.java | 33 ++++++ .../java/ch/vanwa/lox_interpreter/Parser.java | 104 +++++++++++++++++- .../java/ch/vanwa/lox_interpreter/Stmt.java | 36 ++++++ .../main/java/ch/vanwa/tool/GenerateAst.java | 6 +- 5 files changed, 196 insertions(+), 2 deletions(-) diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java index c23d4a3..02909ff 100644 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java +++ b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java @@ -12,6 +12,8 @@ abstract class Expr { R visitLiteralExpr(Literal expr); + R visitLogicalExpr(Logical expr); + R visitUnaryExpr(Unary expr); R visitVariableExpr(Variable expr); @@ -75,6 +77,23 @@ abstract class Expr { final Object value; } + static class Logical extends Expr { + Logical(Expr left, Token operator, Expr right) { + this.left = left; + this.operator = operator; + this.right = right; + } + + @Override + R accept(Visitor visitor) { + return visitor.visitLogicalExpr(this); + } + + final Expr left; + final Token operator; + final Expr right; + } + static class Unary extends Expr { Unary(Token operator, Expr right) { this.operator = operator; diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java index f27fade..2221a0f 100644 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java +++ b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java @@ -24,6 +24,21 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return expr.value; } + @Override + public Object visitLogicalExpr(Expr.Logical expr) { + Object 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); + } + @Override public Object visitGroupingExpr(Expr.Grouping expr) { return evaluate(expr.expression); @@ -67,6 +82,16 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return null; } + @Override + public Void visitIfStmt(Stmt.If stmt) { + if (isTruthy(evaluate(stmt.condition))) { + execute(stmt.thenBranch); + } else if (stmt.elseBranch != null) { + execute(stmt.elseBranch); + } + return null; + } + @Override public Void visitPrintStmt(Stmt.Print stmt) { Object value = evaluate(stmt.expression); @@ -85,6 +110,14 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return null; } + @Override + public Void visitWhileStmt(Stmt.While stmt) { + while (isTruthy(evaluate(stmt.condition))) { + execute(stmt.body); + } + return null; + } + @Override public Object visitAssignExpr(Expr.Assign expr) { Object value = evaluate(expr.value); diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java index 7b56515..81c05aa 100644 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java +++ b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java @@ -1,6 +1,7 @@ package ch.vanwa.lox_interpreter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static ch.vanwa.lox_interpreter.TokenType.*; @@ -69,9 +70,18 @@ class Parser { } private Stmt statement() { + if (match(FOR)) { + return forStatement(); + } + if (match(IF)) { + return ifStatement(); + } if (match(PRINT)) { return printStatement(); } + if (match(WHILE)) { + return whileStatement(); + } if (match(LEFT_BRACE)) { return new Stmt.Block(block()); } @@ -79,6 +89,65 @@ class Parser { return expressionStatement(); } + private Stmt forStatement() { + consume(LEFT_PAREN, "Expect '(' after 'for'."); + + Stmt initializer; + if (match(SEMICOLON)) { + initializer = null; + } else if (match(VAR)) { + initializer = varDeclaration(); + } else { + initializer = expressionStatement(); + } + + Expr condition = null; + if (!check(SEMICOLON)) { + condition = expression(); + } + consume(SEMICOLON, "Expect ';' after loop condition."); + + Expr increment = null; + if (!check(RIGHT_PAREN)) { + increment = expression(); + } + consume(RIGHT_PAREN, "Expect ')' after for clauses."); + + Stmt body = statement(); + + if (increment != null) { + body = new Stmt.Block( + Arrays.asList( + body, + new Stmt.Expression(increment))); + } + + if (condition == null) { + condition = new Expr.Literal(true); + } + body = new Stmt.While(condition, body); + + if (initializer != null) { + body = new Stmt.Block(Arrays.asList(initializer, body)); + } + + return body; + } + + private Stmt ifStatement() { + consume(LEFT_PAREN, "Expect '(' after 'if'."); + Expr condition = expression(); + consume(RIGHT_PAREN, "Expect ')' after if condition."); + + Stmt thenBranch = statement(); + Stmt elseBranch = null; + if (match(ELSE)) { + elseBranch = statement(); + } + + return new Stmt.If(condition, thenBranch, elseBranch); + } + private Stmt printStatement() { Expr value = expression(); consume(SEMICOLON, "Expect ';' after value."); @@ -97,6 +166,15 @@ class Parser { return new Stmt.Var(name, initializer); } + private Stmt whileStatement() { + consume(LEFT_PAREN, "Expect '(' after 'while'."); + Expr condition = expression(); + consume(RIGHT_PAREN, "Expect ')' after condition."); + Stmt body = statement(); + + return new Stmt.While(condition, body); + } + private Stmt expressionStatement() { Expr expr = expression(); consume(SEMICOLON, "Expect ';' after expression."); @@ -115,7 +193,7 @@ class Parser { } private Expr assignment() { - Expr expr = equality(); + Expr expr = or(); if (match(EQUAL)) { Token equals = previous(); @@ -132,6 +210,30 @@ class Parser { return expr; } + private Expr or() { + Expr expr = and(); + + while (match(OR)) { + Token operator = previous(); + Expr right = and(); + expr = new Expr.Logical(expr, operator, right); + } + + return expr; + } + + private Expr and() { + Expr expr = equality(); + + while (match(AND)) { + Token operator = previous(); + Expr right = equality(); + expr = new Expr.Logical(expr, operator, right); + } + + return expr; + } + private Expr equality() { Expr expr = comparison(); diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java index a6546c7..c7f41a1 100644 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java +++ b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java @@ -8,9 +8,13 @@ abstract class Stmt { R visitExpressionStmt(Expression stmt); + R visitIfStmt(If stmt); + R visitPrintStmt(Print stmt); R visitVarStmt(Var stmt); + + R visitWhileStmt(While stmt); } static class Block extends Stmt { @@ -39,6 +43,23 @@ abstract class Stmt { final Expr expression; } + static class If extends Stmt { + If(Expr condition, Stmt thenBranch, Stmt elseBranch) { + this.condition = condition; + this.thenBranch = thenBranch; + this.elseBranch = elseBranch; + } + + @Override + R accept(Visitor visitor) { + return visitor.visitIfStmt(this); + } + + final Expr condition; + final Stmt thenBranch; + final Stmt elseBranch; + } + static class Print extends Stmt { Print(Expr expression) { this.expression = expression; @@ -67,5 +88,20 @@ abstract class Stmt { final Expr initializer; } + static class While extends Stmt { + While(Expr condition, Stmt body) { + this.condition = condition; + this.body = body; + } + + @Override + R accept(Visitor visitor) { + return visitor.visitWhileStmt(this); + } + + final Expr condition; + final Stmt body; + } + abstract R accept(Visitor visitor); } diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java b/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java index 9e3020d..8d1e96d 100644 --- a/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java +++ b/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java @@ -18,14 +18,18 @@ public class GenerateAst { "Binary : Expr left, Token operator, Expr right", "Grouping : Expr expression", "Literal : Object value", + "Logical : Expr left, Token operator, Expr right", "Unary : Token operator, Expr right", "Variable : Token name")); defineAst(outputDir, "Stmt", Arrays.asList( "Block : List statements", "Expression : Expr expression", + "If : Expr condition, Stmt thenBranch," + + " Stmt elseBranch", "Print : Expr expression", - "Var : Token name, Expr initializer")); + "Var : Token name, Expr initializer", + "While : Expr condition, Stmt body")); } private static void defineAst(