126 lines
4.1 KiB
C#
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);
|
|
}
|
|
|
|
}
|
|
} |