|
|
@@ -0,0 +1,187 @@
|
|
|
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
|
|
|
+ * All right reserved.*/
|
|
|
+package com.craftinginterpreters.lox;
|
|
|
+
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static com.craftinginterpreters.lox.TokenType.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author simon
|
|
|
+ * @date 2023-06-05 14:56
|
|
|
+ * @desc <p>| Name | Operators | Associates | </p>
|
|
|
+ * <p>|------------|-----------|------------| </p>
|
|
|
+ * | Equality | == != | Left |
|
|
|
+ * | Comparison | > >= < <= | Left |
|
|
|
+ * | Term | - + | Left |
|
|
|
+ * | Factor | / * | Left |
|
|
|
+ * | Unary | ! - | Right |
|
|
|
+ * </p>
|
|
|
+ * <p>
|
|
|
+ * <p>
|
|
|
+ * expression → equality ; <p>
|
|
|
+ * equality → comparison ( ( "!=" | "==" ) comparison )* ; <p>
|
|
|
+ * comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ; <p>
|
|
|
+ * term → factor ( ( "-" | "+" ) factor )* ; <p>
|
|
|
+ * factor → unary ( ( "/" | "*" ) unary )* ; <p>
|
|
|
+ * unary → ( "!" | "-" ) unary | primary ; <p>
|
|
|
+ * primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ; <p>
|
|
|
+ * </p>
|
|
|
+ */
|
|
|
+public class Parser {
|
|
|
+ private static class ParseError extends RuntimeException {
|
|
|
+ }
|
|
|
+
|
|
|
+ private final List<Token> tokens;
|
|
|
+ private int current = 0;
|
|
|
+
|
|
|
+ Parser(List<Token> tokens) {
|
|
|
+ this.tokens = tokens;
|
|
|
+ }
|
|
|
+
|
|
|
+ Expr parse() {
|
|
|
+ try {
|
|
|
+ return expression();
|
|
|
+ } catch (ParseError e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // expression → equality ;
|
|
|
+ private Expr expression() {
|
|
|
+ return equality();
|
|
|
+ }
|
|
|
+
|
|
|
+ // equality → comparison ( ( "!=" | "==" ) comparison )* ;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
|
|
|
+ private Expr comparison() {
|
|
|
+ Expr expr = term();
|
|
|
+ while (match(GRATER, GRATER_EQUAL, LESS, LESS_EQUAL)) {
|
|
|
+ Token operator = previous();
|
|
|
+ Expr right = term();
|
|
|
+ expr = new Expr.Binary(expr, operator, right);
|
|
|
+ }
|
|
|
+ return expr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // term → factor ( ( "-" | "+" ) factor )* ;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // factor → unary ( ( "/" | "*" ) unary )* ;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // unary → ( "!" | "-" ) unary | primary
|
|
|
+ private Expr unary() {
|
|
|
+ if (match(BANG, MINUS)) {
|
|
|
+ Token operator = previous();
|
|
|
+ Expr right = unary();
|
|
|
+ return new Expr.Unary(operator, right);
|
|
|
+ }
|
|
|
+ return primary();
|
|
|
+ }
|
|
|
+
|
|
|
+ // primary → NUMBER | STRING | "true" | "false" | "nil"
|
|
|
+ // | "(" expression ")" ;
|
|
|
+ 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_PARAM)) {
|
|
|
+ Expr expr = expression();
|
|
|
+ consume(RIGHT_PARAM, "Expect ') after expression.");
|
|
|
+ return new Expr.Grouping(expr);
|
|
|
+ }
|
|
|
+
|
|
|
+ throw error(peek(), "Expect expression.");
|
|
|
+ }
|
|
|
+
|
|
|
+ private Token consume(TokenType type, String message) {
|
|
|
+ if (check(type)) return advance();
|
|
|
+ throw error(peek(), message);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private boolean match(TokenType... types) {
|
|
|
+ for (TokenType type : types) {
|
|
|
+ if (check(type)) {
|
|
|
+ advance();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Token advance() {
|
|
|
+ if (!isAtEnd()) current++;
|
|
|
+ return previous();
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean check(TokenType type) {
|
|
|
+ if (isAtEnd()) return false;
|
|
|
+ return peek().type == type;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void synchronize() {
|
|
|
+ advance();
|
|
|
+
|
|
|
+ while (!isAtEnd()) {
|
|
|
+ if (previous().type == SEMICOLON) return;
|
|
|
+
|
|
|
+ switch (peek().type) {
|
|
|
+ case CLASS, FUN, VAR, FOR, IF, PRINT, WHILE, RETURN -> {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ default -> advance();
|
|
|
+ }
|
|
|
+ } // End while
|
|
|
+ }
|
|
|
+}
|