2018-11-13 11:48:50 +00:00
|
|
|
using System;
|
2018-11-10 12:11:36 +00:00
|
|
|
using System.Collections.Immutable;
|
2018-11-13 12:54:51 +00:00
|
|
|
using System.Linq;
|
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-12 16:45:50 +00:00
|
|
|
public static BlockStatementSyntax 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);
|
2018-11-12 16:45:50 +00:00
|
|
|
return (BlockStatementSyntax) 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-15 14:51:05 +00:00
|
|
|
_diagnostics.LogBadCharacter(Current.Span, kind, Current.Kind);
|
2018-11-10 12:11:36 +00:00
|
|
|
return new SyntaxToken(kind, Current.Span.Start, "", null);
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:54:51 +00:00
|
|
|
private StatementSyntax ParseScriptSyntax()
|
2018-11-10 12:11:36 +00:00
|
|
|
{
|
2018-11-13 12:54:51 +00:00
|
|
|
var statement = ParseBlockStatement(new []{SyntaxKind.EndOfFile});
|
2018-11-13 11:48:50 +00:00
|
|
|
MatchToken(SyntaxKind.EndOfFile);
|
2018-11-12 16:45:50 +00:00
|
|
|
return statement;
|
2018-11-10 12:11:36 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 12:54:51 +00:00
|
|
|
private 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-13 11:48:50 +00:00
|
|
|
if (Current.Kind == SyntaxKind.IfKeyword)
|
|
|
|
{
|
2018-11-13 14:15:44 +00:00
|
|
|
return ParseIfStatement(SyntaxKind.IfKeyword);
|
2018-11-13 12:54:51 +00:00
|
|
|
}
|
2018-11-12 15:21:59 +00:00
|
|
|
|
2018-11-15 14:51:05 +00:00
|
|
|
if (Current.Kind == SyntaxKind.FunctionKeyword)
|
|
|
|
{
|
|
|
|
return ParseFunctionStatement();
|
|
|
|
}
|
|
|
|
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.FunctionKeyword)
|
|
|
|
{
|
|
|
|
return ParseFunctionStatement();
|
|
|
|
}
|
2018-11-16 12:45:03 +00:00
|
|
|
if (Current.Kind == SyntaxKind.ReturnKeyword)
|
|
|
|
{
|
|
|
|
return ParseReturnStatement();
|
|
|
|
}
|
2018-11-15 14:51:05 +00:00
|
|
|
|
2018-11-12 15:21:59 +00:00
|
|
|
return ParseExpressionStatement();
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:54:51 +00:00
|
|
|
private StatementSyntax ParseBlockStatement(SyntaxKind[] endTokens)
|
2018-11-12 16:45:50 +00:00
|
|
|
{
|
|
|
|
var statements = ImmutableArray.CreateBuilder<StatementSyntax>();
|
2018-11-13 12:54:51 +00:00
|
|
|
while (!endTokens.Contains(Current.Kind))
|
2018-11-12 16:45:50 +00:00
|
|
|
{
|
|
|
|
var next = ParseStatement();
|
|
|
|
statements.Add(next);
|
|
|
|
}
|
2018-11-13 11:48:50 +00:00
|
|
|
return new BlockStatementSyntax(statements.ToImmutable());
|
|
|
|
}
|
|
|
|
|
2018-11-13 14:15:44 +00:00
|
|
|
private StatementSyntax ParseIfStatement(SyntaxKind requiredToken)
|
2018-11-13 11:48:50 +00:00
|
|
|
{
|
2018-11-13 14:15:44 +00:00
|
|
|
var ifToken = MatchToken(requiredToken);
|
2018-11-13 11:48:50 +00:00
|
|
|
var condition = ParseExpressionStatement();
|
|
|
|
var thenToken = MatchToken(SyntaxKind.ThenKeyword);
|
2018-11-13 12:54:51 +00:00
|
|
|
var block = ParseBlockStatement(new []{SyntaxKind.EndKeyword, SyntaxKind.ElseIfKeyword, SyntaxKind.ElseKeyword});
|
2018-11-13 14:15:44 +00:00
|
|
|
switch (Current.Kind)
|
2018-11-13 12:54:51 +00:00
|
|
|
{
|
2018-11-13 14:15:44 +00:00
|
|
|
case SyntaxKind.ElseIfKeyword:
|
|
|
|
var nextElseIf =
|
|
|
|
new ElseIfStatementSyntax((IfStatementSyntax) ParseIfStatement(SyntaxKind.ElseIfKeyword));
|
|
|
|
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block,
|
|
|
|
nextElseIf);
|
2018-11-13 12:54:51 +00:00
|
|
|
case SyntaxKind.ElseKeyword:
|
|
|
|
{
|
2018-11-13 14:15:44 +00:00
|
|
|
var elseToken = MatchToken(SyntaxKind.ElseKeyword);
|
2018-11-13 12:54:51 +00:00
|
|
|
var elseBlock = ParseBlockStatement(new[]{SyntaxKind.EndKeyword});
|
|
|
|
var endEndToken = MatchToken(SyntaxKind.EndKeyword);
|
2018-11-13 14:15:44 +00:00
|
|
|
var elseStatement = new ElseStatementSyntax(elseToken, (BlockStatementSyntax) elseBlock, endEndToken);
|
2018-11-13 12:54:51 +00:00
|
|
|
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, elseStatement);
|
|
|
|
}
|
2018-11-13 14:15:44 +00:00
|
|
|
case SyntaxKind.EndKeyword:
|
|
|
|
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
|
|
|
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, endToken);
|
2018-11-13 12:54:51 +00:00
|
|
|
default:
|
2018-11-13 14:15:44 +00:00
|
|
|
throw new ArgumentOutOfRangeException();
|
2018-11-13 12:54:51 +00:00
|
|
|
}
|
2018-11-12 16:45:50 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 14:51:05 +00:00
|
|
|
private StatementSyntax ParseFunctionStatement()
|
|
|
|
{
|
|
|
|
SyntaxToken localToken = null;
|
|
|
|
if (Current.Kind == SyntaxKind.LocalKeyword)
|
|
|
|
{
|
|
|
|
localToken = NextToken();
|
|
|
|
}
|
|
|
|
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);
|
|
|
|
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
|
|
|
|
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
|
|
|
var variableBuilder = ImmutableArray.CreateBuilder<IdentifierToken>();
|
|
|
|
while (Current.Kind != SyntaxKind.CloseParenthesis)
|
|
|
|
{
|
|
|
|
var variableIdentifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
|
|
|
|
variableBuilder.Add(variableIdentifier);
|
|
|
|
if (Current.Kind == SyntaxKind.Comma)
|
|
|
|
NextToken();
|
|
|
|
}
|
|
|
|
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
|
|
|
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
|
|
|
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
|
|
|
return new FunctionStatementSyntax(localToken, functionToken, identifier, openParenthesis,
|
|
|
|
variableBuilder.ToImmutable(), closeParenthesis, (BlockStatementSyntax) block, endToken);
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:54:51 +00:00
|
|
|
private ExpressionStatementSyntax ParseExpressionStatement()
|
2018-11-12 15:21:59 +00:00
|
|
|
{
|
|
|
|
var expression = ParseExpression();
|
|
|
|
return new ExpressionStatementSyntax(expression);
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:45:03 +00:00
|
|
|
private StatementSyntax ParseReturnStatement()
|
|
|
|
{
|
|
|
|
var returnToken = MatchToken(SyntaxKind.ReturnKeyword);
|
|
|
|
var expression = ParseExpression();
|
|
|
|
return new ReturnStatementSyntax(returnToken, expression);
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:54:51 +00:00
|
|
|
private ExpressionSyntax ParseExpression()
|
2018-11-12 15:21:59 +00:00
|
|
|
{
|
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:
|
2018-11-15 19:13:53 +00:00
|
|
|
if (Next.Kind == SyntaxKind.OpenParenthesis)
|
|
|
|
return ParseFunctionCallExpression();
|
2018-11-10 16:00:39 +00:00
|
|
|
var token = MatchToken(SyntaxKind.Identifier);
|
|
|
|
return new VariableExpressionSyntax((IdentifierToken) token);
|
2018-11-14 12:45:49 +00:00
|
|
|
case SyntaxKind.NilKeyword:
|
|
|
|
var nilToken = MatchToken(SyntaxKind.NilKeyword);
|
|
|
|
return new LiteralExpressionSyntax(nilToken, null);
|
2018-11-10 12:11:36 +00:00
|
|
|
default:
|
2018-11-13 11:48:50 +00:00
|
|
|
_diagnostics.LogBadCharacter(new TextSpan(_position, 1), SyntaxKind.Identifier);
|
2018-11-11 09:26:52 +00:00
|
|
|
NextToken();
|
|
|
|
return new BadExpressionSyntax();
|
2018-11-10 12:11:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 19:13:53 +00:00
|
|
|
private ExpressionSyntax ParseFunctionCallExpression()
|
|
|
|
{
|
|
|
|
var identifier = MatchToken(SyntaxKind.Identifier);
|
|
|
|
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
|
|
|
var parameters = ImmutableArray.CreateBuilder<ExpressionSyntax>();
|
|
|
|
while (Current.Kind != SyntaxKind.CloseParenthesis)
|
|
|
|
{
|
|
|
|
var exp = ParseExpression();
|
|
|
|
parameters.Add(exp);
|
|
|
|
if (Current.Kind == SyntaxKind.Comma)
|
|
|
|
NextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
|
|
|
return new FunctionCallExpressionSyntax((IdentifierToken) identifier, openParenthesis,
|
|
|
|
parameters.ToImmutable(), closeParenthesis);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|