module jlox.parser; import std.stdio; import jlox.token; import jlox.tokentype; import common.util; import jlox.expr; import jlox.main; import jlox.stmt; import jlox.loxfunction; class Parser{ private Token[] tokens; private int current = 0; mixin defaultCtor; Stmt[] parse(){ Stmt[] statements; while(!isAtEnd) statements ~= declaration(); return statements; } private bool isAtEnd() => peek().type == TokenType.EOF; private Token peek() => tokens[current]; private Token previous() => tokens[current-1]; private bool check(TokenType type){ if(isAtEnd) return false; return peek().type == type; } private Token advance(){ if(!isAtEnd) current++; return previous(); } private bool match(TokenType[] types...){ foreach(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 static class ParseError : Exception { this(string s){ super(s); } } private ParseError error(Token token, string message){ Lox.error(token, message); return new ParseError("hello"); } private void synchronise(){ advance(); with(TokenType) 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; default: /* assert(0); */ } advance(); } } private Stmt printStatement(){ version(LoxPrintMultiple){ Expr[] values; if(!check(TokenType.SEMICOLON)) do { values ~= expression(); } while(match(TokenType.COMMA)); consume(TokenType.SEMICOLON, "Expect ';' after values."); return new Stmt.Print(values); } else { Expr value = expression(); consume(TokenType.SEMICOLON, "Expect ';' after value."); return new Stmt.Print(value); } } private Stmt returnStatement(){ Token keyword = previous(); Expr value; if(!check(TokenType.SEMICOLON)) value = expression(); consume(TokenType.SEMICOLON, "Expect ';' after return value."); return new Stmt.Return(keyword, value); } private Stmt expressionStatement(){ Expr expr = expression(); consume(TokenType.SEMICOLON, "Expect ';' after expression."); return new Stmt.Expression(expr); } private Stmt.Function fun(string kind){ Token name = consume(TokenType.IDENTIFIER, "Expect " ~ kind ~ " name."); consume(TokenType.LEFT_PAREN, "Expect '(' after " ~ kind ~ " name."); Token[] parameters; if(!check(TokenType.RIGHT_PAREN)){ do{ if(parameters.length >= 255) error(peek(), "Can't have more than 255 parameters."); parameters ~= consume(TokenType.IDENTIFIER, "Expect parameter name."); } while(match(TokenType.COMMA)); } consume(TokenType.RIGHT_PAREN, "Expect ')' after parameters."); consume(TokenType.LEFT_BRACE, "Expect '{' before " ~ kind ~ " body."); Stmt[] body = block(); return new Stmt.Function(name, parameters, body); } private Stmt ifStatement(){ consume(TokenType.LEFT_PAREN, "Expect '(' after 'if'."); Expr condition = expression(); consume(TokenType.RIGHT_PAREN, "Expect ')' after if condition."); Stmt thenBranch = statement(); Stmt elseBranch = null; if(match(TokenType.ELSE)) elseBranch = statement(); return new Stmt.If(condition, thenBranch, elseBranch); } private Stmt forStatement(){ consume(TokenType.LEFT_PAREN, "Expect '(' after 'for'."); Stmt initialiser; if(match(TokenType.SEMICOLON)) initialiser = null; else if(match(TokenType.VAR)) initialiser = varDeclaration(); else initialiser = expressionStatement(); Expr condition = check(TokenType.SEMICOLON) ? new Expr.Literal(new LoxBool(true)) : expression(); consume(TokenType.SEMICOLON, "Expect ';' after loop condition."); Expr increment; if(!check(TokenType.RIGHT_PAREN)) increment = expression(); consume(TokenType.RIGHT_PAREN, "Expect ')' after for clauses."); Stmt body = statement(); if(increment !is null) body = new Stmt.Block([ body, new Stmt.Expression(increment) ]); body = new Stmt.While(condition, body); if(initialiser !is null) body = new Stmt.Block([ initialiser, body ]); return body; } private Stmt whileStatement(){ consume(TokenType.LEFT_PAREN, "Expect '(' after 'while'."); Expr condition = expression(); consume(TokenType.RIGHT_PAREN, "Expect ')' after condition."); Stmt body = statement(); return new Stmt.While(condition, body); } private Stmt statement(){ if(match(TokenType.IF)) return ifStatement(); if(match(TokenType.FOR)) return forStatement(); if(match(TokenType.WHILE)) return whileStatement(); if(match(TokenType.PRINT)) return printStatement(); if(match(TokenType.RETURN)) return returnStatement(); if(match(TokenType.LEFT_BRACE)) return new Stmt.Block(block()); return expressionStatement(); } private Stmt[] block(){ Stmt[] statements; while(!check(TokenType.RIGHT_BRACE) && !isAtEnd) statements ~= declaration(); consume(TokenType.RIGHT_BRACE, "Expect '}' after block."); return statements; } private Expr expression(){ return assignment(); } private Expr assignment(){ Expr expr = or(); if(match(TokenType.EQUAL)){ Token equals = previous(); Expr value = assignment(); if(auto v = cast(Expr.Variable)expr){ return new Expr.Assign(v.name, value); } else if(auto get = cast(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(TokenType.OR)){ Token operator = previous(); Expr right = and(); expr = new Expr.Logical(expr, operator, right); } return expr; } private Expr and(){ Expr expr = equality(); while(match(TokenType.AND)){ Token operator = previous(); Expr right = equality(); expr = new Expr.Logical(expr, operator, right); } return expr; } private Expr equality(){ Expr expr = comparison(); while(match(TokenType.BANG_EQUAL, TokenType.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(TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.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(TokenType.MINUS, TokenType.PLUS)){ Token operator = previous(); Expr right = factor(); expr = new Expr.Binary(expr, operator, right); } return expr; } private Expr factor(){ Expr expr = unary(); while(match(TokenType.SLASH, TokenType.STAR)){ Token operator = previous(); Expr right = unary(); expr = new Expr.Binary(expr, operator, right); } return expr; } private Expr unary(){ if(match(TokenType.BANG, TokenType.MINUS)){ Token operator = previous(); Expr right = unary(); return new Expr.Unary(operator, right); } return call(); } private Expr call(){ Expr finishCall(Expr callee){ Expr[] arguments; if(!check(TokenType.RIGHT_PAREN)){ do { if(arguments.length >= 255) error(peek(), "Can't have more than 255 arguments."); arguments ~= expression(); } while(match(TokenType.COMMA)); } Token paren = consume(TokenType.RIGHT_PAREN, "Expect ')' after arguments."); return new Expr.Call(callee, paren, arguments); } Expr expr = primary(); while(true){ if(match(TokenType.LEFT_PAREN)){ expr = finishCall(expr); } else if(match(TokenType.DOT)){ Token name = consume(TokenType.IDENTIFIER, "Expect property name after '.'."); expr = new Expr.Get(expr, name); } else { break; } } return expr; } private Expr primary(){ if(match(TokenType.IDENTIFIER)) return new Expr.Variable(previous()); if(match(TokenType.FALSE)) return new Expr.Literal(new LoxBool(false)); if(match(TokenType.TRUE)) return new Expr.Literal(new LoxBool(true)); if(match(TokenType.NIL)) return new Expr.Literal(new LoxNil()); if(match(TokenType.NUMBER, TokenType.STRING)) return new Expr.Literal(previous().literal); if(match(TokenType.THIS)) return new Expr.This(previous()); if(match(TokenType.LEFT_PAREN)){ Expr expr = expression(); consume(TokenType.RIGHT_PAREN, "Expect ')' after expression."); return new Expr.Grouping(expr); } if(match(TokenType.SUPER)){ Token keyword = previous(); consume(TokenType.DOT, "Expect '.' after 'super'."); Token method = consume(TokenType.IDENTIFIER, "Expect superclass method name."); return new Expr.Super(keyword, method); } throw error(peek(), "Expect expression."); } private Stmt varDeclaration(){ Token name = consume(TokenType.IDENTIFIER, "Expect variable name."); Expr initialiser = null; if(match(TokenType.EQUAL)) initialiser = expression(); consume(TokenType.SEMICOLON, "Expect ';' after variable declaration."); return new Stmt.Var(name, initialiser); } private Stmt classDeclaration() { Token name = consume(TokenType.IDENTIFIER, "Expect class name."); Expr.Variable superclass; if(match(TokenType.LESS)){ consume(TokenType.IDENTIFIER, "Expect superlcass name."); superclass = new Expr.Variable(previous); } consume(TokenType.LEFT_BRACE, "Expect '{' before class body."); Stmt.Function[] methods; while(!check(TokenType.RIGHT_BRACE) && !isAtEnd) methods ~= fun("method"); consume(TokenType.RIGHT_BRACE, "Expect '}' after class body."); return new Stmt.Class(name, superclass, methods); } private Stmt declaration(){ try { if(match(TokenType.VAR)) return varDeclaration(); if(match(TokenType.FUN)) return fun("function"); if(match(TokenType.CLASS)) return classDeclaration(); return statement(); } catch(ParseError error){ synchronise(); return null; } } }