namespace Finn; using Finn.AST; using System; using System.Collections.Generic; class Parser { private class ParseError : Exception { } private readonly List tokens; private int current = 0; public Parser(List tokens) { this.tokens = tokens; } public Expr? parse() { try { return expression(); } catch (ParseError) { return null; } } // Parsing primitives private bool match(params TokenType[] types) { foreach (TokenType type in types) { if (check(type)) { advance(); return true; } } return false; } private bool check(TokenType type) { if (isAtEnd()) return false; return peek().type == type; } private Token advance() { if (!isAtEnd()) current++; return previous(); } private bool isAtEnd() { return peek().type == TokenType.EOF; } private Token peek() { return tokens[current]; } private Token previous() { return tokens[current - 1]; } private Token consume(TokenType type, string message) { if (check(type)) return advance(); throw error(peek(), message); } private ParseError error(Token token, string message) { Program.error(token, message); return new ParseError(); } private void synchronize() { advance(); while (!isAtEnd()) { switch (peek().type) { case TokenType.Def: case TokenType.Type: return; } advance(); } } // Helpers private Expr binaryLeft(Func next, params TokenType[] types) { Expr expr = next(); while (match(types)) { Token op = previous(); Expr right = next(); expr = new Binary { Left = expr, Op = op, Right = right }; } return expr; } // Rules private Expr expression() { var expr = equality(); while (match(TokenType.Semicolon)) { var right = equality(); expr = new Sequence { Left = expr, Right = right }; } return expr; } private Expr equality() { Expr expr = comparison(); while (match(TokenType.BangEqual, TokenType.DoubleEqual)) { Token op = previous(); Expr right = comparison(); expr = new Binary { Left = expr, Op = op, Right = right }; } return expr; } private Expr comparison() => binaryLeft(sum, TokenType.Greater, TokenType.GreaterEqual, TokenType.Less, TokenType.LessEqual); private Expr sum() => binaryLeft(product, TokenType.Minus, TokenType.Plus); private Expr product() => binaryLeft(concat, TokenType.Slash, TokenType.Asterisk); private Expr concat() => binaryLeft(unary, TokenType.PlusPlus); private Expr unary() { if (match(TokenType.Bang, TokenType.Minus)) { Token op = previous(); Expr right = unary(); return new Unary { Op = op, Right = right }; } return ifExpr(); } private Expr ifExpr() { if (match(TokenType.If)) { Expr condition = expression(); consume(TokenType.Then, "Expect 'then' after condition."); Expr thenCase = expression(); consume(TokenType.Else, "Expect 'else' after 'then' case."); Expr elseCase = expression(); return new If { Condition = condition, Then = thenCase, Else = elseCase }; } return primary(); } private Expr primary() { if (match(TokenType.Number, TokenType.String)) { return new Literal { Value = previous().literal }; } if (match(TokenType.LParen)) { Expr expr = expression(); consume(TokenType.RParen, "Expect ')' after expression."); return new Grouping { Expression = expr }; } throw error(peek(), "Expect expression."); } }