|
|
@@ -11,424 +11,473 @@ import java.util.List;
|
|
|
/**
|
|
|
* @author simon
|
|
|
* @date 2023-06-05 14:56
|
|
|
- * @desc <p>| Name | Operators | Associates |
|
|
|
- * <p>|------------|-----------|------------| | Equality | == != | Left | | Comparison | > >= <
|
|
|
- * <= | Left | | Term | - + | Left | | Factor | / * | Left | | Unary | ! - | Right |
|
|
|
- * <p>
|
|
|
- * <p>expression → assignment;
|
|
|
- * <p>assignment → IDENTIFIER "=" assignment | 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>
|
|
|
+ * @desc
|
|
|
+ * <p>| Name | Operators | Associates |
|
|
|
+ * <p>|------------|-----------|------------| | Equality | == != | Left | | Comparison | > >= <
|
|
|
+ * <= | Left | | Term | - + | Left | | Factor | / * | Left | | Unary | ! - | Right |
|
|
|
+ * <p>
|
|
|
+ * <p>expression → assignment;
|
|
|
+ * <p>assignment → IDENTIFIER "=" assignment | 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>
|
|
|
*/
|
|
|
public class Parser {
|
|
|
- private final List<Token> tokens;
|
|
|
- private int current = 0;
|
|
|
-
|
|
|
- Parser(List<Token> tokens) {
|
|
|
- this.tokens = tokens;
|
|
|
- }
|
|
|
-
|
|
|
- // program → declaration* EOF ;
|
|
|
- List<Stmt> parse() {
|
|
|
- List<Stmt> statements = new ArrayList<>();
|
|
|
- while (!isAtEnd()) {
|
|
|
- statements.add(declaration());
|
|
|
- }
|
|
|
- return statements;
|
|
|
- }
|
|
|
-
|
|
|
- // declaration → classDecl | funDecl | varDecl | statement ;
|
|
|
- private Stmt declaration() {
|
|
|
- try {
|
|
|
- if (match(CLASS)) return classDeclaration();
|
|
|
- if (match(FUN)) return function("function");
|
|
|
- if (match(VAR)) return varDecl();
|
|
|
- return statement();
|
|
|
- } catch (ParseError error) {
|
|
|
- synchronize();
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // statement → exprStmt | forStmt | ifStmt | printStmt | returnStmt | whileStmt | block;
|
|
|
- private Stmt statement() {
|
|
|
- if (match(FOR)) return forStatement();
|
|
|
- if (match(IF)) return ifStatement();
|
|
|
- if (match(PRINT)) return printStatement();
|
|
|
- if (match(RETURN)) return returnStatement();
|
|
|
- if (match(WHILE)) return whileStatement();
|
|
|
- if (match(LEFT_BRACE)) return new Stmt.Block(block());
|
|
|
- return expressionStatement();
|
|
|
- }
|
|
|
-
|
|
|
- private Stmt forStatement() {
|
|
|
- consume(LEFT_PARAM, "Expect '(' after 'for'.");
|
|
|
-
|
|
|
- Stmt initializer;
|
|
|
- if (match(SEMICOLON)) {
|
|
|
- initializer = null;
|
|
|
- } else if (match(VAR)) {
|
|
|
- initializer = varDecl();
|
|
|
- } else {
|
|
|
- initializer = expressionStatement();
|
|
|
- }
|
|
|
-
|
|
|
- Expr condition = null;
|
|
|
- if (!check(SEMICOLON)) {
|
|
|
- condition = expression();
|
|
|
- }
|
|
|
- consume(SEMICOLON, "Expect ';' after loop condition.");
|
|
|
-
|
|
|
- Expr increment = null;
|
|
|
- if (!check(SEMICOLON)) {
|
|
|
- increment = expression();
|
|
|
- }
|
|
|
- consume(RIGHT_PARAM, "Expect ')' after for clauses.");
|
|
|
-
|
|
|
- Stmt body = statement();
|
|
|
-
|
|
|
- // desugaring
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- // whileStmt → "while" "(" expression ")" statement ;
|
|
|
- private Stmt whileStatement() {
|
|
|
- consume(LEFT_PARAM, "Expect '(' after 'while'.");
|
|
|
- Expr condition = expression();
|
|
|
- consume(RIGHT_PARAM, "Expect ')' after 'while'.");
|
|
|
- Stmt body = statement();
|
|
|
-
|
|
|
- return new Stmt.While(condition, body);
|
|
|
- }
|
|
|
-
|
|
|
- // ifStmt → "if" "(" expression ")" statement ( "else" statement)? ;
|
|
|
- private Stmt ifStatement() {
|
|
|
- consume(LEFT_PARAM, "Expect '(' after if.");
|
|
|
- Expr condition = expression();
|
|
|
- consume(RIGHT_PARAM, "Expect ')' after if condition.");
|
|
|
-
|
|
|
- Stmt thenBranch = statement();
|
|
|
- Stmt elseBranch = null;
|
|
|
- if (match(ELSE)) elseBranch = statement();
|
|
|
-
|
|
|
- return new Stmt.If(condition, thenBranch, elseBranch);
|
|
|
- }
|
|
|
-
|
|
|
- // block → "{" declaration* "}" ;
|
|
|
- private List<Stmt> block() {
|
|
|
- List<Stmt> statements = new ArrayList<>();
|
|
|
-
|
|
|
- while (!check(RIGHT_BRACE) && !isAtEnd()) {
|
|
|
- statements.add(declaration());
|
|
|
+ private final List<Token> tokens;
|
|
|
+ private int current = 0;
|
|
|
+
|
|
|
+ Parser(List<Token> tokens) {
|
|
|
+ this.tokens = tokens;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * program → declaration* EOF ;
|
|
|
+ *
|
|
|
+ * @return List<Stmt>
|
|
|
+ */
|
|
|
+ List<Stmt> parse() {
|
|
|
+ List<Stmt> statements = new ArrayList<>();
|
|
|
+ while (!isAtEnd()) {
|
|
|
+ statements.add(declaration());
|
|
|
+ }
|
|
|
+ return statements;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * declaration → classDecl | funDecl | varDecl | statement ;
|
|
|
+ *
|
|
|
+ * @return Stmt
|
|
|
+ */
|
|
|
+ private Stmt declaration() {
|
|
|
+ try {
|
|
|
+ if (match(CLASS)) return classDeclaration();
|
|
|
+ if (match(FUN)) return function("function");
|
|
|
+ if (match(VAR)) return varDecl();
|
|
|
+ return statement();
|
|
|
+ } catch (ParseError error) {
|
|
|
+ synchronize();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * statement → exprStmt | forStmt | ifStmt | printStmt | returnStmt | whileStmt | block;
|
|
|
+ *
|
|
|
+ * @return Stmt
|
|
|
+ */
|
|
|
+ private Stmt statement() {
|
|
|
+ if (match(FOR)) return forStatement();
|
|
|
+ if (match(IF)) return ifStatement();
|
|
|
+ if (match(PRINT)) return printStatement();
|
|
|
+ if (match(RETURN)) return returnStatement();
|
|
|
+ if (match(WHILE)) return whileStatement();
|
|
|
+ if (match(LEFT_BRACE)) return new Stmt.Block(block());
|
|
|
+ return expressionStatement();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * <code>for (var i = 0; i < 5; i++ ) {}</code>
|
|
|
+ *
|
|
|
+ * @return Stmt
|
|
|
+ */
|
|
|
+ private Stmt forStatement() {
|
|
|
+ consume(LEFT_PARAM, "Expect '(' after 'for'.");
|
|
|
+
|
|
|
+ Stmt initializer;
|
|
|
+ if (match(SEMICOLON)) {
|
|
|
+ initializer = null;
|
|
|
+ } else if (match(VAR)) {
|
|
|
+ initializer = varDecl();
|
|
|
+ } else {
|
|
|
+ initializer = expressionStatement();
|
|
|
+ }
|
|
|
+
|
|
|
+ Expr condition = null;
|
|
|
+ if (!check(SEMICOLON)) {
|
|
|
+ condition = expression();
|
|
|
+ }
|
|
|
+ consume(SEMICOLON, "Expect ';' after loop condition.");
|
|
|
+
|
|
|
+ Expr increment = null;
|
|
|
+ if (!check(SEMICOLON)) {
|
|
|
+ increment = expression();
|
|
|
+ }
|
|
|
+ consume(RIGHT_PARAM, "Expect ')' after for clauses.");
|
|
|
+
|
|
|
+ Stmt body = statement();
|
|
|
+
|
|
|
+ // desugaring
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * whileStmt → "while" "(" expression ")" statement ;
|
|
|
+ *
|
|
|
+ * @return Stmt
|
|
|
+ */
|
|
|
+ private Stmt whileStatement() {
|
|
|
+ consume(LEFT_PARAM, "Expect '(' after 'while'.");
|
|
|
+ Expr condition = expression();
|
|
|
+ consume(RIGHT_PARAM, "Expect ')' after 'while'.");
|
|
|
+ Stmt body = statement();
|
|
|
+
|
|
|
+ return new Stmt.While(condition, body);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ifStmt → "if" "(" expression ")" statement ( "else" statement)? ;
|
|
|
+ *
|
|
|
+ * @return Stmt
|
|
|
+ */
|
|
|
+ private Stmt ifStatement() {
|
|
|
+ consume(LEFT_PARAM, "Expect '(' after if.");
|
|
|
+ Expr condition = expression();
|
|
|
+ consume(RIGHT_PARAM, "Expect ')' after if condition.");
|
|
|
+
|
|
|
+ Stmt thenBranch = statement();
|
|
|
+ Stmt elseBranch = null;
|
|
|
+ if (match(ELSE)) elseBranch = statement();
|
|
|
+
|
|
|
+ return new Stmt.If(condition, thenBranch, elseBranch);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * block → "{" declaration* "}" ;
|
|
|
+ *
|
|
|
+ * @return List<Stmt>
|
|
|
+ */
|
|
|
+ private List<Stmt> block() {
|
|
|
+ List<Stmt> statements = new ArrayList<>();
|
|
|
+
|
|
|
+ while (!check(RIGHT_BRACE) && !isAtEnd()) {
|
|
|
+ statements.add(declaration());
|
|
|
+ }
|
|
|
+
|
|
|
+ consume(RIGHT_BRACE, "Expect '}' after block.");
|
|
|
+ return statements;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * exprStmt → expression ";" ;
|
|
|
+ *
|
|
|
+ * @return Stmt
|
|
|
+ */
|
|
|
+ private Stmt expressionStatement() {
|
|
|
+ Expr expr = expression();
|
|
|
+ consume(SEMICOLON, "Expect ';' after expression.");
|
|
|
+ return new Stmt.Expression(expr);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Stmt classDeclaration() {
|
|
|
+ Token name = consume(IDENTIFIER, "Expect class name.");
|
|
|
+ consume(LEFT_BRACE, "Expect '{' before class body.");
|
|
|
+
|
|
|
+ List<Stmt.Function> methods = new ArrayList<>();
|
|
|
+ while (!check(RIGHT_BRACE) && !isAtEnd()) {
|
|
|
+ methods.add(function("method"));
|
|
|
+ }
|
|
|
+
|
|
|
+ consume(RIGHT_BRACE, "Expect '}' after class body.");
|
|
|
+
|
|
|
+ return new Stmt.Class(name, methods);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Stmt.Function function(String kind) {
|
|
|
+ Token name = consume(IDENTIFIER, "Expect " + kind + " name.");
|
|
|
+ consume(LEFT_PARAM, "Expect '(' after " + kind + " name.");
|
|
|
+ List<Token> parameters = new ArrayList<>();
|
|
|
+ if (!check(RIGHT_PARAM)) {
|
|
|
+ do {
|
|
|
+ if (parameters.size() >= 255) {
|
|
|
+ error(peek(), "Can't have more than 255 parameters.");
|
|
|
}
|
|
|
-
|
|
|
- consume(RIGHT_BRACE, "Expect '}' after block.");
|
|
|
- return statements;
|
|
|
- }
|
|
|
-
|
|
|
- // exprStmt → expression ";" ;
|
|
|
- private Stmt expressionStatement() {
|
|
|
- Expr expr = expression();
|
|
|
- consume(SEMICOLON, "Expect ';' after expression.");
|
|
|
- return new Stmt.Expression(expr);
|
|
|
+ parameters.add(consume(IDENTIFIER, "Expect parameter name."));
|
|
|
+ } while (match(COMMA));
|
|
|
}
|
|
|
+ consume(RIGHT_PARAM, "Expect ')' after parameters.");
|
|
|
+
|
|
|
+ // body wrap
|
|
|
+ consume(LEFT_BRACE, "Expect '{' before " + kind + " body.");
|
|
|
+ List<Stmt> body = block();
|
|
|
+ return new Stmt.Function(name, parameters, body);
|
|
|
+ }
|
|
|
|
|
|
- private Stmt classDeclaration() {
|
|
|
- Token name = consume(IDENTIFIER, "Expect class name.");
|
|
|
- consume(LEFT_BRACE, "Expect '{' before class body.");
|
|
|
-
|
|
|
- List<Stmt.Function> methods = new ArrayList<>();
|
|
|
- while (!check(RIGHT_BRACE) && !isAtEnd()) {
|
|
|
- methods.add(function("method"));
|
|
|
+ // printStmt → "print" expression ";" ;
|
|
|
+ private Stmt printStatement() {
|
|
|
+ Expr value = expression();
|
|
|
+ consume(SEMICOLON, "Expect ';' after value.");
|
|
|
+ return new Stmt.Print(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // returnStatement → "return" expression? ";" ;
|
|
|
+ private Stmt returnStatement() {
|
|
|
+ Token keyword = previous();
|
|
|
+ Expr value = null;
|
|
|
+ if (!check(SEMICOLON)) {
|
|
|
+ value = expression();
|
|
|
+ }
|
|
|
+
|
|
|
+ consume(SEMICOLON, "Expect ';' after return value.");
|
|
|
+ return new Stmt.Return(keyword, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // varDecl → "var" IDENTIFIER ( "=" expression )? ";" ;
|
|
|
+ private Stmt varDecl() {
|
|
|
+ Token name = consume(IDENTIFIER, "Expect variable name.");
|
|
|
+
|
|
|
+ Expr initializer = null;
|
|
|
+ if (match(EQUAL)) {
|
|
|
+ initializer = expression();
|
|
|
+ }
|
|
|
+
|
|
|
+ consume(SEMICOLON, "Expect ';' after variable declaration.");
|
|
|
+ return new Stmt.Var(name, initializer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // expression → assignment ;
|
|
|
+ private Expr expression() {
|
|
|
+ return assignment();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * assignment → (call ".")? IDENTIFIER "=" assignment | equality | logic_or
|
|
|
+ *
|
|
|
+ * @return Expr
|
|
|
+ */
|
|
|
+ private Expr assignment() {
|
|
|
+ Expr expr = or();
|
|
|
+ if (match(EQUAL)) {
|
|
|
+ Token equals = previous();
|
|
|
+ Expr value = assignment();
|
|
|
+
|
|
|
+ if (expr instanceof Expr.Variable) {
|
|
|
+ Token name = ((Expr.Variable) expr).name;
|
|
|
+ return new Expr.Assign(name, value);
|
|
|
+ }else if (expr instanceof Expr.Get get) {
|
|
|
+ return new Expr.Set(get.object, get.name, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ error(equals, "Invalid assignment target.");
|
|
|
+ }
|
|
|
+ return expr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // logic_or → logic_and ( "or" logic_and )* ;
|
|
|
+ private Expr or() {
|
|
|
+ Expr expr = and();
|
|
|
+
|
|
|
+ while (match(OR)) {
|
|
|
+ Token operator = previous();
|
|
|
+ Expr right = and();
|
|
|
+ expr = new Expr.Logical(expr, operator, right);
|
|
|
+ }
|
|
|
+
|
|
|
+ return expr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // logic_and → equality ( "and" equality )* ;
|
|
|
+ private Expr and() {
|
|
|
+ Expr expr = equality();
|
|
|
+
|
|
|
+ while (match(AND)) {
|
|
|
+ Token operator = previous();
|
|
|
+ Expr right = equality();
|
|
|
+ expr = new Expr.Logical(expr, operator, right);
|
|
|
+ }
|
|
|
+
|
|
|
+ return expr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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 | call
|
|
|
+ *
|
|
|
+ * @return Expr
|
|
|
+ */
|
|
|
+ private Expr unary() {
|
|
|
+ if (match(BANG, MINUS)) {
|
|
|
+ Token operator = previous();
|
|
|
+ Expr right = unary();
|
|
|
+ return new Expr.Unary(operator, right);
|
|
|
+ }
|
|
|
+ return call();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * call → primary ( "(" arguments? ")" )* | "." IDENTIFIER;
|
|
|
+ *
|
|
|
+ * @return Expr
|
|
|
+ */
|
|
|
+ private Expr call() {
|
|
|
+ Expr expr = primary();
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ if (match(LEFT_PARAM)) {
|
|
|
+ expr = finishCall(expr);
|
|
|
+ } else if (match(DOT)) {
|
|
|
+ Token name = consume(IDENTIFIER, "Expect property name after '.'.");
|
|
|
+ expr = new Expr.Get(expr, name);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return expr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // arguments → expression ( "," expression)* ;
|
|
|
+ private Expr finishCall(Expr callee) {
|
|
|
+ List<Expr> arguments = new ArrayList<>();
|
|
|
+ if (!check(RIGHT_PARAM)) {
|
|
|
+ do {
|
|
|
+ if (arguments.size() >= 255) {
|
|
|
+ error(peek(), "Can't have more than 255 arguments.");
|
|
|
}
|
|
|
-
|
|
|
- consume(RIGHT_BRACE, "Expect '}' after class body.");
|
|
|
-
|
|
|
- return new Stmt.Class(name, methods);
|
|
|
+ arguments.add(expression());
|
|
|
+ } while (match(COMMA));
|
|
|
}
|
|
|
+ Token paren = consume(RIGHT_PARAM, "Expect ')' after arguments");
|
|
|
|
|
|
- private Stmt.Function function(String kind) {
|
|
|
- Token name = consume(IDENTIFIER, "Expect " + kind + " name.");
|
|
|
- consume(LEFT_PARAM, "Expect '(' after " + kind + " name.");
|
|
|
- List<Token> parameters = new ArrayList<>();
|
|
|
- if (!check(RIGHT_PARAM)) {
|
|
|
- do {
|
|
|
- if (parameters.size() >= 255) {
|
|
|
- error(peek(), "Can't have more than 255 parameters.");
|
|
|
- }
|
|
|
- parameters.add(consume(IDENTIFIER, "Expect parameter name."));
|
|
|
- } while (match(COMMA));
|
|
|
- }
|
|
|
- consume(RIGHT_PARAM, "Expect ')' after parameters.");
|
|
|
+ return new Expr.Call(callee, paren, arguments);
|
|
|
+ }
|
|
|
|
|
|
- // body wrap
|
|
|
- consume(LEFT_BRACE, "Expect '{' before " + kind + " body.");
|
|
|
- List<Stmt> body = block();
|
|
|
- return new Stmt.Function(name, parameters, body);
|
|
|
- }
|
|
|
+ // primary → NUMBER | STRING | "true" | "false" | "nil"
|
|
|
+ // | "(" expression ")" | IDENTIFIER ;
|
|
|
+ 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);
|
|
|
|
|
|
- // printStmt → "print" expression ";" ;
|
|
|
- private Stmt printStatement() {
|
|
|
- Expr value = expression();
|
|
|
- consume(SEMICOLON, "Expect ';' after value.");
|
|
|
- return new Stmt.Print(value);
|
|
|
+ if (match(NUMBER, STRING)) {
|
|
|
+ return new Expr.Literal(previous().literal);
|
|
|
}
|
|
|
|
|
|
- // returnStatement → "return" expression? ";" ;
|
|
|
- private Stmt returnStatement() {
|
|
|
- Token keyword = previous();
|
|
|
- Expr value = null;
|
|
|
- if (!check(SEMICOLON)) {
|
|
|
- value = expression();
|
|
|
- }
|
|
|
-
|
|
|
- consume(SEMICOLON, "Expect ';' after return value.");
|
|
|
- return new Stmt.Return(keyword, value);
|
|
|
+ if (match(LEFT_PARAM)) {
|
|
|
+ Expr expr = expression();
|
|
|
+ consume(RIGHT_PARAM, "Expect ') after expression.");
|
|
|
+ return new Expr.Grouping(expr);
|
|
|
}
|
|
|
|
|
|
- // varDecl → "var" IDENTIFIER ( "=" expression )? ";" ;
|
|
|
- private Stmt varDecl() {
|
|
|
- Token name = consume(IDENTIFIER, "Expect variable name.");
|
|
|
-
|
|
|
- Expr initializer = null;
|
|
|
- if (match(EQUAL)) {
|
|
|
- initializer = expression();
|
|
|
- }
|
|
|
-
|
|
|
- consume(SEMICOLON, "Expect ';' after variable declaration.");
|
|
|
- return new Stmt.Var(name, initializer);
|
|
|
+ if (match(IDENTIFIER)) {
|
|
|
+ return new Expr.Variable(previous());
|
|
|
}
|
|
|
|
|
|
- // expression → assignment ;
|
|
|
- private Expr expression() {
|
|
|
- return assignment();
|
|
|
- }
|
|
|
+ throw error(peek(), "Expect expression.");
|
|
|
+ }
|
|
|
|
|
|
- // assignment → IDENTIFIER "=" assignment | equality | logic_or
|
|
|
- private Expr assignment() {
|
|
|
- Expr expr = or();
|
|
|
- if (match(EQUAL)) {
|
|
|
- Token equals = previous();
|
|
|
- Expr value = assignment();
|
|
|
+ private Token consume(TokenType type, String message) {
|
|
|
+ if (check(type)) return advance();
|
|
|
+ throw error(peek(), message);
|
|
|
+ }
|
|
|
|
|
|
- if (expr instanceof Expr.Variable) {
|
|
|
- Token name = ((Expr.Variable) expr).name;
|
|
|
- return new Expr.Assign(name, value);
|
|
|
- }
|
|
|
-
|
|
|
- error(equals, "Invalid assignment target.");
|
|
|
- }
|
|
|
- return expr;
|
|
|
- }
|
|
|
-
|
|
|
- // logic_or → logic_and ( "or" logic_and )* ;
|
|
|
- private Expr or() {
|
|
|
- Expr expr = and();
|
|
|
-
|
|
|
- while (match(OR)) {
|
|
|
- Token operator = previous();
|
|
|
- Expr right = and();
|
|
|
- expr = new Expr.Logical(expr, operator, right);
|
|
|
- }
|
|
|
-
|
|
|
- return expr;
|
|
|
- }
|
|
|
-
|
|
|
- // logic_and → equality ( "and" equality )* ;
|
|
|
- private Expr and() {
|
|
|
- Expr expr = equality();
|
|
|
-
|
|
|
- while (match(AND)) {
|
|
|
- Token operator = previous();
|
|
|
- Expr right = equality();
|
|
|
- expr = new Expr.Logical(expr, operator, right);
|
|
|
- }
|
|
|
-
|
|
|
- return expr;
|
|
|
- }
|
|
|
-
|
|
|
- // 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;
|
|
|
+ private boolean match(TokenType... types) {
|
|
|
+ for (TokenType type : types) {
|
|
|
+ if (check(type)) {
|
|
|
+ advance();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // 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;
|
|
|
- }
|
|
|
+ private Token advance() {
|
|
|
+ if (!isAtEnd()) current++;
|
|
|
+ return previous();
|
|
|
+ }
|
|
|
|
|
|
- // unary → ( "!" | "-" ) unary | call
|
|
|
- private Expr unary() {
|
|
|
- if (match(BANG, MINUS)) {
|
|
|
- Token operator = previous();
|
|
|
- Expr right = unary();
|
|
|
- return new Expr.Unary(operator, right);
|
|
|
- }
|
|
|
- return call();
|
|
|
- }
|
|
|
+ private boolean check(TokenType type) {
|
|
|
+ if (isAtEnd()) return false;
|
|
|
+ return peek().type == type;
|
|
|
+ }
|
|
|
|
|
|
- // call → primary ( "(" arguments? ")" )* ;
|
|
|
- private Expr call() {
|
|
|
- Expr expr = primary();
|
|
|
+ private boolean isAtEnd() {
|
|
|
+ return peek().type == EOF;
|
|
|
+ }
|
|
|
|
|
|
- while (true) {
|
|
|
- if (match(LEFT_PARAM)) {
|
|
|
- expr = finishCall(expr);
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- return expr;
|
|
|
- }
|
|
|
+ private Token peek() {
|
|
|
+ return tokens.get(current);
|
|
|
+ }
|
|
|
|
|
|
- // arguments → expression ( "," expression)* ;
|
|
|
- private Expr finishCall(Expr callee) {
|
|
|
- List<Expr> arguments = new ArrayList<>();
|
|
|
- if (!check(RIGHT_PARAM)) {
|
|
|
- do {
|
|
|
- if (arguments.size() >= 255) {
|
|
|
- error(peek(), "Can't have more than 255 arguments.");
|
|
|
- }
|
|
|
- arguments.add(expression());
|
|
|
- } while (match(COMMA));
|
|
|
- }
|
|
|
- Token paren = consume(RIGHT_PARAM, "Expect ')' after arguments");
|
|
|
+ private Token previous() {
|
|
|
+ return tokens.get(current - 1);
|
|
|
+ }
|
|
|
|
|
|
- return new Expr.Call(callee, paren, arguments);
|
|
|
- }
|
|
|
+ private ParseError error(Token token, String message) {
|
|
|
+ Lox.error(token, message);
|
|
|
+ return new ParseError();
|
|
|
+ }
|
|
|
|
|
|
- // primary → NUMBER | STRING | "true" | "false" | "nil"
|
|
|
- // | "(" expression ")" | IDENTIFIER ;
|
|
|
- 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);
|
|
|
+ private void synchronize() {
|
|
|
+ advance();
|
|
|
|
|
|
- if (match(NUMBER, STRING)) {
|
|
|
- return new Expr.Literal(previous().literal);
|
|
|
- }
|
|
|
+ while (!isAtEnd()) {
|
|
|
+ if (previous().type == SEMICOLON) return;
|
|
|
|
|
|
- if (match(LEFT_PARAM)) {
|
|
|
- Expr expr = expression();
|
|
|
- consume(RIGHT_PARAM, "Expect ') after expression.");
|
|
|
- return new Expr.Grouping(expr);
|
|
|
+ switch (peek().type) {
|
|
|
+ case CLASS, FUN, VAR, FOR, IF, PRINT, WHILE, RETURN -> {
|
|
|
+ return;
|
|
|
}
|
|
|
+ default -> advance();
|
|
|
+ }
|
|
|
+ } // End while
|
|
|
+ }
|
|
|
|
|
|
- if (match(IDENTIFIER)) {
|
|
|
- return new Expr.Variable(previous());
|
|
|
- }
|
|
|
-
|
|
|
- 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
|
|
|
- }
|
|
|
-
|
|
|
- private static class ParseError extends RuntimeException {
|
|
|
- }
|
|
|
+ private static class ParseError extends RuntimeException {}
|
|
|
}
|