Upsilon/Upsilon/Parser/Parser.cs

167 lines
5.8 KiB
C#
Raw Normal View History

2018-11-10 12:11:36 +00:00
using System.Collections.Immutable;
2018-11-11 09:26:52 +00:00
using Upsilon.Text;
2018-11-10 12:11:36 +00:00
namespace Upsilon.Parser
{
public class Parser
{
private readonly ImmutableArray<SyntaxToken> _tokens;
2018-11-11 09:26:52 +00:00
private readonly Diagnostics _diagnostics;
2018-11-10 12:11:36 +00:00
private int _position;
2018-11-11 09:26:52 +00:00
private Parser(ImmutableArray<SyntaxToken> tokens, Diagnostics diagnostics)
2018-11-10 12:11:36 +00:00
{
_tokens = tokens;
2018-11-11 09:26:52 +00:00
_diagnostics = diagnostics;
2018-11-10 12:11:36 +00:00
}
2018-11-11 09:26:52 +00:00
public static ScriptSyntax Parse(string text, Diagnostics diagnostics)
2018-11-10 12:11:36 +00:00
{
2018-11-11 09:26:52 +00:00
var tokens = Lexer.Lex(text, diagnostics);
return new Parser(tokens, diagnostics).ParseScriptSyntax();
2018-11-10 12:11:36 +00:00
}
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();
2018-11-11 18:56:53 +00:00
_diagnostics.LogBadCharacter(Current.Span);
2018-11-10 12:11:36 +00:00
return new SyntaxToken(kind, Current.Span.Start, "", null);
}
public ScriptSyntax ParseScriptSyntax()
{
var statement = ParseStatement();
2018-11-10 12:11:36 +00:00
var eof = MatchToken(SyntaxKind.EndOfFile);
return new ScriptSyntax(statement, eof);
2018-11-10 12:11:36 +00:00
}
public StatementSyntax ParseStatement()
2018-11-10 12:11:36 +00:00
{
2018-11-10 16:00:39 +00:00
if (Current.Kind == SyntaxKind.Identifier && Next.Kind == SyntaxKind.Equals)
{
return ParseAssignmentExpression();
2018-11-10 16:00:39 +00:00
}
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.Identifier)
{
return ParseAssignmentExpression();
2018-11-10 16:00:39 +00:00
}
return ParseExpressionStatement();
}
public ExpressionStatementSyntax ParseExpressionStatement()
{
var expression = ParseExpression();
return new ExpressionStatementSyntax(expression);
}
public ExpressionSyntax ParseExpression()
{
2018-11-10 12:11:36 +00:00
return ParseBinaryExpression();
}
private AssignmentExpressionSyntax ParseAssignmentExpression()
2018-11-10 16:00:39 +00:00
{
SyntaxToken localKeyword = null;
if (Current.Kind == SyntaxKind.LocalKeyword)
{
localKeyword = MatchToken(SyntaxKind.LocalKeyword);
}
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
var assignmentToken = MatchToken(SyntaxKind.Equals);
var expression = ParseExpression();
return new AssignmentExpressionSyntax(localKeyword, identifier, assignmentToken, expression);
}
2018-11-10 12:11:36 +00:00
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();
2018-11-10 16:00:39 +00:00
case SyntaxKind.Identifier:
var token = MatchToken(SyntaxKind.Identifier);
return new VariableExpressionSyntax((IdentifierToken) token);
2018-11-10 12:11:36 +00:00
default:
2018-11-11 09:26:52 +00:00
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
NextToken();
return new BadExpressionSyntax();
2018-11-10 12:11:36 +00:00
}
}
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);
}
}
}