From 5327b3746a42bdd6a0f259b381545f71586c256c Mon Sep 17 00:00:00 2001 From: ctsk <9384305+ctsk@users.noreply.github.com> Date: Sat, 10 Sep 2022 11:08:21 +0200 Subject: [PATCH] [jlox] Add branching --- .../main/java/xyz/ctsk/lox/AstPrinter.java | 17 +++++++ .../main/java/xyz/ctsk/lox/Interpreter.java | 23 ++++++++++ .../src/main/java/xyz/ctsk/lox/Parser.java | 44 ++++++++++++++++++- .../main/java/xyz/ctsk/lox/package-info.java | 2 + 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java b/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java index 60ae97c..8bade14 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/AstPrinter.java @@ -43,6 +43,15 @@ public class AstPrinter { return expr.value().toString(); } + @Override + public String visitLogicalExpr(Expr.Logical expr) { + var left = expr.left().accept(this); + var op = expr.operator().lexeme(); + var right = expr.right().accept(this); + + return reverse ? wrap(left, right, op) : wrap(op, left, right); + } + @Override public String visitUnaryExpr(Expr.Unary expr) { var op = expr.operator().lexeme(); @@ -91,6 +100,14 @@ public class AstPrinter { return Interpreter.stringify(expr.value()); } + @Override + public String visitLogicalExpr(Expr.Logical expr) { + return String.join(" ", + expr.left().accept(this), + expr.operator().lexeme(), + expr.right().accept(this)); + } + @Override public String visitUnaryExpr(Expr.Unary expr) { return expr.operator().lexeme() + expr.right().accept(this); diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java b/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java index a40c565..b08512f 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/Interpreter.java @@ -55,6 +55,16 @@ public class Interpreter implements Expr.Visitor, Stmt.Visitor { return null; } + @Override + public Void visitIfStmt(Stmt.If stmt) { + if (isTruthy(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()); @@ -121,6 +131,19 @@ public class Interpreter implements Expr.Visitor, Stmt.Visitor { return expr.value(); } + @Override + public Object visitLogicalExpr(Expr.Logical expr) { + var leftVal = evaluate(expr.left()); + + if (expr.operator().type() == TokenType.OR) { + if (isTruthy(leftVal)) return leftVal; + } else { + if (!isTruthy(leftVal)) return leftVal; + } + + return evaluate(expr.right()); + } + @Override public Object visitUnaryExpr(Expr.Unary expr) { var right = evaluate(expr.right()); diff --git a/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java b/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java index 10b924c..d2e3323 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/Parser.java @@ -15,6 +15,7 @@ import static xyz.ctsk.lox.TokenType.*; * | statement ; * varDecl → "var" IDENTIFIER ( "=" expression )? ";" ; * statement → exprStmt + * | ifStmt * | printStmt * | block ; * block → "{" declaration* "}" ; @@ -23,7 +24,9 @@ import static xyz.ctsk.lox.TokenType.*; * expression → equality ; * expression → assignment ; * assignment → IDENTIFIER "=" assignment - * | equality ; + * | logic_or ; + * logic_or → logic_and ( "or" logic_and )* ; + * logic_and → equality ( "and" equality )* ; * equality → comparison ( ( "!=" | "==" ) comparison )* ; * comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ; * term → factor ( ( "-" | "+" ) factor )* ; @@ -76,10 +79,23 @@ public class Parser { private Stmt statement() { + if (match(IF)) return ifStatement(); if (match(PRINT)) return printStatement(); if (match(LEFT_BRACE)) return new Stmt.Block(blockStatement()); return expressionStatement(); } + + private Stmt ifStatement() { + consume(LEFT_PAREN, "Expect '(' after 'if'."); + Expr condition = expression(); + consume(RIGHT_PAREN, "Expect ')' after condition."); + + Stmt thenBranch = statement(); + Stmt elseBranch = match(ELSE) ? statement() : null; + + return new Stmt.If(condition, thenBranch, elseBranch); + } + private Stmt printStatement() { Expr value = expression(); consume(SEMICOLON, "Expect ';' after value."); @@ -108,7 +124,7 @@ public class Parser { } private Expr assignment() { - Expr expr = equality(); + Expr expr = or(); if (match(EQUAL)) { Token equals = previous(); @@ -126,6 +142,30 @@ public 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/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java b/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java index b98049c..153f3be 100644 --- a/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java +++ b/jlox/lox/src/main/java/xyz/ctsk/lox/package-info.java @@ -5,6 +5,7 @@ @Rule(head = "Binary", body = {"Expr left", "Token operator", "Expr right"}), @Rule(head = "Grouping", body = {"Expr expression"}), @Rule(head = "Literal", body = {"Object value"}), + @Rule(head = "Logical", body = {"Expr left", "Token operator", "Expr right"}), @Rule(head = "Unary", body = {"Token operator", "Expr right"}), @Rule(head = "Variable", body = {"Token name"}) }), @@ -12,6 +13,7 @@ rules = { @Rule(head = "Block", body = {"List statements"}), @Rule(head = "Expression", body = {"Expr expression"}), + @Rule(head = "If", body = {"Expr condition", "Stmt thenBranch", "Stmt elseBranch"}), @Rule(head = "Print", body = {"Expr expression"}), @Rule(head = "Var", body = {"Token name", "Expr initializer"}) })