From 0a892df176374687da46b442bc2bcbe45c8809a9 Mon Sep 17 00:00:00 2001 From: Mariano Riefolo Date: Fri, 28 Jun 2024 11:12:26 +0200 Subject: [PATCH] Parsing expressions chapter --- src/lox/Lox.java | 17 ++++- src/lox/Parser.java | 170 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 src/lox/Parser.java diff --git a/src/lox/Lox.java b/src/lox/Lox.java index 8262a47..03f3b87 100644 --- a/src/lox/Lox.java +++ b/src/lox/Lox.java @@ -46,9 +46,12 @@ public class Lox { Scanner scanner = new Scanner(source); List tokens = scanner.scanTokens(); - for (Token token : tokens) { - System.out.println(token); - } + Parser parser = new Parser(tokens); + Expr expression = parser.parse(); + + if (hadError) return; + + System.out.println(new AstPrinter().print(expression)); } static void error(int line, String message) { @@ -59,4 +62,12 @@ public class Lox { System.err.println("[line " + line + "] Error" + where + ": " + message); hadError = true; } + + static void error(Token token, String message) { + if (token.type == TokenType.EOF) { + report(token.line, " at end", message); + } else { + report(token.line, " at '" + token.lexeme + "'", message); + } + } } diff --git a/src/lox/Parser.java b/src/lox/Parser.java new file mode 100644 index 0000000..9ebd54a --- /dev/null +++ b/src/lox/Parser.java @@ -0,0 +1,170 @@ +package lox; + +import java.util.List; + +import static lox.TokenType.*; + +public class Parser { + private static class ParseError extends RuntimeException {} + + private final List tokens; + private int current; + + Parser(List tokens) { + this.tokens = tokens; + } + + Expr parse() { + try { + return expression(); + } catch (ParseError error) { + return null; + } + } + + private Expr expression() { + return equality(); + } + + private Expr equality() { + Expr expr = comparison(); + + while (match(BANG_EQUAL, EQUAL_EQUAL)) { + Token operator = previous(); + Expr right = comparison(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + private Expr comparison() { + Expr expr = term(); + + while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) { + Token operator = previous(); + Expr right = term(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + private Expr term() { + Expr expr = factor(); + + while (match(MINUS, PLUS)) { + Token operator = previous(); + Expr right = factor(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + private Expr factor() { + Expr expr = unary(); + + while (match(SLASH, STAR)) { + Token operator = previous(); + Expr right = unary(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + private Expr unary() { + if (match(BANG, MINUS)) { + Token operator = previous(); + Expr right = unary(); + return new Expr.Unary(operator, right); + } + + return primary(); + } + + private Expr primary() { + if (match(FALSE)) return new Expr.Literal(false); + if (match(TRUE)) return new Expr.Literal(true); + if (match(NIL)) return new Expr.Literal(null); + + if (match(NUMBER, STRING)) { + return new Expr.Literal(previous().literal); + } + + if (match(LEFT_PAREN)) { + Expr expr = expression(); + consume(RIGHT_PAREN, "Expect ')' after expression."); + return new Expr.Grouping(expr); + } + + throw error(peek(), "Expect expression"); + } + + private boolean match(TokenType... types) { + for (TokenType type : types) { + if (check(type)) { + advance(); + return true; + } + } + + return false; + } + + private Token consume(TokenType type, String message) { + if (check(type)) return advance(); + + throw error(peek(), message); + } + + private void synchronize() { + advance(); + + while (!isAtEnd()) { + if (previous().type == SEMICOLON) return; + + switch (peek().type) { + case CLASS: + case FUN: + case VAR: + case FOR: + case IF: + case WHILE: + case PRINT: + case RETURN: + return; + } + + advance(); + } + } + + private boolean check(TokenType type) { + if (isAtEnd()) return false; + return peek().type == type; + } + + private Token advance() { + if (!isAtEnd()) current++; + return previous(); + } + + private boolean isAtEnd() { + return peek().type == EOF; + } + + private Token peek() { + return tokens.get(current); + } + + private Token previous() { + return tokens.get(current - 1); + } + + private ParseError error(Token token, String message) { + Lox.error(token, message); + return new ParseError(); + } +}