From 68a2ebd34fc94488e89ffb82b359ec6e7e152ae9 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Thu, 8 Jul 2021 00:14:31 -0700 Subject: Restructure project to make room for clox --- jlox/src/com/craftinginterpreters/lox/Parser.java | 457 ++++++++++++++++++++++ 1 file changed, 457 insertions(+) create mode 100644 jlox/src/com/craftinginterpreters/lox/Parser.java (limited to 'jlox/src/com/craftinginterpreters/lox/Parser.java') diff --git a/jlox/src/com/craftinginterpreters/lox/Parser.java b/jlox/src/com/craftinginterpreters/lox/Parser.java new file mode 100644 index 0000000..ab4baaa --- /dev/null +++ b/jlox/src/com/craftinginterpreters/lox/Parser.java @@ -0,0 +1,457 @@ +package com.craftinginterpreters.lox; + +import static com.craftinginterpreters.lox.TokenType.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +class Parser { + private static class ParseError extends RuntimeException { + } + + private final List tokens; + private int current = 0; + + Parser(List tokens) { + this.tokens = tokens; + } + + public List parse() { + List statements = new ArrayList<>(); + + while (!isAtEnd()) { + statements.add(declaration()); + } + + return statements; + } + + private Expr expression() { + return assignment(); + } + + private Stmt declaration() { + try { + if (match(CLASS)) + return classDeclaration(); + if (match(FUN)) + return function("function"); + if (match(VAR)) + return varDeclaration(); + + return statement(); + } catch (ParseError error) { + synchronize(); + return null; + } + } + + private Stmt classDeclaration() { + Token name = consume(IDENTIFIER, "Expect class name."); + + Expr.Variable superclass = null; + if (match(LESS)) { + consume(IDENTIFIER, "Expect superclass name."); + superclass = new Expr.Variable(previous()); + } + + consume(LEFT_BRACE, "Expect '{' before class body."); + + List methods = new ArrayList<>(); + while (!check(RIGHT_BRACE) && !isAtEnd()) { + methods.add(function("method")); + } + + consume(RIGHT_BRACE, "Expect '}' after class body."); + + return new Stmt.Class(name, superclass, methods); + } + + 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_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."); + return new Stmt.Print(value); + } + + 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); + } + + private Stmt varDeclaration() { + 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); + } + + 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."); + return new Stmt.Expression(expr); + } + + private Stmt.Function function(String kind) { + Token name = consume(IDENTIFIER, "Expect " + kind + " name."); + consume(LEFT_PAREN, "Expect '(' after " + kind + " name."); + List parameters = new ArrayList(); + if (!check(RIGHT_PAREN)) { + 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_PAREN, "Expect ')' after parameters."); + + consume(LEFT_BRACE, "Expect '{' before " + kind + " body."); + List body = block(); + return new Stmt.Function(name, parameters, body); + } + + private List block() { + List statements = new ArrayList<>(); + + while (!check(RIGHT_BRACE) && !isAtEnd()) { + statements.add(declaration()); + } + + consume(RIGHT_BRACE, "Expect '}' after block."); + return statements; + } + + 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) { + Expr.Get get = (Expr.Get) expr; + return new Expr.Set(get.object, get.name, value); + } + + 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; + } + + 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 call(); + } + + private Expr finishCall(Expr callee) { + List arguments = new ArrayList<>(); + if (!check(RIGHT_PAREN)) { + do { + if (arguments.size() >= 255) { + error(peek(), "Can't have more than 255 arguments."); + } + arguments.add(expression()); + } while (match(COMMA)); + } + + Token paren = consume(RIGHT_PAREN, "Expect ')' after arguments."); + + return new Expr.Call(callee, paren, arguments); + } + + private Expr call() { + Expr expr = primary(); + + while (true) { + if (match(LEFT_PAREN)) { + 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; + } + + 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(SUPER)) { + Token keyword = previous(); + consume(DOT, "Expect '.' after 'super'."); + Token method = consume(IDENTIFIER, "Expect superclass method name."); + return new Expr.Super(keyword, method); + } + + if (match(THIS)) + return new Expr.This(previous()); + + if (match(IDENTIFIER)) { + return new Expr.Variable(previous()); + } + + 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 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(); + } + + 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(); + } + } +} -- cgit v1.2.3-54-g00ecf