Upsilon/Upsilon/Parser/Parser.cs

126 lines
4.1 KiB
C#

using System;
using System.Collections.Immutable;
namespace Upsilon.Parser
{
public class Parser
{
private readonly ImmutableArray<SyntaxToken> _tokens;
private int _position;
private Parser(ImmutableArray<SyntaxToken> tokens)
{
_tokens = tokens;
}
public static ScriptSyntax Parse(string text)
{
var tokens = Lexer.Lex(text);
return new Parser(tokens).ParseScriptSyntax();
}
private SyntaxToken Current => Get(0);
private SyntaxToken Next => Get(1);
private SyntaxToken Get(int offset)
{
if (_position + offset >= _tokens.Length)
return new SyntaxToken(SyntaxKind.EndOfFile, _position + offset, "\0", null);
return _tokens[_position + offset];
}
private SyntaxToken NextToken()
{
var current = Current;
_position++;
return current;
}
private SyntaxToken MatchToken(SyntaxKind kind)
{
if (Current.Kind == kind)
return NextToken();
return new SyntaxToken(kind, Current.Span.Start, "", null);
}
public ScriptSyntax ParseScriptSyntax()
{
var expression = ParseExpression();
var eof = MatchToken(SyntaxKind.EndOfFile);
return new ScriptSyntax(expression, eof);
}
public ExpressionSyntax ParseExpression()
{
return ParseBinaryExpression();
}
private ExpressionSyntax ParseBinaryExpression(SyntaxKindPrecedence.Precedence parentPrecedence = SyntaxKindPrecedence.Precedence.None)
{
ExpressionSyntax left;
var unaryOperatorPrecedence = Current.Kind.UnaryOperatorPrecedence();
if (unaryOperatorPrecedence != SyntaxKindPrecedence.Precedence.None
&& unaryOperatorPrecedence >= parentPrecedence)
{
var operatorToken = NextToken();
var operand = ParseBinaryExpression(unaryOperatorPrecedence);
left = new UnaryExpressionSyntax(operatorToken, operand);
}
else
{
left = ParsePrimaryExpression();
}
while (true)
{
var precedence = Current.Kind.BinaryOperatorPrecedence();
if (precedence == SyntaxKindPrecedence.Precedence.None || precedence <= parentPrecedence)
break;
var op = NextToken();
var right = ParseBinaryExpression(precedence);
left = new BinaryExpressionSyntax(left, op, right);
}
return left;
}
private ExpressionSyntax ParsePrimaryExpression()
{
switch (Current.Kind)
{
case SyntaxKind.OpenParenthesis:
return ParseParenthesizedExpression();
case SyntaxKind.Number:
return ParseNumber();
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return ParseBoolean();
default:
throw new Exception("Unknown primary expression type: " + Current.Kind);
}
}
private ExpressionSyntax ParseParenthesizedExpression()
{
var l = MatchToken(SyntaxKind.OpenParenthesis);
var e = ParseExpression();
var r = MatchToken(SyntaxKind.CloseParenthesis);
return new ParenthesizedExpressionSyntax(l, e, r);
}
private ExpressionSyntax ParseNumber()
{
var numberToken = MatchToken(SyntaxKind.Number);
return new LiteralExpressionSyntax(numberToken, numberToken.Value);
}
private ExpressionSyntax ParseBoolean()
{
var isTrue = Current.Kind == SyntaxKind.TrueKeyword;
var token = MatchToken(isTrue ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
return new LiteralExpressionSyntax(token, isTrue);
}
}
}