using System; using System.Collections.Generic; using System.Linq; namespace Parser.Internal { internal class MParserGreen { private List<(SyntaxToken token, Position position)> Pairs { get; } private int _index; private SyntaxToken CurrentToken => Pairs[_index].token; private Position CurrentPosition => Pairs[_index].position; private SyntaxToken PeekToken(int n) => Pairs[_index + n].token; private SyntaxFactory Factory { get; } public DiagnosticsBag Diagnostics { get; } public MParserGreen(List<(SyntaxToken, Position)> pairs, SyntaxFactory factory) { Pairs = pairs; Factory = factory; Diagnostics = new DiagnosticsBag(); } private SyntaxToken EatToken() { var token = CurrentToken; _index++; return token; } private SyntaxToken CreateMissingToken(TokenKind kind) { return TokenFactory .CreateMissing(kind, null, null) .WithDiagnostics(TokenDiagnostic.MissingToken(kind)); } private SyntaxToken EatToken(TokenKind kind) { var token = CurrentToken; if (token.Kind != kind) { Diagnostics.ReportUnexpectedToken(kind, token.Kind); return CreateMissingToken(kind); } _index++; return token; } private SyntaxToken EatIdentifier() { return EatToken(TokenKind.IdentifierToken); } private bool IsIdentifier(SyntaxToken token, string s) { return token.Kind == TokenKind.IdentifierToken && token.Text == s; } private SyntaxToken EatIdentifier(string s) { var token = CurrentToken; if (token.Kind != TokenKind.IdentifierToken) { Diagnostics.ReportUnexpectedToken(TokenKind.IdentifierToken, token.Kind); return TokenFactory.CreateMissing(TokenKind.IdentifierToken, null, null); } if (token.Text != s) { Diagnostics.ReportUnexpectedToken(TokenKind.IdentifierToken, token.Kind); return TokenFactory.CreateMissing(TokenKind.IdentifierToken, null, null); } _index++; return token; } private SyntaxToken? PossiblyEatIdentifier(string s) { var token = CurrentToken; if (token.Kind == TokenKind.IdentifierToken && token.Text == s) { return EatToken(); } return null; } private SyntaxToken EatPossiblyMissingIdentifier(string s) { var token = CurrentToken; if (token.Kind == TokenKind.IdentifierToken && token.Text == s) { return EatToken(); } return TokenFactory.CreateMissing( TokenKind.IdentifierToken, new List(), new List()); } private SyntaxList? ParseFunctionOutputList() { var outputs = new SyntaxListBuilder(); var firstToken = true; while (CurrentToken.Kind != TokenKind.CloseSquareBracketToken) { if (!firstToken && CurrentToken.Kind == TokenKind.CommaToken) { outputs.Add(EatToken()); } firstToken = false; outputs.Add(Factory.IdentifierNameExpressionSyntax(EatToken(TokenKind.IdentifierToken))); } return outputs.ToList(); } private FunctionOutputDescriptionSyntaxNode? ParseFunctionOutputDescription() { SyntaxToken assignmentSign; var builder = new SyntaxListBuilder(); if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken) { builder.Add(EatToken()); var outputs = ParseFunctionOutputList(); if (outputs != null) { builder.AddRange(outputs); } builder.Add(EatToken(TokenKind.CloseSquareBracketToken)); assignmentSign = EatToken(TokenKind.EqualsToken); } else if (CurrentToken.Kind == TokenKind.IdentifierToken) { if (PeekToken(1).Kind == TokenKind.EqualsToken) { var identifierToken = EatIdentifier(); builder.Add(Factory.IdentifierNameExpressionSyntax(identifierToken)); assignmentSign = EatToken(TokenKind.EqualsToken); } else { return null; } } else { return null; } return Factory.FunctionOutputDescriptionSyntax(builder.ToList(), assignmentSign); } private SyntaxList ParseParameterList() { var builder = new SyntaxListBuilder(); var firstToken = true; while (CurrentToken.Kind != TokenKind.CloseParenthesisToken) { if (!firstToken) { builder.Add(EatToken(TokenKind.CommaToken)); } else { firstToken = false; } if (CurrentToken.Kind == TokenKind.TildeToken) { var notToken = EatToken(); builder.Add(notToken); } else { var identifierToken = EatToken(TokenKind.IdentifierToken); builder.Add(Factory.IdentifierNameExpressionSyntax(identifierToken)); } } return builder.ToList(); } private FunctionInputDescriptionSyntaxNode? ParseFunctionInputDescription() { if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { var openingBracket = EatToken(TokenKind.OpenParenthesisToken); var parameterList = ParseParameterList(); var closingBracket = EatToken(TokenKind.CloseParenthesisToken); return Factory.FunctionInputDescriptionSyntax( openingBracket, parameterList, closingBracket); } else { return null; } } private FunctionDeclarationSyntaxNode ParseFunctionDeclaration() { var functionKeyword = EatIdentifier("function"); var outputDescription = ParseFunctionOutputDescription(); var name = EatToken(TokenKind.IdentifierToken); var inputDescription = ParseFunctionInputDescription(); var commas = ParseOptionalCommas(); var body = ParseStatementList(); var endKeyword = ParseEndKeyword(); //var endKeyword = return Factory.FunctionDeclarationSyntax( functionKeyword, outputDescription, name, inputDescription, commas, body, endKeyword); } private EndKeywordSyntaxNode? ParseEndKeyword() { var keyword = EatPossiblyMissingIdentifier("end"); return keyword is null ? null : Factory.EndKeywordSyntax(keyword); } internal struct ParseOptions { public bool ParsingArrayElements { get; set; } public static ParseOptions Default = new ParseOptions { ParsingArrayElements = false }; } private ExpressionSyntaxNode? ParseExpression() { return ParseExpression(ParseOptions.Default); } private ExpressionSyntaxNode? ParseExpression(ParseOptions options) { return ParseSubExpression(options, SyntaxFacts.Precedence.Expression); } private ArrayLiteralExpressionSyntaxNode ParseArrayLiteral() { var openingSquareBracket = EatToken(TokenKind.OpenSquareBracketToken); var elements = ParseArrayElementList(TokenKind.CloseSquareBracketToken); var closingSquareBracket = EatToken(TokenKind.CloseSquareBracketToken); return Factory.ArrayLiteralExpressionSyntax( openingSquareBracket, elements, closingSquareBracket); } private CellArrayLiteralExpressionSyntaxNode ParseCellArrayLiteral() { var openingBrace = EatToken(TokenKind.OpenBraceToken); var elements = ParseArrayElementList(TokenKind.CloseBraceToken); var closingBrace = EatToken(TokenKind.CloseBraceToken); return Factory.CellArrayLiteralExpressionSyntax( openingBrace, elements, closingBrace); } private SyntaxList ParseArrayElementList(TokenKind closing) { var builder = new SyntaxListBuilder(); var firstToken = true; while (CurrentToken.Kind != closing) { if (!firstToken) { if (CurrentToken.Kind == TokenKind.CommaToken || CurrentToken.Kind == TokenKind.SemicolonToken) { builder.Add(EatToken()); } } else { firstToken = false; } var expression = ParseExpression(new ParseOptions { ParsingArrayElements = true }); if (expression != null) { builder.Add(expression); } } return builder.ToList(); } private ExpressionSyntaxNode? ParseTerm(ParseOptions options) { var token = CurrentToken; ExpressionSyntaxNode? expression = null; switch (token.Kind) { case TokenKind.NumberLiteralToken: expression = Factory.NumberLiteralExpressionSyntax(EatToken()); break; case TokenKind.StringLiteralToken: expression = Factory.StringLiteralExpressionSyntax(EatToken()); break; case TokenKind.DoubleQuotedStringLiteralToken: expression = Factory.DoubleQuotedStringLiteralExpressionSyntax(EatToken()); break; case TokenKind.OpenSquareBracketToken: expression = ParseArrayLiteral(); break; case TokenKind.OpenBraceToken: expression = ParseCellArrayLiteral(); break; case TokenKind.ColonToken: expression = Factory.EmptyExpressionSyntax(); break; case TokenKind.OpenParenthesisToken: expression = ParseParenthesizedExpression(); break; default: var id = EatToken(TokenKind.IdentifierToken); expression = Factory.IdentifierNameExpressionSyntax(id); break; } if (expression == null) { return null; } return ParsePostfix(options, expression); } private ExpressionSyntaxNode ParsePostfix(ParseOptions options, ExpressionSyntaxNode expression) { while (true) { var token = CurrentToken; switch (token.Kind) { case TokenKind.OpenBraceToken: // cell array element access if (options.ParsingArrayElements && expression.TrailingTrivia.Any()) { return expression; } var openingBrace = EatToken(); var indices = ParseArrayElementList(TokenKind.CloseBraceToken); var closingBrace = EatToken(TokenKind.CloseBraceToken); expression = Factory.CellArrayElementAccessExpressionSyntax( expression, openingBrace, indices, closingBrace ); break; case TokenKind.OpenParenthesisToken: // function call if (options.ParsingArrayElements && expression.TrailingTrivia.Any()) { return expression; } var openingBracket = EatToken(); var parameters = ParseFunctionCallParameterList(); var closingBracket = EatToken(TokenKind.CloseParenthesisToken); expression = Factory.FunctionCallExpressionSyntax( expression, openingBracket, parameters, closingBracket); break; case TokenKind.DotToken: // member access if (expression is IdentifierNameExpressionSyntaxNode || expression is MemberAccessExpressionSyntaxNode || expression is FunctionCallExpressionSyntaxNode || expression is CellArrayElementAccessExpressionSyntaxNode) { var dot = EatToken(); var member = ParseMemberAccess(); expression = Factory.MemberAccessExpressionSyntax(expression, dot, member); } else { throw new ParsingException( $"Unexpected token {token} at {CurrentPosition}."); } break; case TokenKind.ApostropheToken: case TokenKind.DotApostropheToken: var operation = EatToken(); expression = Factory.UnaryPostfixOperationExpressionSyntax(expression, operation); break; case TokenKind.UnquotedStringLiteralToken: return ParseCommandExpression(expression); case TokenKind.AtToken: if (expression.TrailingTrivia.Any()) { return expression; } return ParseBaseClassInvokation(expression); default: return expression; } } } private CommandExpressionSyntaxNode ParseCommandExpression(ExpressionSyntaxNode expression) { if (expression is IdentifierNameExpressionSyntaxNode idNameNode) { var builder = new SyntaxListBuilder(); while (CurrentToken.Kind == TokenKind.UnquotedStringLiteralToken) { builder.Add(Factory.UnquotedStringLiteralExpressionSyntax(EatToken())); } return Factory.CommandExpressionSyntax(idNameNode._name, builder.ToList()); } if (expression is null) { throw new Exception("Command expression identifier cannot be empty."); } throw new ParsingException($"Unexpected token \"{CurrentToken}\" while parsing expression \"{expression.FullText}\" at {CurrentPosition}."); } private ClassInvokationExpressionSyntaxNode ParseBaseClassInvokation(ExpressionSyntaxNode expression) { if (expression is IdentifierNameExpressionSyntaxNode methodName && !expression.TrailingTrivia.Any()) { var atToken = EatToken(); var baseClassNameWithArguments = ParseExpression(); if (baseClassNameWithArguments is null) { throw new Exception($"Base class name cannot be empty."); } return Factory.ClassInvokationExpressionSyntax(methodName, atToken, baseClassNameWithArguments); } if (expression is MemberAccessExpressionSyntaxNode memberAccess && !expression.TrailingTrivia.Any()) { var atToken = EatToken(); var baseClassNameWithArguments = ParseExpression(); if (baseClassNameWithArguments is null) { throw new Exception($"Base class name cannot be empty."); } return Factory.ClassInvokationExpressionSyntax(memberAccess, atToken, baseClassNameWithArguments); } throw new ParsingException($"Unexpected token \"{CurrentToken}\" at {CurrentPosition}."); } private ExpressionSyntaxNode ParseMemberAccess() { if (CurrentToken.Kind == TokenKind.IdentifierToken) { return Factory.IdentifierNameExpressionSyntax(EatToken()); } if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { var openingBracket = EatToken(); var indirectMember = ParseExpression(); if (indirectMember is null) { throw new Exception("Indirect member invokation cannot be empty."); } var closingBracket = EatToken(TokenKind.CloseParenthesisToken); return Factory.IndirectMemberAccessExpressionSyntax( openingBracket, indirectMember, closingBracket); } throw new ParsingException($"Unexpected token {CurrentToken} at {CurrentPosition}."); } private SyntaxList ParseFunctionCallParameterList() { var builder = new SyntaxListBuilder(); var firstToken = true; while (CurrentToken.Kind != TokenKind.CloseParenthesisToken) { if (!firstToken) { builder.Add(EatToken(TokenKind.CommaToken)); } else { firstToken = false; } var expression = ParseExpression(); if (expression is null) { throw new Exception("Function call parameter cannot be empty."); } builder.Add(expression); } return builder.ToList(); } private ParenthesizedExpressionSyntaxNode ParseParenthesizedExpression() { var openParen = EatToken(TokenKind.OpenParenthesisToken); var expression = ParseExpression(); if (expression is null) { throw new Exception("Parenthesized expression cannot be empty."); } var closeParen = EatToken(TokenKind.CloseParenthesisToken); return Factory.ParenthesizedExpressionSyntax( openParen, expression, closeParen); } private CompoundNameExpressionSyntaxNode ParseCompoundName() { var lastToken = EatToken(TokenKind.IdentifierToken); var firstName = lastToken; var builder = new SyntaxListBuilder(); builder.Add(firstName); while (CurrentToken.Kind == TokenKind.DotToken && !lastToken.TrailingTrivia.Any()) { var dot = EatToken(); builder.Add(dot); lastToken = EatToken(TokenKind.IdentifierToken); builder.Add(lastToken); } return Factory.CompoundNameExpressionSyntax(builder.ToList()); } private FunctionHandleExpressionSyntaxNode ParseFunctionHandle() { var atSign = EatToken(); if (CurrentToken.Kind == TokenKind.IdentifierToken) { var compoundName = ParseCompoundName(); return Factory.NamedFunctionHandleExpressionSyntax( atSign, compoundName); } else if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { var inputs = ParseFunctionInputDescription(); if (inputs is null) { throw new Exception($"Lambda expression inputs cannot be empty."); } var body = ParseExpression(); if (body is null) { throw new Exception($"Lambda expression body cannot be empty."); } return Factory.LambdaExpressionSyntax(atSign, inputs, body); } throw new ParsingException($"Unexpected token {CurrentToken} while parsing function handle at {CurrentPosition}."); } private ExpressionSyntaxNode? ParseSubExpression( ParseOptions options, SyntaxFacts.Precedence precedence) { ExpressionSyntaxNode? lhs; if (SyntaxFacts.IsUnaryOperator(CurrentToken.Kind)) { var operation = EatToken(); var unaryTokenKind = SyntaxFacts.ConvertToUnaryTokenKind(operation.Kind); var newPrecedence = SyntaxFacts.GetPrecedence(unaryTokenKind); var operand = ParseSubExpression(options, newPrecedence); if (operand == null) { if (options.ParsingArrayElements && operation.Kind == TokenKind.TildeToken) { operand = Factory.EmptyExpressionSyntax(); } else { throw new ParsingException($"Unexpected token {CurrentToken} at {CurrentPosition}."); } } lhs = Factory.UnaryPrefixOperationExpressionSyntax(operation, operand); } else if (CurrentToken.Kind == TokenKind.AtToken) { return ParseFunctionHandle(); } else { lhs = ParseTerm(options); if (lhs is null) { throw new Exception("Left-hand side in subexpression cannot be empty."); } } while (true) { var token = CurrentToken; if (SyntaxFacts.IsBinaryOperator(token.Kind)) { var newPrecedence = SyntaxFacts.GetPrecedence(token.Kind); if (newPrecedence < precedence) { break; } if (newPrecedence == precedence && SyntaxFacts.IsLeftAssociative(token.Kind)) { break; } EatToken(); var rhs = ParseSubExpression(options, newPrecedence); if (rhs == null) { if (token.Kind == TokenKind.ColonToken) // for parsing things like a{:} { rhs = Factory.EmptyExpressionSyntax(); } else { throw new Exception("Right-hand side in subexpression cannot be empty."); } } if (token.Kind == TokenKind.EqualsToken) { lhs = Factory.AssignmentExpressionSyntax(lhs, token, rhs); } else { lhs = Factory.BinaryOperationExpressionSyntax(lhs, token, rhs); } } else { break; } } return lhs; } private SyntaxList ParseOptionalCommas() { var builder = new SyntaxListBuilder(); while (CurrentToken.Kind == TokenKind.CommaToken) { builder.Add(EatToken()); } return builder.ToList(); } private SyntaxList ParseOptionalSemicolonsOrCommas() { var builder = new SyntaxListBuilder(); while (CurrentToken.Kind == TokenKind.CommaToken || CurrentToken.Kind == TokenKind.SemicolonToken) { builder.Add(EatToken()); } return builder.ToList(); } private SwitchCaseSyntaxNode ParseSwitchCase() { var caseKeyword = EatIdentifier("case"); var caseId = ParseExpression(); if (caseId is null) { throw new Exception("Case label cannot be empty."); } var commas = ParseOptionalCommas(); var statementList = ParseStatementList(); return Factory.SwitchCaseSyntax(caseKeyword, caseId, commas, statementList); } private SwitchStatementSyntaxNode ParseSwitchStatement() { var switchKeyword = EatIdentifier("switch"); var expression = ParseExpression(); if (expression is null) { throw new Exception("Match expression in switch statement cannot be empty."); } var commas = ParseOptionalCommas(); var builder = new SyntaxListBuilder(); while (IsIdentifier(CurrentToken, "case")) { builder.Add(ParseSwitchCase()); } var endKeyword = EatIdentifier("end"); return Factory.SwitchStatementSyntax( switchKeyword, expression, commas, builder.ToList(), endKeyword); } private WhileStatementSyntaxNode ParseWhileStatement() { var whileKeyword = EatIdentifier("while"); var condition = ParseExpression(); if (condition is null) { throw new Exception("Condition in while statement cannot be empty."); } var commas = ParseOptionalCommas(); var body = ParseStatementList(); var endKeyword = EatIdentifier("end"); return Factory.WhileStatementSyntax( whileKeyword, condition, commas, body, endKeyword); } private ElseifClause ParseElseifClause() { var elseifKeyword = EatIdentifier("elseif"); var condition = ParseExpression(); if (condition is null) { throw new Exception("Condition in elseif clause cannot be empty."); } var commas = ParseOptionalCommas(); var body = ParseStatementList(); return Factory.ElseifClause(elseifKeyword, condition, commas, body); } private ElseClause ParseElseClause() { var elseKeyword = EatIdentifier("else"); var body = ParseStatementList(); return Factory.ElseClause(elseKeyword, body); } private IfStatementSyntaxNode ParseIfStatement() { var ifKeyword = EatIdentifier(); var condition = ParseExpression(); if (condition is null) { throw new Exception("Condition in if statement cannot be empty."); } var commas = ParseOptionalSemicolonsOrCommas(); var body = ParseStatementList(); var elseifClauses = new SyntaxListBuilder(); ElseClause? elseClause = null; while (true) { var token = CurrentToken; if (IsIdentifier(token, "elseif")) { var elseifClause = ParseElseifClause(); elseifClauses.Add(elseifClause); continue; } else if (IsIdentifier(token, "else")) { elseClause = ParseElseClause(); break; } else if (IsIdentifier(token, "end")) { break; } throw new ParsingException($"Unexpected token \"{token}\" while parsing \"if\" statement at {CurrentPosition}."); } var endKeyword = EatIdentifier("end"); return Factory.IfStatementSyntax( ifKeyword, condition, commas, body, elseifClauses.ToList(), elseClause, endKeyword); } private ForStatementSyntaxNode ParseForStatement() { var forKeyword = EatIdentifier("for"); var expression = ParseExpression(); if (!(expression is AssignmentExpressionSyntaxNode)) { throw new ParsingException($"Unexpected expression \"{expression}\" while parsing FOR statement at {CurrentPosition}."); } var forAssignment = (AssignmentExpressionSyntaxNode) expression; var commas = ParseOptionalSemicolonsOrCommas(); var body = ParseStatementList(); var endKeyword = EatIdentifier("end"); return Factory.ForStatementSyntax( forKeyword, forAssignment, commas, body, endKeyword); } private CatchClauseSyntaxNode? ParseCatchClause() { if (IsIdentifier(CurrentToken, "catch")) { var catchKeyword = EatIdentifier(); var catchBody = ParseStatementList(); return Factory.CatchClauseSyntax( catchKeyword, catchBody); } return null; } private TryCatchStatementSyntaxNode ParseTryCatchStatement() { var tryKeyword = EatIdentifier("try"); var tryBody = ParseStatementList(); var catchClause = ParseCatchClause(); var endKeyword = EatIdentifier("end"); return Factory.TryCatchStatementSyntax(tryKeyword, tryBody, catchClause, endKeyword); } private ExpressionStatementSyntaxNode ParseExpressionStatement() { var expression = ParseExpression(); if (expression is null) { throw new Exception("Expression statement cannot be empty."); } return Factory.ExpressionStatementSyntax(expression); } private AttributeAssignmentSyntaxNode? ParseAttributeAssignment() { if (CurrentToken.Kind == TokenKind.EqualsToken) { var assignmentSign = EatToken(); var value = ParseExpression(); if (value is null) { throw new Exception("Right-hand side in attribute assignment cannot be empty."); } return Factory.AttributeAssignmentSyntax(assignmentSign, value); } return null; } private AttributeSyntaxNode ParseAttribute() { var name = EatToken(TokenKind.IdentifierToken); var assignment = ParseAttributeAssignment(); return Factory.AttributeSyntax(name, assignment); } private AttributeListSyntaxNode ParseAttributesList() { var openingBracket = EatToken(); var first = true; var builder = new SyntaxListBuilder(); while (CurrentToken.Kind != TokenKind.CloseParenthesisToken) { if (!first) { var comma = EatToken(TokenKind.CommaToken); builder.Add(comma); } first = false; builder.Add(ParseAttribute()); } var closingBracket = EatToken(); return Factory.AttributeListSyntax(openingBracket, builder.ToList(), closingBracket); } private AbstractMethodDeclarationSyntaxNode ParseAbstractMethodDeclaration() { var outputDescription = ParseFunctionOutputDescription(); var name = ParseCompoundName(); var inputDescription = ParseFunctionInputDescription(); return Factory.AbstractMethodDeclarationSyntax(outputDescription, name, inputDescription); } private StatementSyntaxNode ParseMethodDeclaration() { if (IsIdentifier(CurrentToken, "function")) { return ParseMethodDefinition(); } if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken || CurrentToken.Kind == TokenKind.IdentifierToken) { return ParseAbstractMethodDeclaration(); } if (CurrentToken.Kind == TokenKind.SemicolonToken) { return Factory.EmptyStatementSyntax(EatToken()); } throw new ParsingException($"Unexpected token {CurrentToken} while parsing method declaration at {CurrentPosition}."); } private ConcreteMethodDeclarationSyntaxNode ParseMethodDefinition() { var functionKeyword = EatIdentifier("function"); var outputDescription = ParseFunctionOutputDescription(); var name = ParseCompoundName(); var inputDescription = ParseFunctionInputDescription(); var commas = ParseOptionalCommas(); var body = ParseStatementList(); var endKeyword = ParseEndKeyword(); return Factory.ConcreteMethodDeclarationSyntax( functionKeyword, outputDescription, name, inputDescription, commas, body, endKeyword); } private MethodsListSyntaxNode ParseMethods() { var methodsKeyword = EatToken(); AttributeListSyntaxNode? attributes = null; if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { attributes = ParseAttributesList(); } var builder = new SyntaxListBuilder(); while (!IsIdentifier(CurrentToken, "end")) { var method = ParseMethodDeclaration(); builder.Add(method); } var endKeyword = EatToken(); return Factory.MethodsListSyntax(methodsKeyword, attributes, builder.ToList(), endKeyword); } private GreenNode? ParsePropertyDeclaration() { if (CurrentToken.Kind == TokenKind.CommaToken) { return EatToken(); } return ParseStatement(); } private SyntaxNode? ParseEventDeclaration() { return ParseStatement(); } private PropertiesListSyntaxNode ParseProperties() { var propertiesKeyword = EatToken(); AttributeListSyntaxNode? attributes = null; if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { attributes = ParseAttributesList(); } var builder = new SyntaxListBuilder(); while (!IsIdentifier(CurrentToken, "end")) { var declaration = ParsePropertyDeclaration(); if (declaration is null) { throw new Exception("Property declaration cannot be null."); } builder.Add(declaration); } var endKeyword = EatToken(); return Factory.PropertiesListSyntax(propertiesKeyword, attributes, builder.ToList(), endKeyword); } private EnumerationItemValueSyntaxNode? ParseEnumerationValue() { if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { var builder = new SyntaxListBuilder(); var openingBracket = EatToken(TokenKind.OpenParenthesisToken); var expression = ParseExpression() ?? Factory.EmptyExpressionSyntax(); builder.Add(expression); while (CurrentToken.Kind == TokenKind.CommaToken) { builder.Add(EatToken()); var nextExpression = ParseExpression(); if (nextExpression is null) { throw new Exception("Enumeration identifier cannot be empty."); } builder.Add(nextExpression); } var closingBracket = EatToken(TokenKind.CloseParenthesisToken); return Factory.EnumerationItemValueSyntax(openingBracket, builder.ToList(), closingBracket); } return null; } private EnumerationItemSyntaxNode ParseEnumerationItem() { var name = EatToken(); var values = ParseEnumerationValue(); var commas = ParseOptionalCommas(); return Factory.EnumerationItemSyntax(name, values, commas); } private EnumerationListSyntaxNode ParseEnumeration() { var enumerationKeyword = EatToken(); var builder = new SyntaxListBuilder(); AttributeListSyntaxNode? attributes = null; if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { attributes = ParseAttributesList(); } while (!IsIdentifier(CurrentToken, "end")) { var item = ParseEnumerationItem(); builder.Add(item); } var endkeyword = EatToken(); return Factory.EnumerationListSyntax( enumerationKeyword, attributes, builder.ToList(), endkeyword); } private SyntaxNode ParseEvents() { var eventsKeyword = EatToken(); AttributeListSyntaxNode? attributes = null; if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { attributes = ParseAttributesList(); } var builder = new SyntaxListBuilder(); while (!IsIdentifier(CurrentToken, "end")) { var eventDeclaration = ParseEventDeclaration(); if (eventDeclaration is null) { throw new Exception("Event declaration cannot be empty."); } builder.Add(eventDeclaration); } var endKeyword = EatToken(); return Factory.EventsListSyntax(eventsKeyword, attributes, builder.ToList(), endKeyword); } private SyntaxList ParseBaseClassNames() { var builder = new SyntaxListBuilder(); builder.Add(ParseCompoundName()); while (CurrentToken.Kind == TokenKind.AmpersandToken) { builder.Add(EatToken()); builder.Add(ParseCompoundName()); } return builder.ToList(); } private BaseClassListSyntaxNode ParseBaseClassList() { var lessSign = EatToken(); var baseClassNames = ParseBaseClassNames(); return Factory.BaseClassListSyntax(lessSign, baseClassNames); } private StatementSyntaxNode ParseClassDeclaration() { var classdefKeyword = EatToken(); AttributeListSyntaxNode? attributes = null; if (CurrentToken.Kind == TokenKind.OpenParenthesisToken) { attributes = ParseAttributesList(); } var className = EatToken(TokenKind.IdentifierToken); BaseClassListSyntaxNode? baseClassList = null; if (CurrentToken.Kind == TokenKind.LessToken) { baseClassList = ParseBaseClassList(); } var builder = new SyntaxListBuilder(); while (!IsIdentifier(CurrentToken, "end")) { if (IsIdentifier(CurrentToken, "methods")) { var methods = ParseMethods(); builder.Add(methods); } else if (IsIdentifier(CurrentToken, "properties")) { var properties = ParseProperties(); builder.Add(properties); } else if (IsIdentifier(CurrentToken, "events")) { var events = ParseEvents(); builder.Add(events); } else if (IsIdentifier(CurrentToken, "enumeration")) { var enumeration = ParseEnumeration(); builder.Add(enumeration); } else { throw new ParsingException($"Unknown token \"{CurrentToken}\" while parsing class definition at {CurrentPosition}."); } } var endKeyword = EatToken(); return Factory.ClassDeclarationSyntax( classdefKeyword, attributes, className, baseClassList, builder.ToList(), endKeyword); } private StatementSyntaxNode? ParseStatement() { if (CurrentToken.Kind == TokenKind.IdentifierToken) { switch (CurrentToken.Text) { case "function": return ParseFunctionDeclaration(); case "classdef": return ParseClassDeclaration(); case "switch": return ParseSwitchStatement(); case "while": return ParseWhileStatement(); case "if": return ParseIfStatement(); case "for": return ParseForStatement(); case "try": return ParseTryCatchStatement(); case "case": case "catch": case "else": case "elseif": case "end": return null; } } if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken) { return ParseExpressionStatement(); } if (CurrentToken.Kind == TokenKind.SemicolonToken) { return Factory.EmptyStatementSyntax(EatToken()); } return ParseExpressionStatement(); } private SyntaxList ParseStatementList() { var builder = new SyntaxListBuilder(); while (CurrentToken.Kind != TokenKind.EndOfFileToken) { var node = ParseStatement(); if (node == null) { break; } builder.Add(node); while (CurrentToken.Kind == TokenKind.CommaToken || CurrentToken.Kind == TokenKind.SemicolonToken) { builder.Add(EatToken()); } } return builder.ToList(); } public FileSyntaxNode ParseFile() { var statementList = ParseStatementList(); var endOfFileToken = EatToken(); return Factory.FileSyntax(statementList, endOfFileToken); } public RootSyntaxNode ParseRoot() { var file = ParseFile(); return Factory.RootSyntax(file); } } }