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()
|
|
|
|
{
|
2018-11-12 15:21:59 +00:00
|
|
|
var statement = ParseStatement();
|
2018-11-10 12:11:36 +00:00
|
|
|
var eof = MatchToken(SyntaxKind.EndOfFile);
|
2018-11-12 15:21:59 +00:00
|
|
|
return new ScriptSyntax(statement, eof);
|
2018-11-10 12:11:36 +00:00
|
|
|
}
|
|
|
|
|
2018-11-12 15:21:59 +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)
|
|
|
|
{
|
2018-11-12 15:21:59 +00:00
|
|
|
return ParseAssignmentExpression();
|
2018-11-10 16:00:39 +00:00
|
|
|
}
|
|
|
|
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.Identifier)
|
|
|
|
{
|
2018-11-12 15:21:59 +00:00
|
|
|
return ParseAssignmentExpression();
|
2018-11-10 16:00:39 +00:00
|
|
|
}
|
2018-11-12 15:21:59 +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();
|
|
|
|
}
|
|
|
|
|
2018-11-12 15:21:59 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|