finn-lang/Parser.cs

253 lines
4.5 KiB
C#
Raw Normal View History

2023-06-28 18:12:14 +00:00
namespace Finn;
using Finn.AST;
using System;
using System.Collections.Generic;
class Parser
{
private class ParseError : Exception { }
private readonly List<Token> tokens;
private int current = 0;
public Parser(List<Token> 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)
{
return consume(new TokenType[] { type }, message);
}
private Token consume(TokenType[] types, string message)
{
foreach (var type in types)
{
if (check(type)) return advance();
}
2023-06-28 18:12:14 +00:00
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<Expr> 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()
{
2023-06-28 23:18:31 +00:00
var expr = equality();
while (match(TokenType.Semicolon))
{
var right = equality();
expr = new Sequence { Left = expr, Right = right };
}
return expr;
2023-06-28 18:12:14 +00:00
}
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);
2023-06-28 22:38:00 +00:00
private Expr product() => binaryLeft(concat, TokenType.Slash, TokenType.Asterisk);
private Expr concat() => binaryLeft(unary, TokenType.PlusPlus);
2023-06-28 22:26:08 +00:00
2023-06-28 18:12:14 +00:00
private Expr unary()
{
if (match(TokenType.Bang, TokenType.Minus))
{
Token op = previous();
Expr right = unary();
return new Unary { Op = op, Right = right };
}
return control();
2023-06-28 22:26:08 +00:00
}
2023-06-28 23:25:28 +00:00
private Expr control()
{
switch (peek().type)
{
case TokenType.If:
advance();
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 };
case TokenType.When:
throw new NotImplementedException();
}
return primary();
}
private Name? name()
2023-06-28 22:26:08 +00:00
{
if (match(TokenType.Identifier))
2023-06-28 22:26:08 +00:00
{
return new(previous().lexeme, false);
2023-06-28 22:26:08 +00:00
}
if (match(TokenType.At))
{
Token literal = consume(TokenType.String, "Expect string literal after '@'.");
return new((string)(literal.literal ?? ""), true);
}
return null;
2023-06-28 18:12:14 +00:00
}
private Expr primary()
{
Expr expr = operand();
if (match(TokenType.Period))
{
Name? ident = name();
if (ident == null)
{
throw error(advance(), "Expect identifier after dot.");
}
return new Selector { Left = expr, FieldName = ident };
}
if (match(TokenType.LBracket))
{
throw new NotImplementedException("TODO index");
}
if (match(TokenType.LParen))
{
throw new NotImplementedException("TODO apply");
}
return expr;
}
private Expr operand()
2023-06-28 18:12:14 +00:00
{
if (match(TokenType.Number, TokenType.String))
{
return new Literal { Value = previous().literal };
}
var ident = name();
if (ident != null)
{
return new Identifier { Value = ident };
}
2023-06-28 18:12:14 +00:00
if (match(TokenType.LParen))
{
Expr expr = expression();
consume(TokenType.RParen, "Expect ')' after expression.");
return new Grouping { Expression = expr };
}
throw error(peek(), "Expect expression.");
}
}