diff --git a/src/lox/AstPrinter.java b/src/lox/AstPrinter.java index 61e7f7a..d22c950 100644 --- a/src/lox/AstPrinter.java +++ b/src/lox/AstPrinter.java @@ -27,6 +27,11 @@ public class AstPrinter implements Expr.Visitor { return expr.value.toString(); } + @Override + public String visitLogicalExpr(Expr.Logical expr) { + return parethesize(expr.operator.lexeme, expr.left, expr.right); + } + @Override public String visitUnaryExpr(Expr.Unary expr) { return parethesize(expr.operator.lexeme, expr.right); diff --git a/src/lox/Expr.java b/src/lox/Expr.java index 77fd402..fb1aedb 100644 --- a/src/lox/Expr.java +++ b/src/lox/Expr.java @@ -8,6 +8,7 @@ abstract class Expr { R visitBinaryExpr(Binary expr); R visitGroupingExpr(Grouping expr); R visitLiteralExpr(Literal expr); + R visitLogicalExpr(Logical expr); R visitUnaryExpr(Unary expr); R visitVariableExpr(Variable expr); } @@ -65,6 +66,22 @@ 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/src/lox/Interpreter.java b/src/lox/Interpreter.java index 820173e..304a600 100644 --- a/src/lox/Interpreter.java +++ b/src/lox/Interpreter.java @@ -79,6 +79,19 @@ public class Interpreter implements Expr.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 visitUnaryExpr(Expr.Unary expr) { Object right = evaluate(expr.right); @@ -171,6 +184,16 @@ public class Interpreter implements Expr.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); @@ -188,4 +211,12 @@ public class Interpreter implements Expr.Visitor, environment.define(stmt.name.lexeme, value); return null; } + + @Override + public Void visitWhileStmt(Stmt.While stmt) { + while (isTruthy(evaluate(stmt.condition))) { + execute(stmt.body); + } + return null; + } } diff --git a/src/lox/Parser.java b/src/lox/Parser.java index 50832e6..9fc58ea 100644 --- a/src/lox/Parser.java +++ b/src/lox/Parser.java @@ -1,12 +1,14 @@ package lox; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static lox.TokenType.*; public class Parser { - private static class ParseError extends RuntimeException {} + private static class ParseError extends RuntimeException { + } private final List tokens; private int current; @@ -40,12 +42,71 @@ public 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()); 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."); @@ -64,6 +125,15 @@ public 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."); @@ -82,18 +152,42 @@ public class Parser { } private Expr assignment() { - Expr expr = equality(); + Expr expr = or(); if (match(EQUAL)) { Token equals = previous(); Expr value = assignment(); if (expr instanceof Expr.Variable) { - Token name = ((Expr.Variable)expr).name; + Token name = ((Expr.Variable) expr).name; return new Expr.Assign(name, value); } - error(equals, "Invalid assignment target."); + throw error(equals, "Invalid assignment target."); + } + + 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; diff --git a/src/lox/Stmt.java b/src/lox/Stmt.java index 8014fe4..e23ec6c 100644 --- a/src/lox/Stmt.java +++ b/src/lox/Stmt.java @@ -6,8 +6,10 @@ abstract class Stmt { interface Visitor { R visitBlockStmt(Block 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 { Block(List statements) { @@ -33,6 +35,22 @@ 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; @@ -59,6 +77,20 @@ abstract class Stmt { final Token name; 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/src/tool/GenerateAst.java b/src/tool/GenerateAst.java index 79a01bd..7142b39 100644 --- a/src/tool/GenerateAst.java +++ b/src/tool/GenerateAst.java @@ -18,6 +18,7 @@ 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" )); @@ -25,8 +26,11 @@ public class GenerateAst { 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" )); }