Merge parser & lexer
This commit is contained in:
parent
51a0cc863f
commit
fb4cb901f2
10
ConsoleDemo/ConsoleDemo.csproj
Normal file
10
ConsoleDemo/ConsoleDemo.csproj
Normal file
@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Parser\Parser.csproj" />
|
||||
<ProjectReference Include="..\Semantics\Semantics.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
189
ConsoleDemo/DumbWalker.cs
Normal file
189
ConsoleDemo/DumbWalker.cs
Normal file
@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Parser;
|
||||
using Semantics;
|
||||
|
||||
namespace ConsoleDemo
|
||||
{
|
||||
public class DumbWalker : SyntaxWalker
|
||||
{
|
||||
private bool _insideMethod;
|
||||
private bool _insideFunction;
|
||||
private VariableAssignments _variableAssignments;
|
||||
private MethodAssignments _methodAssignments;
|
||||
private Context _context;
|
||||
|
||||
public DumbWalker(Context context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private void Assign(SyntaxNode lhs, SyntaxNode rhs)
|
||||
{
|
||||
switch (lhs.Kind)
|
||||
{
|
||||
case TokenKind.IdentifierName:
|
||||
var name = ((IdentifierNameSyntaxNode) lhs).Name.Text;
|
||||
Console.WriteLine($"Adding variable assignment for {name}");
|
||||
_variableAssignments.Add(name, new Variable());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitAssignmentExpression(AssignmentExpressionSyntaxNode node)
|
||||
{
|
||||
if (_insideMethod || _insideFunction)
|
||||
{
|
||||
Console.Write($"Assignment: {node.Lhs} <- {node.Rhs}...");
|
||||
if (IsDefined(node.Rhs))
|
||||
{
|
||||
Console.WriteLine("Ok.");
|
||||
Assign(node.Lhs, node.Rhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Right-hand side is not defined!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsDefinedToken(SyntaxToken token)
|
||||
{
|
||||
switch (token.Kind)
|
||||
{
|
||||
case TokenKind.Comma:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsDefinedFunctionName(SyntaxNode node)
|
||||
{
|
||||
switch (node.Kind)
|
||||
{
|
||||
case TokenKind.IdentifierName:
|
||||
var name = (IdentifierNameSyntaxNode) node;
|
||||
if (_context.FindFunction(name.Name.Text))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_methodAssignments.Find(name.Text) != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsDefined(SyntaxNode node)
|
||||
{
|
||||
Variable assignment;
|
||||
switch (node.Kind)
|
||||
{
|
||||
case TokenKind.IdentifierName:
|
||||
assignment = _variableAssignments.Find(node.Text);
|
||||
if (assignment != null || node.Text == "end")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case TokenKind.FunctionCall:
|
||||
var functionCall = (FunctionCallExpressionSyntaxNode)node;
|
||||
return
|
||||
(IsDefined(functionCall.FunctionName) && IsDefined(functionCall.Nodes)) ||
|
||||
(IsDefinedFunctionName(functionCall.FunctionName) && IsDefined(functionCall.Nodes));
|
||||
case TokenKind.CellArrayElementAccess:
|
||||
var cellArrayElementAccess = (CellArrayElementAccessExpressionSyntaxNode) node;
|
||||
return IsDefined(cellArrayElementAccess.Expression) && IsDefined(cellArrayElementAccess.Nodes);
|
||||
case TokenKind.List:
|
||||
var list = (SyntaxNodeOrTokenList) node;
|
||||
return list.All(x => x.IsNode ? IsDefined(x.AsNode()) : IsDefinedToken(x.AsToken()));
|
||||
case TokenKind.NumberLiteralExpression:
|
||||
return true;
|
||||
case TokenKind.StringLiteralExpression:
|
||||
return true;
|
||||
case TokenKind.BinaryOperation:
|
||||
var binaryOperation = (BinaryOperationExpressionSyntaxNode) node;
|
||||
return IsDefined(binaryOperation.Lhs) && IsDefined(binaryOperation.Rhs);
|
||||
case TokenKind.UnaryPrefixOperationExpression:
|
||||
var unaryOperation = (UnaryPrefixOperationExpressionSyntaxNode) node;
|
||||
return IsDefined(unaryOperation.Operand);
|
||||
case TokenKind.ArrayLiteralExpression:
|
||||
var arrayLiteral = (ArrayLiteralExpressionSyntaxNode) node;
|
||||
return arrayLiteral.Nodes == null || IsDefined(arrayLiteral.Nodes);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void VisitFunctionDeclaration(FunctionDeclarationSyntaxNode node)
|
||||
{
|
||||
_insideFunction = true;
|
||||
_variableAssignments = new VariableAssignments();
|
||||
var parameterList = node.InputDescription.ParameterList;
|
||||
foreach (var parameter in parameterList)
|
||||
{
|
||||
if (parameter.IsNode)
|
||||
{
|
||||
var parameterAsNode = parameter.AsNode();
|
||||
Console.WriteLine($"Parameter node: {parameterAsNode}");
|
||||
if (parameterAsNode.Kind == TokenKind.IdentifierName)
|
||||
{
|
||||
Console.WriteLine($"Adding variable assignment for {parameterAsNode.Text}");
|
||||
_variableAssignments.Add(parameterAsNode.Text, new Variable());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Don't know how to add assignment for {parameterAsNode.Text}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Parameter token: {parameter.AsToken()}");
|
||||
}
|
||||
}
|
||||
base.VisitFunctionDeclaration(node);
|
||||
_variableAssignments = null;
|
||||
_insideFunction = false;
|
||||
}
|
||||
|
||||
public override void VisitMethodDefinition(MethodDefinitionSyntaxNode node)
|
||||
{
|
||||
_insideMethod = true;
|
||||
base.VisitMethodDefinition(node);
|
||||
_insideMethod = false;
|
||||
}
|
||||
|
||||
public override void VisitFile(FileSyntaxNode node)
|
||||
{
|
||||
_methodAssignments = new MethodAssignments();
|
||||
foreach (var nodeOrToken in node.StatementList)
|
||||
{
|
||||
if (nodeOrToken.IsToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var n = nodeOrToken.AsNode();
|
||||
if (n.Kind == TokenKind.FunctionDeclaration)
|
||||
{
|
||||
var functionDeclaration = (FunctionDeclarationSyntaxNode) n;
|
||||
_methodAssignments.Add(functionDeclaration.Name.Text, new Variable());
|
||||
}
|
||||
}
|
||||
base.VisitFile(node);
|
||||
}
|
||||
}
|
||||
}
|
504
ConsoleDemo/PrettyPrinter.cs
Normal file
504
ConsoleDemo/PrettyPrinter.cs
Normal file
@ -0,0 +1,504 @@
|
||||
using System;
|
||||
using Parser;
|
||||
using Parser.Internal;
|
||||
|
||||
namespace ProjectConsole
|
||||
{
|
||||
public class PrettyPrinter : SyntaxVisitor
|
||||
{
|
||||
public override void VisitFile(FileSyntaxNode node)
|
||||
{
|
||||
Visit(node.StatementList);
|
||||
OutputKeyword(node.EndOfFile);
|
||||
}
|
||||
|
||||
private void PrintToken(SyntaxToken token, ConsoleColor color, bool useBold = false)
|
||||
{
|
||||
if (token == default(SyntaxToken))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
foreach (var t in token.LeadingTrivia)
|
||||
{
|
||||
Console.Write(t);
|
||||
}
|
||||
|
||||
Console.ForegroundColor = color;
|
||||
if (useBold)
|
||||
{
|
||||
BoldOn();
|
||||
}
|
||||
Console.Write(token.Text);
|
||||
if (useBold)
|
||||
{
|
||||
BoldOff();
|
||||
}
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
foreach (var t in token.TrailingTrivia)
|
||||
{
|
||||
Console.Write(t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnderlineOn()
|
||||
{
|
||||
Console.Write("\x1b[4m");
|
||||
}
|
||||
|
||||
private static void UnderlineOff()
|
||||
{
|
||||
Console.Write("\x1b[0m");
|
||||
}
|
||||
|
||||
private static void BoldOn()
|
||||
{
|
||||
Console.Write("\x1b[1m");
|
||||
}
|
||||
|
||||
private static void BoldOff()
|
||||
{
|
||||
Console.Write("\x1b[0m");
|
||||
}
|
||||
|
||||
private void OutputKeyword(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.Green, useBold: true);
|
||||
}
|
||||
|
||||
private void OutputControlKeyword(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.Yellow, useBold: true);
|
||||
}
|
||||
|
||||
private void OutputPunctuation(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.DarkBlue);
|
||||
}
|
||||
|
||||
private void OutputOperator(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.Cyan);
|
||||
}
|
||||
|
||||
private void OutputIdentifier(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.White, useBold: true);
|
||||
}
|
||||
|
||||
private void OutputUnquotedStringLiteral(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.Blue);
|
||||
}
|
||||
|
||||
private void OutputStringLiteral(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.Magenta);
|
||||
}
|
||||
|
||||
private void OutputNumberLiteral(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.DarkGreen);
|
||||
}
|
||||
|
||||
private void OutputBracket(SyntaxToken token)
|
||||
{
|
||||
PrintToken(token, ConsoleColor.DarkYellow);
|
||||
}
|
||||
|
||||
public override void VisitBaseClassList(BaseClassListSyntaxNode node)
|
||||
{
|
||||
OutputPunctuation(node.LessSign);
|
||||
Visit(node.BaseClasses);
|
||||
}
|
||||
|
||||
public override void VisitClassDeclaration(ClassDeclarationSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.ClassdefKeyword);
|
||||
Visit(node.Attributes);
|
||||
BoldOn();
|
||||
Visit(node.ClassName);
|
||||
BoldOff();
|
||||
Visit(node.BaseClassList);
|
||||
Visit(node.Nodes);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
foreach (var t in node.LeadingTrivia)
|
||||
{
|
||||
Console.Write(t);
|
||||
}
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.Write(node.Text);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
foreach (var t in node.TrailingTrivia)
|
||||
{
|
||||
Console.Write(t);
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitList(SyntaxNodeOrTokenList list)
|
||||
{
|
||||
foreach (var nodeOrToken in list)
|
||||
{
|
||||
if (nodeOrToken.IsToken)
|
||||
{
|
||||
var token = nodeOrToken.AsToken();
|
||||
if (token.Kind == TokenKind.Identifier)
|
||||
{
|
||||
OutputIdentifier(token);
|
||||
}
|
||||
else if (SyntaxFacts.IsBracket(token.Kind))
|
||||
{
|
||||
OutputBracket(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputPunctuation(token);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Visit(nodeOrToken.AsNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitMethodsList(MethodsListSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.MethodsKeyword);
|
||||
Visit(node.Attributes);
|
||||
Visit(node.Methods);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitPropertiesList(PropertiesListSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.PropertiesKeyword);
|
||||
Visit(node.Attributes);
|
||||
Visit(node.Properties);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitMethodDefinition(MethodDefinitionSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.FunctionKeyword);
|
||||
Visit(node.OutputDescription);
|
||||
BoldOn();
|
||||
Visit(node.Name);
|
||||
BoldOff();
|
||||
Visit(node.InputDescription);
|
||||
Visit(node.Commas);
|
||||
Visit(node.Body);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitFunctionDeclaration(FunctionDeclarationSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.FunctionKeyword);
|
||||
Visit(node.OutputDescription);
|
||||
OutputIdentifier(node.Name);
|
||||
Visit(node.InputDescription);
|
||||
Visit(node.Commas);
|
||||
Visit(node.Body);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitIfStatement(IfStatementSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.IfKeyword);
|
||||
Visit(node.Condition);
|
||||
Visit(node.OptionalCommas);
|
||||
Visit(node.Body);
|
||||
Visit(node.ElseifClauses);
|
||||
Visit(node.ElseClause);
|
||||
OutputControlKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitElseClause(ElseClause node)
|
||||
{
|
||||
OutputControlKeyword(node.ElseKeyword);
|
||||
Visit(node.Body);
|
||||
}
|
||||
|
||||
public override void VisitElseifClause(ElseifClause node)
|
||||
{
|
||||
OutputControlKeyword(node.ElseifKeyword);
|
||||
Visit(node.Condition);
|
||||
Visit(node.Body);
|
||||
}
|
||||
|
||||
public override void VisitAbstractMethodDeclaration(AbstractMethodDeclarationSyntaxNode node)
|
||||
{
|
||||
Visit(node.OutputDescription);
|
||||
BoldOn();
|
||||
Visit(node.Name);
|
||||
BoldOff();
|
||||
Visit(node.InputDescription);
|
||||
}
|
||||
|
||||
public override void VisitAssignmentExpression(AssignmentExpressionSyntaxNode node)
|
||||
{
|
||||
Visit(node.Lhs);
|
||||
OutputOperator(node.AssignmentSign);
|
||||
Visit(node.Rhs);
|
||||
}
|
||||
|
||||
public override void VisitExpressionStatement(ExpressionStatementSyntaxNode node)
|
||||
{
|
||||
Visit(node.Expression);
|
||||
}
|
||||
|
||||
public override void VisitArrayLiteralExpression(ArrayLiteralExpressionSyntaxNode node)
|
||||
{
|
||||
OutputBracket(node.OpeningSquareBracket);
|
||||
Visit(node.Nodes);
|
||||
OutputBracket(node.ClosingSquareBracket);
|
||||
}
|
||||
|
||||
public override void VisitCellArrayLiteralExpression(CellArrayLiteralExpressionSyntaxNode node)
|
||||
{
|
||||
OutputBracket(node.OpeningBrace);
|
||||
Visit(node.Nodes);
|
||||
OutputBracket(node.ClosingBrace);
|
||||
}
|
||||
|
||||
public override void VisitIdentifierName(IdentifierNameSyntaxNode node)
|
||||
{
|
||||
OutputIdentifier(node.Name);
|
||||
}
|
||||
|
||||
public override void VisitForStatement(ForStatementSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.ForKeyword);
|
||||
Visit(node.Assignment);
|
||||
Visit(node.OptionalCommas);
|
||||
Visit(node.Body);
|
||||
OutputControlKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitSwitchStatement(SwitchStatementSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.SwitchKeyword);
|
||||
Visit(node.SwitchExpression);
|
||||
Visit(node.OptionalCommas);
|
||||
Visit(node.Cases);
|
||||
OutputControlKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitWhileStatement(WhileStatementSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.WhileKeyword);
|
||||
Visit(node.Condition);
|
||||
Visit(node.OptionalCommas);
|
||||
Visit(node.Body);
|
||||
OutputControlKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitUnquotedStringLiteral(UnquotedStringLiteralSyntaxNode node)
|
||||
{
|
||||
OutputUnquotedStringLiteral(node.StringToken);
|
||||
}
|
||||
|
||||
public override void VisitStringLiteral(StringLiteralSyntaxNode node)
|
||||
{
|
||||
OutputStringLiteral(node.StringToken);
|
||||
}
|
||||
|
||||
public override void VisitBinaryOperationExpression(BinaryOperationExpressionSyntaxNode node)
|
||||
{
|
||||
Visit(node.Lhs);
|
||||
OutputOperator(node.Operation);
|
||||
Visit(node.Rhs);
|
||||
}
|
||||
|
||||
public override void VisitFunctionCallExpression(FunctionCallExpressionSyntaxNode node)
|
||||
{
|
||||
Visit(node.FunctionName);
|
||||
OutputBracket(node.OpeningBracket);
|
||||
Visit(node.Nodes);
|
||||
OutputBracket(node.ClosingBracket);
|
||||
}
|
||||
|
||||
public override void VisitSwitchCase(SwitchCaseSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.CaseKeyword);
|
||||
Visit(node.CaseIdentifier);
|
||||
Visit(node.OptionalCommas);
|
||||
Visit(node.Body);
|
||||
}
|
||||
|
||||
public override void VisitCatchClause(CatchClauseSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.CatchKeyword);
|
||||
Visit(node.CatchBody);
|
||||
}
|
||||
|
||||
public override void VisitTryCatchStatement(TryCatchStatementSyntaxNode node)
|
||||
{
|
||||
OutputControlKeyword(node.TryKeyword);
|
||||
Visit(node.TryBody);
|
||||
Visit(node.CatchClause);
|
||||
OutputControlKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitCommandExpression(CommandExpressionSyntaxNode node)
|
||||
{
|
||||
Visit(node.CommandName);
|
||||
Visit(node.Arguments);
|
||||
}
|
||||
|
||||
public override void VisitNumberLiteral(NumberLiteralSyntaxNode node)
|
||||
{
|
||||
OutputNumberLiteral(node.Number);
|
||||
}
|
||||
|
||||
public override void VisitUnaryPrefixOperationExpression(UnaryPrefixOperationExpressionSyntaxNode node)
|
||||
{
|
||||
OutputOperator(node.Operation);
|
||||
Visit(node.Operand);
|
||||
}
|
||||
|
||||
public override void VisitUnaryPostixOperationExpression(UnaryPostixOperationExpressionSyntaxNode node)
|
||||
{
|
||||
Visit(node.Operand);
|
||||
OutputOperator(node.Operation);
|
||||
}
|
||||
|
||||
public override void VisitBaseClassInvokation(BaseClassInvokationSyntaxNode node)
|
||||
{
|
||||
Visit(node.MethodName);
|
||||
OutputOperator(node.AtSign);
|
||||
Visit(node.BaseClassNameAndArguments);
|
||||
}
|
||||
|
||||
public override void VisitAttributeAssignment(AttributeAssignmentSyntaxNode node)
|
||||
{
|
||||
OutputOperator(node.AssignmentSign);
|
||||
Visit(node.Value);
|
||||
}
|
||||
|
||||
public override void VisitAttribute(AttributeSyntaxNode node)
|
||||
{
|
||||
Visit(node.Name);
|
||||
Visit(node.Assignment);
|
||||
}
|
||||
|
||||
public override void VisitAttributeList(AttributeListSyntaxNode node)
|
||||
{
|
||||
OutputBracket(node.OpeningBracket);
|
||||
Visit(node.Nodes);
|
||||
OutputBracket(node.ClosingBracket);
|
||||
}
|
||||
|
||||
public override void VisitCellArrayElementAccessExpression(CellArrayElementAccessExpressionSyntaxNode node)
|
||||
{
|
||||
Visit(node.Expression);
|
||||
OutputBracket(node.OpeningBrace);
|
||||
Visit(node.Nodes);
|
||||
OutputBracket(node.ClosingBrace);
|
||||
}
|
||||
|
||||
public override void VisitCompoundName(CompoundNameSyntaxNode node)
|
||||
{
|
||||
Visit(node.Nodes);
|
||||
}
|
||||
|
||||
public override void VisitDoubleQuotedStringLiteral(DoubleQuotedStringLiteralSyntaxNode node)
|
||||
{
|
||||
OutputStringLiteral(node.StringToken);
|
||||
}
|
||||
|
||||
public override void VisitEmptyExpression(EmptyExpressionSyntaxNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public override void VisitEmptyStatement(EmptyStatementSyntaxNode node)
|
||||
{
|
||||
OutputPunctuation(node.Semicolon);
|
||||
}
|
||||
|
||||
public override void VisitEnumerationList(EnumerationListSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.EnumerationKeyword);
|
||||
Visit(node.Attributes);
|
||||
Visit(node.Items);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitEventsList(EventsListSyntaxNode node)
|
||||
{
|
||||
OutputKeyword(node.EventsKeyword);
|
||||
Visit(node.Attributes);
|
||||
Visit(node.Events);
|
||||
OutputKeyword(node.EndKeyword);
|
||||
}
|
||||
|
||||
public override void VisitEnumerationItemValue(EnumerationItemValueSyntaxNode node)
|
||||
{
|
||||
OutputPunctuation(node.OpeningBracket);
|
||||
Visit(node.Values);
|
||||
OutputPunctuation(node.ClosingBracket);
|
||||
}
|
||||
|
||||
public override void VisitEnumerationItem(EnumerationItemSyntaxNode node)
|
||||
{
|
||||
Visit(node.Name);
|
||||
Visit(node.Values);
|
||||
Visit(node.Commas);
|
||||
}
|
||||
|
||||
public override void VisitFunctionInputDescription(FunctionInputDescriptionSyntaxNode node)
|
||||
{
|
||||
OutputBracket(node.OpeningBracket);
|
||||
Visit(node.ParameterList);
|
||||
OutputBracket(node.ClosingBracket);
|
||||
}
|
||||
|
||||
public override void VisitFunctionOutputDescription(FunctionOutputDescriptionSyntaxNode node)
|
||||
{
|
||||
Visit(node.OutputList);
|
||||
OutputOperator(node.AssignmentSign);
|
||||
}
|
||||
|
||||
public override void VisitIndirectMemberAccess(IndirectMemberAccessSyntaxNode node)
|
||||
{
|
||||
OutputBracket(node.OpeningBracket);
|
||||
Visit(node.Expression);
|
||||
OutputBracket(node.ClosingBracket);
|
||||
}
|
||||
|
||||
public override void VisitLambda(LambdaSyntaxNode node)
|
||||
{
|
||||
OutputOperator(node.AtSign);
|
||||
Visit(node.Input);
|
||||
Visit(node.Body);
|
||||
}
|
||||
|
||||
public override void VisitNamedFunctionHandle(NamedFunctionHandleSyntaxNode node)
|
||||
{
|
||||
OutputOperator(node.AtSign);
|
||||
Visit(node.FunctionName);
|
||||
}
|
||||
|
||||
public override void VisitMemberAccess(MemberAccessSyntaxNode node)
|
||||
{
|
||||
Visit(node.LeftOperand);
|
||||
OutputOperator(node.Dot);
|
||||
Visit(node.RightOperand);
|
||||
}
|
||||
|
||||
public override void VisitParenthesizedExpression(ParenthesizedExpressionSyntaxNode node)
|
||||
{
|
||||
OutputBracket(node.OpeningBracket);
|
||||
Visit(node.Expression);
|
||||
OutputBracket(node.ClosingBracket);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,19 +2,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Lexer;
|
||||
using Parser;
|
||||
using ProjectConsole;
|
||||
using Semantics;
|
||||
|
||||
namespace ProjectConsole
|
||||
namespace ConsoleDemo
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private static readonly string BaseDirectory;
|
||||
private const string BaseDirectoryMacOs = @"/Applications/MATLAB_R2017b.app/toolbox/matlab/";
|
||||
private const string BaseDirectoryWindows = @"C:\Program Files\MATLAB\R2018a\toolbox\matlab\";
|
||||
private const string BaseDirectoryWindows = @"D:\Program Files\MATLAB\R2018a\toolbox\matlab\";
|
||||
|
||||
private static HashSet<string> skipFiles = new HashSet<string>
|
||||
private static readonly HashSet<string> SkipFiles = new HashSet<string>
|
||||
{
|
||||
@"codetools\private\template.m", // this is a template, so it contains '$' characters.
|
||||
@"plottools\+matlab\+graphics\+internal\+propertyinspector\+views\CategoricalHistogramPropertyView.m", // this one contains a 0xA0 character (probably it's 'non-breakable space' in Win-1252).
|
||||
@ -26,32 +26,31 @@ namespace ProjectConsole
|
||||
@"plottools/+matlab/+graphics/+internal/+propertyinspector/+views/PrimitiveHistogramPropertyView.m", // same
|
||||
};
|
||||
|
||||
static void ProcessFile(string fileName)
|
||||
private static MParser CreateParser(ITextWindow window)
|
||||
{
|
||||
return new MParser(window);
|
||||
}
|
||||
|
||||
private static void ProcessFile(string fileName)
|
||||
{
|
||||
var text = File.ReadAllText(fileName);
|
||||
Console.WriteLine($"Parsing {fileName}...");
|
||||
//Console.Write($"Parsing {fileName}...");
|
||||
var window = new TextWindowWithNull(text, fileName);
|
||||
ILexer<Token> lexer = new MLexer(window, new PureTokenFactory(window));
|
||||
var tokens = lexer.ParseAll();
|
||||
//AfterFunction(tokens);
|
||||
//FirstToken(tokens);
|
||||
var parser = new MParser(tokens);
|
||||
var parser = CreateParser(window);
|
||||
var tree = parser.Parse();
|
||||
var back = string.Join("", tokens.Select(token => token.FullText));
|
||||
if (text != back)
|
||||
//Console.WriteLine("Done.");
|
||||
var actual = tree.FullText;
|
||||
if (actual != text)
|
||||
{
|
||||
throw new ApplicationException();
|
||||
}
|
||||
//var printer = new PrettyPrinter();
|
||||
//printer.Visit(tree);
|
||||
//Console.ReadKey();
|
||||
}
|
||||
|
||||
private static readonly int[] firstTokenCount;
|
||||
private static readonly int[] afterFunctionCount;
|
||||
|
||||
static Program()
|
||||
{
|
||||
var maxKind = ((int[]) typeof(TokenKind).GetEnumValues()).Max();
|
||||
firstTokenCount = new int[maxKind + 1];
|
||||
afterFunctionCount = new int[maxKind + 1];
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
@ -64,57 +63,14 @@ namespace ProjectConsole
|
||||
}
|
||||
}
|
||||
|
||||
static void AfterFunction(List<Token> tokens)
|
||||
{
|
||||
for (var i = 0; i < tokens.Count; i++)
|
||||
{
|
||||
if (tokens[i].PureToken.Kind == TokenKind.Identifier &&
|
||||
tokens[i].PureToken.LiteralText == "function")
|
||||
{
|
||||
var nextKind = tokens[i + 1].PureToken.Kind;
|
||||
afterFunctionCount[(int) nextKind]++;
|
||||
if (nextKind != TokenKind.Identifier && nextKind != TokenKind.OpeningSquareBracket)
|
||||
{
|
||||
Console.WriteLine("===EXAMPLE===");
|
||||
Console.WriteLine($"{tokens[i]}{tokens[i+1]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FirstToken(List<Token> tokens)
|
||||
{
|
||||
var firstKind = tokens[0].PureToken.Kind;
|
||||
firstTokenCount[(int) firstKind]++;
|
||||
}
|
||||
|
||||
static void AfterFunctionFinish()
|
||||
{
|
||||
for (var i = 0; i < afterFunctionCount.Length; i++)
|
||||
{
|
||||
Console.WriteLine($"{(TokenKind)i}: {afterFunctionCount[i]}.");
|
||||
}
|
||||
}
|
||||
|
||||
static void FirstTokenFinish()
|
||||
{
|
||||
for (var i = 0; i < firstTokenCount.Length; i++)
|
||||
{
|
||||
if (firstTokenCount[i] != 0)
|
||||
{
|
||||
Console.WriteLine($"{(TokenKind) i}: {firstTokenCount[i]}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ProcessDirectory(string directory)
|
||||
private static int ProcessDirectory(string directory)
|
||||
{
|
||||
var counter = 0;
|
||||
var files = Directory.GetFiles(directory, "*.m");
|
||||
foreach (var file in files)
|
||||
{
|
||||
var relativePath = Path.GetRelativePath(BaseDirectory, file);
|
||||
if (skipFiles.Contains(relativePath))
|
||||
if (SkipFiles.Contains(relativePath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -130,8 +86,8 @@ namespace ProjectConsole
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
private static void ParserDemo()
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
var sw = new Stopwatch();
|
||||
@ -141,6 +97,80 @@ namespace ProjectConsole
|
||||
Console.WriteLine($"{processed} files parsed. Elapsed: {sw.Elapsed}.");
|
||||
//AfterFunctionFinish();
|
||||
//FirstTokenFinish();
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
private static FileSyntaxNode GetTree(string fileName)
|
||||
{
|
||||
var text = File.ReadAllText(fileName);
|
||||
var window = new TextWindowWithNull(text, fileName);
|
||||
var parser = CreateParser(window);
|
||||
var tree = parser.Parse();
|
||||
return tree;
|
||||
}
|
||||
|
||||
public static void SemanticsDemo()
|
||||
{
|
||||
var fileName = Path.Combine(
|
||||
BaseDirectory,
|
||||
"datatypes",
|
||||
"@table",
|
||||
"table.m");
|
||||
var tree = GetTree(fileName);
|
||||
var childNodesAndTokens = tree.GetChildNodesAndTokens();
|
||||
var node = childNodesAndTokens[0].AsNode();
|
||||
var classChildNodesAndTokens = node.GetChildNodesAndTokens();
|
||||
var c = GetClass.FromTree(tree, fileName);
|
||||
Console.WriteLine(c.Name);
|
||||
foreach (var m in c.Methods)
|
||||
{
|
||||
Console.WriteLine($"* Method {m.Name}");
|
||||
if (m.Description != "")
|
||||
{
|
||||
Console.WriteLine($"* Description: {m.Description}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ContextDemo()
|
||||
{
|
||||
var context = new Context();
|
||||
context.ScanPath(BaseDirectory);
|
||||
}
|
||||
|
||||
public static void DumbPrinterDemo()
|
||||
{
|
||||
var context = new Context();
|
||||
context.ScanPath(BaseDirectory);
|
||||
var fileName = Path.Combine(
|
||||
BaseDirectory,
|
||||
"specgraph",
|
||||
"heatmap.m");
|
||||
var tree = GetTree(fileName);
|
||||
var printer = new DumbWalker(context);
|
||||
printer.Visit(tree);
|
||||
}
|
||||
|
||||
public static void UsageDemo()
|
||||
{
|
||||
var context = new Context();
|
||||
context.ScanPath(BaseDirectory);
|
||||
var fileName = Path.Combine(
|
||||
BaseDirectory,
|
||||
"specgraph",
|
||||
"heatmap.m");
|
||||
var tree = GetTree(fileName);
|
||||
var printer = new UsageGathering(context);
|
||||
printer.Visit(tree);
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
//ParserDemo();
|
||||
//SemanticsDemo();
|
||||
//ContextDemo();
|
||||
//DumbPrinterDemo();
|
||||
UsageDemo();
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
151
ConsoleDemo/UsageGathering.cs
Normal file
151
ConsoleDemo/UsageGathering.cs
Normal file
@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using Parser;
|
||||
using Semantics;
|
||||
|
||||
namespace ConsoleDemo
|
||||
{
|
||||
public class UsageGathering : SyntaxWalker
|
||||
{
|
||||
private MethodAssignments _methodAssignments;
|
||||
private VariableAssignments _variableAssignments;
|
||||
private bool _insideFunction;
|
||||
private bool _insideMethod;
|
||||
|
||||
private Context _context;
|
||||
|
||||
public UsageGathering(Context context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public override void VisitFunctionCallExpression(FunctionCallExpressionSyntaxNode node)
|
||||
{
|
||||
if (!(_insideFunction || _insideMethod))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = node.FunctionName.Text;
|
||||
if (_variableAssignments.Find(name) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Console.Write($"Function call: {name}...");
|
||||
if (_context.FindFunction(name) || _methodAssignments.Find(name) != null)
|
||||
{
|
||||
Console.WriteLine("found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("NOT FOUND.");
|
||||
}
|
||||
base.VisitFunctionCallExpression(node);
|
||||
}
|
||||
|
||||
private void Assign(SyntaxNode lhs, SyntaxNode rhs)
|
||||
{
|
||||
switch (lhs.Kind)
|
||||
{
|
||||
case TokenKind.IdentifierName:
|
||||
var name = ((IdentifierNameSyntaxNode)lhs).Name.Text;
|
||||
Console.WriteLine($"Adding variable assignment for {name}");
|
||||
_variableAssignments.Add(name, new Variable());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitAssignmentExpression(AssignmentExpressionSyntaxNode node)
|
||||
{
|
||||
if (!(_insideFunction || _insideMethod))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Assign(node.Lhs, node.Rhs);
|
||||
base.VisitAssignmentExpression(node);
|
||||
}
|
||||
|
||||
public override void VisitFile(FileSyntaxNode node)
|
||||
{
|
||||
_methodAssignments = new MethodAssignments();
|
||||
foreach (var nodeOrToken in node.StatementList)
|
||||
{
|
||||
if (nodeOrToken.IsToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var n = nodeOrToken.AsNode();
|
||||
if (n.Kind == TokenKind.FunctionDeclaration)
|
||||
{
|
||||
var functionDeclaration = (FunctionDeclarationSyntaxNode)n;
|
||||
_methodAssignments.Add(functionDeclaration.Name.Text, new Variable());
|
||||
}
|
||||
}
|
||||
base.VisitFile(node);
|
||||
}
|
||||
|
||||
public override void VisitFunctionDeclaration(FunctionDeclarationSyntaxNode node)
|
||||
{
|
||||
_insideFunction = true;
|
||||
_variableAssignments = new VariableAssignments();
|
||||
var parameterList = node.InputDescription.ParameterList;
|
||||
foreach (var parameter in parameterList)
|
||||
{
|
||||
if (parameter.IsNode)
|
||||
{
|
||||
var parameterAsNode = parameter.AsNode();
|
||||
Console.WriteLine($"Parameter node: {parameterAsNode}");
|
||||
if (parameterAsNode.Kind == TokenKind.IdentifierName)
|
||||
{
|
||||
Console.WriteLine($"Adding variable assignment for {parameterAsNode.Text}");
|
||||
_variableAssignments.Add(parameterAsNode.Text, new Variable());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Don't know how to add assignment for {parameterAsNode.Text}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Parameter token: {parameter.AsToken()}");
|
||||
}
|
||||
}
|
||||
base.VisitFunctionDeclaration(node);
|
||||
_variableAssignments = null;
|
||||
_insideFunction = false;
|
||||
}
|
||||
|
||||
public override void VisitMethodDefinition(MethodDefinitionSyntaxNode node)
|
||||
{
|
||||
_insideMethod = true;
|
||||
_variableAssignments = new VariableAssignments();
|
||||
var parameterList = node.InputDescription.ParameterList;
|
||||
foreach (var parameter in parameterList)
|
||||
{
|
||||
if (parameter.IsNode)
|
||||
{
|
||||
var parameterAsNode = parameter.AsNode();
|
||||
Console.WriteLine($"Parameter node: {parameterAsNode}");
|
||||
if (parameterAsNode.Kind == TokenKind.IdentifierName)
|
||||
{
|
||||
Console.WriteLine($"Adding variable assignment for {parameterAsNode.Text}");
|
||||
_variableAssignments.Add(parameterAsNode.Text, new Variable());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Don't know how to add assignment for {parameterAsNode.Text}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Parameter token: {parameter.AsToken()}");
|
||||
}
|
||||
}
|
||||
base.VisitMethodDefinition(node);
|
||||
_variableAssignments = null;
|
||||
_insideMethod = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lexer\Lexer.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,276 +0,0 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Lexer.Tests
|
||||
{
|
||||
public class MLexerShould
|
||||
{
|
||||
private static MLexer CreateLexer(string text)
|
||||
{
|
||||
var window = new TextWindowWithNull(text);
|
||||
return new MLexer(window, new PureTokenFactory(window));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseSequenceOfIdentifiers()
|
||||
{
|
||||
var sut = CreateLexer("undefined is not\n a function");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(6, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[] {"undefined", "is", "not", "a", "function"},
|
||||
tokens.Take(5).Select(token => token.PureToken.LiteralText));
|
||||
CollectionAssert.AreEqual(
|
||||
new[] { TokenKind.Identifier, TokenKind.UnquotedStringLiteral, TokenKind.UnquotedStringLiteral,
|
||||
TokenKind.Identifier, TokenKind.UnquotedStringLiteral },
|
||||
tokens.Take(5).Select(token => token.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseIdentifierAndBrackets()
|
||||
{
|
||||
var sut = CreateLexer("undefined()");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(4, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.Identifier,
|
||||
TokenKind.OpeningBracket,
|
||||
TokenKind.ClosingBracket,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTransposeSignAfterClosingSquareBracket()
|
||||
{
|
||||
var sut = CreateLexer("[undefined]'");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(5, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.OpeningSquareBracket,
|
||||
TokenKind.Identifier,
|
||||
TokenKind.ClosingSquareBracket,
|
||||
TokenKind.Transpose,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTransposeSignAfterClosingBrace()
|
||||
{
|
||||
var sut = CreateLexer("{undefined}'");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(5, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.OpeningBrace,
|
||||
TokenKind.Identifier,
|
||||
TokenKind.ClosingBrace,
|
||||
TokenKind.Transpose,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTransposeSignAfterClosingBracket()
|
||||
{
|
||||
var sut = CreateLexer("undefined()'");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(5, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.Identifier,
|
||||
TokenKind.OpeningBracket,
|
||||
TokenKind.ClosingBracket,
|
||||
TokenKind.Transpose,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTransposeSignAfterIdentifier()
|
||||
{
|
||||
var sut = CreateLexer("undefined'");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(3, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.Identifier,
|
||||
TokenKind.Transpose,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTransposeSignAfterDot()
|
||||
{
|
||||
var sut = CreateLexer("undefined.'");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(3, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.Identifier,
|
||||
TokenKind.DotTranspose,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDotPowerAfterNumber()
|
||||
{
|
||||
var sut = CreateLexer("26.^[1]");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(6, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.NumberLiteral,
|
||||
TokenKind.DotPower,
|
||||
TokenKind.OpeningSquareBracket,
|
||||
TokenKind.NumberLiteral,
|
||||
TokenKind.ClosingSquareBracket,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDotInNumberBeforeSemicolon()
|
||||
{
|
||||
var sut = CreateLexer("42.;");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(3, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.NumberLiteral,
|
||||
TokenKind.Semicolon,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseEAfterDotInANumber()
|
||||
{
|
||||
var sut = CreateLexer("42.e-5");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
CollectionAssert.AreEqual(
|
||||
new[]
|
||||
{
|
||||
TokenKind.NumberLiteral,
|
||||
TokenKind.EndOfFile
|
||||
},
|
||||
tokens.Select(token => token.PureToken.Kind));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseEmptyLine()
|
||||
{
|
||||
var sut = CreateLexer("\n\nfunction shmunction\n\n\n");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(3, tokens.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseCommentsAfterDotDotDot()
|
||||
{
|
||||
var sut = CreateLexer("something ... #$@#%*^!@#\n");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
}
|
||||
|
||||
[TestCase("something ... #$@#%*^!@#\n")]
|
||||
[TestCase("undefined is not a function")]
|
||||
[TestCase("\n\nfunction shmunction\n\n\n")]
|
||||
public void ReconstructTest(string s)
|
||||
{
|
||||
var sut = CreateLexer(s);
|
||||
var tokens = sut.ParseAll();
|
||||
var actual = string.Join("", tokens.Select(token => token.FullText));
|
||||
Assert.AreEqual(s, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseStringLiteral()
|
||||
{
|
||||
var sut = CreateLexer("'just a string'");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
Assert.AreEqual(TokenKind.StringLiteral, tokens[0].Kind);
|
||||
Assert.AreEqual("just a string", tokens[0].PureToken.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseStringLiteralWithEscapedQuotes()
|
||||
{
|
||||
var sut = CreateLexer("'just a ''string'''");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
Assert.AreEqual(TokenKind.StringLiteral, tokens[0].Kind);
|
||||
Assert.AreEqual("just a 'string'", tokens[0].PureToken.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDoubleQuotedStringLiteralWithEscapedQuotes()
|
||||
{
|
||||
var sut = CreateLexer("\"just a \"\"string\"\"\"");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
Assert.AreEqual(TokenKind.DoubleQuotedStringLiteral, tokens[0].Kind);
|
||||
Assert.AreEqual("just a \"string\"", tokens[0].PureToken.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseNumberStartingWithDot()
|
||||
{
|
||||
var sut = CreateLexer(".42");
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
Assert.AreEqual(TokenKind.NumberLiteral, tokens[0].Kind);
|
||||
}
|
||||
|
||||
[TestCase("%{\nabc\n%}", true)]
|
||||
[TestCase("%{ a\nabc\n%}", false)]
|
||||
[TestCase("if %{\nabc\n%}", false)]
|
||||
public void ParseMultilineComments(string text, bool isMultiline)
|
||||
{
|
||||
var sut = CreateLexer(text);
|
||||
var tokens = sut.ParseAll();
|
||||
if (isMultiline)
|
||||
{
|
||||
Assert.AreEqual(1, tokens.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Less(1, tokens.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(".42i")]
|
||||
[TestCase("42i")]
|
||||
[TestCase("42e-1i")]
|
||||
public void ParseComplexNumbers(string text)
|
||||
{
|
||||
var sut = CreateLexer(text);
|
||||
var tokens = sut.ParseAll();
|
||||
Assert.AreEqual(2, tokens.Count);
|
||||
Assert.AreEqual(TokenKind.NumberLiteral, tokens[0].Kind);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public interface IPosition
|
||||
{
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,14 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public struct PositionInsideFile : IPosition
|
||||
{
|
||||
public string File { get; set; }
|
||||
public int Line { get; set; }
|
||||
public int Column { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"line {Line}, column {Column}" + (File != null ? $" of {File}" : "");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public struct PureToken
|
||||
{
|
||||
public TokenKind Kind { get; }
|
||||
public string LiteralText { get; }
|
||||
public object Value { get; }
|
||||
public IPosition Position { get; }
|
||||
|
||||
public PureToken(TokenKind kind, string literalText, object value, IPosition position)
|
||||
{
|
||||
Kind = kind;
|
||||
LiteralText = literalText;
|
||||
Value = value;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public override string ToString() => LiteralText;
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public class PureTokenFactory
|
||||
{
|
||||
private ITextWindow Window { get; }
|
||||
|
||||
public PureTokenFactory(ITextWindow window)
|
||||
{
|
||||
Window = window;
|
||||
}
|
||||
|
||||
private static readonly string[] PureTokenOfKind =
|
||||
{
|
||||
null, // None = 0,
|
||||
null, // EndOfFile = 1,
|
||||
null, // Identifier = 2,
|
||||
null, // NumberLiteral = 3,
|
||||
null, // StringLiteral = 4,
|
||||
null, // DoubleQuotedStringLiteral = 5,
|
||||
null, // UnquotedStringLiteral = 6
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
"=", // Assignment = 20,
|
||||
"==", // Equality = 21,
|
||||
"~=", // Inequality = 22,
|
||||
"&&", // LogicalAnd = 23,
|
||||
"||", // LogicalOr = 24,
|
||||
"&", // BitwiseAnd = 25,
|
||||
"|", // BitwiseOr = 26,
|
||||
"<", // Less = 27,
|
||||
"<=", // LessOrEqual = 28,
|
||||
">", // Greater = 29,
|
||||
">=", // GreaterOrEqual = 30,
|
||||
"~", // Not = 31,
|
||||
"+", // Plus = 32,
|
||||
"-", // Minus = 33,
|
||||
"*", // Multiply = 34,
|
||||
"/", // Divide = 35,
|
||||
"^", // Power = 36,
|
||||
"\\", // Backslash = 37,
|
||||
"'", // Transpose = 38,
|
||||
".*", // DotMultiply = 39,
|
||||
"./", // DotDivide = 40,
|
||||
".^", // DotPower = 41,
|
||||
".\\", // DotBackslash = 42,
|
||||
".'", // DotTranspose = 43,
|
||||
"@", // At = 44,
|
||||
":", // Colon = 45,
|
||||
"?", // QuestionMark = 46,
|
||||
",", // Comma = 47,
|
||||
";", // Semicolon = 48,
|
||||
"{", // OpeningBrace = 49,
|
||||
"}", // ClosingBrace = 50,
|
||||
"[", // OpeningSquareBracket = 51,
|
||||
"]", // ClosingSquareBracket = 52,
|
||||
"(", // OpeningBracket = 53,
|
||||
")", // ClosingBracket = 54,
|
||||
".", // Dot = 55,
|
||||
"...", // DotDotDot = 56,
|
||||
|
||||
"+", // UnaryPlus = 57,
|
||||
"-", // UnaryMinus = 58,
|
||||
"~", // UnaryNot = 59,
|
||||
"?", // UnaryQuestionMark = 60,
|
||||
};
|
||||
|
||||
public PureToken CreatePunctuation(TokenKind kind)
|
||||
{
|
||||
return new PureToken(kind, PureTokenOfKind[(int)kind], null, Window.Position);
|
||||
}
|
||||
|
||||
public PureToken CreateIdentifier(string s)
|
||||
{
|
||||
return new PureToken(TokenKind.Identifier, s, null, Window.Position);
|
||||
}
|
||||
|
||||
public PureToken CreateNumberLiteral(string s)
|
||||
{
|
||||
return new PureToken(TokenKind.NumberLiteral, s, null, Window.Position); // TODO: actually parse number (here or in the lexer?)
|
||||
}
|
||||
|
||||
private string EscapeStringLiteral(string s)
|
||||
{
|
||||
return s.Replace("'", "''");
|
||||
}
|
||||
|
||||
public PureToken CreateStringLiteral(string s)
|
||||
{
|
||||
return new PureToken(TokenKind.StringLiteral, "'" + EscapeStringLiteral(s) + "'", s, Window.Position);
|
||||
}
|
||||
|
||||
private string EscapeDoubleQuotedStringLiteral(string s)
|
||||
{
|
||||
return s.Replace("\"", "\"\"");
|
||||
}
|
||||
|
||||
public PureToken CreateDoubleQuotedStringLiteral(string s)
|
||||
{
|
||||
return new PureToken(TokenKind.DoubleQuotedStringLiteral, "\"" + EscapeDoubleQuotedStringLiteral(s) + "\"", s, Window.Position);
|
||||
}
|
||||
|
||||
public PureToken CreateUnquotedStringLiteral(string s)
|
||||
{
|
||||
return new PureToken(TokenKind.UnquotedStringLiteral, s, s, Window.Position);
|
||||
}
|
||||
|
||||
public PureToken CreateEndOfFileToken()
|
||||
{
|
||||
return new PureToken(TokenKind.EndOfFile, "", null, Window.Position);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Lexer
|
||||
{
|
||||
public class Token
|
||||
{
|
||||
public List<Trivia> LeadingTrivia { get; }
|
||||
public List<Trivia> TrailingTrivia { get; }
|
||||
public PureToken PureToken { get; }
|
||||
public string FullText { get; }
|
||||
public TokenKind Kind => PureToken.Kind;
|
||||
|
||||
public Token(PureToken pureToken, List<Trivia> leadingTrivia, List<Trivia> trailingTrivia)
|
||||
{
|
||||
PureToken = pureToken;
|
||||
LeadingTrivia = leadingTrivia;
|
||||
TrailingTrivia = trailingTrivia;
|
||||
FullText = BuildFullText();
|
||||
}
|
||||
|
||||
private string BuildFullText()
|
||||
{
|
||||
var leading = LeadingTrivia.Select(t => t.LiteralText);
|
||||
var token = PureToken.LiteralText;
|
||||
var trailing = TrailingTrivia.Select(t => t.LiteralText);
|
||||
return string.Join("", leading.Concat(new[] {token}).Concat(trailing));
|
||||
}
|
||||
|
||||
public override string ToString() => FullText;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public enum TokenKind
|
||||
{
|
||||
None = 0,
|
||||
EndOfFile = 1,
|
||||
Identifier = 2,
|
||||
NumberLiteral = 3,
|
||||
StringLiteral = 4,
|
||||
DoubleQuotedStringLiteral = 5,
|
||||
UnquotedStringLiteral = 6,
|
||||
|
||||
Assignment = 20,
|
||||
Equality = 21,
|
||||
Inequality = 22,
|
||||
LogicalAnd = 23,
|
||||
LogicalOr = 24,
|
||||
BitwiseAnd = 25,
|
||||
BitwiseOr = 26,
|
||||
Less = 27,
|
||||
LessOrEqual = 28,
|
||||
Greater = 29,
|
||||
GreaterOrEqual = 30,
|
||||
Not = 31,
|
||||
Plus = 32,
|
||||
Minus = 33,
|
||||
Multiply = 34,
|
||||
Divide = 35,
|
||||
Power = 36,
|
||||
Backslash = 37,
|
||||
Transpose = 38,
|
||||
DotMultiply = 39,
|
||||
DotDivide = 40,
|
||||
DotPower = 41,
|
||||
DotBackslash = 42,
|
||||
DotTranspose = 43,
|
||||
At = 44,
|
||||
Colon = 45,
|
||||
QuestionMark = 46,
|
||||
Comma = 47,
|
||||
Semicolon = 48,
|
||||
OpeningBrace = 49,
|
||||
ClosingBrace = 50,
|
||||
OpeningSquareBracket = 51,
|
||||
ClosingSquareBracket = 52,
|
||||
OpeningBracket = 53,
|
||||
ClosingBracket = 54,
|
||||
Dot = 55,
|
||||
DotDotDot = 56,
|
||||
// unary tokens are not recognized during lexing; they are contextually recognized while parsing.
|
||||
UnaryPlus = 57,
|
||||
UnaryMinus = 58,
|
||||
UnaryNot = 59,
|
||||
UnaryQuestionMark = 60,
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public class Trivia
|
||||
{
|
||||
public TriviaType Type { get; }
|
||||
public string LiteralText { get; }
|
||||
|
||||
public Trivia(TriviaType type, string literalText)
|
||||
{
|
||||
Type = type;
|
||||
LiteralText = literalText;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace Lexer
|
||||
{
|
||||
public enum TriviaType
|
||||
{
|
||||
Whitespace,
|
||||
NewLine,
|
||||
Comment,
|
||||
MultiLineComment
|
||||
}
|
||||
}
|
@ -1,495 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lexer;
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Parser.Tests
|
||||
{
|
||||
public class MParserShould
|
||||
{
|
||||
private static MParser CreateParser(string text)
|
||||
private static MParser GetSut(string text)
|
||||
{
|
||||
var window = new TextWindowWithNull(text);
|
||||
var lexer = new MLexer(window, new PureTokenFactory(window));
|
||||
var tokens = lexer.ParseAll();
|
||||
var parser = new MParser(tokens);
|
||||
var parser = new MParser(window);
|
||||
return parser;
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ParseAssignmentExpression()
|
||||
{
|
||||
var text = "a = b";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<AssignmentExpressionNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseSimpleStatement()
|
||||
{
|
||||
var text = "a = b";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<ExpressionStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseFunctionCallExpression()
|
||||
{
|
||||
var text = "func(a, 2, 'abc', d)";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<FunctionCallExpressionNode>(actual);
|
||||
var f = actual as FunctionCallExpressionNode;
|
||||
Assert.AreEqual(4, f?.Parameters.Parameters.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseArrayLiteralExpression()
|
||||
{
|
||||
var text = "[a, 2, 'text']";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var a = actual as ArrayLiteralExpressionNode;
|
||||
Assert.AreEqual(3, a?.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseLeftAssociativeSamePrecedence()
|
||||
{
|
||||
var text = "2 + 3 + 4";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(actual);
|
||||
var e = (BinaryOperationExpressionNode)actual;
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(e.Lhs);
|
||||
Assert.IsInstanceOf<NumberLiteralNode>(e.Rhs);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseLeftAssociativeRaisingPrecedence()
|
||||
{
|
||||
var text = "2 + 3 * 4";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(actual);
|
||||
var e = (BinaryOperationExpressionNode) actual;
|
||||
Assert.AreEqual(TokenKind.Plus, e.Operation.Token.Kind);
|
||||
Assert.IsInstanceOf<NumberLiteralNode>(e.Lhs);
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(e.Rhs);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseLeftAssociativeLoweringPrecedence()
|
||||
{
|
||||
var text = "2 * 3 + 4";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(actual);
|
||||
var e = (BinaryOperationExpressionNode) actual;
|
||||
Assert.AreEqual(TokenKind.Plus, e.Operation.Token.Kind);
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(e.Lhs);
|
||||
Assert.IsInstanceOf<NumberLiteralNode>(e.Rhs);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseUnaryOperators()
|
||||
{
|
||||
var text = "-42";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<UnaryPrefixOperationExpressionNode>(actual);
|
||||
var e = (UnaryPrefixOperationExpressionNode) actual;
|
||||
Assert.AreEqual(TokenKind.Minus, e.Operation.Token.Kind);
|
||||
Assert.IsInstanceOf<NumberLiteralNode>(e.Operand);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseMemberAccess()
|
||||
{
|
||||
var text = "a.b.c";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<MemberAccessNode>(actual);
|
||||
var m = (MemberAccessNode) actual;
|
||||
Assert.IsInstanceOf<MemberAccessNode>(m.LeftOperand);
|
||||
Assert.IsInstanceOf<IdentifierNameNode>(m.RightOperand);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseWhileStatement()
|
||||
{
|
||||
var text = "while a < b c = d end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<WhileStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseWhileStatementWithComma()
|
||||
{
|
||||
var text = "while a < b, c = d end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<WhileStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseIfStatement()
|
||||
{
|
||||
var text = "if 2 < 3 a = b end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<IfStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseIfElseStatement()
|
||||
{
|
||||
var text = "if 2 < 3 a = b else c = d end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<IfStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseParenthesizedExpression()
|
||||
{
|
||||
var text = "2 * (3 + 4)";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(actual);
|
||||
var e = (BinaryOperationExpressionNode) actual;
|
||||
Assert.IsInstanceOf<NumberLiteralNode>(e.Lhs);
|
||||
Assert.IsInstanceOf<ParenthesizedExpressionNode>(e.Rhs);
|
||||
var p = (ParenthesizedExpressionNode) e.Rhs;
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(p.Expression);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseForStatement()
|
||||
{
|
||||
var text = "for i = 1:5 a = i end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<ForStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseEmptyArray()
|
||||
{
|
||||
var text = "[]";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var a = (ArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(0, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseCellArrayLiteral()
|
||||
{
|
||||
var text = "{ 1 2, 3 }";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
|
||||
var a = (CellArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(3, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseIndirectMemberAccess()
|
||||
{
|
||||
var text = "abc.(def)";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<MemberAccessNode>(actual);
|
||||
var a = (MemberAccessNode) actual;
|
||||
Assert.IsInstanceOf<IdentifierNameNode>(a.LeftOperand);
|
||||
Assert.IsInstanceOf<IndirectMemberAccessNode>(a.RightOperand);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseMemberAccessAfterElementAccess()
|
||||
{
|
||||
var text = "a(1).b";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<MemberAccessNode>(actual);
|
||||
var m = (MemberAccessNode) actual;
|
||||
Assert.IsInstanceOf<FunctionCallExpressionNode>(m.LeftOperand);
|
||||
Assert.IsInstanceOf<IdentifierNameNode>(m.RightOperand);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseFunctionDeclarationWithoutInputs()
|
||||
{
|
||||
var text = "function a = b a = 1";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<FunctionDeclarationNode>(actual);
|
||||
var f = (FunctionDeclarationNode) actual;
|
||||
Assert.AreEqual(f.InputDescription, null);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseCellArrayWithCellArrayLiteralInside()
|
||||
{
|
||||
var text = "{1 2 a {3}}";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
|
||||
var a = (CellArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(4, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseCellArrayWithCellArrayAccessInside()
|
||||
{
|
||||
var text = "{1 2 a{3}}";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
|
||||
var a = (CellArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(3, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseCellArrayWithElementInBracketsInside()
|
||||
{
|
||||
var text = "{1 2 a (3)}";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
|
||||
var a = (CellArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(4, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseCellArrayWithFunctionCallInside()
|
||||
{
|
||||
var text = "{1 2 a(3)}";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
|
||||
var a = (CellArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(3, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseArrayWithCellArrayLiteralInside()
|
||||
{
|
||||
var text = "[1 2 a {3}]";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var a = (ArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(4, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseArrayWithCellArrayAccessInside()
|
||||
{
|
||||
var text = "[1 2 a{3}]";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var a = (ArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(3, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseArrayWithElementInBracketsInside()
|
||||
{
|
||||
var text = "[1 2 a (3)]";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var a = (ArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(4, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseArrayWithFunctionCallInside()
|
||||
{
|
||||
var text = "[1 2 a(3)]";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var a = (ArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(3, a.Elements.Elements.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseFunctionHandle()
|
||||
{
|
||||
var text = "@sqrt";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<NamedFunctionHandleNode>(actual);
|
||||
var f = (NamedFunctionHandleNode) actual;
|
||||
Assert.AreEqual(1, f.FunctionName.Names.Count);
|
||||
Assert.AreEqual("sqrt", f.FunctionName.Names[0].Token.PureToken.LiteralText);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseFunctionHandleWithCompoundName()
|
||||
{
|
||||
var text = "@a.b.c";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<NamedFunctionHandleNode>(actual);
|
||||
var f = (NamedFunctionHandleNode) actual;
|
||||
Assert.AreEqual(3, f.FunctionName.Names.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseLambda()
|
||||
{
|
||||
var text = "@(x, y) x + y";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<LambdaNode>(actual);
|
||||
var f = (LambdaNode) actual;
|
||||
Assert.AreEqual(2, f.Input.Parameters.Parameters.Count);
|
||||
Assert.IsInstanceOf<BinaryOperationExpressionNode>(f.Body);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTildeAsResultReplacement()
|
||||
{
|
||||
var text = "[a, ~, b]";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
|
||||
var f = (ArrayLiteralExpressionNode) actual;
|
||||
Assert.AreEqual(3, f.Elements.Elements.Count);
|
||||
Assert.IsInstanceOf<UnaryPrefixOperationExpressionNode>(f.Elements.Elements[1]);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTildeAsFunctionInputReplacement()
|
||||
{
|
||||
var text = "function a(b, ~, c) end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<FunctionDeclarationNode>(actual);
|
||||
var f = (FunctionDeclarationNode) actual;
|
||||
Assert.AreEqual(3, f.InputDescription.Parameters.Parameters.Count);
|
||||
CollectionAssert.AreEqual(new[] { "b", "~", "c" }, f.InputDescription.Parameters.Parameters.Select(p => (p as TokenNode).Token.PureToken.LiteralText));
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDotTranspose()
|
||||
{
|
||||
var text = "a.'";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<UnaryPostfixOperationExpressionNode>(actual);
|
||||
var e = (UnaryPostfixOperationExpressionNode) actual;
|
||||
Assert.AreEqual(TokenKind.DotTranspose, e.Operation.Token.Kind);
|
||||
Assert.AreEqual("a", (e.Operand as IdentifierNameNode)?.Token.PureToken.LiteralText);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDoubleQuotedStringLiteral()
|
||||
{
|
||||
var text = "\"some string\"";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<DoubleQuotedStringLiteralNode>(actual);
|
||||
var s = (DoubleQuotedStringLiteralNode) actual;
|
||||
Assert.AreEqual("some string", s.Token.PureToken.Value);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseTryCatchStatement()
|
||||
{
|
||||
var text = "try a = b catch c = d end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<TryCatchStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseElseif()
|
||||
{
|
||||
var text = @"if a == 1
|
||||
f()
|
||||
elseif a == 2
|
||||
g()
|
||||
elseif a == 3
|
||||
h()
|
||||
end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<IfStatementNode>(actual);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseBaseClassInvokation()
|
||||
{
|
||||
var text = "a@b.c.d(e, f)";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseExpression();
|
||||
Assert.IsInstanceOf<BaseClassInvokationNode>(actual);
|
||||
var e = (BaseClassInvokationNode) actual;
|
||||
Assert.AreEqual("a", e.MethodName.Token.PureToken.LiteralText);
|
||||
Assert.IsInstanceOf<FunctionCallExpressionNode>(e.BaseClassNameAndArguments);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseFunctionWithEmptyOutputsList()
|
||||
{
|
||||
var text = "function [] = a(b) end";
|
||||
var sut = CreateParser(text);
|
||||
var actual = sut.ParseStatement();
|
||||
Assert.IsInstanceOf<FunctionDeclarationNode>(actual);
|
||||
var f = (FunctionDeclarationNode) actual;
|
||||
Assert.AreEqual(0, f.OutputDescription.Outputs.Count);
|
||||
Assert.AreEqual(text, actual.FullText);
|
||||
|
||||
var sut = GetSut(text);
|
||||
var actual = sut.Parse();
|
||||
var assignment = actual.StatementList[0].AsNode();
|
||||
Assert.IsInstanceOf<ExpressionStatementSyntaxNode>(assignment);
|
||||
Assert.IsInstanceOf<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
@ -9,9 +9,6 @@
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Parser\Parser.csproj">
|
||||
<Project>{B20EDC10-E6E6-4430-8527-B95206DEF941}</Project>
|
||||
<Name>Parser</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Parser\Parser.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,6 +1,6 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Lexer.Tests
|
||||
namespace Parser.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestWindowShould
|
@ -1,6 +1,6 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Lexer.Tests
|
||||
namespace Parser.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestWindowWithNullShould
|
145
Parser/ChildNodesAndTokensList.cs
Normal file
145
Parser/ChildNodesAndTokensList.cs
Normal file
@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Parser.Internal;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
public class ChildNodesAndTokensList : IReadOnlyList<SyntaxNodeOrToken>
|
||||
{
|
||||
private readonly SyntaxNode _node;
|
||||
private readonly int _count;
|
||||
|
||||
internal ChildNodesAndTokensList(SyntaxNode node)
|
||||
{
|
||||
_node = node;
|
||||
_count = CountChildNodes(node._green);
|
||||
}
|
||||
|
||||
private int CountChildNodes(GreenNode green)
|
||||
{
|
||||
var counter = 0;
|
||||
for (var i = 0; i < green.Slots; i++)
|
||||
{
|
||||
var child = green.GetSlot(i);
|
||||
if (child == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.IsList)
|
||||
{
|
||||
counter += child.Slots;
|
||||
}
|
||||
else
|
||||
{
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
public IEnumerator<SyntaxNodeOrToken> GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public int Count => _count;
|
||||
|
||||
public SyntaxNodeOrToken this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
return ThisInternal(index);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal SyntaxNodeOrToken ThisInternal(int index)
|
||||
{
|
||||
var currentSlotIndex = 0;
|
||||
GreenNode currentSlot = null;
|
||||
while (true)
|
||||
{
|
||||
currentSlot = _node._green.GetSlot(currentSlotIndex);
|
||||
if (currentSlot == null)
|
||||
{
|
||||
currentSlotIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var nodesInCurrentSlot = currentSlot.IsList ? currentSlot.Slots : 1;
|
||||
if (index < nodesInCurrentSlot)
|
||||
{
|
||||
if (currentSlot.IsList)
|
||||
{
|
||||
var listSlot = _node.GetNode(currentSlotIndex);
|
||||
var red = listSlot.GetNode(index);
|
||||
if (red != null)
|
||||
{
|
||||
return red;
|
||||
}
|
||||
// this is a token
|
||||
return new SyntaxToken(listSlot, listSlot._green.GetSlot(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
var red = _node.GetNode(currentSlotIndex);
|
||||
if (red != null)
|
||||
{
|
||||
return red;
|
||||
}
|
||||
// this is a token
|
||||
return new SyntaxToken(_node, _node._green.GetSlot(currentSlotIndex));
|
||||
}
|
||||
}
|
||||
|
||||
index -= nodesInCurrentSlot;
|
||||
currentSlotIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private struct Enumerator : IEnumerator<SyntaxNodeOrToken>
|
||||
{
|
||||
private int _index;
|
||||
private readonly ChildNodesAndTokensList _list;
|
||||
private readonly int _count;
|
||||
|
||||
internal Enumerator(ChildNodesAndTokensList list)
|
||||
{
|
||||
_index = -1;
|
||||
_list = list;
|
||||
_count = _list.Count;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _count;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public SyntaxNodeOrToken Current => _list[_index];
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lexer
|
||||
namespace Parser
|
||||
{
|
||||
public interface ILexer<T> where T : class
|
||||
public interface ILexer<T>
|
||||
{
|
||||
T NextToken();
|
||||
List<T> ParseAll();
|
@ -1,4 +1,4 @@
|
||||
namespace Lexer
|
||||
namespace Parser
|
||||
{
|
||||
public interface ITextWindow
|
||||
{
|
||||
@ -10,6 +10,6 @@
|
||||
char GetAndConsumeChar();
|
||||
string GetAndConsumeChars(int n);
|
||||
int CharactersLeft();
|
||||
IPosition Position { get; }
|
||||
Position Position { get; }
|
||||
}
|
||||
}
|
217
Parser/Internal/GreenNode.cs
Normal file
217
Parser/Internal/GreenNode.cs
Normal file
@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal abstract class GreenNode
|
||||
{
|
||||
public TokenKind Kind { get; }
|
||||
public int Slots { get; }
|
||||
public abstract GreenNode GetSlot(int i);
|
||||
|
||||
public GreenNode(TokenKind kind)
|
||||
{
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
public GreenNode(TokenKind kind, int slots)
|
||||
{
|
||||
Kind =kind;
|
||||
Slots = slots;
|
||||
}
|
||||
|
||||
internal abstract Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent);
|
||||
|
||||
public virtual string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
using (var writer = new System.IO.StringWriter(sb, CultureInfo.InvariantCulture))
|
||||
{
|
||||
WriteTo(writer, leading: false, trailing: false);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
protected bool _isMissing = false;
|
||||
|
||||
internal bool IsMissing => _isMissing;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
private void WriteTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
var stack = new Stack<(GreenNode node, bool leading, bool trailing)>();
|
||||
stack.Push((this, leading, trailing));
|
||||
WriteStackTo(writer, stack);
|
||||
}
|
||||
|
||||
public virtual bool IsToken => false;
|
||||
public virtual bool IsTrivia => false;
|
||||
public virtual bool IsNode => true;
|
||||
public virtual bool IsList => false;
|
||||
|
||||
public virtual void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void WriteTriviaTo(TextWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private int GetFirstNonNullChildIndex()
|
||||
{
|
||||
for (var i = 0; i < Slots; i++)
|
||||
{
|
||||
if (GetSlot(i) != null)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return Slots;
|
||||
}
|
||||
|
||||
private int GetLastNonNullChildIndex()
|
||||
{
|
||||
for (var i = Slots - 1; i >= 0; i--)
|
||||
{
|
||||
if (GetSlot(i) != null)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private GreenNode GetFirstTerminal()
|
||||
{
|
||||
var current = this;
|
||||
while (true)
|
||||
{
|
||||
GreenNode next = null;
|
||||
if (current.Slots == 0)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
for (var i = 0; i < current.Slots; i++)
|
||||
{
|
||||
var child = current.GetSlot(i);
|
||||
if (child == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
next = child;
|
||||
break;
|
||||
}
|
||||
|
||||
if (next == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
private GreenNode GetLastTerminal()
|
||||
{
|
||||
var current = this;
|
||||
while (true)
|
||||
{
|
||||
GreenNode next = null;
|
||||
if (current.Slots == 0)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
for (var i = current.Slots - 1; i >= 0; i--)
|
||||
{
|
||||
var child = current.GetSlot(i);
|
||||
if (child == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
next = child;
|
||||
break;
|
||||
}
|
||||
|
||||
if (next == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IReadOnlyList<SyntaxTrivia> LeadingTrivia => GetFirstTerminal()?.LeadingTriviaCore ?? new List<SyntaxTrivia>();
|
||||
public virtual IReadOnlyList<SyntaxTrivia> TrailingTrivia => GetLastTerminal()?.TrailingTriviaCore ?? new List<SyntaxTrivia>();
|
||||
|
||||
public abstract IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
|
||||
public abstract IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
|
||||
|
||||
public virtual string FullText
|
||||
{
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
using (var writer = new System.IO.StringWriter(sb, CultureInfo.InvariantCulture))
|
||||
{
|
||||
WriteTo(writer, leading: true, trailing: true);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteStackTo(TextWriter writer, Stack<(GreenNode node, bool leading, bool trailing)> stack)
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var currentTriple = stack.Pop();
|
||||
if (currentTriple.node.IsToken)
|
||||
{
|
||||
currentTriple.node.WriteTokenTo(writer, currentTriple.leading, currentTriple.trailing);
|
||||
} else if (currentTriple.node.IsTrivia)
|
||||
{
|
||||
currentTriple.node.WriteTriviaTo(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstIndex = currentTriple.node.GetFirstNonNullChildIndex();
|
||||
var lastIndex = currentTriple.node.GetLastNonNullChildIndex();
|
||||
for (var i = lastIndex; i >= firstIndex; i--)
|
||||
{
|
||||
var child = currentTriple.node.GetSlot(i);
|
||||
if (child != null)
|
||||
{
|
||||
stack.Push((
|
||||
child,
|
||||
currentTriple.leading || i != firstIndex,
|
||||
currentTriple.trailing || i != lastIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Lexer
|
||||
namespace Parser.Internal
|
||||
{
|
||||
public class MLexer : ILexer<Token>
|
||||
internal class MLexerGreen : ILexer<(SyntaxToken, Position)>
|
||||
{
|
||||
private class TokenInfo
|
||||
{
|
||||
public TokenKind Kind { get; set; }
|
||||
public string Text { get; set; }
|
||||
public string StringValue { get; set; }
|
||||
public double DoubleValue { get; set; }
|
||||
}
|
||||
|
||||
private ITextWindow Window { get; }
|
||||
private Token LastToken { get; set; }
|
||||
private SyntaxToken LastToken { get; set; }
|
||||
private int TokensSinceNewLine { get; set; }
|
||||
private PureTokenFactory PureTokenFactory { get; }
|
||||
private Stack<TokenKind> TokenStack { get; }
|
||||
|
||||
public MLexer(ITextWindow window, PureTokenFactory pureTokenFactory)
|
||||
public MLexerGreen(ITextWindow window)
|
||||
{
|
||||
Window = window;
|
||||
PureTokenFactory = pureTokenFactory;
|
||||
TokenStack = new Stack<TokenKind>();
|
||||
}
|
||||
|
||||
private static bool IsEolOrEof(char c)
|
||||
{
|
||||
return c == '\n' || c == '\r' || c == '\0';
|
||||
}
|
||||
|
||||
private Trivia LexComment()
|
||||
private SyntaxTrivia LexComment()
|
||||
{
|
||||
if (TokensSinceNewLine == 0 && Window.PeekChar(1) == '{')
|
||||
{
|
||||
return LexMultilineComment();
|
||||
}
|
||||
var n = 1;
|
||||
while (!IsEolOrEof(Window.PeekChar(n)))
|
||||
while (!SyntaxFacts.IsEolOrEof(Window.PeekChar(n)))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
return new Trivia(TriviaType.Comment, Window.GetAndConsumeChars(n));
|
||||
return TokenFactory.CreateTrivia(TokenKind.Comment, Window.GetAndConsumeChars(n));
|
||||
}
|
||||
|
||||
private Trivia LexMultilineComment()
|
||||
private SyntaxTrivia LexMultilineComment()
|
||||
{
|
||||
var n = 2;
|
||||
var metPercentSign = false;
|
||||
@ -50,7 +50,7 @@ namespace Lexer
|
||||
var c = Window.PeekChar(n);
|
||||
if (c == '\0')
|
||||
{
|
||||
throw new ParsingException($"Unexpected end of file while parsing multi-line comment.");
|
||||
throw new ParsingException("Unexpected end of file while parsing multi-line comment.");
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
@ -58,58 +58,62 @@ namespace Lexer
|
||||
atFirstLine = false;
|
||||
}
|
||||
|
||||
if (atFirstLine && !IsWhitespace(c)) // this is a one-line comment
|
||||
if (atFirstLine && !SyntaxFacts.IsWhitespace(c)) // this is a one-line comment
|
||||
{
|
||||
while (!IsEolOrEof(Window.PeekChar(n)))
|
||||
while (!SyntaxFacts.IsEolOrEof(Window.PeekChar(n)))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
return new Trivia(TriviaType.Comment, Window.GetAndConsumeChars(n));
|
||||
return TokenFactory.CreateTrivia(TokenKind.Comment, Window.GetAndConsumeChars(n));
|
||||
}
|
||||
|
||||
if (metPercentSign && c == '}')
|
||||
{
|
||||
return new Trivia(TriviaType.MultiLineComment, Window.GetAndConsumeChars(n+1));
|
||||
return TokenFactory.CreateTrivia(TokenKind.Comment, Window.GetAndConsumeChars(n+1));
|
||||
}
|
||||
|
||||
if (c == '%')
|
||||
{
|
||||
metPercentSign = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
metPercentSign = false;
|
||||
}
|
||||
metPercentSign = c == '%';
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Trivia> LexCommentAfterDotDotDot()
|
||||
private List<SyntaxTrivia> LexCommentAfterDotDotDot()
|
||||
{
|
||||
var n = 0;
|
||||
while (!IsEolOrEof(Window.PeekChar(n)))
|
||||
while (!SyntaxFacts.IsEolOrEof(Window.PeekChar(n)))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
var comment = new Trivia(TriviaType.Comment, Window.GetAndConsumeChars(n));
|
||||
var result = new List<Trivia> { comment };
|
||||
var comment = TokenFactory.CreateTrivia(TokenKind.Comment, Window.GetAndConsumeChars(n));
|
||||
var result = new List<SyntaxTrivia> { comment };
|
||||
var character = Window.PeekChar();
|
||||
if (character == '\n' || character == '\r')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
result.Add(new Trivia(TriviaType.Whitespace, character.ToString()));
|
||||
result.Add(TokenFactory.CreateTrivia(TokenKind.Whitespace, character.ToString()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Trivia> LexTrivia(bool isTrailing)
|
||||
private List<SyntaxTrivia> LexTrivia(bool isTrailing)
|
||||
{
|
||||
var triviaList = new List<Trivia>();
|
||||
var whiteSpaceCache = new StringBuilder();
|
||||
var triviaList = new List<SyntaxTrivia>();
|
||||
var whitespaceCache = new StringBuilder();
|
||||
|
||||
void FlushWhitespaceCache()
|
||||
{
|
||||
if (whitespaceCache.Length > 0)
|
||||
{
|
||||
triviaList.Add(TokenFactory.CreateTrivia(TokenKind.Whitespace, whitespaceCache.ToString()));
|
||||
}
|
||||
|
||||
whitespaceCache.Clear();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
var character = Window.PeekChar();
|
||||
@ -118,18 +122,13 @@ namespace Lexer
|
||||
case ' ':
|
||||
case '\t':
|
||||
Window.ConsumeChar();
|
||||
whiteSpaceCache.Append(character);
|
||||
whitespaceCache.Append(character);
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
if (whiteSpaceCache.Length > 0)
|
||||
{
|
||||
triviaList.Add(new Trivia(TriviaType.Whitespace, whiteSpaceCache.ToString()));
|
||||
}
|
||||
|
||||
whiteSpaceCache.Clear();
|
||||
FlushWhitespaceCache();
|
||||
Window.ConsumeChar();
|
||||
triviaList.Add(new Trivia(TriviaType.NewLine, character.ToString()));
|
||||
triviaList.Add(TokenFactory.CreateTrivia(TokenKind.Newline, character.ToString()));
|
||||
if (isTrailing)
|
||||
{
|
||||
return triviaList;
|
||||
@ -137,39 +136,23 @@ namespace Lexer
|
||||
|
||||
break;
|
||||
case '%':
|
||||
if (whiteSpaceCache.Length > 0)
|
||||
{
|
||||
triviaList.Add(new Trivia(TriviaType.Whitespace, whiteSpaceCache.ToString()));
|
||||
}
|
||||
|
||||
whiteSpaceCache.Clear();
|
||||
FlushWhitespaceCache();
|
||||
triviaList.Add(LexComment());
|
||||
break;
|
||||
case '.':
|
||||
if (Window.PeekChar(1) == '.' && Window.PeekChar(2) == '.')
|
||||
{
|
||||
if (whiteSpaceCache.Length > 0)
|
||||
{
|
||||
triviaList.Add(new Trivia(TriviaType.Whitespace, whiteSpaceCache.ToString()));
|
||||
}
|
||||
|
||||
whiteSpaceCache.Clear();
|
||||
FlushWhitespaceCache();
|
||||
triviaList.AddRange(LexCommentAfterDotDotDot());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (whiteSpaceCache.Length > 0)
|
||||
{
|
||||
triviaList.Add(new Trivia(TriviaType.Whitespace, whiteSpaceCache.ToString()));
|
||||
}
|
||||
FlushWhitespaceCache();
|
||||
return triviaList;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (whiteSpaceCache.Length > 0)
|
||||
{
|
||||
triviaList.Add(new Trivia(TriviaType.Whitespace, whiteSpaceCache.ToString()));
|
||||
}
|
||||
FlushWhitespaceCache();
|
||||
return triviaList;
|
||||
}
|
||||
}
|
||||
@ -180,7 +163,8 @@ namespace Lexer
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_');
|
||||
}
|
||||
|
||||
private PureToken ContinueParsingIdentifier()
|
||||
|
||||
private bool ContinueLexingIdentifier(ref TokenInfo tokenInfo)
|
||||
{
|
||||
var n = 1;
|
||||
while (IsLetterOrDigitOrUnderscore(Window.PeekChar(n)))
|
||||
@ -189,7 +173,28 @@ namespace Lexer
|
||||
}
|
||||
|
||||
var identifier = Window.GetAndConsumeChars(n);
|
||||
return PureTokenFactory.CreateIdentifier(identifier);
|
||||
tokenInfo.Kind = TokenKind.Identifier;
|
||||
tokenInfo.Text = identifier;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ContinueParsingUnquotedStringLiteral(ref TokenInfo tokenInfo)
|
||||
{
|
||||
var n = 0;
|
||||
while (true)
|
||||
{
|
||||
var c = Window.PeekChar(n);
|
||||
if (c == ' ' || c == '\n' || c == '\0')
|
||||
{
|
||||
var literal = Window.GetAndConsumeChars(n);
|
||||
tokenInfo.Kind = TokenKind.UnquotedStringLiteral;
|
||||
tokenInfo.Text = literal;
|
||||
tokenInfo.StringValue = literal;
|
||||
return true;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
private enum NumberParsingState
|
||||
@ -203,22 +208,7 @@ namespace Lexer
|
||||
DigitsAfterE
|
||||
}
|
||||
|
||||
private static bool IsDigit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
private static bool IsDigitOrDot(char c)
|
||||
{
|
||||
return c == '.' || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
private static bool IsWhitespace(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\n';
|
||||
}
|
||||
|
||||
private PureToken? ContinueParsingNumber()
|
||||
private bool ContinueLexingNumber(ref TokenInfo tokenInfo)
|
||||
{
|
||||
var state = NumberParsingState.Start;
|
||||
var n = 0;
|
||||
@ -231,7 +221,7 @@ namespace Lexer
|
||||
switch (state)
|
||||
{
|
||||
case NumberParsingState.Start:
|
||||
if (IsDigitOrDot(c))
|
||||
if (SyntaxFacts.IsDigitOrDot(c))
|
||||
{
|
||||
state = NumberParsingState.DigitsBeforeDot;
|
||||
}
|
||||
@ -241,7 +231,7 @@ namespace Lexer
|
||||
}
|
||||
break;
|
||||
case NumberParsingState.DigitsBeforeDot:
|
||||
if (IsDigit(c))
|
||||
if (SyntaxFacts.IsDigit(c))
|
||||
{
|
||||
}
|
||||
else if (c == '.')
|
||||
@ -258,7 +248,7 @@ namespace Lexer
|
||||
}
|
||||
break;
|
||||
case NumberParsingState.AfterDot:
|
||||
if (IsDigit(c))
|
||||
if (SyntaxFacts.IsDigit(c))
|
||||
{
|
||||
state = NumberParsingState.DigitsAfterDot;
|
||||
}
|
||||
@ -266,7 +256,7 @@ namespace Lexer
|
||||
{
|
||||
state = NumberParsingState.AfterE;
|
||||
}
|
||||
else if (IsWhitespace(c) || c == ';' || c == ']' || c == ')' || c == '}')
|
||||
else if (SyntaxFacts.IsWhitespace(c) || c == ';' || c == ']' || c == ')' || c == '}')
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
@ -282,7 +272,7 @@ namespace Lexer
|
||||
|
||||
break;
|
||||
case NumberParsingState.DigitsAfterDot:
|
||||
if (IsDigit(c))
|
||||
if (SyntaxFacts.IsDigit(c))
|
||||
{
|
||||
}
|
||||
else if (c == 'e' || c == 'E')
|
||||
@ -296,7 +286,7 @@ namespace Lexer
|
||||
|
||||
break;
|
||||
case NumberParsingState.AfterE:
|
||||
if (IsDigit(c))
|
||||
if (SyntaxFacts.IsDigit(c))
|
||||
{
|
||||
state = NumberParsingState.DigitsAfterE;
|
||||
}
|
||||
@ -311,7 +301,7 @@ namespace Lexer
|
||||
|
||||
break;
|
||||
case NumberParsingState.SignAfterE:
|
||||
if (IsDigit(c))
|
||||
if (SyntaxFacts.IsDigit(c))
|
||||
{
|
||||
state = NumberParsingState.DigitsAfterE;
|
||||
}
|
||||
@ -322,7 +312,7 @@ namespace Lexer
|
||||
|
||||
break;
|
||||
case NumberParsingState.DigitsAfterE:
|
||||
if (IsDigit(c))
|
||||
if (SyntaxFacts.IsDigit(c))
|
||||
{
|
||||
}
|
||||
else
|
||||
@ -364,27 +354,36 @@ namespace Lexer
|
||||
n++;
|
||||
}
|
||||
var s = Window.GetAndConsumeChars(n);
|
||||
return PureTokenFactory.CreateNumberLiteral(s);
|
||||
|
||||
tokenInfo.Kind = TokenKind.NumberLiteral;
|
||||
tokenInfo.Text = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private PureToken ContinueParsingStringLiteral()
|
||||
private bool ContinueLexingGeneralStringLiteral(ref TokenInfo tokenInfo, char quote)
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
var pieces = new List<string>();
|
||||
var textBuilder = new StringBuilder();
|
||||
textBuilder.Append(quote);
|
||||
var valueBuilder = new StringBuilder();
|
||||
var n = 0;
|
||||
while (true) {
|
||||
if (Window.PeekChar(n) == '\'')
|
||||
while (true)
|
||||
{
|
||||
if (Window.PeekChar(n) == quote)
|
||||
{
|
||||
if (Window.PeekChar(n + 1) == '\'')
|
||||
if (Window.PeekChar(n + 1) == quote)
|
||||
{
|
||||
var piece = Window.GetAndConsumeChars(n);
|
||||
pieces.Add(piece);
|
||||
textBuilder.Append(piece);
|
||||
valueBuilder.Append(piece);
|
||||
Window.ConsumeChar();
|
||||
Window.ConsumeChar();
|
||||
pieces.Add("'");
|
||||
textBuilder.Append(quote);
|
||||
textBuilder.Append(quote);
|
||||
valueBuilder.Append(quote);
|
||||
n = -1;
|
||||
}
|
||||
else
|
||||
@ -392,7 +391,7 @@ namespace Lexer
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsEolOrEof(Window.PeekChar(n)))
|
||||
if (SyntaxFacts.IsEolOrEof(Window.PeekChar(n)))
|
||||
{
|
||||
throw new ParsingException("Unfinished string literal.");
|
||||
}
|
||||
@ -400,101 +399,56 @@ namespace Lexer
|
||||
}
|
||||
|
||||
var lastPiece = Window.GetAndConsumeChars(n);
|
||||
pieces.Add(lastPiece);
|
||||
var total = string.Join("", pieces);
|
||||
textBuilder.Append(lastPiece);
|
||||
valueBuilder.Append(lastPiece);
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreateStringLiteral(total);
|
||||
textBuilder.Append(quote);
|
||||
tokenInfo.Text = textBuilder.ToString();
|
||||
tokenInfo.StringValue = valueBuilder.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
private PureToken ContinueParsingDoubleQuotedStringLiteral()
|
||||
private bool ContinueLexingStringLiteral(ref TokenInfo tokenInfo)
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
var n = 0;
|
||||
var pieces = new List<string>();
|
||||
while (true)
|
||||
{
|
||||
if (Window.PeekChar(n) == '"')
|
||||
{
|
||||
if (Window.PeekChar(n + 1) == '"')
|
||||
{
|
||||
var piece = Window.GetAndConsumeChars(n);
|
||||
pieces.Add(piece);
|
||||
Window.ConsumeChar();
|
||||
Window.ConsumeChar();
|
||||
pieces.Add("\"");
|
||||
n = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsEolOrEof(Window.PeekChar(n)))
|
||||
{
|
||||
throw new ParsingException("Unfinished double-quoted string literal.");
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
var lastPiece = Window.GetAndConsumeChars(n);
|
||||
pieces.Add(lastPiece);
|
||||
var total = string.Join("", pieces);
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreateDoubleQuotedStringLiteral(total);
|
||||
ContinueLexingGeneralStringLiteral(ref tokenInfo, '\'');
|
||||
tokenInfo.Kind = TokenKind.StringLiteral;
|
||||
return true;
|
||||
}
|
||||
|
||||
private PureToken ContinueParsingUnquotedStringLiteral()
|
||||
private bool ContinueLexingDoubleQuotedStringLiteral(ref TokenInfo tokenInfo)
|
||||
{
|
||||
var n = 0;
|
||||
while (true)
|
||||
{
|
||||
var c = Window.PeekChar(n);
|
||||
if (c == ' ' || c == '\n' || c == '\0')
|
||||
{
|
||||
var literal = Window.GetAndConsumeChars(n);
|
||||
return PureTokenFactory.CreateUnquotedStringLiteral(literal);
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
ContinueLexingGeneralStringLiteral(ref tokenInfo, '"');
|
||||
tokenInfo.Kind = TokenKind.StringLiteral;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> Keywords;
|
||||
|
||||
static MLexer()
|
||||
{
|
||||
Keywords = new HashSet<string>
|
||||
{
|
||||
"for", "if", "function", "while", "case", "try", "catch", "end",
|
||||
"switch", "classdef", "elseif", "persistent", "else"
|
||||
};
|
||||
}
|
||||
|
||||
private PureToken LexTokenWithoutTrivia(List<Trivia> leadingTrivia)
|
||||
private bool LexTokenWithoutTrivia(List<SyntaxTrivia> leadingTrivia, ref TokenInfo tokenInfo)
|
||||
{
|
||||
var character = Window.PeekChar();
|
||||
if (character == '\0')
|
||||
{
|
||||
return PureTokenFactory.CreateEndOfFileToken();
|
||||
tokenInfo.Kind = TokenKind.EndOfFile;
|
||||
tokenInfo.Text = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (TokensSinceNewLine == 1
|
||||
&& !TokenStack.Any()
|
||||
&& LastToken.Kind == TokenKind.Identifier
|
||||
&& LastToken.TrailingTrivia.Any()
|
||||
&& character != '='
|
||||
&& character != '('
|
||||
&& !Keywords.Contains(LastToken.PureToken.LiteralText))
|
||||
&& !SyntaxFacts.Keywords.Contains(LastToken.Text))
|
||||
{
|
||||
return ContinueParsingUnquotedStringLiteral();
|
||||
return ContinueParsingUnquotedStringLiteral(ref tokenInfo);
|
||||
}
|
||||
if (LastToken?.Kind == TokenKind.UnquotedStringLiteral
|
||||
&& !TokenStack.Any()
|
||||
&& TokensSinceNewLine > 0)
|
||||
{
|
||||
return ContinueParsingUnquotedStringLiteral();
|
||||
return ContinueParsingUnquotedStringLiteral(ref tokenInfo);
|
||||
}
|
||||
|
||||
switch (character)
|
||||
{
|
||||
case 'a':
|
||||
@ -549,7 +503,7 @@ namespace Lexer
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
return ContinueParsingIdentifier();
|
||||
return ContinueLexingIdentifier(ref tokenInfo);
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
@ -560,31 +514,35 @@ namespace Lexer
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
var possiblyNumberToken = ContinueParsingNumber();
|
||||
if (possiblyNumberToken == null)
|
||||
var parsedNumber = ContinueLexingNumber(ref tokenInfo);
|
||||
if (!parsedNumber)
|
||||
{
|
||||
throw new ParsingException($"Unexpected character \"{Window.PeekChar()}\" while parsing a number");
|
||||
}
|
||||
|
||||
return (PureToken)possiblyNumberToken;
|
||||
return true;
|
||||
case '=':
|
||||
Window.ConsumeChar();
|
||||
if (Window.PeekChar() == '=')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Equality);
|
||||
tokenInfo.Kind = TokenKind.Equality;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenInfo.Kind = TokenKind.Assignment;
|
||||
}
|
||||
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Assignment);
|
||||
return true;
|
||||
case '.':
|
||||
if (IsDigit(Window.PeekChar(1)))
|
||||
if (SyntaxFacts.IsDigit(Window.PeekChar(1)))
|
||||
{
|
||||
var possiblyNumberToken2 = ContinueParsingNumber();
|
||||
if (possiblyNumberToken2 == null)
|
||||
var possiblyNumberToken2 = ContinueLexingNumber(ref tokenInfo);
|
||||
if (!possiblyNumberToken2)
|
||||
{
|
||||
throw new ParsingException($"Unexpected character \"{Window.PeekChar()}\" while parsing a number");
|
||||
}
|
||||
return (PureToken)possiblyNumberToken2;
|
||||
|
||||
return true;
|
||||
}
|
||||
Window.ConsumeChar();
|
||||
var c = Window.PeekChar();
|
||||
@ -592,113 +550,164 @@ namespace Lexer
|
||||
{
|
||||
case '*':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.DotMultiply);
|
||||
tokenInfo.Kind = TokenKind.DotMultiply;
|
||||
break;
|
||||
case '/':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.DotDivide);
|
||||
tokenInfo.Kind = TokenKind.DotDivide;
|
||||
break;
|
||||
case '^':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.DotPower);
|
||||
tokenInfo.Kind = TokenKind.DotPower;
|
||||
break;
|
||||
case '\\':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.DotBackslash);
|
||||
tokenInfo.Kind = TokenKind.DotBackslash;
|
||||
break;
|
||||
case '\'':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.DotTranspose);
|
||||
tokenInfo.Kind = TokenKind.DotTranspose;
|
||||
break;
|
||||
default:
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Dot);
|
||||
tokenInfo.Kind = TokenKind.Dot;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
case '(':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.OpeningBracket);
|
||||
tokenInfo.Kind = TokenKind.OpeningBracket;
|
||||
return true;
|
||||
case ')':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.ClosingBracket);
|
||||
tokenInfo.Kind = TokenKind.ClosingBracket;
|
||||
return true;
|
||||
case '[':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.OpeningSquareBracket);
|
||||
tokenInfo.Kind = TokenKind.OpeningSquareBracket;
|
||||
return true;
|
||||
case ']':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.ClosingSquareBracket);
|
||||
tokenInfo.Kind = TokenKind.ClosingSquareBracket;
|
||||
return true;
|
||||
case '{':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.OpeningBrace);
|
||||
tokenInfo.Kind = TokenKind.OpeningBrace;
|
||||
return true;
|
||||
case '}':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.ClosingBrace);
|
||||
tokenInfo.Kind = TokenKind.ClosingBrace;
|
||||
return true;
|
||||
case ',':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Comma);
|
||||
tokenInfo.Kind = TokenKind.Comma;
|
||||
return true;
|
||||
case ';':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Semicolon);
|
||||
tokenInfo.Kind = TokenKind.Semicolon;
|
||||
return true;
|
||||
case '&':
|
||||
Window.ConsumeChar();
|
||||
if (Window.PeekChar() == '&')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.LogicalAnd);
|
||||
tokenInfo.Kind = TokenKind.LogicalAnd;
|
||||
}
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.BitwiseAnd);
|
||||
else
|
||||
{
|
||||
tokenInfo.Kind = TokenKind.BitwiseAnd;
|
||||
}
|
||||
|
||||
return true;
|
||||
case '|':
|
||||
Window.ConsumeChar();
|
||||
if (Window.PeekChar() == '|')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.LogicalOr);
|
||||
tokenInfo.Kind = TokenKind.LogicalOr;
|
||||
}
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.BitwiseOr);
|
||||
else
|
||||
{
|
||||
tokenInfo.Kind = TokenKind.BitwiseOr;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
case '<':
|
||||
Window.ConsumeChar();
|
||||
if (Window.PeekChar() == '=')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.LessOrEqual);
|
||||
tokenInfo.Kind = TokenKind.LessOrEqual;
|
||||
}
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Less);
|
||||
else
|
||||
{
|
||||
tokenInfo.Kind = TokenKind.Less;
|
||||
}
|
||||
|
||||
return true;
|
||||
case '>':
|
||||
Window.ConsumeChar();
|
||||
if (Window.PeekChar() == '=')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.GreaterOrEqual);
|
||||
tokenInfo.Kind = TokenKind.GreaterOrEqual;
|
||||
}
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Greater);
|
||||
else
|
||||
{
|
||||
tokenInfo.Kind = TokenKind.Greater;
|
||||
}
|
||||
|
||||
return true;
|
||||
case '~':
|
||||
Window.ConsumeChar();
|
||||
if (Window.PeekChar() == '=')
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Inequality);
|
||||
tokenInfo.Kind = TokenKind.Inequality;
|
||||
}
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Not);
|
||||
else
|
||||
{
|
||||
tokenInfo.Kind = TokenKind.Not;
|
||||
}
|
||||
|
||||
return true;
|
||||
case '+':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Plus);
|
||||
tokenInfo.Kind = TokenKind.Plus;
|
||||
return true;
|
||||
case '-':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Minus);
|
||||
tokenInfo.Kind = TokenKind.Minus;
|
||||
return true;
|
||||
case '*':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Multiply);
|
||||
tokenInfo.Kind = TokenKind.Multiply;
|
||||
return true;
|
||||
case '/':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Divide);
|
||||
tokenInfo.Kind = TokenKind.Divide;
|
||||
return true;
|
||||
case '\\':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Backslash);
|
||||
tokenInfo.Kind = TokenKind.Backslash;
|
||||
return true;
|
||||
case '^':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Power);
|
||||
tokenInfo.Kind = TokenKind.Power;
|
||||
return true;
|
||||
case '@':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.At);
|
||||
tokenInfo.Kind = TokenKind.At;
|
||||
return true;
|
||||
case ':':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Colon);
|
||||
tokenInfo.Kind = TokenKind.Colon;
|
||||
return true;
|
||||
case '?':
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.QuestionMark);
|
||||
tokenInfo.Kind = TokenKind.QuestionMark;
|
||||
return true;
|
||||
case '\'':
|
||||
if (LastToken != null &&
|
||||
(LastToken.Kind == TokenKind.ClosingBrace
|
||||
@ -709,14 +718,16 @@ namespace Lexer
|
||||
if (LastToken.TrailingTrivia.Count == 0 && leadingTrivia.Count == 0)
|
||||
{
|
||||
Window.ConsumeChar();
|
||||
return PureTokenFactory.CreatePunctuation(TokenKind.Transpose);
|
||||
tokenInfo.Kind = TokenKind.Transpose;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ContinueParsingStringLiteral();
|
||||
return ContinueLexingStringLiteral(ref tokenInfo);
|
||||
case '"':
|
||||
return ContinueParsingDoubleQuotedStringLiteral();
|
||||
return ContinueLexingDoubleQuotedStringLiteral(ref tokenInfo);
|
||||
case '\0':
|
||||
return PureTokenFactory.CreateEndOfFileToken();
|
||||
tokenInfo.Kind = TokenKind.EndOfFile;
|
||||
return true;
|
||||
default:
|
||||
throw new ParsingException(
|
||||
$"Unknown symbol \"{character}\" at {Window.Position}."
|
||||
@ -724,53 +735,14 @@ namespace Lexer
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsOpeningToken(TokenKind tokenKind)
|
||||
{
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.OpeningBrace:
|
||||
case TokenKind.OpeningBracket:
|
||||
case TokenKind.OpeningSquareBracket:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsClosingToken(TokenKind tokenKind)
|
||||
{
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.ClosingBrace:
|
||||
case TokenKind.ClosingBracket:
|
||||
case TokenKind.ClosingSquareBracket:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private TokenKind? OpeningFromClosing(TokenKind tokenKind)
|
||||
{
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.ClosingBrace:
|
||||
return TokenKind.OpeningBrace;
|
||||
case TokenKind.ClosingBracket:
|
||||
return TokenKind.OpeningBracket;
|
||||
case TokenKind.ClosingSquareBracket:
|
||||
return TokenKind.OpeningSquareBracket;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Token NextToken()
|
||||
public (SyntaxToken, Position) NextToken()
|
||||
{
|
||||
var leadingTrivia = LexTrivia(false);
|
||||
var token = LexTokenWithoutTrivia(leadingTrivia);
|
||||
var position = Window.Position;
|
||||
var tokenInfo = new TokenInfo();
|
||||
LexTokenWithoutTrivia(leadingTrivia, ref tokenInfo);
|
||||
var trailingTrivia = LexTrivia(true);
|
||||
if (trailingTrivia.Where(t => t.Type == TriviaType.NewLine).Any())
|
||||
if (trailingTrivia.Any(t => t.Kind == TokenKind.Newline))
|
||||
{
|
||||
TokensSinceNewLine = 0;
|
||||
}
|
||||
@ -778,58 +750,104 @@ namespace Lexer
|
||||
{
|
||||
TokensSinceNewLine++;
|
||||
}
|
||||
|
||||
if (IsOpeningToken(token.Kind))
|
||||
|
||||
if (SyntaxFacts.IsOpeningToken(tokenInfo.Kind))
|
||||
{
|
||||
TokenStack.Push(token.Kind);
|
||||
TokenStack.Push(tokenInfo.Kind);
|
||||
}
|
||||
|
||||
if (IsClosingToken(token.Kind))
|
||||
if (SyntaxFacts.IsClosingToken(tokenInfo.Kind))
|
||||
{
|
||||
if (TokenStack.TryPeek(out var t))
|
||||
if (TokenStack.Count > 0)
|
||||
{
|
||||
if (t == OpeningFromClosing(token.Kind))
|
||||
var t = TokenStack.Peek();
|
||||
if (t == SyntaxFacts.OpeningFromClosing(tokenInfo.Kind))
|
||||
{
|
||||
TokenStack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException($"Unmatched \"{token.LiteralText}\" at {token.Position}.");
|
||||
throw new ParsingException($"Unmatched \"{tokenInfo.Text}\" at {Window.Position}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException($"Unmatched \"{token.LiteralText}\" at {token.Position}.");
|
||||
throw new ParsingException($"Unmatched \"{tokenInfo.Text}\" at {Window.Position}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (token.Kind == TokenKind.EndOfFile
|
||||
if (tokenInfo.Kind == TokenKind.EndOfFile
|
||||
&& TokenStack.Any())
|
||||
{
|
||||
throw new ParsingException($"Unmatched \"{TokenStack.Pop()}\" by the end of file.");
|
||||
}
|
||||
|
||||
var result = new Token(token, leadingTrivia, trailingTrivia);
|
||||
var result = Create(
|
||||
tokenInfo,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
LastToken = result;
|
||||
return result;
|
||||
return (result, position);
|
||||
}
|
||||
|
||||
public List<Token> ParseAll()
|
||||
private SyntaxToken Create(
|
||||
TokenInfo tokenInfo,
|
||||
List<SyntaxTrivia> leadingTrivia,
|
||||
List<SyntaxTrivia> trailingTrivia)
|
||||
{
|
||||
var result = new List<Token>();
|
||||
switch (tokenInfo.Kind)
|
||||
{
|
||||
case TokenKind.Identifier:
|
||||
return TokenFactory.CreateIdentifier(
|
||||
tokenInfo.Text,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
case TokenKind.UnquotedStringLiteral:
|
||||
return TokenFactory.CreateUnquotedStringLiteral(
|
||||
tokenInfo.Text,
|
||||
tokenInfo.StringValue,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
case TokenKind.NumberLiteral:
|
||||
return TokenFactory.CreateTokenWithValueAndTrivia<double>(
|
||||
tokenInfo.Kind,
|
||||
tokenInfo.Text,
|
||||
tokenInfo.DoubleValue,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
case TokenKind.StringLiteral:
|
||||
return TokenFactory.CreateTokenWithValueAndTrivia<string>(
|
||||
tokenInfo.Kind,
|
||||
tokenInfo.Text,
|
||||
tokenInfo.StringValue,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
default:
|
||||
return TokenFactory.CreateTokenWithTrivia(
|
||||
tokenInfo.Kind,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
}
|
||||
}
|
||||
|
||||
public List<(SyntaxToken, Position)> ParseAll()
|
||||
{
|
||||
var result = new List<(SyntaxToken, Position)>();
|
||||
while (true)
|
||||
{
|
||||
var token = NextToken();
|
||||
var pair = NextToken();
|
||||
var (token, _) = pair;
|
||||
if (token == null)
|
||||
{
|
||||
throw new ParsingException($"Unexpected character: '{Window.PeekChar()}' at {Window.Position}.");
|
||||
}
|
||||
result.Add(token);
|
||||
if (token.PureToken.Kind == TokenKind.EndOfFile)
|
||||
result.Add(pair);
|
||||
if (token.Kind == TokenKind.EndOfFile)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
1117
Parser/Internal/MParserGreen.cs
Normal file
1117
Parser/Internal/MParserGreen.cs
Normal file
File diff suppressed because it is too large
Load Diff
555
Parser/Internal/SyntaxFactory.Generated.cs
Normal file
555
Parser/Internal/SyntaxFactory.Generated.cs
Normal file
@ -0,0 +1,555 @@
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal partial class SyntaxFactory
|
||||
{
|
||||
public FileSyntaxNode FileSyntax(
|
||||
SyntaxList statementList,
|
||||
SyntaxToken endOfFile)
|
||||
{
|
||||
return new FileSyntaxNode(
|
||||
statementList,
|
||||
endOfFile);
|
||||
}
|
||||
|
||||
public FunctionDeclarationSyntaxNode FunctionDeclarationSyntax(
|
||||
SyntaxToken functionKeyword,
|
||||
FunctionOutputDescriptionSyntaxNode outputDescription,
|
||||
SyntaxToken name,
|
||||
FunctionInputDescriptionSyntaxNode inputDescription,
|
||||
SyntaxList<SyntaxToken> commas,
|
||||
SyntaxList body,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new FunctionDeclarationSyntaxNode(
|
||||
functionKeyword,
|
||||
outputDescription,
|
||||
name,
|
||||
inputDescription,
|
||||
commas,
|
||||
body,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public FunctionOutputDescriptionSyntaxNode FunctionOutputDescriptionSyntax(
|
||||
SyntaxList outputList,
|
||||
SyntaxToken assignmentSign)
|
||||
{
|
||||
return new FunctionOutputDescriptionSyntaxNode(
|
||||
outputList,
|
||||
assignmentSign);
|
||||
}
|
||||
|
||||
public FunctionInputDescriptionSyntaxNode FunctionInputDescriptionSyntax(
|
||||
SyntaxToken openingBracket,
|
||||
SyntaxList parameterList,
|
||||
SyntaxToken closingBracket)
|
||||
{
|
||||
return new FunctionInputDescriptionSyntaxNode(
|
||||
openingBracket,
|
||||
parameterList,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public SwitchStatementSyntaxNode SwitchStatementSyntax(
|
||||
SyntaxToken switchKeyword,
|
||||
ExpressionSyntaxNode switchExpression,
|
||||
SyntaxList<SyntaxToken> optionalCommas,
|
||||
SyntaxList<SwitchCaseSyntaxNode> cases,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new SwitchStatementSyntaxNode(
|
||||
switchKeyword,
|
||||
switchExpression,
|
||||
optionalCommas,
|
||||
cases,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public SwitchCaseSyntaxNode SwitchCaseSyntax(
|
||||
SyntaxToken caseKeyword,
|
||||
ExpressionSyntaxNode caseIdentifier,
|
||||
SyntaxList<SyntaxToken> optionalCommas,
|
||||
SyntaxList body)
|
||||
{
|
||||
return new SwitchCaseSyntaxNode(
|
||||
caseKeyword,
|
||||
caseIdentifier,
|
||||
optionalCommas,
|
||||
body);
|
||||
}
|
||||
|
||||
public WhileStatementSyntaxNode WhileStatementSyntax(
|
||||
SyntaxToken whileKeyword,
|
||||
ExpressionSyntaxNode condition,
|
||||
SyntaxList<SyntaxToken> optionalCommas,
|
||||
SyntaxList body,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new WhileStatementSyntaxNode(
|
||||
whileKeyword,
|
||||
condition,
|
||||
optionalCommas,
|
||||
body,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public ElseifClause ElseifClause(
|
||||
SyntaxToken elseifKeyword,
|
||||
ExpressionSyntaxNode condition,
|
||||
SyntaxList<SyntaxToken> optionalCommas,
|
||||
SyntaxList body)
|
||||
{
|
||||
return new ElseifClause(
|
||||
elseifKeyword,
|
||||
condition,
|
||||
optionalCommas,
|
||||
body);
|
||||
}
|
||||
|
||||
public ElseClause ElseClause(
|
||||
SyntaxToken elseKeyword,
|
||||
SyntaxList body)
|
||||
{
|
||||
return new ElseClause(
|
||||
elseKeyword,
|
||||
body);
|
||||
}
|
||||
|
||||
public IfStatementSyntaxNode IfStatementSyntax(
|
||||
SyntaxToken ifKeyword,
|
||||
ExpressionSyntaxNode condition,
|
||||
SyntaxList<SyntaxToken> optionalCommas,
|
||||
SyntaxList body,
|
||||
SyntaxList<ElseifClause> elseifClauses,
|
||||
ElseClause elseClause,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new IfStatementSyntaxNode(
|
||||
ifKeyword,
|
||||
condition,
|
||||
optionalCommas,
|
||||
body,
|
||||
elseifClauses,
|
||||
elseClause,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public ForStatementSyntaxNode ForStatementSyntax(
|
||||
SyntaxToken forKeyword,
|
||||
AssignmentExpressionSyntaxNode assignment,
|
||||
SyntaxList<SyntaxToken> optionalCommas,
|
||||
SyntaxList body,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new ForStatementSyntaxNode(
|
||||
forKeyword,
|
||||
assignment,
|
||||
optionalCommas,
|
||||
body,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public AssignmentExpressionSyntaxNode AssignmentExpressionSyntax(
|
||||
ExpressionSyntaxNode lhs,
|
||||
SyntaxToken assignmentSign,
|
||||
ExpressionSyntaxNode rhs)
|
||||
{
|
||||
return new AssignmentExpressionSyntaxNode(
|
||||
lhs,
|
||||
assignmentSign,
|
||||
rhs);
|
||||
}
|
||||
|
||||
public CatchClauseSyntaxNode CatchClauseSyntax(
|
||||
SyntaxToken catchKeyword,
|
||||
SyntaxList catchBody)
|
||||
{
|
||||
return new CatchClauseSyntaxNode(
|
||||
catchKeyword,
|
||||
catchBody);
|
||||
}
|
||||
|
||||
public TryCatchStatementSyntaxNode TryCatchStatementSyntax(
|
||||
SyntaxToken tryKeyword,
|
||||
SyntaxList tryBody,
|
||||
CatchClauseSyntaxNode catchClause,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new TryCatchStatementSyntaxNode(
|
||||
tryKeyword,
|
||||
tryBody,
|
||||
catchClause,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public ExpressionStatementSyntaxNode ExpressionStatementSyntax(
|
||||
ExpressionSyntaxNode expression)
|
||||
{
|
||||
return new ExpressionStatementSyntaxNode(
|
||||
expression);
|
||||
}
|
||||
|
||||
public EmptyStatementSyntaxNode EmptyStatementSyntax(
|
||||
SyntaxToken semicolon)
|
||||
{
|
||||
return new EmptyStatementSyntaxNode(
|
||||
semicolon);
|
||||
}
|
||||
|
||||
public EmptyExpressionSyntaxNode EmptyExpressionSyntax()
|
||||
{
|
||||
return new EmptyExpressionSyntaxNode();
|
||||
}
|
||||
|
||||
public UnaryPrefixOperationExpressionSyntaxNode UnaryPrefixOperationExpressionSyntax(
|
||||
SyntaxToken operation,
|
||||
ExpressionSyntaxNode operand)
|
||||
{
|
||||
return new UnaryPrefixOperationExpressionSyntaxNode(
|
||||
operation,
|
||||
operand);
|
||||
}
|
||||
|
||||
public CompoundNameSyntaxNode CompoundNameSyntax(
|
||||
SyntaxList nodes)
|
||||
{
|
||||
return new CompoundNameSyntaxNode(
|
||||
nodes);
|
||||
}
|
||||
|
||||
public NamedFunctionHandleSyntaxNode NamedFunctionHandleSyntax(
|
||||
SyntaxToken atSign,
|
||||
CompoundNameSyntaxNode functionName)
|
||||
{
|
||||
return new NamedFunctionHandleSyntaxNode(
|
||||
atSign,
|
||||
functionName);
|
||||
}
|
||||
|
||||
public LambdaSyntaxNode LambdaSyntax(
|
||||
SyntaxToken atSign,
|
||||
FunctionInputDescriptionSyntaxNode input,
|
||||
ExpressionSyntaxNode body)
|
||||
{
|
||||
return new LambdaSyntaxNode(
|
||||
atSign,
|
||||
input,
|
||||
body);
|
||||
}
|
||||
|
||||
public BinaryOperationExpressionSyntaxNode BinaryOperationExpressionSyntax(
|
||||
ExpressionSyntaxNode lhs,
|
||||
SyntaxToken operation,
|
||||
ExpressionSyntaxNode rhs)
|
||||
{
|
||||
return new BinaryOperationExpressionSyntaxNode(
|
||||
lhs,
|
||||
operation,
|
||||
rhs);
|
||||
}
|
||||
|
||||
public IdentifierNameSyntaxNode IdentifierNameSyntax(
|
||||
SyntaxToken name)
|
||||
{
|
||||
return new IdentifierNameSyntaxNode(
|
||||
name);
|
||||
}
|
||||
|
||||
public NumberLiteralSyntaxNode NumberLiteralSyntax(
|
||||
SyntaxToken number)
|
||||
{
|
||||
return new NumberLiteralSyntaxNode(
|
||||
number);
|
||||
}
|
||||
|
||||
public StringLiteralSyntaxNode StringLiteralSyntax(
|
||||
SyntaxToken stringToken)
|
||||
{
|
||||
return new StringLiteralSyntaxNode(
|
||||
stringToken);
|
||||
}
|
||||
|
||||
public DoubleQuotedStringLiteralSyntaxNode DoubleQuotedStringLiteralSyntax(
|
||||
SyntaxToken stringToken)
|
||||
{
|
||||
return new DoubleQuotedStringLiteralSyntaxNode(
|
||||
stringToken);
|
||||
}
|
||||
|
||||
public UnquotedStringLiteralSyntaxNode UnquotedStringLiteralSyntax(
|
||||
SyntaxToken stringToken)
|
||||
{
|
||||
return new UnquotedStringLiteralSyntaxNode(
|
||||
stringToken);
|
||||
}
|
||||
|
||||
public ArrayLiteralExpressionSyntaxNode ArrayLiteralExpressionSyntax(
|
||||
SyntaxToken openingSquareBracket,
|
||||
SyntaxList nodes,
|
||||
SyntaxToken closingSquareBracket)
|
||||
{
|
||||
return new ArrayLiteralExpressionSyntaxNode(
|
||||
openingSquareBracket,
|
||||
nodes,
|
||||
closingSquareBracket);
|
||||
}
|
||||
|
||||
public CellArrayLiteralExpressionSyntaxNode CellArrayLiteralExpressionSyntax(
|
||||
SyntaxToken openingBrace,
|
||||
SyntaxList nodes,
|
||||
SyntaxToken closingBrace)
|
||||
{
|
||||
return new CellArrayLiteralExpressionSyntaxNode(
|
||||
openingBrace,
|
||||
nodes,
|
||||
closingBrace);
|
||||
}
|
||||
|
||||
public ParenthesizedExpressionSyntaxNode ParenthesizedExpressionSyntax(
|
||||
SyntaxToken openingBracket,
|
||||
ExpressionSyntaxNode expression,
|
||||
SyntaxToken closingBracket)
|
||||
{
|
||||
return new ParenthesizedExpressionSyntaxNode(
|
||||
openingBracket,
|
||||
expression,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public CellArrayElementAccessExpressionSyntaxNode CellArrayElementAccessExpressionSyntax(
|
||||
ExpressionSyntaxNode expression,
|
||||
SyntaxToken openingBrace,
|
||||
SyntaxList nodes,
|
||||
SyntaxToken closingBrace)
|
||||
{
|
||||
return new CellArrayElementAccessExpressionSyntaxNode(
|
||||
expression,
|
||||
openingBrace,
|
||||
nodes,
|
||||
closingBrace);
|
||||
}
|
||||
|
||||
public FunctionCallExpressionSyntaxNode FunctionCallExpressionSyntax(
|
||||
ExpressionSyntaxNode functionName,
|
||||
SyntaxToken openingBracket,
|
||||
SyntaxList nodes,
|
||||
SyntaxToken closingBracket)
|
||||
{
|
||||
return new FunctionCallExpressionSyntaxNode(
|
||||
functionName,
|
||||
openingBracket,
|
||||
nodes,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public MemberAccessSyntaxNode MemberAccessSyntax(
|
||||
SyntaxNode leftOperand,
|
||||
SyntaxToken dot,
|
||||
SyntaxNode rightOperand)
|
||||
{
|
||||
return new MemberAccessSyntaxNode(
|
||||
leftOperand,
|
||||
dot,
|
||||
rightOperand);
|
||||
}
|
||||
|
||||
public UnaryPostixOperationExpressionSyntaxNode UnaryPostixOperationExpressionSyntax(
|
||||
ExpressionSyntaxNode operand,
|
||||
SyntaxToken operation)
|
||||
{
|
||||
return new UnaryPostixOperationExpressionSyntaxNode(
|
||||
operand,
|
||||
operation);
|
||||
}
|
||||
|
||||
public IndirectMemberAccessSyntaxNode IndirectMemberAccessSyntax(
|
||||
SyntaxToken openingBracket,
|
||||
ExpressionSyntaxNode expression,
|
||||
SyntaxToken closingBracket)
|
||||
{
|
||||
return new IndirectMemberAccessSyntaxNode(
|
||||
openingBracket,
|
||||
expression,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public CommandExpressionSyntaxNode CommandExpressionSyntax(
|
||||
IdentifierNameSyntaxNode commandName,
|
||||
SyntaxList<UnquotedStringLiteralSyntaxNode> arguments)
|
||||
{
|
||||
return new CommandExpressionSyntaxNode(
|
||||
commandName,
|
||||
arguments);
|
||||
}
|
||||
|
||||
public BaseClassInvokationSyntaxNode BaseClassInvokationSyntax(
|
||||
ExpressionSyntaxNode methodName,
|
||||
SyntaxToken atSign,
|
||||
ExpressionSyntaxNode baseClassNameAndArguments)
|
||||
{
|
||||
return new BaseClassInvokationSyntaxNode(
|
||||
methodName,
|
||||
atSign,
|
||||
baseClassNameAndArguments);
|
||||
}
|
||||
|
||||
public AttributeAssignmentSyntaxNode AttributeAssignmentSyntax(
|
||||
SyntaxToken assignmentSign,
|
||||
ExpressionSyntaxNode value)
|
||||
{
|
||||
return new AttributeAssignmentSyntaxNode(
|
||||
assignmentSign,
|
||||
value);
|
||||
}
|
||||
|
||||
public AttributeSyntaxNode AttributeSyntax(
|
||||
IdentifierNameSyntaxNode name,
|
||||
AttributeAssignmentSyntaxNode assignment)
|
||||
{
|
||||
return new AttributeSyntaxNode(
|
||||
name,
|
||||
assignment);
|
||||
}
|
||||
|
||||
public AttributeListSyntaxNode AttributeListSyntax(
|
||||
SyntaxToken openingBracket,
|
||||
SyntaxList nodes,
|
||||
SyntaxToken closingBracket)
|
||||
{
|
||||
return new AttributeListSyntaxNode(
|
||||
openingBracket,
|
||||
nodes,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public MethodDefinitionSyntaxNode MethodDefinitionSyntax(
|
||||
SyntaxToken functionKeyword,
|
||||
FunctionOutputDescriptionSyntaxNode outputDescription,
|
||||
CompoundNameSyntaxNode name,
|
||||
FunctionInputDescriptionSyntaxNode inputDescription,
|
||||
SyntaxList<SyntaxToken> commas,
|
||||
SyntaxList body,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new MethodDefinitionSyntaxNode(
|
||||
functionKeyword,
|
||||
outputDescription,
|
||||
name,
|
||||
inputDescription,
|
||||
commas,
|
||||
body,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public AbstractMethodDeclarationSyntaxNode AbstractMethodDeclarationSyntax(
|
||||
FunctionOutputDescriptionSyntaxNode outputDescription,
|
||||
CompoundNameSyntaxNode name,
|
||||
FunctionInputDescriptionSyntaxNode inputDescription)
|
||||
{
|
||||
return new AbstractMethodDeclarationSyntaxNode(
|
||||
outputDescription,
|
||||
name,
|
||||
inputDescription);
|
||||
}
|
||||
|
||||
public MethodsListSyntaxNode MethodsListSyntax(
|
||||
SyntaxToken methodsKeyword,
|
||||
AttributeListSyntaxNode attributes,
|
||||
SyntaxList methods,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new MethodsListSyntaxNode(
|
||||
methodsKeyword,
|
||||
attributes,
|
||||
methods,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public PropertiesListSyntaxNode PropertiesListSyntax(
|
||||
SyntaxToken propertiesKeyword,
|
||||
AttributeListSyntaxNode attributes,
|
||||
SyntaxList properties,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new PropertiesListSyntaxNode(
|
||||
propertiesKeyword,
|
||||
attributes,
|
||||
properties,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public BaseClassListSyntaxNode BaseClassListSyntax(
|
||||
SyntaxToken lessSign,
|
||||
SyntaxList baseClasses)
|
||||
{
|
||||
return new BaseClassListSyntaxNode(
|
||||
lessSign,
|
||||
baseClasses);
|
||||
}
|
||||
|
||||
public ClassDeclarationSyntaxNode ClassDeclarationSyntax(
|
||||
SyntaxToken classdefKeyword,
|
||||
AttributeListSyntaxNode attributes,
|
||||
IdentifierNameSyntaxNode className,
|
||||
BaseClassListSyntaxNode baseClassList,
|
||||
SyntaxList nodes,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new ClassDeclarationSyntaxNode(
|
||||
classdefKeyword,
|
||||
attributes,
|
||||
className,
|
||||
baseClassList,
|
||||
nodes,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public EnumerationItemValueSyntaxNode EnumerationItemValueSyntax(
|
||||
SyntaxToken openingBracket,
|
||||
SyntaxList values,
|
||||
SyntaxToken closingBracket)
|
||||
{
|
||||
return new EnumerationItemValueSyntaxNode(
|
||||
openingBracket,
|
||||
values,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public EnumerationItemSyntaxNode EnumerationItemSyntax(
|
||||
IdentifierNameSyntaxNode name,
|
||||
EnumerationItemValueSyntaxNode values,
|
||||
SyntaxList<SyntaxToken> commas)
|
||||
{
|
||||
return new EnumerationItemSyntaxNode(
|
||||
name,
|
||||
values,
|
||||
commas);
|
||||
}
|
||||
|
||||
public EnumerationListSyntaxNode EnumerationListSyntax(
|
||||
SyntaxToken enumerationKeyword,
|
||||
AttributeListSyntaxNode attributes,
|
||||
SyntaxList<EnumerationItemSyntaxNode> items,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new EnumerationListSyntaxNode(
|
||||
enumerationKeyword,
|
||||
attributes,
|
||||
items,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public EventsListSyntaxNode EventsListSyntax(
|
||||
SyntaxToken eventsKeyword,
|
||||
AttributeListSyntaxNode attributes,
|
||||
SyntaxList events,
|
||||
SyntaxToken endKeyword)
|
||||
{
|
||||
return new EventsListSyntaxNode(
|
||||
eventsKeyword,
|
||||
attributes,
|
||||
events,
|
||||
endKeyword);
|
||||
}
|
||||
}
|
||||
}
|
6
Parser/Internal/SyntaxFactory.cs
Normal file
6
Parser/Internal/SyntaxFactory.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal partial class SyntaxFactory
|
||||
{
|
||||
}
|
||||
}
|
276
Parser/Internal/SyntaxFacts.cs
Normal file
276
Parser/Internal/SyntaxFacts.cs
Normal file
@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
public static class SyntaxFacts
|
||||
{
|
||||
public static readonly HashSet<string> Keywords;
|
||||
|
||||
static SyntaxFacts()
|
||||
{
|
||||
Keywords = new HashSet<string>
|
||||
{
|
||||
"for", "if", "function", "while", "case", "try", "catch", "end",
|
||||
"switch", "classdef", "elseif", "persistent", "else"
|
||||
};
|
||||
}
|
||||
|
||||
public enum Precedence
|
||||
{
|
||||
// see https://mathworks.com/help/matlab/matlab_prog/operator-precedence.html
|
||||
Expression = 0,
|
||||
Assignment,
|
||||
LogicalOr,
|
||||
LogicalAnd,
|
||||
BitwiseOr,
|
||||
BitwiseAnd,
|
||||
Relational,
|
||||
Colon,
|
||||
Additive,
|
||||
Multiplicative,
|
||||
Unary,
|
||||
WeirdPower,
|
||||
Power
|
||||
}
|
||||
|
||||
public static Precedence GetPrecedence(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Assignment:
|
||||
return Precedence.Assignment;
|
||||
case TokenKind.LogicalOr:
|
||||
return Precedence.LogicalOr;
|
||||
case TokenKind.LogicalAnd:
|
||||
return Precedence.LogicalAnd;
|
||||
case TokenKind.BitwiseOr:
|
||||
return Precedence.BitwiseOr;
|
||||
case TokenKind.BitwiseAnd:
|
||||
return Precedence.BitwiseAnd;
|
||||
case TokenKind.Less:
|
||||
case TokenKind.LessOrEqual:
|
||||
case TokenKind.Greater:
|
||||
case TokenKind.GreaterOrEqual:
|
||||
case TokenKind.Equality:
|
||||
case TokenKind.Inequality:
|
||||
return Precedence.Relational;
|
||||
case TokenKind.Colon:
|
||||
return Precedence.Colon;
|
||||
case TokenKind.Plus:
|
||||
case TokenKind.Minus:
|
||||
return Precedence.Additive;
|
||||
case TokenKind.Multiply:
|
||||
case TokenKind.DotMultiply:
|
||||
case TokenKind.Divide:
|
||||
case TokenKind.DotDivide:
|
||||
case TokenKind.Backslash:
|
||||
case TokenKind.DotBackslash:
|
||||
return Precedence.Multiplicative;
|
||||
case TokenKind.Not:
|
||||
return Precedence.Unary;
|
||||
case TokenKind.Power:
|
||||
case TokenKind.DotPower:
|
||||
case TokenKind.Transpose:
|
||||
case TokenKind.DotTranspose:
|
||||
return Precedence.Power;
|
||||
default:
|
||||
return Precedence.Expression;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDigit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
public static bool IsDigitOrDot(char c)
|
||||
{
|
||||
return c == '.' || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
public static bool IsEolOrEof(char c)
|
||||
{
|
||||
return c == '\n' || c == '\r' || c == '\0';
|
||||
}
|
||||
|
||||
public static bool IsWhitespace(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\n';
|
||||
}
|
||||
|
||||
public static bool IsOpeningToken(TokenKind tokenKind)
|
||||
{
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.OpeningBrace:
|
||||
case TokenKind.OpeningBracket:
|
||||
case TokenKind.OpeningSquareBracket:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsClosingToken(TokenKind tokenKind)
|
||||
{
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.ClosingBrace:
|
||||
case TokenKind.ClosingBracket:
|
||||
case TokenKind.ClosingSquareBracket:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsBracket(TokenKind tokenKind)
|
||||
{
|
||||
return IsOpeningToken(tokenKind) || IsClosingToken(tokenKind);
|
||||
}
|
||||
|
||||
public static TokenKind? OpeningFromClosing(TokenKind tokenKind)
|
||||
{
|
||||
switch (tokenKind)
|
||||
{
|
||||
case TokenKind.ClosingBrace:
|
||||
return TokenKind.OpeningBrace;
|
||||
case TokenKind.ClosingBracket:
|
||||
return TokenKind.OpeningBracket;
|
||||
case TokenKind.ClosingSquareBracket:
|
||||
return TokenKind.OpeningSquareBracket;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] StringFromKind =
|
||||
{
|
||||
null, // None = 0,
|
||||
"", // EndOfFile = 1,
|
||||
null, // Identifier = 2,
|
||||
null, // NumberLiteral = 3,
|
||||
null, // StringLiteral = 4,
|
||||
null, // DoubleQuotedStringLiteral = 5,
|
||||
null, // UnquotedStringLiteral = 6
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
"=", // Assignment = 20,
|
||||
"==", // Equality = 21,
|
||||
"~=", // Inequality = 22,
|
||||
"&&", // LogicalAnd = 23,
|
||||
"||", // LogicalOr = 24,
|
||||
"&", // BitwiseAnd = 25,
|
||||
"|", // BitwiseOr = 26,
|
||||
"<", // Less = 27,
|
||||
"<=", // LessOrEqual = 28,
|
||||
">", // Greater = 29,
|
||||
">=", // GreaterOrEqual = 30,
|
||||
"~", // Not = 31,
|
||||
"+", // Plus = 32,
|
||||
"-", // Minus = 33,
|
||||
"*", // Multiply = 34,
|
||||
"/", // Divide = 35,
|
||||
"^", // Power = 36,
|
||||
"\\", // Backslash = 37,
|
||||
"'", // Transpose = 38,
|
||||
".*", // DotMultiply = 39,
|
||||
"./", // DotDivide = 40,
|
||||
".^", // DotPower = 41,
|
||||
".\\", // DotBackslash = 42,
|
||||
".'", // DotTranspose = 43,
|
||||
"@", // At = 44,
|
||||
":", // Colon = 45,
|
||||
"?", // QuestionMark = 46,
|
||||
",", // Comma = 47,
|
||||
";", // Semicolon = 48,
|
||||
"{", // OpeningBrace = 49,
|
||||
"}", // ClosingBrace = 50,
|
||||
"[", // OpeningSquareBracket = 51,
|
||||
"]", // ClosingSquareBracket = 52,
|
||||
"(", // OpeningBracket = 53,
|
||||
")", // ClosingBracket = 54,
|
||||
".", // Dot = 55,
|
||||
"...", // DotDotDot = 56,
|
||||
|
||||
"+", // UnaryPlus = 57,
|
||||
"-", // UnaryMinus = 58,
|
||||
"~", // UnaryNot = 59,
|
||||
"?", // UnaryQuestionMark = 60,
|
||||
};
|
||||
|
||||
public static string GetText(TokenKind kind)
|
||||
{
|
||||
return StringFromKind[(int) kind];
|
||||
}
|
||||
|
||||
public static bool IsUnaryOperator(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Plus:
|
||||
case TokenKind.Minus:
|
||||
case TokenKind.Not:
|
||||
case TokenKind.QuestionMark:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsBinaryOperator(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Assignment:
|
||||
case TokenKind.LogicalOr:
|
||||
case TokenKind.LogicalAnd:
|
||||
case TokenKind.BitwiseOr:
|
||||
case TokenKind.BitwiseAnd:
|
||||
case TokenKind.Less:
|
||||
case TokenKind.LessOrEqual:
|
||||
case TokenKind.Greater:
|
||||
case TokenKind.GreaterOrEqual:
|
||||
case TokenKind.Equality:
|
||||
case TokenKind.Inequality:
|
||||
case TokenKind.Colon:
|
||||
case TokenKind.Plus:
|
||||
case TokenKind.Minus:
|
||||
case TokenKind.Multiply:
|
||||
case TokenKind.DotMultiply:
|
||||
case TokenKind.Divide:
|
||||
case TokenKind.DotDivide:
|
||||
case TokenKind.Backslash:
|
||||
case TokenKind.DotBackslash:
|
||||
case TokenKind.Not:
|
||||
case TokenKind.Power:
|
||||
case TokenKind.DotPower:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsLeftAssociative(TokenKind kind)
|
||||
{
|
||||
return true; // TODO: really?
|
||||
}
|
||||
|
||||
public static TokenKind ConvertToUnaryTokenKind(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Plus:
|
||||
return TokenKind.UnaryPlus;
|
||||
case TokenKind.Minus:
|
||||
return TokenKind.UnaryMinus;
|
||||
case TokenKind.Not:
|
||||
return TokenKind.UnaryNot;
|
||||
case TokenKind.QuestionMark:
|
||||
return TokenKind.UnaryQuestionMark;
|
||||
default:
|
||||
throw new ArgumentException(nameof(kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
Parser/Internal/SyntaxList.cs
Normal file
32
Parser/Internal/SyntaxList.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal class SyntaxList : SyntaxNode
|
||||
{
|
||||
private readonly GreenNode[] _elements;
|
||||
|
||||
protected SyntaxList(GreenNode[] elements) : base(TokenKind.List, elements.Length)
|
||||
{
|
||||
_elements = elements;
|
||||
}
|
||||
|
||||
public override GreenNode GetSlot(int i)
|
||||
{
|
||||
return _elements[i];
|
||||
}
|
||||
|
||||
public static SyntaxList List(GreenNode[] elements)
|
||||
{
|
||||
return new SyntaxList(elements);
|
||||
}
|
||||
|
||||
public override bool IsList => true;
|
||||
|
||||
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent)
|
||||
{
|
||||
return new Parser.SyntaxNodeOrTokenList(parent, this);
|
||||
}
|
||||
}
|
||||
}
|
33
Parser/Internal/SyntaxListBuilder.cs
Normal file
33
Parser/Internal/SyntaxListBuilder.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal class SyntaxListBuilder
|
||||
{
|
||||
private readonly List<GreenNode> _list;
|
||||
|
||||
public SyntaxListBuilder()
|
||||
{
|
||||
_list = new List<GreenNode>();
|
||||
}
|
||||
|
||||
public void Add(GreenNode node)
|
||||
{
|
||||
_list.Add(node);
|
||||
}
|
||||
|
||||
public void AddRange(SyntaxList list)
|
||||
{
|
||||
for (var i = 0; i < list.Slots; i++)
|
||||
{
|
||||
var element = list.GetSlot(i);
|
||||
_list.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
public SyntaxList ToList()
|
||||
{
|
||||
return _list.Count == 0 ? null : SyntaxList.List(_list.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
25
Parser/Internal/SyntaxListBuilder`1.cs
Normal file
25
Parser/Internal/SyntaxListBuilder`1.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal class SyntaxListBuilder<T> where T : GreenNode
|
||||
{
|
||||
private readonly List<T> _list;
|
||||
|
||||
public SyntaxListBuilder()
|
||||
{
|
||||
_list = new List<T>();
|
||||
}
|
||||
|
||||
public void Add(T node)
|
||||
{
|
||||
_list.Add(node);
|
||||
}
|
||||
|
||||
public SyntaxList<T> ToList()
|
||||
{
|
||||
return _list.Count == 0 ? null : SyntaxList<T>.List(_list.ToArray());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
32
Parser/Internal/SyntaxList`1.cs
Normal file
32
Parser/Internal/SyntaxList`1.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal class SyntaxList<T> : SyntaxNode where T : GreenNode
|
||||
{
|
||||
private readonly SyntaxList _list;
|
||||
|
||||
protected SyntaxList(T[] list) : base(TokenKind.List, list.Length)
|
||||
{
|
||||
_list = SyntaxList.List(list);
|
||||
}
|
||||
|
||||
public override GreenNode GetSlot(int i)
|
||||
{
|
||||
return (T)_list.GetSlot(i);
|
||||
}
|
||||
|
||||
public static SyntaxList<T> List(T[] elements)
|
||||
{
|
||||
return new SyntaxList<T>(elements);
|
||||
}
|
||||
|
||||
public override bool IsList => true;
|
||||
|
||||
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent)
|
||||
{
|
||||
return new Parser.SyntaxNodeOrTokenList(parent, this);
|
||||
}
|
||||
}
|
||||
}
|
1652
Parser/Internal/SyntaxNode.Generated.cs
Normal file
1652
Parser/Internal/SyntaxNode.Generated.cs
Normal file
File diff suppressed because it is too large
Load Diff
82
Parser/Internal/SyntaxNode.cs
Normal file
82
Parser/Internal/SyntaxNode.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal abstract class SyntaxNode : GreenNode
|
||||
{
|
||||
protected SyntaxNode(TokenKind kind, int slots) : base(kind, slots)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<SyntaxToken> DescendantTokens => CalculateChildTokens();
|
||||
|
||||
private IEnumerable<SyntaxToken> CalculateChildTokens()
|
||||
{
|
||||
for (var i = 0; i < Slots; i++)
|
||||
{
|
||||
var slot = GetSlot(i);
|
||||
switch (slot)
|
||||
{
|
||||
case null:
|
||||
continue;
|
||||
case SyntaxToken token:
|
||||
yield return token;
|
||||
break;
|
||||
case SyntaxNode node:
|
||||
foreach (var t in node.DescendantTokens)
|
||||
{
|
||||
yield return t;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string CollectFullText()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var token in DescendantTokens)
|
||||
{
|
||||
builder.Append(token.FullText);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public override string FullText => CollectFullText();
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => throw new NotImplementedException();
|
||||
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal abstract class StatementSyntaxNode : SyntaxNode
|
||||
{
|
||||
protected StatementSyntaxNode(TokenKind kind, int slots) : base(kind, slots + 1)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class ExpressionSyntaxNode : SyntaxNode
|
||||
{
|
||||
protected ExpressionSyntaxNode(TokenKind kind, int slots) : base(kind, slots)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class FunctionHandleSyntaxNode : ExpressionSyntaxNode
|
||||
{
|
||||
protected FunctionHandleSyntaxNode(TokenKind kind, int slots) : base(kind, slots)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class MethodDeclarationSyntaxNode : StatementSyntaxNode
|
||||
{
|
||||
protected MethodDeclarationSyntaxNode(TokenKind kind, int slots) : base(kind, slots)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
215
Parser/Internal/SyntaxToken.cs
Normal file
215
Parser/Internal/SyntaxToken.cs
Normal file
@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal abstract class SyntaxToken : GreenNode
|
||||
{
|
||||
internal class SyntaxTokenWithTrivia : SyntaxToken
|
||||
{
|
||||
private readonly string _text;
|
||||
|
||||
public SyntaxTokenWithTrivia(
|
||||
TokenKind kind,
|
||||
string text,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia) : base(kind)
|
||||
{
|
||||
_text = text;
|
||||
LeadingTriviaCore = leadingTrivia;
|
||||
TrailingTriviaCore = trailingTrivia;
|
||||
}
|
||||
|
||||
public SyntaxTokenWithTrivia(
|
||||
TokenKind kind,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia) : base(kind)
|
||||
{
|
||||
_text = base.Text;
|
||||
LeadingTriviaCore = leadingTrivia;
|
||||
TrailingTriviaCore = trailingTrivia;
|
||||
}
|
||||
|
||||
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
if (leading)
|
||||
{
|
||||
foreach (var trivia in LeadingTrivia)
|
||||
{
|
||||
writer.Write(trivia.Text);
|
||||
}
|
||||
}
|
||||
base.WriteTokenTo(writer, leading, trailing);
|
||||
if (trailing)
|
||||
{
|
||||
foreach (var trivia in TrailingTrivia)
|
||||
{
|
||||
writer.Write(trivia.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
|
||||
}
|
||||
|
||||
internal class SyntaxTokenWithValue<T> : SyntaxToken
|
||||
{
|
||||
protected readonly string _text;
|
||||
private readonly T _value;
|
||||
|
||||
public SyntaxTokenWithValue(
|
||||
TokenKind kind,
|
||||
string text,
|
||||
T value) : base(kind)
|
||||
{
|
||||
_text = text;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
writer.Write(_text);
|
||||
}
|
||||
public T Value => _value;
|
||||
}
|
||||
|
||||
internal class SyntaxTokenWithValueAndTrivia<T> : SyntaxTokenWithValue<T>
|
||||
{
|
||||
public SyntaxTokenWithValueAndTrivia(
|
||||
TokenKind kind,
|
||||
string text,
|
||||
T value,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia) : base(kind, text, value)
|
||||
{
|
||||
LeadingTriviaCore = leadingTrivia;
|
||||
TrailingTriviaCore = trailingTrivia;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
|
||||
|
||||
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
if (leading)
|
||||
{
|
||||
foreach (var trivia in LeadingTrivia)
|
||||
{
|
||||
writer.Write(trivia.Text);
|
||||
}
|
||||
}
|
||||
base.WriteTokenTo(writer, leading, trailing);
|
||||
if (trailing)
|
||||
{
|
||||
foreach (var trivia in TrailingTrivia)
|
||||
{
|
||||
writer.Write(trivia.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyntaxIdentifier : SyntaxToken
|
||||
{
|
||||
private readonly string _text;
|
||||
|
||||
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
writer.Write(_text);
|
||||
}
|
||||
|
||||
public SyntaxIdentifier(
|
||||
string text
|
||||
) : base(TokenKind.Identifier)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyntaxIdentifierWithTrivia : SyntaxIdentifier
|
||||
{
|
||||
private readonly IReadOnlyList<SyntaxTrivia> _leadingTrivia;
|
||||
private readonly IReadOnlyList<SyntaxTrivia> _trailingTrivia;
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => _leadingTrivia;
|
||||
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => _trailingTrivia;
|
||||
|
||||
public SyntaxIdentifierWithTrivia(
|
||||
string text,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia
|
||||
) : base(text)
|
||||
{
|
||||
_leadingTrivia = leadingTrivia;
|
||||
_trailingTrivia = trailingTrivia;
|
||||
}
|
||||
|
||||
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
if (leading)
|
||||
{
|
||||
foreach (var trivia in LeadingTrivia)
|
||||
{
|
||||
writer.Write(trivia.Text);
|
||||
}
|
||||
}
|
||||
base.WriteTokenTo(writer, leading, trailing);
|
||||
if (trailing)
|
||||
{
|
||||
foreach (var trivia in TrailingTrivia)
|
||||
{
|
||||
writer.Write(trivia.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsToken => true;
|
||||
public override bool IsNode => false;
|
||||
}
|
||||
|
||||
internal class MissingTokenWithTrivia : SyntaxTokenWithTrivia
|
||||
{
|
||||
public MissingTokenWithTrivia(
|
||||
TokenKind kind,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia
|
||||
) : base(kind, leadingTrivia, trailingTrivia)
|
||||
{
|
||||
_isMissing = true;
|
||||
}
|
||||
|
||||
public override string Text => "";
|
||||
}
|
||||
|
||||
protected SyntaxToken(TokenKind kind) : base(kind, 0)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual int Width => Text.Length;
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => new List<SyntaxTrivia>();
|
||||
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => new List<SyntaxTrivia>();
|
||||
|
||||
public override GreenNode GetSlot(int i)
|
||||
{
|
||||
throw new System.InvalidOperationException();
|
||||
}
|
||||
|
||||
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override bool IsToken => true;
|
||||
public override bool IsNode => false;
|
||||
|
||||
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
|
||||
{
|
||||
writer.Write(SyntaxFacts.GetText(Kind));
|
||||
}
|
||||
}
|
||||
}
|
41
Parser/Internal/SyntaxTrivia.cs
Normal file
41
Parser/Internal/SyntaxTrivia.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal class SyntaxTrivia : GreenNode
|
||||
{
|
||||
private readonly string _text;
|
||||
|
||||
public SyntaxTrivia(TokenKind kind, string text) : base(kind, 0)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
|
||||
public override string Text => _text;
|
||||
public int Width => _text.Length;
|
||||
|
||||
public override GreenNode GetSlot(int i)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public override bool IsTrivia => true;
|
||||
public override bool IsNode => false;
|
||||
|
||||
public override void WriteTriviaTo(TextWriter writer)
|
||||
{
|
||||
writer.Write(_text);
|
||||
}
|
||||
|
||||
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => new List<SyntaxTrivia>();
|
||||
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => new List<SyntaxTrivia>();
|
||||
}
|
||||
}
|
60
Parser/Internal/TokenFactory.cs
Normal file
60
Parser/Internal/TokenFactory.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
internal static class TokenFactory
|
||||
{
|
||||
public static SyntaxTrivia CreateTrivia(TokenKind kind, string text)
|
||||
{
|
||||
return new SyntaxTrivia(kind, text);
|
||||
}
|
||||
|
||||
public static SyntaxToken CreateTokenWithTrivia(
|
||||
TokenKind kind,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia)
|
||||
{
|
||||
return new SyntaxToken.SyntaxTokenWithTrivia(kind, leadingTrivia, trailingTrivia);
|
||||
}
|
||||
|
||||
public static SyntaxToken CreateIdentifier(
|
||||
string text,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia)
|
||||
{
|
||||
return new SyntaxToken.SyntaxIdentifierWithTrivia(text, leadingTrivia, trailingTrivia);
|
||||
}
|
||||
|
||||
public static SyntaxToken CreateTokenWithValueAndTrivia<T>(
|
||||
TokenKind kind,
|
||||
string text,
|
||||
T value,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia)
|
||||
{
|
||||
return new SyntaxToken.SyntaxTokenWithValueAndTrivia<T>(kind, text, value, leadingTrivia, trailingTrivia);
|
||||
}
|
||||
|
||||
public static SyntaxToken CreateUnquotedStringLiteral(
|
||||
string text,
|
||||
string value,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia)
|
||||
{
|
||||
return new SyntaxToken.SyntaxTokenWithValueAndTrivia<string>(
|
||||
TokenKind.UnquotedStringLiteral,
|
||||
text,
|
||||
value,
|
||||
leadingTrivia,
|
||||
trailingTrivia);
|
||||
}
|
||||
|
||||
public static SyntaxToken CreateMissing(
|
||||
TokenKind kind,
|
||||
IReadOnlyList<SyntaxTrivia> leadingTrivia,
|
||||
IReadOnlyList<SyntaxTrivia> trailingTrivia)
|
||||
{
|
||||
return new SyntaxToken.MissingTokenWithTrivia(kind, leadingTrivia, trailingTrivia);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,939 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lexer;
|
||||
|
||||
namespace Parser
|
||||
namespace Parser
|
||||
{
|
||||
public class MParser
|
||||
{
|
||||
public enum Precedence
|
||||
{
|
||||
// see https://mathworks.com/help/matlab/matlab_prog/operator-precedence.html
|
||||
Expression = 0,
|
||||
Assignment,
|
||||
LogicalOr,
|
||||
LogicalAnd,
|
||||
BitwiseOr,
|
||||
BitwiseAnd,
|
||||
Relational,
|
||||
Colon,
|
||||
Additive,
|
||||
Multiplicative,
|
||||
Unary,
|
||||
WeirdPower,
|
||||
Power
|
||||
}
|
||||
|
||||
private static Precedence GetPrecedence(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Assignment:
|
||||
return Precedence.Assignment;
|
||||
case TokenKind.LogicalOr:
|
||||
return Precedence.LogicalOr;
|
||||
case TokenKind.LogicalAnd:
|
||||
return Precedence.LogicalAnd;
|
||||
case TokenKind.BitwiseOr:
|
||||
return Precedence.BitwiseOr;
|
||||
case TokenKind.BitwiseAnd:
|
||||
return Precedence.BitwiseAnd;
|
||||
case TokenKind.Less:
|
||||
case TokenKind.LessOrEqual:
|
||||
case TokenKind.Greater:
|
||||
case TokenKind.GreaterOrEqual:
|
||||
case TokenKind.Equality:
|
||||
case TokenKind.Inequality:
|
||||
return Precedence.Relational;
|
||||
case TokenKind.Colon:
|
||||
return Precedence.Colon;
|
||||
case TokenKind.Plus:
|
||||
case TokenKind.Minus:
|
||||
return Precedence.Additive;
|
||||
case TokenKind.Multiply:
|
||||
case TokenKind.DotMultiply:
|
||||
case TokenKind.Divide:
|
||||
case TokenKind.DotDivide:
|
||||
case TokenKind.Backslash:
|
||||
case TokenKind.DotBackslash:
|
||||
return Precedence.Multiplicative;
|
||||
case TokenKind.Not:
|
||||
return Precedence.Unary;
|
||||
case TokenKind.Power:
|
||||
case TokenKind.DotPower:
|
||||
case TokenKind.Transpose:
|
||||
case TokenKind.DotTranspose:
|
||||
return Precedence.Power;
|
||||
default:
|
||||
return Precedence.Expression;
|
||||
}
|
||||
}
|
||||
private readonly ITextWindow _window;
|
||||
|
||||
private List<Token> Tokens { get; }
|
||||
private int _index;
|
||||
private Token CurrentToken => Tokens[_index];
|
||||
private Token PeekToken(int n) => Tokens[_index + n];
|
||||
private SyntaxFactory Factory { get; }
|
||||
|
||||
public MParser(List<Token> tokens)
|
||||
public MParser(ITextWindow window)
|
||||
{
|
||||
Tokens = tokens;
|
||||
_index = 0;
|
||||
Factory = new SyntaxFactory();
|
||||
_window = window;
|
||||
}
|
||||
|
||||
private Token EatToken()
|
||||
public FileSyntaxNode Parse()
|
||||
{
|
||||
var token = Tokens[_index];
|
||||
//Console.WriteLine($"{token} at {token.PureToken.Position}");
|
||||
_index++;
|
||||
return token;
|
||||
}
|
||||
|
||||
private Token EatToken(TokenKind kind)
|
||||
{
|
||||
var token = Tokens[_index];
|
||||
//Console.WriteLine($"{token} at {token.PureToken.Position}");
|
||||
if (token.Kind != kind)
|
||||
{
|
||||
throw new ParsingException($"Unexpected token \"{token.PureToken}\" instead of {kind} at {token.PureToken.Position}.");
|
||||
}
|
||||
_index++;
|
||||
return token;
|
||||
}
|
||||
|
||||
private Token EatIdentifier(string s)
|
||||
{
|
||||
var token = Tokens[_index];
|
||||
//Console.WriteLine($"{token} at {token.PureToken.Position}");
|
||||
if (token.PureToken.Kind != TokenKind.Identifier)
|
||||
{
|
||||
throw new ParsingException($"Unexpected token \"{token.PureToken}\" instead of identifier \"{s}\" at {token.PureToken.Position}.");
|
||||
}
|
||||
|
||||
if (token.PureToken.LiteralText != s)
|
||||
{
|
||||
throw new ParsingException($"Unexpected identifier \"{token.PureToken.LiteralText}\" instead of \"{s}\" at {token.PureToken.Position}.");
|
||||
}
|
||||
_index++;
|
||||
return token;
|
||||
}
|
||||
|
||||
private void EatAll()
|
||||
{
|
||||
_index = Tokens.Count - 1;
|
||||
}
|
||||
|
||||
private List<SyntaxNode> ParseFunctionOutputList()
|
||||
{
|
||||
var outputs = new List<Token>();
|
||||
while (CurrentToken.Kind != TokenKind.ClosingSquareBracket)
|
||||
{
|
||||
if (outputs.Count > 0 && CurrentToken.Kind == TokenKind.Comma)
|
||||
{
|
||||
outputs.Add(EatToken());
|
||||
}
|
||||
outputs.Add(EatToken(TokenKind.Identifier));
|
||||
}
|
||||
|
||||
return outputs.Select(token => new TokenNode(token) as SyntaxNode).ToList();
|
||||
}
|
||||
|
||||
private FunctionOutputDescriptionNode ParseFunctionOutputDescription()
|
||||
{
|
||||
if (CurrentToken.Kind == TokenKind.Identifier)
|
||||
{
|
||||
if (PeekToken(1).Kind == TokenKind.Assignment)
|
||||
{
|
||||
var identifier = EatToken();
|
||||
var assignmentSign = EatToken(TokenKind.Assignment);
|
||||
return Factory.FunctionOutputDescription(
|
||||
new List<SyntaxNode> { Factory.Token(identifier) },
|
||||
Factory.Token(assignmentSign)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
} else if (CurrentToken.Kind == TokenKind.OpeningSquareBracket)
|
||||
{
|
||||
var leftBracket = EatToken();
|
||||
var outputs = ParseFunctionOutputList();
|
||||
var rightBracket = EatToken(TokenKind.ClosingSquareBracket);
|
||||
var nodes = new List<SyntaxNode> {Factory.Token(leftBracket)};
|
||||
nodes.AddRange(outputs);
|
||||
nodes.Add(Factory.Token(rightBracket));
|
||||
var assignmentSign = EatToken(TokenKind.Assignment);
|
||||
return Factory.FunctionOutputDescription(nodes, Factory.Token(assignmentSign));
|
||||
}
|
||||
throw new ParsingException($"Unexpected token {CurrentToken.PureToken} during parsing function output descritpion at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
|
||||
private ParameterListNode ParseParameterList()
|
||||
{
|
||||
var identifierTokens = new List<Token>();
|
||||
|
||||
while (CurrentToken.Kind != TokenKind.ClosingBracket)
|
||||
{
|
||||
if (identifierTokens.Count > 0)
|
||||
{
|
||||
identifierTokens.Add(EatToken(TokenKind.Comma));
|
||||
}
|
||||
|
||||
if (CurrentToken.Kind == TokenKind.Not)
|
||||
{
|
||||
var notToken = EatToken();
|
||||
identifierTokens.Add(notToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
identifierTokens.Add(EatToken(TokenKind.Identifier));
|
||||
}
|
||||
}
|
||||
|
||||
return Factory.ParameterList(identifierTokens.Select(token => Factory.Token(token) as SyntaxNode).ToList());
|
||||
}
|
||||
|
||||
private FunctionInputDescriptionNode ParseFunctionInputDescription()
|
||||
{
|
||||
if (CurrentToken.Kind == TokenKind.OpeningBracket)
|
||||
{
|
||||
var openingBracket = EatToken(TokenKind.OpeningBracket);
|
||||
var parameterList = ParseParameterList();
|
||||
var closingBracket = EatToken(TokenKind.ClosingBracket);
|
||||
return Factory.FunctionInputDescription(
|
||||
new TokenNode(openingBracket),
|
||||
parameterList,
|
||||
new TokenNode(closingBracket));
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private TokenNode PossibleSemicolonOrComma()
|
||||
{
|
||||
if (CurrentToken.Kind == TokenKind.Semicolon
|
||||
|| CurrentToken.Kind == TokenKind.Comma)
|
||||
{
|
||||
return Factory.Token(EatToken());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private FunctionDeclarationNode ParseFunctionDeclaration()
|
||||
{
|
||||
var functionKeyword = EatIdentifier("function");
|
||||
var outputDescription = ParseFunctionOutputDescription();
|
||||
var name = EatToken(TokenKind.Identifier);
|
||||
var inputDescription = ParseFunctionInputDescription();
|
||||
var body = ParseStatements();
|
||||
TokenNode end = null;
|
||||
if (CurrentToken.Kind == TokenKind.Identifier
|
||||
&& CurrentToken.PureToken.LiteralText == "end")
|
||||
{
|
||||
end = Factory.Token(EatIdentifier("end"));
|
||||
}
|
||||
|
||||
var semicolonOrComma = PossibleSemicolonOrComma();
|
||||
return Factory.FunctionDeclaration(
|
||||
Factory.Token(functionKeyword),
|
||||
outputDescription,
|
||||
Factory.Token(name),
|
||||
inputDescription,
|
||||
body,
|
||||
end,
|
||||
semicolonOrComma);
|
||||
}
|
||||
|
||||
private StatementNode ParseClassDeclaration()
|
||||
{
|
||||
var node = new TokenNode(CurrentToken);
|
||||
EatAll();
|
||||
return null;
|
||||
}
|
||||
|
||||
private FunctionCallParameterListNode ParseFunctionCallParameterList()
|
||||
{
|
||||
var nodes = new List<SyntaxNode>();
|
||||
while (CurrentToken.Kind != TokenKind.ClosingBracket)
|
||||
{
|
||||
if (nodes.Count > 0)
|
||||
{
|
||||
nodes.Add(Factory.Token(EatToken(TokenKind.Comma)));
|
||||
}
|
||||
|
||||
nodes.Add(ParseExpression());
|
||||
}
|
||||
|
||||
return Factory.FunctionCallParameterList(nodes);
|
||||
}
|
||||
|
||||
private ExpressionNode ParseMember()
|
||||
{
|
||||
if (CurrentToken.Kind == TokenKind.Identifier)
|
||||
{
|
||||
return Factory.IdentifierName(EatToken());
|
||||
} else if (CurrentToken.Kind == TokenKind.OpeningBracket)
|
||||
{
|
||||
var openingBracket = EatToken();
|
||||
var indirectMember = ParseExpression();
|
||||
var closingBracket = EatToken(TokenKind.ClosingBracket);
|
||||
return Factory.IndirectMemberAccess(
|
||||
Factory.Token(openingBracket),
|
||||
indirectMember,
|
||||
Factory.Token(closingBracket));
|
||||
}
|
||||
throw new ParsingException($"Unexpected token {CurrentToken.PureToken} at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
|
||||
private ExpressionNode ParsePostfix(ParseOptions options, ExpressionNode expression)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var token = CurrentToken;
|
||||
switch(token.Kind) {
|
||||
case TokenKind.OpeningBrace: // cell array element access
|
||||
if (options.ParsingArrayElements && expression.TrailingTrivia.Any())
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
var openingBrace = EatToken();
|
||||
var indices = ParseArrayElementList(TokenKind.ClosingBrace);
|
||||
var closingBrace = EatToken(TokenKind.ClosingBrace);
|
||||
expression = Factory.CellArrayElementAccessExpression(
|
||||
expression,
|
||||
Factory.Token(openingBrace),
|
||||
indices,
|
||||
Factory.Token(closingBrace)
|
||||
);
|
||||
break;
|
||||
case TokenKind.OpeningBracket: // function call
|
||||
if (options.ParsingArrayElements && expression.TrailingTrivia.Any())
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
var openingBracket = EatToken();
|
||||
var parameters = ParseFunctionCallParameterList();
|
||||
var closingBracket = EatToken(TokenKind.ClosingBracket);
|
||||
expression = Factory.FunctionCallExpression(
|
||||
expression,
|
||||
Factory.Token(openingBracket),
|
||||
parameters,
|
||||
Factory.Token(closingBracket));
|
||||
break;
|
||||
case TokenKind.Dot: // member access
|
||||
if (expression is IdentifierNameNode
|
||||
|| expression is MemberAccessNode
|
||||
|| expression is FunctionCallExpressionNode
|
||||
|| expression is CellArrayElementAccessExpressionNode)
|
||||
{
|
||||
var dot = EatToken();
|
||||
var member = ParseMember();
|
||||
expression = Factory.MemberAccess(expression, Factory.Token(dot), member);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException(
|
||||
$"Unexpected token {token.PureToken} at {token.PureToken.Position}.");
|
||||
}
|
||||
|
||||
break;
|
||||
case TokenKind.Transpose:
|
||||
case TokenKind.DotTranspose:
|
||||
var operation = Factory.Token(EatToken());
|
||||
expression = Factory.UnaryPostfixOperationExpression(expression, operation);
|
||||
break;
|
||||
case TokenKind.UnquotedStringLiteral:
|
||||
if (expression is IdentifierNameNode idNameNode)
|
||||
{
|
||||
var arguments = new List<UnquotedStringLiteralNode>();
|
||||
while (CurrentToken.Kind == TokenKind.UnquotedStringLiteral)
|
||||
{
|
||||
arguments.Add(Factory.UnquotedStringLiteral(EatToken()));
|
||||
}
|
||||
|
||||
return Factory.CommandExpression(idNameNode, arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException($"Unexpected token \"{CurrentToken.PureToken.LiteralText}\" while parsing expression \"{expression.FullText}\" at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
case TokenKind.At:
|
||||
if (expression is IdentifierNameNode idNameNode2
|
||||
&& !expression.TrailingTrivia.Any())
|
||||
{
|
||||
var atToken = Factory.Token(EatToken());
|
||||
var baseClassNameWithArguments = ParseExpression();
|
||||
return Factory.BaseClassInvokation(idNameNode2, atToken, baseClassNameWithArguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException($"Unexpected token \"{CurrentToken.PureToken.LiteralText}\" at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
default:
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayElementListNode ParseArrayElementList(TokenKind closing)
|
||||
{
|
||||
var nodes = new List<SyntaxNode> {};
|
||||
|
||||
while (CurrentToken.Kind != closing)
|
||||
{
|
||||
if (nodes.Count > 0)
|
||||
{
|
||||
if (CurrentToken.Kind == TokenKind.Comma
|
||||
|| CurrentToken.Kind == TokenKind.Semicolon)
|
||||
{
|
||||
nodes.Add(Factory.Token(EatToken()));
|
||||
}
|
||||
}
|
||||
|
||||
var expression = ParseExpression(new ParseOptions {ParsingArrayElements = true});
|
||||
if (expression != null)
|
||||
{
|
||||
nodes.Add(expression);
|
||||
}
|
||||
}
|
||||
|
||||
return Factory.ArrayElementList(nodes);
|
||||
}
|
||||
|
||||
private ArrayLiteralExpressionNode ParseArrayLiteral()
|
||||
{
|
||||
var openingSquareBracket = EatToken(TokenKind.OpeningSquareBracket);
|
||||
var elements = ParseArrayElementList(TokenKind.ClosingSquareBracket);
|
||||
var closingSquareBracket = EatToken(TokenKind.ClosingSquareBracket);
|
||||
return Factory.ArrayLiteralExpression(
|
||||
Factory.Token(openingSquareBracket),
|
||||
elements,
|
||||
Factory.Token(closingSquareBracket));
|
||||
}
|
||||
|
||||
private CellArrayLiteralExpressionNode ParseCellArrayLiteral()
|
||||
{
|
||||
var openingBrace = EatToken(TokenKind.OpeningBrace);
|
||||
var elements = ParseArrayElementList(TokenKind.ClosingBrace);
|
||||
var closingBrace = EatToken(TokenKind.ClosingBrace);
|
||||
return Factory.CellArrayLiteralExpression(
|
||||
Factory.Token(openingBrace),
|
||||
elements,
|
||||
Factory.Token(closingBrace));
|
||||
}
|
||||
|
||||
private ParenthesizedExpressionNode ParseParenthesizedExpression()
|
||||
{
|
||||
var openParen = Factory.Token(EatToken(TokenKind.OpeningBracket));
|
||||
var expression = ParseExpression();
|
||||
var closeParen = Factory.Token(EatToken(TokenKind.ClosingBracket));
|
||||
return Factory.ParenthesizedExpression(
|
||||
openParen,
|
||||
expression,
|
||||
closeParen);
|
||||
}
|
||||
|
||||
private ExpressionNode ParseTerm(ParseOptions options)
|
||||
{
|
||||
var token = CurrentToken;
|
||||
ExpressionNode expression = null;
|
||||
if (token.Kind == TokenKind.Identifier)
|
||||
{
|
||||
var term = EatToken();
|
||||
expression = Factory.IdentifierName(term);
|
||||
}
|
||||
else if (token.Kind == TokenKind.NumberLiteral)
|
||||
{
|
||||
var number = EatToken();
|
||||
expression = Factory.NumberLiteral(number);
|
||||
}
|
||||
else if (token.Kind == TokenKind.StringLiteral)
|
||||
{
|
||||
var str = EatToken();
|
||||
expression = Factory.StringLiteral(str);
|
||||
}
|
||||
else if (token.Kind == TokenKind.DoubleQuotedStringLiteral)
|
||||
{
|
||||
var str = EatToken();
|
||||
expression = Factory.DoubleQuotedStringLiteral(str);
|
||||
}
|
||||
else if (token.Kind == TokenKind.OpeningSquareBracket) // array literal expression
|
||||
{
|
||||
expression = ParseArrayLiteral();
|
||||
}
|
||||
else if (token.Kind == TokenKind.OpeningBrace) // cell array literal expression
|
||||
{
|
||||
expression = ParseCellArrayLiteral();
|
||||
}
|
||||
else if (token.Kind == TokenKind.Colon) // for parsing things like a{:}
|
||||
{
|
||||
expression = Factory.EmptyExpression();
|
||||
}
|
||||
else if (token.Kind == TokenKind.OpeningBracket)
|
||||
{
|
||||
expression = ParseParenthesizedExpression();
|
||||
}
|
||||
|
||||
if (expression == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return ParsePostfix(options, expression);
|
||||
}
|
||||
|
||||
internal struct ParseOptions
|
||||
{
|
||||
public bool ParsingArrayElements { get; set; }
|
||||
|
||||
public static ParseOptions Default = new ParseOptions { ParsingArrayElements = false };
|
||||
}
|
||||
|
||||
public ExpressionNode ParseExpression()
|
||||
{
|
||||
return ParseExpression(ParseOptions.Default);
|
||||
}
|
||||
|
||||
private ExpressionNode ParseExpression(ParseOptions options)
|
||||
{
|
||||
return ParseSubExpression(options, Precedence.Expression);
|
||||
}
|
||||
|
||||
private bool IsUnaryOperator(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Plus:
|
||||
case TokenKind.Minus:
|
||||
case TokenKind.Not:
|
||||
case TokenKind.QuestionMark:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsBinaryOperator(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Assignment:
|
||||
case TokenKind.LogicalOr:
|
||||
case TokenKind.LogicalAnd:
|
||||
case TokenKind.BitwiseOr:
|
||||
case TokenKind.BitwiseAnd:
|
||||
case TokenKind.Less:
|
||||
case TokenKind.LessOrEqual:
|
||||
case TokenKind.Greater:
|
||||
case TokenKind.GreaterOrEqual:
|
||||
case TokenKind.Equality:
|
||||
case TokenKind.Inequality:
|
||||
case TokenKind.Colon:
|
||||
case TokenKind.Plus:
|
||||
case TokenKind.Minus:
|
||||
case TokenKind.Multiply:
|
||||
case TokenKind.DotMultiply:
|
||||
case TokenKind.Divide:
|
||||
case TokenKind.DotDivide:
|
||||
case TokenKind.Backslash:
|
||||
case TokenKind.DotBackslash:
|
||||
case TokenKind.Not:
|
||||
case TokenKind.Power:
|
||||
case TokenKind.DotPower:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsLeftAssociative(TokenKind kind)
|
||||
{
|
||||
return true; // TODO: really?
|
||||
}
|
||||
|
||||
private TokenKind ConvertToUnaryTokenKind(TokenKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TokenKind.Plus:
|
||||
return TokenKind.UnaryPlus;
|
||||
case TokenKind.Minus:
|
||||
return TokenKind.UnaryMinus;
|
||||
case TokenKind.Not:
|
||||
return TokenKind.UnaryNot;
|
||||
case TokenKind.QuestionMark:
|
||||
return TokenKind.UnaryQuestionMark;
|
||||
default:
|
||||
throw new ArgumentException(nameof(kind));
|
||||
}
|
||||
}
|
||||
|
||||
private CompoundNameNode ParseCompoundName()
|
||||
{
|
||||
var lastToken = EatToken(TokenKind.Identifier);
|
||||
var firstName = Factory.IdentifierName(lastToken);
|
||||
var nodes = new List<SyntaxNode> {firstName};
|
||||
while (CurrentToken.Kind == TokenKind.Dot
|
||||
&& !lastToken.TrailingTrivia.Any())
|
||||
{
|
||||
var dot = Factory.Token(EatToken());
|
||||
nodes.Add(dot);
|
||||
lastToken = EatToken(TokenKind.Identifier);
|
||||
nodes.Add(Factory.IdentifierName(lastToken));
|
||||
}
|
||||
|
||||
return Factory.CompoundName(nodes);
|
||||
}
|
||||
|
||||
private FunctionHandleNode ParseFunctionHandle()
|
||||
{
|
||||
var atSign = EatToken();
|
||||
if (CurrentToken.Kind == TokenKind.Identifier)
|
||||
{
|
||||
var compoundName = ParseCompoundName();
|
||||
return Factory.NamedFunctionHandle(
|
||||
Factory.Token(atSign),
|
||||
compoundName);
|
||||
} else if (CurrentToken.Kind == TokenKind.OpeningBracket)
|
||||
{
|
||||
var inputs = ParseFunctionInputDescription();
|
||||
var body = ParseExpression();
|
||||
return Factory.Lambda(Factory.Token(atSign), inputs, body);
|
||||
}
|
||||
throw new ParsingException($"Unexpected token {CurrentToken.PureToken} while parsing function handle at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
|
||||
private ExpressionNode ParseSubExpression(ParseOptions options, Precedence precedence)
|
||||
{
|
||||
ExpressionNode lhs = null;
|
||||
if (IsUnaryOperator(CurrentToken.Kind))
|
||||
{
|
||||
var operation = EatToken();
|
||||
var unaryTokenKind = ConvertToUnaryTokenKind(operation.Kind);
|
||||
var newPrecedence = GetPrecedence(unaryTokenKind);
|
||||
var operand = ParseSubExpression(options, newPrecedence);
|
||||
if (operand == null)
|
||||
{
|
||||
if (options.ParsingArrayElements && operation.Kind == TokenKind.Not)
|
||||
{
|
||||
operand = Factory.EmptyExpression();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException($"Unexpected token {CurrentToken.Kind} at {operation.PureToken.Position}.");
|
||||
}
|
||||
}
|
||||
lhs = Factory.UnaryPrefixOperationExpression(Factory.Token(operation), operand);
|
||||
}
|
||||
else if (CurrentToken.Kind == TokenKind.At)
|
||||
{
|
||||
return ParseFunctionHandle();
|
||||
}
|
||||
else
|
||||
{
|
||||
lhs = ParseTerm(options);
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
var token = CurrentToken;
|
||||
if (IsBinaryOperator(token.Kind))
|
||||
{
|
||||
var newPrecedence = GetPrecedence(token.Kind);
|
||||
if (newPrecedence < precedence)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (newPrecedence == precedence && IsLeftAssociative(token.Kind))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
EatToken();
|
||||
var rhs = ParseSubExpression(options, newPrecedence);
|
||||
if (rhs == null && token.Kind == TokenKind.Colon) // for parsing things like a{:}
|
||||
{
|
||||
rhs = Factory.EmptyExpression();
|
||||
}
|
||||
if (token.Kind == TokenKind.Assignment)
|
||||
{
|
||||
lhs = Factory.AssignmentExpression(lhs, Factory.Token(token), rhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
lhs = Factory.BinaryOperationExpression(lhs, Factory.Token(token), rhs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private List<TokenNode> ParseOptionalCommas()
|
||||
{
|
||||
var commas = new List<TokenNode>();
|
||||
while (CurrentToken.Kind == TokenKind.Comma)
|
||||
{
|
||||
commas.Add(Factory.Token(EatToken()));
|
||||
}
|
||||
|
||||
return commas;
|
||||
}
|
||||
|
||||
private List<TokenNode> ParseOptionalSemicolonsOrCommas()
|
||||
{
|
||||
var commas = new List<TokenNode>();
|
||||
while (CurrentToken.Kind == TokenKind.Comma
|
||||
|| CurrentToken.Kind == TokenKind.Semicolon)
|
||||
{
|
||||
commas.Add(Factory.Token(EatToken()));
|
||||
}
|
||||
|
||||
return commas;
|
||||
}
|
||||
|
||||
private SwitchCaseNode ParseSwitchCase()
|
||||
{
|
||||
var caseKeyword = EatIdentifier("case");
|
||||
var caseId = ParseExpression();
|
||||
var commas = ParseOptionalCommas();
|
||||
var statementList = ParseStatements();
|
||||
return Factory.SwitchCase(Factory.Token(caseKeyword), caseId, statementList, commas);
|
||||
}
|
||||
|
||||
private SwitchStatementNode ParseSwitchStatement()
|
||||
{
|
||||
var switchKeyword = EatIdentifier("switch");
|
||||
var expression = ParseExpression();
|
||||
var commas = ParseOptionalCommas();
|
||||
var casesList = new List<SwitchCaseNode>();
|
||||
while (CurrentToken.Kind == TokenKind.Identifier
|
||||
&& CurrentToken.PureToken.LiteralText == "case")
|
||||
{
|
||||
casesList.Add(ParseSwitchCase());
|
||||
}
|
||||
|
||||
var endKeyword = EatIdentifier("end");
|
||||
return Factory.SwitchStatement(
|
||||
Factory.Token(switchKeyword),
|
||||
expression,
|
||||
casesList,
|
||||
Factory.Token(endKeyword),
|
||||
commas);
|
||||
}
|
||||
|
||||
public ExpressionStatementNode ParseExpressionStatement()
|
||||
{
|
||||
var statement = ParseExpression();
|
||||
var possibleSemicolonOrComma = PossibleSemicolonOrComma();
|
||||
return Factory.ExpressionStatement(statement, possibleSemicolonOrComma);
|
||||
}
|
||||
|
||||
public WhileStatementNode ParseWhileStatement()
|
||||
{
|
||||
var whileKeyword = EatToken();
|
||||
var condition = ParseExpression();
|
||||
var commas = ParseOptionalCommas();
|
||||
var body = ParseStatements();
|
||||
var endKeyword = EatIdentifier("end");
|
||||
var semicolonOrComma = PossibleSemicolonOrComma();
|
||||
return Factory.WhileStatement(
|
||||
Factory.Token(whileKeyword),
|
||||
condition,
|
||||
commas,
|
||||
body,
|
||||
Factory.Token(endKeyword),
|
||||
semicolonOrComma);
|
||||
}
|
||||
|
||||
public StatementNode ParseStatement()
|
||||
{
|
||||
var statement = ParseStatementCore();
|
||||
return statement;
|
||||
}
|
||||
|
||||
public IfStatementNode ParseIfStatement()
|
||||
{
|
||||
var ifKeyword = Factory.Token(EatToken());
|
||||
var condition = ParseExpression();
|
||||
var commas = ParseOptionalSemicolonsOrCommas();
|
||||
var body = ParseStatements();
|
||||
TokenNode elseKeyword = null;
|
||||
StatementListNode elseBody = null;
|
||||
if (CurrentToken.Kind == TokenKind.Identifier
|
||||
&& CurrentToken.PureToken.LiteralText == "else")
|
||||
{
|
||||
elseKeyword = Factory.Token(EatToken());
|
||||
elseBody = ParseStatements();
|
||||
}
|
||||
if (CurrentToken.Kind == TokenKind.Identifier
|
||||
&& CurrentToken.PureToken.LiteralText == "elseif")
|
||||
{
|
||||
elseKeyword = null;
|
||||
var ifStatement = ParseIfStatement();
|
||||
elseBody = Factory.StatementList(new List<SyntaxNode> { ifStatement });
|
||||
return Factory.IfStatement(
|
||||
ifKeyword,
|
||||
condition,
|
||||
commas,
|
||||
body,
|
||||
null,
|
||||
elseBody,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
var endKeyword = Factory.Token(EatIdentifier("end"));
|
||||
var possibleSemicolonOrComma = PossibleSemicolonOrComma();
|
||||
return Factory.IfStatement(
|
||||
ifKeyword,
|
||||
condition,
|
||||
commas,
|
||||
body,
|
||||
elseKeyword,
|
||||
elseBody,
|
||||
endKeyword,
|
||||
possibleSemicolonOrComma);
|
||||
}
|
||||
|
||||
public ForStatementNode ParseForStatement()
|
||||
{
|
||||
var forKeyword = Factory.Token(EatIdentifier("for"));
|
||||
var expression = ParseExpression();
|
||||
if (!(expression is AssignmentExpressionNode))
|
||||
{
|
||||
throw new ParsingException($"Unexpected expression \"{expression.FullText}\" while parsing FOR statement at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
|
||||
var forAssignment = (AssignmentExpressionNode) expression;
|
||||
var commas = ParseOptionalSemicolonsOrCommas();
|
||||
|
||||
var body = ParseStatements();
|
||||
var endKeyword = Factory.Token(EatIdentifier("end"));
|
||||
return Factory.ForStatement(forKeyword, forAssignment, body, endKeyword, commas);
|
||||
}
|
||||
|
||||
public TryCatchStatementNode ParseTryCatchStatement()
|
||||
{
|
||||
var tryKeyword = Factory.Token(EatIdentifier("try"));
|
||||
var tryBody = ParseStatements();
|
||||
if (CurrentToken.PureToken.LiteralText == "catch")
|
||||
{
|
||||
var catchKeyword = Factory.Token(EatIdentifier("catch"));
|
||||
var catchBody = ParseStatements();
|
||||
var endKeyword = Factory.Token(EatIdentifier("end"));
|
||||
return Factory.TryCatchStatement(tryKeyword, tryBody, catchKeyword, catchBody, endKeyword);
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "end")
|
||||
{
|
||||
var endKeyword = Factory.Token(EatIdentifier("end"));
|
||||
return Factory.TryCatchStatement(tryKeyword, tryBody, endKeyword);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException($"Unexpected token {CurrentToken.PureToken} while parsing try/catch statement at {CurrentToken.PureToken.Position}.");
|
||||
}
|
||||
}
|
||||
|
||||
public StatementNode ParseStatementCore()
|
||||
{
|
||||
if (CurrentToken.Kind == TokenKind.Identifier)
|
||||
{
|
||||
if (CurrentToken.PureToken.LiteralText == "function")
|
||||
{
|
||||
return ParseFunctionDeclaration();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "classdef")
|
||||
{
|
||||
return ParseClassDeclaration();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "switch")
|
||||
{
|
||||
return ParseSwitchStatement();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "while")
|
||||
{
|
||||
return ParseWhileStatement();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "if")
|
||||
{
|
||||
return ParseIfStatement();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "case")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "else")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "elseif")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "end")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "for")
|
||||
{
|
||||
return ParseForStatement();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "try")
|
||||
{
|
||||
return ParseTryCatchStatement();
|
||||
}
|
||||
else if (CurrentToken.PureToken.LiteralText == "catch")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ParseExpressionStatement();
|
||||
}
|
||||
|
||||
if (CurrentToken.Kind == TokenKind.OpeningSquareBracket)
|
||||
{
|
||||
return ParseExpressionStatement();
|
||||
}
|
||||
|
||||
if (CurrentToken.Kind == TokenKind.Semicolon)
|
||||
{
|
||||
return Factory.ExpressionStatement(Factory.EmptyExpression(), Factory.Token(EatToken()));
|
||||
}
|
||||
throw new ParsingException($"Unexpected token: \"{CurrentToken.PureToken}\" at {CurrentToken.PureToken.Position}");
|
||||
}
|
||||
|
||||
private StatementListNode ParseStatements()
|
||||
{
|
||||
var statements = new List<SyntaxNode>();
|
||||
while (CurrentToken.PureToken.Kind != TokenKind.EndOfFile)
|
||||
{
|
||||
var node = ParseStatement();
|
||||
if (node == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
statements.Add(node);
|
||||
}
|
||||
|
||||
return Factory.StatementList(statements);
|
||||
}
|
||||
|
||||
private StatementListNode ParseFile()
|
||||
{
|
||||
return ParseStatements();
|
||||
}
|
||||
|
||||
public StatementListNode Parse()
|
||||
{
|
||||
return ParseFile();
|
||||
var lexer = new Internal.MLexerGreen(_window);
|
||||
var tokens = lexer.ParseAll();
|
||||
var parser = new Internal.MParserGreen(tokens, new Internal.SyntaxFactory());
|
||||
var green = parser.ParseFile();
|
||||
return new FileSyntaxNode(null, green);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lexer\Lexer.csproj" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Lexer
|
||||
namespace Parser
|
||||
{
|
||||
public class ParsingException : Exception
|
||||
{
|
14
Parser/Position.cs
Normal file
14
Parser/Position.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace Parser
|
||||
{
|
||||
public struct Position
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public int Line { get; set; }
|
||||
public int Column { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"line {Line}, column {Column} of {FileName}.";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,592 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lexer;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
public class SyntaxFactory
|
||||
{
|
||||
private static List<SyntaxNode> RemoveNulls(List<SyntaxNode> children)
|
||||
{
|
||||
return children.Where(x => x != null).ToList();
|
||||
}
|
||||
|
||||
public FunctionDeclarationNode FunctionDeclaration(
|
||||
TokenNode functionKeyword,
|
||||
FunctionOutputDescriptionNode outputDescription,
|
||||
TokenNode name,
|
||||
FunctionInputDescriptionNode inputDescription,
|
||||
StatementListNode body,
|
||||
TokenNode end,
|
||||
TokenNode semicolonOrComma = null)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
functionKeyword,
|
||||
outputDescription,
|
||||
name,
|
||||
inputDescription,
|
||||
body,
|
||||
end,
|
||||
semicolonOrComma
|
||||
};
|
||||
return new FunctionDeclarationNode(
|
||||
RemoveNulls(children),
|
||||
functionKeyword,
|
||||
outputDescription,
|
||||
name,
|
||||
inputDescription,
|
||||
body,
|
||||
end,
|
||||
semicolonOrComma);
|
||||
}
|
||||
|
||||
public FunctionOutputDescriptionNode FunctionOutputDescription(
|
||||
List<SyntaxNode> nodes,
|
||||
TokenNode equalitySign)
|
||||
{
|
||||
var children = new List<SyntaxNode>(nodes);
|
||||
children.Add(equalitySign);
|
||||
return new FunctionOutputDescriptionNode(
|
||||
children,
|
||||
children
|
||||
.Where(node => node is TokenNode tokenNode && tokenNode.Token.Kind == TokenKind.Identifier)
|
||||
.Select(node => (TokenNode)node)
|
||||
.ToList(),
|
||||
equalitySign
|
||||
);
|
||||
}
|
||||
|
||||
public ParameterListNode ParameterList(List<SyntaxNode> nodes)
|
||||
{
|
||||
return new ParameterListNode(
|
||||
nodes,
|
||||
nodes
|
||||
.Where(
|
||||
node => node is TokenNode tokenNode && tokenNode.Token.Kind != TokenKind.Comma
|
||||
)
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public StatementListNode StatementList(List<SyntaxNode> nodes)
|
||||
{
|
||||
return new StatementListNode(nodes);
|
||||
}
|
||||
|
||||
public FunctionInputDescriptionNode FunctionInputDescription(
|
||||
TokenNode openingBracket,
|
||||
ParameterListNode parameterList,
|
||||
TokenNode closingBracket)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
openingBracket,
|
||||
parameterList,
|
||||
closingBracket
|
||||
};
|
||||
return new FunctionInputDescriptionNode(children, openingBracket, parameterList, closingBracket);
|
||||
}
|
||||
|
||||
public TokenNode Token(Token token)
|
||||
{
|
||||
return new TokenNode(token);
|
||||
}
|
||||
|
||||
public SwitchStatementNode SwitchStatement(
|
||||
TokenNode switchKeyword,
|
||||
ExpressionNode switchExpression,
|
||||
List<SwitchCaseNode> cases,
|
||||
TokenNode endKeyword,
|
||||
List<TokenNode> optionalCommasAfterExpression,
|
||||
TokenNode semicolonOrComma = null)
|
||||
{
|
||||
var children = new List<SyntaxNode> { switchKeyword, switchExpression };
|
||||
children.AddRange(optionalCommasAfterExpression);
|
||||
children.AddRange(cases);
|
||||
children.Add(endKeyword);
|
||||
children.Add(semicolonOrComma);
|
||||
|
||||
return new SwitchStatementNode(
|
||||
RemoveNulls(children),
|
||||
switchKeyword,
|
||||
switchExpression,
|
||||
cases,
|
||||
endKeyword,
|
||||
semicolonOrComma,
|
||||
optionalCommasAfterExpression);
|
||||
}
|
||||
|
||||
public SwitchCaseNode SwitchCase(
|
||||
TokenNode caseKeyword,
|
||||
ExpressionNode caseIdentifier,
|
||||
StatementListNode statementList,
|
||||
List<TokenNode> optionalCommasAfterIdentifier)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
caseKeyword,
|
||||
caseIdentifier
|
||||
};
|
||||
children.AddRange(optionalCommasAfterIdentifier);
|
||||
children.Add(statementList);
|
||||
return new SwitchCaseNode(
|
||||
RemoveNulls(children),
|
||||
caseKeyword,
|
||||
caseIdentifier,
|
||||
statementList,
|
||||
optionalCommasAfterIdentifier);
|
||||
}
|
||||
|
||||
public AssignmentExpressionNode AssignmentExpression(
|
||||
ExpressionNode lhs,
|
||||
TokenNode assignmentSign,
|
||||
ExpressionNode rhs)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
lhs,
|
||||
assignmentSign,
|
||||
rhs
|
||||
};
|
||||
return new AssignmentExpressionNode(children, lhs, assignmentSign, rhs);
|
||||
}
|
||||
|
||||
public UnaryPrefixOperationExpressionNode UnaryPrefixOperationExpression(
|
||||
TokenNode operation,
|
||||
ExpressionNode operand)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
operation,
|
||||
operand
|
||||
};
|
||||
return new UnaryPrefixOperationExpressionNode(children, operation, operand);
|
||||
}
|
||||
|
||||
public UnaryPostfixOperationExpressionNode UnaryPostfixOperationExpression(
|
||||
ExpressionNode operand,
|
||||
TokenNode operation)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
operand,
|
||||
operation
|
||||
};
|
||||
return new UnaryPostfixOperationExpressionNode(children, operand, operation);
|
||||
}
|
||||
|
||||
public BinaryOperationExpressionNode BinaryOperationExpression(
|
||||
ExpressionNode lhs,
|
||||
TokenNode operation,
|
||||
ExpressionNode rhs)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
lhs,
|
||||
operation,
|
||||
rhs
|
||||
};
|
||||
return new BinaryOperationExpressionNode(children, lhs, operation, rhs);
|
||||
}
|
||||
|
||||
public IdentifierNameNode IdentifierName(
|
||||
Token identifier)
|
||||
{
|
||||
return new IdentifierNameNode(identifier);
|
||||
}
|
||||
|
||||
public NumberLiteralNode NumberLiteral(
|
||||
Token numberLiteral)
|
||||
{
|
||||
return new NumberLiteralNode(numberLiteral);
|
||||
}
|
||||
|
||||
public StringLiteralNode StringLiteral(
|
||||
Token stringLiteral)
|
||||
{
|
||||
return new StringLiteralNode(stringLiteral);
|
||||
}
|
||||
|
||||
public DoubleQuotedStringLiteralNode DoubleQuotedStringLiteral(
|
||||
Token stringLiteral)
|
||||
{
|
||||
return new DoubleQuotedStringLiteralNode(stringLiteral);
|
||||
}
|
||||
|
||||
public UnquotedStringLiteralNode UnquotedStringLiteral(
|
||||
Token stringLiteral)
|
||||
{
|
||||
return new UnquotedStringLiteralNode(stringLiteral);
|
||||
}
|
||||
|
||||
public ExpressionStatementNode ExpressionStatement(ExpressionNode expression, TokenNode semicolonOrComma)
|
||||
{
|
||||
var children = new List<SyntaxNode> {expression, semicolonOrComma};
|
||||
return new ExpressionStatementNode(
|
||||
RemoveNulls(children),
|
||||
expression,
|
||||
semicolonOrComma);
|
||||
}
|
||||
|
||||
public CellArrayElementAccessExpressionNode CellArrayElementAccessExpression(
|
||||
ExpressionNode cellArray,
|
||||
TokenNode openingBrace,
|
||||
ArrayElementListNode indices,
|
||||
TokenNode closingBrace)
|
||||
{
|
||||
var children = new List<SyntaxNode> {cellArray, openingBrace, indices, closingBrace};
|
||||
return new CellArrayElementAccessExpressionNode(
|
||||
children,
|
||||
cellArray,
|
||||
openingBrace,
|
||||
indices,
|
||||
closingBrace);
|
||||
}
|
||||
|
||||
public FunctionCallExpressionNode FunctionCallExpression(
|
||||
ExpressionNode functionName,
|
||||
TokenNode openingBracket,
|
||||
FunctionCallParameterListNode parameters,
|
||||
TokenNode closingBracket)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
functionName,
|
||||
openingBracket,
|
||||
parameters,
|
||||
closingBracket
|
||||
};
|
||||
return new FunctionCallExpressionNode(
|
||||
children,
|
||||
functionName,
|
||||
openingBracket,
|
||||
parameters,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public FunctionCallParameterListNode FunctionCallParameterList(List<SyntaxNode> nodes)
|
||||
{
|
||||
return new FunctionCallParameterListNode(
|
||||
nodes,
|
||||
nodes
|
||||
.OfType<ExpressionNode>()
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public ArrayElementListNode ArrayElementList(List<SyntaxNode> nodes)
|
||||
{
|
||||
return new ArrayElementListNode(
|
||||
nodes,
|
||||
nodes
|
||||
.OfType<ExpressionNode>()
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public CompoundNameNode CompoundName(List<SyntaxNode> nodes)
|
||||
{
|
||||
return new CompoundNameNode(
|
||||
nodes,
|
||||
nodes
|
||||
.OfType<IdentifierNameNode>()
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public ArrayLiteralExpressionNode ArrayLiteralExpression(
|
||||
TokenNode openingSquareBracket,
|
||||
ArrayElementListNode elements,
|
||||
TokenNode closingSquareBracket)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
openingSquareBracket,
|
||||
elements,
|
||||
closingSquareBracket
|
||||
};
|
||||
return new ArrayLiteralExpressionNode(
|
||||
children,
|
||||
openingSquareBracket,
|
||||
elements,
|
||||
closingSquareBracket);
|
||||
}
|
||||
|
||||
public CellArrayLiteralExpressionNode CellArrayLiteralExpression(
|
||||
TokenNode openingBrace,
|
||||
ArrayElementListNode elements,
|
||||
TokenNode closingBrace)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
openingBrace,
|
||||
elements,
|
||||
closingBrace
|
||||
};
|
||||
return new CellArrayLiteralExpressionNode(
|
||||
children,
|
||||
openingBrace,
|
||||
elements,
|
||||
closingBrace);
|
||||
}
|
||||
|
||||
public EmptyExpressionNode EmptyExpression()
|
||||
{
|
||||
return new EmptyExpressionNode();
|
||||
}
|
||||
|
||||
public MemberAccessNode MemberAccess(
|
||||
SyntaxNode leftOperand,
|
||||
TokenNode dot,
|
||||
SyntaxNode rightOperand)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
leftOperand,
|
||||
dot,
|
||||
rightOperand
|
||||
};
|
||||
return new MemberAccessNode(
|
||||
children,
|
||||
leftOperand,
|
||||
dot,
|
||||
rightOperand);
|
||||
}
|
||||
|
||||
public WhileStatementNode WhileStatement(
|
||||
TokenNode whileKeyword,
|
||||
ExpressionNode condition,
|
||||
List<TokenNode> optionalCommasAfterCondition,
|
||||
StatementListNode body,
|
||||
TokenNode end,
|
||||
TokenNode semicolonOrComma)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
whileKeyword,
|
||||
condition,
|
||||
};
|
||||
children.AddRange(optionalCommasAfterCondition);
|
||||
children.Add(body);
|
||||
children.Add(end);
|
||||
children.Add(semicolonOrComma);
|
||||
return new WhileStatementNode(
|
||||
RemoveNulls(children),
|
||||
whileKeyword,
|
||||
condition,
|
||||
optionalCommasAfterCondition,
|
||||
body,
|
||||
end,
|
||||
semicolonOrComma);
|
||||
}
|
||||
|
||||
public StatementNode AppendSemicolonOrComma(StatementNode statement, TokenNode semicolonOrComma)
|
||||
{
|
||||
statement.SemicolonOrComma = semicolonOrComma;
|
||||
statement.Children.Add(semicolonOrComma);
|
||||
statement.Children[statement.Children.Count - 1].Parent = statement;
|
||||
return statement;
|
||||
}
|
||||
|
||||
public IfStatementNode IfStatement(
|
||||
TokenNode ifKeyword,
|
||||
ExpressionNode condition,
|
||||
List<TokenNode> optionalCommasAfterCondition,
|
||||
StatementListNode body,
|
||||
TokenNode elseKeyword,
|
||||
StatementListNode elseBody,
|
||||
TokenNode endKeyword,
|
||||
TokenNode possibleSemicolonOrComma)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
ifKeyword,
|
||||
condition
|
||||
};
|
||||
children.AddRange(optionalCommasAfterCondition);
|
||||
children.Add(body);
|
||||
children.Add(elseKeyword);
|
||||
children.Add(elseBody);
|
||||
children.Add(endKeyword);
|
||||
children.Add(possibleSemicolonOrComma);
|
||||
|
||||
return new IfStatementNode(
|
||||
RemoveNulls(children),
|
||||
ifKeyword,
|
||||
condition,
|
||||
optionalCommasAfterCondition,
|
||||
body,
|
||||
elseKeyword,
|
||||
elseBody,
|
||||
endKeyword,
|
||||
possibleSemicolonOrComma);
|
||||
}
|
||||
|
||||
public ParenthesizedExpressionNode ParenthesizedExpression(
|
||||
TokenNode openParen,
|
||||
ExpressionNode expression,
|
||||
TokenNode closeParen)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
openParen,
|
||||
expression,
|
||||
closeParen
|
||||
};
|
||||
return new ParenthesizedExpressionNode(
|
||||
children,
|
||||
openParen,
|
||||
expression,
|
||||
closeParen);
|
||||
}
|
||||
|
||||
public ForStatementNode ForStatement(
|
||||
TokenNode forKeyword,
|
||||
AssignmentExpressionNode forAssignment,
|
||||
StatementListNode body,
|
||||
TokenNode endKeyword,
|
||||
List<TokenNode> optionalCommasAfterAssignment)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
forKeyword,
|
||||
forAssignment,
|
||||
};
|
||||
children.AddRange(optionalCommasAfterAssignment);
|
||||
children.Add(body);
|
||||
children.Add(endKeyword);
|
||||
return new ForStatementNode(
|
||||
RemoveNulls(children),
|
||||
forKeyword,
|
||||
forAssignment,
|
||||
body,
|
||||
endKeyword,
|
||||
optionalCommasAfterAssignment);
|
||||
}
|
||||
|
||||
public IndirectMemberAccessNode IndirectMemberAccess(
|
||||
TokenNode openingBracket,
|
||||
ExpressionNode indirectMemberName,
|
||||
TokenNode closingBracket)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
openingBracket,
|
||||
indirectMemberName,
|
||||
closingBracket
|
||||
};
|
||||
return new IndirectMemberAccessNode(
|
||||
children,
|
||||
openingBracket,
|
||||
indirectMemberName,
|
||||
closingBracket);
|
||||
}
|
||||
|
||||
public NamedFunctionHandleNode NamedFunctionHandle(
|
||||
TokenNode atSign,
|
||||
CompoundNameNode functionName)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
atSign,
|
||||
functionName
|
||||
};
|
||||
return new NamedFunctionHandleNode(
|
||||
children,
|
||||
atSign,
|
||||
functionName);
|
||||
}
|
||||
|
||||
public LambdaNode Lambda(
|
||||
TokenNode atSign,
|
||||
FunctionInputDescriptionNode input,
|
||||
ExpressionNode body)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
atSign,
|
||||
input,
|
||||
body
|
||||
};
|
||||
return new LambdaNode(
|
||||
children,
|
||||
atSign,
|
||||
input,
|
||||
body);
|
||||
}
|
||||
|
||||
public TryCatchStatementNode TryCatchStatement(
|
||||
TokenNode tryKeyword,
|
||||
StatementListNode tryBody,
|
||||
TokenNode catchKeyword,
|
||||
StatementListNode catchBody,
|
||||
TokenNode endKeyword)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
tryKeyword,
|
||||
tryBody,
|
||||
catchKeyword,
|
||||
catchBody,
|
||||
endKeyword
|
||||
};
|
||||
return new TryCatchStatementNode(
|
||||
children,
|
||||
tryKeyword,
|
||||
tryBody,
|
||||
catchKeyword,
|
||||
catchBody,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public TryCatchStatementNode TryCatchStatement(
|
||||
TokenNode tryKeyword,
|
||||
StatementListNode tryBody,
|
||||
TokenNode endKeyword)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
tryKeyword,
|
||||
tryBody,
|
||||
endKeyword
|
||||
};
|
||||
return new TryCatchStatementNode(
|
||||
children,
|
||||
tryKeyword,
|
||||
tryBody,
|
||||
null,
|
||||
null,
|
||||
endKeyword);
|
||||
}
|
||||
|
||||
public CommandExpressionNode CommandExpression(
|
||||
IdentifierNameNode identifierName,
|
||||
List<UnquotedStringLiteralNode> arguments)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
identifierName
|
||||
};
|
||||
children.AddRange(arguments);
|
||||
return new CommandExpressionNode(
|
||||
children,
|
||||
identifierName,
|
||||
arguments);
|
||||
}
|
||||
|
||||
public BaseClassInvokationNode BaseClassInvokation(
|
||||
IdentifierNameNode methodName,
|
||||
TokenNode atToken,
|
||||
ExpressionNode baseClassNameAndArguments)
|
||||
{
|
||||
var children = new List<SyntaxNode>
|
||||
{
|
||||
methodName,
|
||||
atToken,
|
||||
baseClassNameAndArguments
|
||||
};
|
||||
return new BaseClassInvokationNode(
|
||||
children,
|
||||
methodName,
|
||||
atToken,
|
||||
baseClassNameAndArguments);
|
||||
}
|
||||
}
|
||||
}
|
2631
Parser/SyntaxNode.Generated.cs
Normal file
2631
Parser/SyntaxNode.Generated.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,750 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Lexer;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
public class SyntaxNode
|
||||
public abstract class SyntaxNode
|
||||
{
|
||||
public SyntaxNode Parent { get; set; }
|
||||
public List<SyntaxNode> Children { get; }
|
||||
|
||||
public virtual IEnumerable<Token> ChildTokens =>
|
||||
Children.SelectMany(c => c.ChildTokens);
|
||||
|
||||
public SyntaxNode(List<SyntaxNode> children)
|
||||
private readonly SyntaxNode _parent;
|
||||
internal readonly Internal.GreenNode _green;
|
||||
internal SyntaxNode(SyntaxNode parent, Internal.GreenNode green)
|
||||
{
|
||||
Children = children;
|
||||
if (children != null)
|
||||
{
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.Parent = this;
|
||||
}
|
||||
}
|
||||
_parent = parent;
|
||||
_green = green;
|
||||
}
|
||||
|
||||
public virtual string FullText =>
|
||||
string.Join("", Children.Select(c => c.FullText));
|
||||
public TokenKind Kind => _green.Kind;
|
||||
|
||||
public List<Trivia> TrailingTrivia
|
||||
|
||||
public SyntaxNode Parent => _parent;
|
||||
|
||||
public ChildNodesAndTokensList GetChildNodesAndTokens()
|
||||
{
|
||||
return new ChildNodesAndTokensList(this);
|
||||
}
|
||||
|
||||
internal abstract SyntaxNode GetNode(int index);
|
||||
|
||||
internal SyntaxNode GetRed(ref SyntaxNode field, int slot)
|
||||
{
|
||||
if (field == null)
|
||||
{
|
||||
var green = _green.GetSlot(slot);
|
||||
if (green != null)
|
||||
{
|
||||
field = green.CreateRed(this);
|
||||
}
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
|
||||
public virtual string Text => _green.Text;
|
||||
|
||||
public virtual string FullText => _green.FullText;
|
||||
|
||||
public virtual IReadOnlyList<SyntaxTrivia> LeadingTrivia
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChildTokens.Any())
|
||||
{
|
||||
return ChildTokens.Last().TrailingTrivia;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new List<Trivia>();
|
||||
}
|
||||
var p = Parent;
|
||||
return _green.LeadingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IReadOnlyList<SyntaxTrivia> TrailingTrivia
|
||||
{
|
||||
get
|
||||
{
|
||||
var p = Parent;
|
||||
return _green.TrailingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void Accept(SyntaxVisitor visitor);
|
||||
}
|
||||
|
||||
public class TokenNode : SyntaxNode
|
||||
|
||||
public abstract class StatementSyntaxNode : SyntaxNode
|
||||
{
|
||||
public Token Token { get; }
|
||||
|
||||
public TokenNode(Token token)
|
||||
: base(null)
|
||||
internal StatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green) : base(parent, green)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public override string FullText => Token.FullText;
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield return Token; }
|
||||
}
|
||||
}
|
||||
|
||||
public class OutputIdentifierNode : SyntaxNode
|
||||
{
|
||||
public OutputIdentifierNode(List<SyntaxNode> children) : base(children)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class FunctionOutputDescriptionNode : SyntaxNode
|
||||
{
|
||||
public List<TokenNode> Outputs { get; }
|
||||
public TokenNode EqualitySign { get; }
|
||||
|
||||
public FunctionOutputDescriptionNode(
|
||||
List<SyntaxNode> children,
|
||||
List<TokenNode> outputs,
|
||||
TokenNode equalitySign) : base(children)
|
||||
{
|
||||
Outputs = outputs;
|
||||
EqualitySign = equalitySign;
|
||||
}
|
||||
}
|
||||
|
||||
public class FunctionInputDescriptionNode : SyntaxNode
|
||||
{
|
||||
public TokenNode OpeningBracket { get; }
|
||||
public ParameterListNode Parameters { get; }
|
||||
public TokenNode ClosingBracket { get; }
|
||||
public FunctionInputDescriptionNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode openingBracket,
|
||||
ParameterListNode parameters,
|
||||
TokenNode closingBracket) : base(children)
|
||||
{
|
||||
OpeningBracket = openingBracket;
|
||||
Parameters = parameters;
|
||||
ClosingBracket = closingBracket;
|
||||
}
|
||||
}
|
||||
|
||||
public class FunctionDeclarationNode : StatementNode
|
||||
{
|
||||
public TokenNode Token { get; }
|
||||
public FunctionOutputDescriptionNode OutputDescription { get; }
|
||||
public TokenNode Name { get; }
|
||||
public FunctionInputDescriptionNode InputDescription { get; }
|
||||
public StatementListNode Body { get; }
|
||||
public TokenNode End { get; }
|
||||
|
||||
public FunctionDeclarationNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode token,
|
||||
FunctionOutputDescriptionNode outputDescription,
|
||||
TokenNode name,
|
||||
FunctionInputDescriptionNode inputDescription,
|
||||
StatementListNode body,
|
||||
TokenNode end,
|
||||
TokenNode semicolonOrComma
|
||||
) : base(children, semicolonOrComma)
|
||||
{
|
||||
Token = token;
|
||||
OutputDescription = outputDescription;
|
||||
Name = name;
|
||||
InputDescription = inputDescription;
|
||||
Body = body;
|
||||
End = end;
|
||||
}
|
||||
}
|
||||
|
||||
public class StatementListNode : SyntaxNode
|
||||
{
|
||||
public List<SyntaxNode> Statements => Children;
|
||||
|
||||
public StatementListNode(List<SyntaxNode> children) : base(children)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ParameterListNode : SyntaxNode
|
||||
{
|
||||
public List<SyntaxNode> Parameters { get; }
|
||||
|
||||
public ParameterListNode(List<SyntaxNode> children, List<SyntaxNode> parameters) : base(children)
|
||||
{
|
||||
Parameters = parameters;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpressionNode : SyntaxNode
|
||||
{
|
||||
public ExpressionNode(List<SyntaxNode> children) : base(children)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class AssignmentExpressionNode : ExpressionNode
|
||||
{
|
||||
public ExpressionNode Lhs { get; }
|
||||
public TokenNode Assignment { get; }
|
||||
public ExpressionNode Rhs { get; }
|
||||
|
||||
public AssignmentExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
ExpressionNode lhs,
|
||||
TokenNode assignment,
|
||||
ExpressionNode rhs) : base(children)
|
||||
{
|
||||
Lhs = lhs;
|
||||
Assignment = assignment;
|
||||
Rhs = rhs;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnaryPrefixOperationExpressionNode : ExpressionNode
|
||||
{
|
||||
public TokenNode Operation { get; }
|
||||
public ExpressionNode Operand { get; }
|
||||
|
||||
public UnaryPrefixOperationExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode operation,
|
||||
ExpressionNode operand) : base(children)
|
||||
{
|
||||
Operation = operation;
|
||||
Operand = operand;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnaryPostfixOperationExpressionNode : ExpressionNode
|
||||
{
|
||||
public ExpressionNode Operand { get; }
|
||||
public TokenNode Operation { get; }
|
||||
public UnaryPostfixOperationExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
ExpressionNode operand,
|
||||
TokenNode operation) : base(children)
|
||||
{
|
||||
Operand = operand;
|
||||
Operation = operation;
|
||||
}
|
||||
}
|
||||
|
||||
public class BinaryOperationExpressionNode : ExpressionNode
|
||||
{
|
||||
public ExpressionNode Lhs { get; }
|
||||
public TokenNode Operation { get; }
|
||||
public ExpressionNode Rhs { get; }
|
||||
|
||||
public BinaryOperationExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
ExpressionNode lhs,
|
||||
TokenNode operation,
|
||||
ExpressionNode rhs) : base(children)
|
||||
{
|
||||
Lhs = lhs;
|
||||
Operation = operation;
|
||||
Rhs = rhs;
|
||||
}
|
||||
}
|
||||
|
||||
public class SwitchStatementNode : StatementNode
|
||||
{
|
||||
public TokenNode SwitchKeyword { get; }
|
||||
public ExpressionNode SwitchExpression { get; }
|
||||
public List<TokenNode> OptionalCommasAfterExpression { get; }
|
||||
public List<SwitchCaseNode> Cases { get; }
|
||||
public TokenNode EndKeyword { get; }
|
||||
|
||||
public SwitchStatementNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode switchKeyword,
|
||||
ExpressionNode switchExpression,
|
||||
List<SwitchCaseNode> cases,
|
||||
TokenNode endKeyword,
|
||||
TokenNode semicolonOrComma,
|
||||
List<TokenNode> optionalCommasAfterExpression = null
|
||||
) : base(children, semicolonOrComma)
|
||||
{
|
||||
SwitchKeyword = switchKeyword;
|
||||
SwitchExpression = switchExpression;
|
||||
OptionalCommasAfterExpression = optionalCommasAfterExpression;
|
||||
Cases = cases;
|
||||
EndKeyword = endKeyword;
|
||||
}
|
||||
}
|
||||
|
||||
public class SwitchCaseNode : SyntaxNode
|
||||
{
|
||||
public TokenNode CaseKeyword { get; }
|
||||
public ExpressionNode CaseIdentifier { get; }
|
||||
public List<TokenNode> OptionalCommasAfterIdentifier { get; }
|
||||
public StatementListNode StatementList { get; }
|
||||
|
||||
public SwitchCaseNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode caseKeyword,
|
||||
ExpressionNode caseIdentifier,
|
||||
StatementListNode statementList,
|
||||
List<TokenNode> optionalCommasAfterIdentifier = null
|
||||
) : base(children)
|
||||
{
|
||||
CaseKeyword = caseKeyword;
|
||||
CaseIdentifier = caseIdentifier;
|
||||
StatementList = statementList;
|
||||
OptionalCommasAfterIdentifier = optionalCommasAfterIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentifierNameNode : ExpressionNode
|
||||
{
|
||||
public Token Token { get; }
|
||||
|
||||
public IdentifierNameNode(Token token)
|
||||
: base(null)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public override string FullText => Token.FullText;
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield return Token; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class NumberLiteralNode : ExpressionNode
|
||||
{
|
||||
public Token Token { get; }
|
||||
|
||||
public NumberLiteralNode(Token token) : base(null)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public override string FullText => Token.FullText;
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield return Token; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StringLiteralNode : ExpressionNode
|
||||
{
|
||||
public Token Token { get; }
|
||||
|
||||
public StringLiteralNode(Token token) : base(null)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public override string FullText => Token.FullText;
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield return Token; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DoubleQuotedStringLiteralNode : ExpressionNode
|
||||
{
|
||||
public Token Token { get; }
|
||||
|
||||
public DoubleQuotedStringLiteralNode(Token token) : base(null)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public override string FullText => Token.FullText;
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield return Token; }
|
||||
}
|
||||
}
|
||||
|
||||
public class UnquotedStringLiteralNode : ExpressionNode
|
||||
{
|
||||
public Token Token { get; }
|
||||
|
||||
public UnquotedStringLiteralNode(Token token) : base(null)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public override string FullText => Token.FullText;
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield return Token; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StatementNode : SyntaxNode
|
||||
{
|
||||
public TokenNode SemicolonOrComma { get; set; }
|
||||
|
||||
public StatementNode(List<SyntaxNode> children, TokenNode semicolonOrComma = null) : base(children)
|
||||
{
|
||||
SemicolonOrComma = semicolonOrComma;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpressionStatementNode : StatementNode
|
||||
{
|
||||
public ExpressionNode Expression { get; }
|
||||
|
||||
public ExpressionStatementNode(List<SyntaxNode> children, ExpressionNode expression, TokenNode semicolonOrComma)
|
||||
: base(children, semicolonOrComma)
|
||||
{
|
||||
Expression = expression;
|
||||
}
|
||||
}
|
||||
|
||||
public class CellArrayElementAccessExpressionNode : ExpressionNode
|
||||
{
|
||||
public ExpressionNode CellArray { get; }
|
||||
public TokenNode OpeningBrace { get; }
|
||||
public ArrayElementListNode Indices { get; }
|
||||
public TokenNode ClosingBrace { get; }
|
||||
|
||||
public CellArrayElementAccessExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
ExpressionNode cellArray,
|
||||
TokenNode openingBrace,
|
||||
ArrayElementListNode indices,
|
||||
TokenNode closingBrace) : base(children)
|
||||
{
|
||||
CellArray = cellArray;
|
||||
OpeningBrace = openingBrace;
|
||||
Indices = indices;
|
||||
ClosingBrace = closingBrace;
|
||||
}
|
||||
}
|
||||
|
||||
public class FunctionCallExpressionNode : ExpressionNode
|
||||
{
|
||||
public ExpressionNode FunctionName { get; }
|
||||
public TokenNode OpeningBracket { get; }
|
||||
public FunctionCallParameterListNode Parameters { get; }
|
||||
public TokenNode ClosingBracket { get; }
|
||||
|
||||
public FunctionCallExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
ExpressionNode functionName,
|
||||
TokenNode openingBracket,
|
||||
FunctionCallParameterListNode parameters,
|
||||
TokenNode closingBracket) : base(children)
|
||||
{
|
||||
FunctionName = functionName;
|
||||
OpeningBracket = openingBracket;
|
||||
Parameters = parameters;
|
||||
ClosingBracket = closingBracket;
|
||||
}
|
||||
}
|
||||
|
||||
public class FunctionCallParameterListNode : SyntaxNode
|
||||
{
|
||||
public List<ExpressionNode> Parameters;
|
||||
|
||||
public FunctionCallParameterListNode(
|
||||
List<SyntaxNode> children,
|
||||
List<ExpressionNode> parameters) : base(children)
|
||||
{
|
||||
Parameters = parameters;
|
||||
}
|
||||
}
|
||||
|
||||
public class ArrayElementListNode : SyntaxNode
|
||||
public abstract class ExpressionSyntaxNode : SyntaxNode
|
||||
{
|
||||
public List<ExpressionNode> Elements;
|
||||
|
||||
public ArrayElementListNode(
|
||||
List<SyntaxNode> children,
|
||||
List<ExpressionNode> elements) : base(children)
|
||||
{
|
||||
Elements = elements;
|
||||
}
|
||||
}
|
||||
|
||||
public class ArrayLiteralExpressionNode : ExpressionNode
|
||||
{
|
||||
public TokenNode OpeningSquareBracket { get; }
|
||||
public ArrayElementListNode Elements { get; }
|
||||
public TokenNode ClosingSquareBracket { get; }
|
||||
|
||||
public ArrayLiteralExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode openingSquareBracket,
|
||||
ArrayElementListNode elements,
|
||||
TokenNode closingSquareBracket) : base(children)
|
||||
{
|
||||
OpeningSquareBracket = openingSquareBracket;
|
||||
Elements = elements;
|
||||
ClosingSquareBracket = closingSquareBracket;
|
||||
}
|
||||
}
|
||||
|
||||
public class CellArrayLiteralExpressionNode : ExpressionNode
|
||||
{
|
||||
public TokenNode OpeningBrace { get; }
|
||||
public ArrayElementListNode Elements { get; }
|
||||
public TokenNode ClosingBrace { get; }
|
||||
|
||||
public CellArrayLiteralExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode openingBrace,
|
||||
ArrayElementListNode elements,
|
||||
TokenNode closingBrace) : base(children)
|
||||
{
|
||||
OpeningBrace = openingBrace;
|
||||
Elements = elements;
|
||||
ClosingBrace = closingBrace;
|
||||
}
|
||||
}
|
||||
|
||||
public class EmptyExpressionNode : ExpressionNode
|
||||
{
|
||||
public EmptyExpressionNode() : base(null)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<Token> ChildTokens
|
||||
{
|
||||
get { yield break; }
|
||||
}
|
||||
|
||||
public override string FullText => "";
|
||||
}
|
||||
|
||||
public class CompoundNameNode : ExpressionNode
|
||||
{
|
||||
public List<IdentifierNameNode> Names;
|
||||
|
||||
public CompoundNameNode(
|
||||
List<SyntaxNode> children,
|
||||
List<IdentifierNameNode> names
|
||||
) : base(children)
|
||||
{
|
||||
Names = names;
|
||||
}
|
||||
}
|
||||
|
||||
public class MemberAccessNode : ExpressionNode
|
||||
{
|
||||
public SyntaxNode LeftOperand { get; }
|
||||
public TokenNode Dot { get; }
|
||||
public SyntaxNode RightOperand { get; }
|
||||
|
||||
public MemberAccessNode(
|
||||
List<SyntaxNode> children,
|
||||
SyntaxNode leftOperand,
|
||||
TokenNode dot,
|
||||
SyntaxNode rightOperand) : base(children)
|
||||
{
|
||||
LeftOperand = leftOperand;
|
||||
Dot = dot;
|
||||
RightOperand = rightOperand;
|
||||
}
|
||||
}
|
||||
|
||||
public class WhileStatementNode : StatementNode
|
||||
{
|
||||
public TokenNode WhileKeyword { get; }
|
||||
public ExpressionNode Condition { get; }
|
||||
public List<TokenNode> OptionalCommasAfterCondition { get; }
|
||||
public StatementListNode Body { get; }
|
||||
public TokenNode End { get; }
|
||||
|
||||
public WhileStatementNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode whileKeyword,
|
||||
ExpressionNode condition,
|
||||
List<TokenNode> optionalCommasAfterCondition,
|
||||
StatementListNode body,
|
||||
TokenNode end,
|
||||
TokenNode semicolonOrComma
|
||||
) : base(children, semicolonOrComma)
|
||||
{
|
||||
WhileKeyword = whileKeyword;
|
||||
Condition = condition;
|
||||
OptionalCommasAfterCondition = optionalCommasAfterCondition;
|
||||
Body = body;
|
||||
End = end;
|
||||
}
|
||||
}
|
||||
|
||||
public class IfStatementNode : StatementNode
|
||||
{
|
||||
public TokenNode IfKeyword { get; }
|
||||
public ExpressionNode Condition { get; }
|
||||
public List<TokenNode> OptionalCommasAfterCondition { get; }
|
||||
public StatementListNode Body { get; }
|
||||
public TokenNode ElseKeyword { get; }
|
||||
public StatementListNode ElseBody { get; }
|
||||
public TokenNode EndKeyword { get; }
|
||||
|
||||
public IfStatementNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode ifKeyword,
|
||||
ExpressionNode condition,
|
||||
List<TokenNode> optionalCommasAfterCondition,
|
||||
StatementListNode body,
|
||||
TokenNode elseKeyword,
|
||||
StatementListNode elseBody,
|
||||
TokenNode endKeyword,
|
||||
TokenNode possibleSemicolonOrComma
|
||||
) : base(children, possibleSemicolonOrComma)
|
||||
{
|
||||
IfKeyword = ifKeyword;
|
||||
Condition = condition;
|
||||
OptionalCommasAfterCondition = optionalCommasAfterCondition;
|
||||
Body = body;
|
||||
ElseKeyword = elseKeyword;
|
||||
ElseBody = elseBody;
|
||||
EndKeyword = endKeyword;
|
||||
}
|
||||
}
|
||||
|
||||
public class ParenthesizedExpressionNode : ExpressionNode
|
||||
{
|
||||
public TokenNode OpenParen { get; }
|
||||
public ExpressionNode Expression { get; }
|
||||
public TokenNode CloseParen { get; }
|
||||
|
||||
public ParenthesizedExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode openParen,
|
||||
ExpressionNode expression,
|
||||
TokenNode closeParen) : base(children)
|
||||
{
|
||||
OpenParen = openParen;
|
||||
Expression = expression;
|
||||
CloseParen = closeParen;
|
||||
}
|
||||
}
|
||||
|
||||
public class ForStatementNode : StatementNode
|
||||
{
|
||||
public TokenNode ForKeyword { get; }
|
||||
public AssignmentExpressionNode ForAssignment { get; }
|
||||
public List<TokenNode> OptionalCommasAfterAssignment { get; }
|
||||
public StatementListNode Body { get; }
|
||||
public TokenNode EndKeyword { get; }
|
||||
|
||||
public ForStatementNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode forKeyword,
|
||||
AssignmentExpressionNode forAssignment,
|
||||
StatementListNode body,
|
||||
TokenNode endKeyword,
|
||||
List<TokenNode> optionalCommasAfterAssignment
|
||||
) : base(children)
|
||||
{
|
||||
ForKeyword = forKeyword;
|
||||
ForAssignment = forAssignment;
|
||||
Body = body;
|
||||
EndKeyword = endKeyword;
|
||||
OptionalCommasAfterAssignment = optionalCommasAfterAssignment;
|
||||
}
|
||||
}
|
||||
|
||||
public class IndirectMemberAccessNode : ExpressionNode
|
||||
{
|
||||
public TokenNode OpeningBracket { get; }
|
||||
public ExpressionNode IndirectMemberName { get; }
|
||||
public TokenNode ClosingBracket { get; }
|
||||
|
||||
public IndirectMemberAccessNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode openingBracket,
|
||||
ExpressionNode indirectMemberName,
|
||||
TokenNode closingBracket) : base(children)
|
||||
{
|
||||
OpeningBracket = openingBracket;
|
||||
IndirectMemberName = indirectMemberName;
|
||||
ClosingBracket = closingBracket;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class FunctionHandleNode : ExpressionNode
|
||||
{
|
||||
protected FunctionHandleNode(
|
||||
List<SyntaxNode> children) : base(children)
|
||||
internal ExpressionSyntaxNode(SyntaxNode parent, Internal.GreenNode green) : base(parent, green)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class NamedFunctionHandleNode : FunctionHandleNode
|
||||
public abstract class FunctionHandleSyntaxNode : ExpressionSyntaxNode
|
||||
{
|
||||
public TokenNode AtSign { get; }
|
||||
public CompoundNameNode FunctionName { get; }
|
||||
|
||||
public NamedFunctionHandleNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode atSign,
|
||||
CompoundNameNode functionName) : base(children)
|
||||
internal FunctionHandleSyntaxNode(SyntaxNode parent, Internal.GreenNode green) : base(parent, green)
|
||||
{
|
||||
AtSign = atSign;
|
||||
FunctionName = functionName;
|
||||
}
|
||||
}
|
||||
|
||||
public class LambdaNode : FunctionHandleNode
|
||||
public abstract class MethodDeclarationSyntaxNode : StatementSyntaxNode
|
||||
{
|
||||
public TokenNode AtSign { get; }
|
||||
public FunctionInputDescriptionNode Input { get; }
|
||||
public ExpressionNode Body { get; }
|
||||
|
||||
public LambdaNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode atSign,
|
||||
FunctionInputDescriptionNode input,
|
||||
ExpressionNode body) : base(children)
|
||||
internal MethodDeclarationSyntaxNode(SyntaxNode parent, Internal.GreenNode green) : base(parent, green)
|
||||
{
|
||||
AtSign = atSign;
|
||||
Input = input;
|
||||
Body = body;
|
||||
}
|
||||
}
|
||||
|
||||
public class TryCatchStatementNode : StatementNode
|
||||
{
|
||||
public TokenNode TryKeyword { get; }
|
||||
public StatementListNode TryBody { get; }
|
||||
public TokenNode CatchKeyword { get; }
|
||||
public StatementListNode CatchBody { get; }
|
||||
public TokenNode EndKeyword { get; }
|
||||
|
||||
public TryCatchStatementNode(
|
||||
List<SyntaxNode> children,
|
||||
TokenNode tryKeyword,
|
||||
StatementListNode tryBody,
|
||||
TokenNode catchKeyword,
|
||||
StatementListNode catchBody,
|
||||
TokenNode endKeyword
|
||||
) : base(children)
|
||||
{
|
||||
TryKeyword = tryKeyword;
|
||||
TryBody = tryBody;
|
||||
CatchKeyword = catchKeyword;
|
||||
CatchBody = catchBody;
|
||||
EndKeyword = endKeyword;
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandExpressionNode : ExpressionNode
|
||||
{
|
||||
public IdentifierNameNode CommandName { get; }
|
||||
public List<UnquotedStringLiteralNode> Arguments { get; }
|
||||
|
||||
public CommandExpressionNode(
|
||||
List<SyntaxNode> children,
|
||||
IdentifierNameNode commandName,
|
||||
List<UnquotedStringLiteralNode> arguments
|
||||
) : base(children)
|
||||
{
|
||||
CommandName = commandName;
|
||||
Arguments = arguments;
|
||||
}
|
||||
}
|
||||
|
||||
public class BaseClassInvokationNode : ExpressionNode
|
||||
{
|
||||
public IdentifierNameNode MethodName { get; }
|
||||
public TokenNode AtToken { get; }
|
||||
public ExpressionNode BaseClassNameAndArguments { get; }
|
||||
|
||||
public BaseClassInvokationNode(
|
||||
List<SyntaxNode> children,
|
||||
IdentifierNameNode methodName,
|
||||
TokenNode atToken,
|
||||
ExpressionNode baseClassNameAndArguments
|
||||
) : base(children)
|
||||
{
|
||||
MethodName = methodName;
|
||||
AtToken = atToken;
|
||||
BaseClassNameAndArguments = baseClassNameAndArguments;
|
||||
}
|
||||
}
|
||||
}
|
55
Parser/SyntaxNodeOrToken.cs
Normal file
55
Parser/SyntaxNodeOrToken.cs
Normal file
@ -0,0 +1,55 @@
|
||||
namespace Parser
|
||||
{
|
||||
public struct SyntaxNodeOrToken
|
||||
{
|
||||
private readonly Internal.GreenNode _token;
|
||||
private readonly SyntaxNode _nodeOrParent;
|
||||
private readonly bool _isToken;
|
||||
|
||||
internal SyntaxNodeOrToken(SyntaxNode node)
|
||||
{
|
||||
_token = null;
|
||||
_nodeOrParent = node;
|
||||
_isToken = false;
|
||||
}
|
||||
|
||||
internal SyntaxNodeOrToken(SyntaxNode parent, Internal.GreenNode token)
|
||||
{
|
||||
_token = token;
|
||||
_nodeOrParent = parent;
|
||||
_isToken = true;
|
||||
}
|
||||
|
||||
public bool IsToken => _isToken;
|
||||
public bool IsNode => !IsToken;
|
||||
|
||||
public SyntaxNode AsNode()
|
||||
{
|
||||
if (_isToken)
|
||||
{
|
||||
return default(SyntaxNode);
|
||||
}
|
||||
|
||||
return _nodeOrParent;
|
||||
}
|
||||
|
||||
public SyntaxToken AsToken()
|
||||
{
|
||||
if (!_isToken)
|
||||
{
|
||||
return default(SyntaxToken);
|
||||
}
|
||||
return new SyntaxToken(_nodeOrParent, _token);
|
||||
}
|
||||
|
||||
public static implicit operator SyntaxNodeOrToken(SyntaxToken token)
|
||||
{
|
||||
return new SyntaxNodeOrToken(token.Parent, token.Token);
|
||||
}
|
||||
|
||||
public static implicit operator SyntaxNodeOrToken(SyntaxNode node)
|
||||
{
|
||||
return new SyntaxNodeOrToken(node);
|
||||
}
|
||||
}
|
||||
}
|
103
Parser/SyntaxNodeOrTokenList.cs
Normal file
103
Parser/SyntaxNodeOrTokenList.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Parser.Internal;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
public class SyntaxNodeOrTokenList : SyntaxNode, IReadOnlyCollection<SyntaxNodeOrToken>
|
||||
{
|
||||
internal SyntaxNodeOrTokenList(SyntaxNode parent, GreenNode green) : base(parent, green)
|
||||
{
|
||||
}
|
||||
|
||||
public SyntaxNodeOrToken this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_green != null && index < _green.Slots)
|
||||
{
|
||||
var green = _green.GetSlot(index);
|
||||
if (green is Internal.SyntaxToken)
|
||||
{
|
||||
return new SyntaxToken(this, green);
|
||||
}
|
||||
else
|
||||
{
|
||||
return green.CreateRed(this);
|
||||
}
|
||||
}
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
internal class Enumerator : IEnumerator<SyntaxNodeOrToken>
|
||||
{
|
||||
private int _index;
|
||||
private readonly SyntaxNodeOrTokenList _list;
|
||||
|
||||
internal Enumerator(SyntaxNodeOrTokenList list)
|
||||
{
|
||||
_index = -1;
|
||||
_list = list;
|
||||
}
|
||||
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
var newIndex = _index + 1;
|
||||
if (newIndex < _list.Count)
|
||||
{
|
||||
_index = newIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public SyntaxNodeOrToken Current => _list[_index];
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<SyntaxNodeOrToken> GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public int Count => _green.Slots;
|
||||
|
||||
internal override SyntaxNode GetNode(int index)
|
||||
{
|
||||
if (index < _green.Slots)
|
||||
{
|
||||
var node = this[index];
|
||||
if (node.IsNode)
|
||||
{
|
||||
return node.AsNode();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Accept(SyntaxVisitor visitor)
|
||||
{
|
||||
visitor.VisitList(this);
|
||||
}
|
||||
}
|
||||
}
|
80
Parser/SyntaxToken.cs
Normal file
80
Parser/SyntaxToken.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
public struct SyntaxToken : IEquatable<SyntaxToken>
|
||||
{
|
||||
private readonly SyntaxNode _parent;
|
||||
private readonly Internal.GreenNode _token;
|
||||
|
||||
public TokenKind Kind => _token.Kind;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _token.ToString();
|
||||
}
|
||||
|
||||
internal SyntaxToken(SyntaxNode parent, Internal.GreenNode token)
|
||||
{
|
||||
_parent = parent;
|
||||
_token = token ?? throw new ArgumentNullException(nameof(token));
|
||||
}
|
||||
|
||||
public SyntaxNode Parent => _parent;
|
||||
internal Internal.GreenNode Token => _token;
|
||||
|
||||
public bool Equals(SyntaxToken other)
|
||||
{
|
||||
return Equals(_parent, other._parent) && Equals(_token, other._token);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
return obj is SyntaxToken token && Equals(token);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((_parent != null ? _parent.GetHashCode() : 0) * 397) ^ (_token != null ? _token.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(SyntaxToken left, SyntaxToken right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(SyntaxToken left, SyntaxToken right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public string Text => _token.Text;
|
||||
public string FullText => _token.FullText;
|
||||
public bool IsMissing => _token.IsMissing;
|
||||
|
||||
public IReadOnlyList<SyntaxTrivia> LeadingTrivia
|
||||
{
|
||||
get
|
||||
{
|
||||
var p = _parent;
|
||||
return _token.LeadingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<SyntaxTrivia> TrailingTrivia
|
||||
{
|
||||
get
|
||||
{
|
||||
var p = _parent;
|
||||
return _token.TrailingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
Parser/SyntaxTrivia.cs
Normal file
57
Parser/SyntaxTrivia.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
public struct SyntaxTrivia : IEquatable<SyntaxTrivia>
|
||||
{
|
||||
private readonly SyntaxNode _parent;
|
||||
private readonly Internal.GreenNode _trivia;
|
||||
|
||||
internal SyntaxTrivia(SyntaxNode parent, Internal.GreenNode trivia)
|
||||
{
|
||||
_parent = parent;
|
||||
_trivia = trivia;
|
||||
}
|
||||
|
||||
public SyntaxNode Parent => _parent;
|
||||
|
||||
internal Internal.GreenNode Trivia => _trivia;
|
||||
|
||||
public bool Equals(SyntaxTrivia other)
|
||||
{
|
||||
return Equals(_parent, other._parent) && Equals(_trivia, other._trivia);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
return obj is SyntaxTrivia trivia && Equals(trivia);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((_parent != null ? _parent.GetHashCode() : 0) * 397) ^ (_trivia != null ? _trivia.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(SyntaxTrivia left, SyntaxTrivia right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(SyntaxTrivia left, SyntaxTrivia right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public string Text => _trivia.Text;
|
||||
public string FullText => _trivia.FullText;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
}
|
||||
}
|
255
Parser/SyntaxVisitor.Generated.cs
Normal file
255
Parser/SyntaxVisitor.Generated.cs
Normal file
@ -0,0 +1,255 @@
|
||||
namespace Parser
|
||||
{
|
||||
public partial class SyntaxVisitor
|
||||
{
|
||||
public virtual void VisitFile(FileSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitFunctionDeclaration(FunctionDeclarationSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitFunctionOutputDescription(FunctionOutputDescriptionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitFunctionInputDescription(FunctionInputDescriptionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitSwitchStatement(SwitchStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitSwitchCase(SwitchCaseSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitWhileStatement(WhileStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitElseifClause(ElseifClause node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitElseClause(ElseClause node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitIfStatement(IfStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitForStatement(ForStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitAssignmentExpression(AssignmentExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitCatchClause(CatchClauseSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitTryCatchStatement(TryCatchStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitExpressionStatement(ExpressionStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEmptyStatement(EmptyStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEmptyExpression(EmptyExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitUnaryPrefixOperationExpression(UnaryPrefixOperationExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitCompoundName(CompoundNameSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitNamedFunctionHandle(NamedFunctionHandleSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitLambda(LambdaSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitBinaryOperationExpression(BinaryOperationExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitIdentifierName(IdentifierNameSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitNumberLiteral(NumberLiteralSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitStringLiteral(StringLiteralSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitDoubleQuotedStringLiteral(DoubleQuotedStringLiteralSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitUnquotedStringLiteral(UnquotedStringLiteralSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitArrayLiteralExpression(ArrayLiteralExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitCellArrayLiteralExpression(CellArrayLiteralExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitParenthesizedExpression(ParenthesizedExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitCellArrayElementAccessExpression(CellArrayElementAccessExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitFunctionCallExpression(FunctionCallExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitMemberAccess(MemberAccessSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitUnaryPostixOperationExpression(UnaryPostixOperationExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitIndirectMemberAccess(IndirectMemberAccessSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitCommandExpression(CommandExpressionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitBaseClassInvokation(BaseClassInvokationSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitAttributeAssignment(AttributeAssignmentSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitAttribute(AttributeSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitAttributeList(AttributeListSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitMethodDefinition(MethodDefinitionSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitAbstractMethodDeclaration(AbstractMethodDeclarationSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitMethodsList(MethodsListSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitPropertiesList(PropertiesListSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitBaseClassList(BaseClassListSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitClassDeclaration(ClassDeclarationSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEnumerationItemValue(EnumerationItemValueSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEnumerationItem(EnumerationItemSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEnumerationList(EnumerationListSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEventsList(EventsListSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
}
|
||||
};
|
19
Parser/SyntaxVisitor.cs
Normal file
19
Parser/SyntaxVisitor.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace Parser
|
||||
{
|
||||
public abstract partial class SyntaxVisitor
|
||||
{
|
||||
public virtual void Visit(SyntaxNode node)
|
||||
{
|
||||
node?.Accept(this);
|
||||
}
|
||||
|
||||
public virtual void DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void VisitList(SyntaxNodeOrTokenList list)
|
||||
{
|
||||
DefaultVisit(list);
|
||||
}
|
||||
}
|
||||
}
|
24
Parser/SyntaxWalker.cs
Normal file
24
Parser/SyntaxWalker.cs
Normal file
@ -0,0 +1,24 @@
|
||||
namespace Parser
|
||||
{
|
||||
public abstract class SyntaxWalker : SyntaxVisitor
|
||||
{
|
||||
public override void DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
foreach (var nodeOrToken in node.GetChildNodesAndTokens())
|
||||
{
|
||||
if (nodeOrToken.IsNode)
|
||||
{
|
||||
Visit(nodeOrToken.AsNode());
|
||||
}
|
||||
else
|
||||
{
|
||||
VisitToken(nodeOrToken.AsToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void VisitToken(SyntaxToken token)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
namespace Lexer
|
||||
namespace Parser
|
||||
{
|
||||
public class TextWindow : ITextWindow
|
||||
{
|
||||
protected readonly string Text;
|
||||
protected int Offset { get; set; }
|
||||
private PositionInsideFile _position;
|
||||
public IPosition Position => _position;
|
||||
private Position _position;
|
||||
public Position Position => _position;
|
||||
|
||||
public TextWindow(string text, string fileName = null)
|
||||
{
|
||||
Text = text;
|
||||
Offset = 0;
|
||||
_position = new PositionInsideFile
|
||||
_position = new Position
|
||||
{
|
||||
File = fileName,
|
||||
FileName = fileName,
|
||||
Line = 0,
|
||||
Column = 0
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
namespace Lexer
|
||||
namespace Parser
|
||||
{
|
||||
public class TextWindowWithNull : TextWindow
|
||||
{
|
115
Parser/TokenKind.cs
Normal file
115
Parser/TokenKind.cs
Normal file
@ -0,0 +1,115 @@
|
||||
namespace Parser
|
||||
{
|
||||
public enum TokenKind
|
||||
{
|
||||
None = 0,
|
||||
EndOfFile = 1,
|
||||
Identifier = 2,
|
||||
NumberLiteral = 3,
|
||||
StringLiteral = 4,
|
||||
DoubleQuotedStringLiteral = 5,
|
||||
UnquotedStringLiteral = 6,
|
||||
|
||||
// trivia
|
||||
|
||||
Whitespace = 10,
|
||||
Newline = 11,
|
||||
Comment = 12,
|
||||
MultilineComment = 13,
|
||||
|
||||
Assignment = 20,
|
||||
Equality = 21,
|
||||
Inequality = 22,
|
||||
LogicalAnd = 23,
|
||||
LogicalOr = 24,
|
||||
BitwiseAnd = 25,
|
||||
BitwiseOr = 26,
|
||||
Less = 27,
|
||||
LessOrEqual = 28,
|
||||
Greater = 29,
|
||||
GreaterOrEqual = 30,
|
||||
Not = 31,
|
||||
Plus = 32,
|
||||
Minus = 33,
|
||||
Multiply = 34,
|
||||
Divide = 35,
|
||||
Power = 36,
|
||||
Backslash = 37,
|
||||
Transpose = 38,
|
||||
DotMultiply = 39,
|
||||
DotDivide = 40,
|
||||
DotPower = 41,
|
||||
DotBackslash = 42,
|
||||
DotTranspose = 43,
|
||||
At = 44,
|
||||
Colon = 45,
|
||||
QuestionMark = 46,
|
||||
Comma = 47,
|
||||
Semicolon = 48,
|
||||
OpeningBrace = 49,
|
||||
ClosingBrace = 50,
|
||||
OpeningSquareBracket = 51,
|
||||
ClosingSquareBracket = 52,
|
||||
OpeningBracket = 53,
|
||||
ClosingBracket = 54,
|
||||
Dot = 55,
|
||||
DotDotDot = 56,
|
||||
// unary tokens are not recognized during lexing; they are contextually recognized while parsing.
|
||||
UnaryPlus = 57,
|
||||
UnaryMinus = 58,
|
||||
UnaryNot = 59,
|
||||
UnaryQuestionMark = 60,
|
||||
// syntax nodes
|
||||
File = 100,
|
||||
List,
|
||||
FunctionDeclaration,
|
||||
FunctionInputDescription,
|
||||
FunctionOutputDescription,
|
||||
SwitchStatement,
|
||||
SwitchCase,
|
||||
WhileStatement,
|
||||
IfStatement,
|
||||
ElseifClause,
|
||||
ElseClause,
|
||||
ForStatement,
|
||||
AssignmentExpression,
|
||||
CatchClause,
|
||||
TryCatchStatement,
|
||||
ExpressionStatement,
|
||||
EmptyStatement,
|
||||
EmptyExpression,
|
||||
UnaryPrefixOperationExpression,
|
||||
CompoundName,
|
||||
NamedFunctionHandle,
|
||||
Lambda,
|
||||
BinaryOperation,
|
||||
IdentifierName,
|
||||
NumberLiteralExpression,
|
||||
StringLiteralExpression,
|
||||
DoubleQuotedStringLiteralExpression,
|
||||
UnquotedStringLiteralExpression,
|
||||
ArrayLiteralExpression,
|
||||
CellArrayLiteralExpression,
|
||||
ParenthesizedExpression,
|
||||
CellArrayElementAccess,
|
||||
FunctionCall,
|
||||
MemberAccess,
|
||||
UnaryPostfixOperationExpression,
|
||||
IndirectMemberAccess,
|
||||
Command,
|
||||
ClassInvokation,
|
||||
AttributeAssignment,
|
||||
Attribute,
|
||||
AttributeList,
|
||||
MethodDefinition,
|
||||
MethodsList,
|
||||
PropertiesList,
|
||||
BaseClassList,
|
||||
ClassDeclaration,
|
||||
EnumerationItemValue,
|
||||
EnumerationItem,
|
||||
EnumerationList,
|
||||
AbstractMethodDeclaration,
|
||||
EventsList,
|
||||
}
|
||||
}
|
32
Semantics/ClassContext.cs
Normal file
32
Semantics/ClassContext.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
internal class ClassContext
|
||||
{
|
||||
public string Path { get; }
|
||||
public Dictionary<string, ClassContext> SubClasses { get; }
|
||||
public Dictionary<string, FunctionContext> Methods { get; }
|
||||
public Dictionary<string, FunctionContext> PrivateMethods { get; }
|
||||
|
||||
public ClassContext(
|
||||
string path,
|
||||
Dictionary<string, ClassContext> subClasses,
|
||||
Dictionary<string, FunctionContext> methods,
|
||||
Dictionary<string, FunctionContext> privateMethods)
|
||||
{
|
||||
Path = path;
|
||||
SubClasses = subClasses;
|
||||
Methods = methods;
|
||||
PrivateMethods = privateMethods;
|
||||
}
|
||||
|
||||
public ClassContext(string path)
|
||||
{
|
||||
Path = path;
|
||||
SubClasses = new Dictionary<string, ClassContext>();
|
||||
Methods = new Dictionary<string, FunctionContext>();
|
||||
PrivateMethods = new Dictionary<string, FunctionContext>();
|
||||
}
|
||||
}
|
||||
}
|
141
Semantics/Context.cs
Normal file
141
Semantics/Context.cs
Normal file
@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
public class Context
|
||||
{
|
||||
internal PackageContext Root { get; }
|
||||
|
||||
public Context()
|
||||
{
|
||||
Root = new PackageContext(
|
||||
null,
|
||||
new Dictionary<string, PackageContext>(),
|
||||
new Dictionary<string, ClassContext>(),
|
||||
new Dictionary<string, FunctionContext>());
|
||||
}
|
||||
|
||||
public bool FindFunction(string name)
|
||||
{
|
||||
return Root.Functions.ContainsKey(name);
|
||||
}
|
||||
|
||||
private void ScanFunction(PackageContext context, string fileName)
|
||||
{
|
||||
var functionName = Path.GetFileNameWithoutExtension(fileName);
|
||||
context.Functions[functionName] = new FunctionContext(fileName);
|
||||
}
|
||||
|
||||
private void ScanMethod(ClassContext context, string fileName)
|
||||
{
|
||||
var methodName = Path.GetFileNameWithoutExtension(fileName);
|
||||
context.Methods[methodName] = new FunctionContext(fileName);
|
||||
}
|
||||
|
||||
private void ScanPrivateMethod(ClassContext context, string fileName)
|
||||
{
|
||||
var methodName = Path.GetFileNameWithoutExtension(fileName);
|
||||
context.PrivateMethods[methodName] = new FunctionContext(fileName);
|
||||
}
|
||||
|
||||
private string GetPackageNameFromFolder(string folderName)
|
||||
{
|
||||
return folderName.StartsWith('+') ? folderName.Substring(1, folderName.Length - 1) : null;
|
||||
}
|
||||
|
||||
private string GetClassNameFromFolder(string folderName)
|
||||
{
|
||||
return folderName.StartsWith('@') ? folderName.Substring(1, folderName.Length - 1) : null;
|
||||
}
|
||||
|
||||
private void ScanPrivateDirectory(ClassContext currentContext, string directory)
|
||||
{
|
||||
var files = Directory.GetFiles(directory, "*.m");
|
||||
foreach (var fileName in files)
|
||||
{
|
||||
ScanPrivateMethod(currentContext, fileName);
|
||||
}
|
||||
|
||||
var subDirectories = Directory.GetDirectories(directory);
|
||||
foreach (var subDirectory in subDirectories)
|
||||
{
|
||||
Console.WriteLine($"A FOLDER INSIDE A PRIVATE SUBFOLDER WHAT TO DO? {subDirectory}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ScanClassDirectory(ClassContext currentContext, string directory)
|
||||
{
|
||||
var files = Directory.GetFiles(directory, "*.m");
|
||||
foreach (var fileName in files)
|
||||
{
|
||||
ScanMethod(currentContext, fileName);
|
||||
}
|
||||
|
||||
var subDirectories = Directory.GetDirectories(directory);
|
||||
foreach (var subDirectory in subDirectories)
|
||||
{
|
||||
var lastName = subDirectory.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Last();
|
||||
if (lastName == "private")
|
||||
{
|
||||
ScanPrivateDirectory(currentContext, subDirectory);
|
||||
continue;
|
||||
}
|
||||
var packageName = GetPackageNameFromFolder(lastName);
|
||||
if (packageName != null)
|
||||
{
|
||||
Console.WriteLine($"A PACKAGE INSIDE A CLASS WHAT TO DO? {subDirectory}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var className = GetClassNameFromFolder(lastName);
|
||||
if (className != null)
|
||||
{
|
||||
currentContext.SubClasses[className] = new ClassContext(className);
|
||||
ScanClassDirectory(currentContext.SubClasses[className], subDirectory);
|
||||
continue;
|
||||
}
|
||||
ScanClassDirectory(currentContext, subDirectory); // Should this really work?
|
||||
}
|
||||
}
|
||||
|
||||
private void ScanDirectory(PackageContext currentContext, string directory)
|
||||
{
|
||||
var files = Directory.GetFiles(directory, "*.m");
|
||||
foreach (var fileName in files)
|
||||
{
|
||||
ScanFunction(currentContext, fileName);
|
||||
}
|
||||
|
||||
var subDirectories = Directory.GetDirectories(directory);
|
||||
foreach (var subDirectory in subDirectories)
|
||||
{
|
||||
var lastName = subDirectory.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Last();
|
||||
var packageName = GetPackageNameFromFolder(lastName);
|
||||
if (packageName != null)
|
||||
{
|
||||
currentContext.SubPackages[packageName] = new PackageContext(packageName);
|
||||
ScanDirectory(currentContext.SubPackages[packageName], subDirectory);
|
||||
continue;
|
||||
}
|
||||
|
||||
var className = GetClassNameFromFolder(lastName);
|
||||
if (className != null)
|
||||
{
|
||||
currentContext.Classes[className] = new ClassContext(className);
|
||||
ScanClassDirectory(currentContext.Classes[className], subDirectory);
|
||||
continue;
|
||||
}
|
||||
ScanDirectory(currentContext, subDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public void ScanPath(string path)
|
||||
{
|
||||
ScanDirectory(Root, path);
|
||||
}
|
||||
}
|
||||
}
|
12
Semantics/FunctionContext.cs
Normal file
12
Semantics/FunctionContext.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Semantics
|
||||
{
|
||||
public class FunctionContext
|
||||
{
|
||||
public string FileName { get; }
|
||||
|
||||
public FunctionContext(string fileName)
|
||||
{
|
||||
FileName = fileName;
|
||||
}
|
||||
}
|
||||
}
|
82
Semantics/GetClass.cs
Normal file
82
Semantics/GetClass.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Parser;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
public class GetClass
|
||||
{
|
||||
private static MMethod MethodFromDefinition(MethodDefinitionSyntaxNode methodDefinition)
|
||||
{
|
||||
var name = methodDefinition.Name.Text;
|
||||
var description = "";
|
||||
description += string.Join("", methodDefinition.LeadingTrivia.Select(x => x.FullText));
|
||||
if (methodDefinition.Body == null)
|
||||
{
|
||||
description += string.Join("", methodDefinition.EndKeyword.LeadingTrivia.Select(x => x.FullText));
|
||||
}
|
||||
else
|
||||
{
|
||||
description += string.Join("", methodDefinition.Body.LeadingTrivia.Select(x => x.FullText));
|
||||
}
|
||||
|
||||
return new MMethod(name, description);
|
||||
}
|
||||
|
||||
private static MMethod MethodFromDeclaration(AbstractMethodDeclarationSyntaxNode methodDeclaration)
|
||||
{
|
||||
var name = methodDeclaration.Name.Text;
|
||||
var description = "";
|
||||
description += string.Join("", methodDeclaration.LeadingTrivia.Select(x => x.FullText));
|
||||
return new MMethod(name, description);
|
||||
}
|
||||
|
||||
private static List<MMethod> MethodsFromList(MethodsListSyntaxNode methodsList)
|
||||
{
|
||||
var result = new List<MMethod>();
|
||||
foreach (var method in methodsList.Methods)
|
||||
{
|
||||
if (method.IsToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.AsNode() is MethodDefinitionSyntaxNode methodDefinition)
|
||||
{
|
||||
result.Add(MethodFromDefinition(methodDefinition));
|
||||
}
|
||||
|
||||
if (method.AsNode() is AbstractMethodDeclarationSyntaxNode methodDeclaration)
|
||||
{
|
||||
result.Add(MethodFromDeclaration(methodDeclaration));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MClass FromTree(FileSyntaxNode tree, string fileName)
|
||||
{
|
||||
var classDeclaration = tree.StatementList[0].AsNode() as ClassDeclarationSyntaxNode;
|
||||
if (classDeclaration == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var name = classDeclaration.ClassName.Text;
|
||||
var methods = new List<MMethod>();
|
||||
foreach (var s in classDeclaration.Nodes)
|
||||
{
|
||||
if (s.IsToken)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (s.AsNode() is MethodsListSyntaxNode methodsList)
|
||||
{
|
||||
methods.AddRange(MethodsFromList(methodsList));
|
||||
}
|
||||
}
|
||||
return new MClass(tree, name, fileName, methods);
|
||||
}
|
||||
}
|
||||
}
|
21
Semantics/MClass.cs
Normal file
21
Semantics/MClass.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using Parser;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
public class MClass
|
||||
{
|
||||
public string Name { get; }
|
||||
public string FileName { get; }
|
||||
public FileSyntaxNode Tree { get; }
|
||||
public List<MMethod> Methods { get; }
|
||||
|
||||
public MClass(FileSyntaxNode tree, string name, string fileName, List<MMethod> methods)
|
||||
{
|
||||
Tree = tree;
|
||||
Name = name;
|
||||
FileName = FileName;
|
||||
Methods = methods;
|
||||
}
|
||||
}
|
||||
}
|
15
Semantics/MMethod.cs
Normal file
15
Semantics/MMethod.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Semantics
|
||||
{
|
||||
public class MMethod
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
public MClass Class { get; internal set; }
|
||||
|
||||
public MMethod(string name, string description)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
}
|
30
Semantics/MethodAssignments.cs
Normal file
30
Semantics/MethodAssignments.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
public class MethodAssignments
|
||||
{
|
||||
public Context Context { get; }
|
||||
|
||||
private readonly Dictionary<string, Variable> _methods;
|
||||
|
||||
public MethodAssignments()
|
||||
{
|
||||
_methods = new Dictionary<string, Variable>();
|
||||
}
|
||||
|
||||
public Variable Find(string name)
|
||||
{
|
||||
if (_methods.ContainsKey(name))
|
||||
{
|
||||
return _methods[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Add(string name, Variable variable)
|
||||
{
|
||||
_methods[name] = variable;
|
||||
}
|
||||
}
|
||||
}
|
33
Semantics/PackageContext.cs
Normal file
33
Semantics/PackageContext.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
internal class PackageContext
|
||||
{
|
||||
public string Path { get; }
|
||||
public Dictionary<string, PackageContext> SubPackages { get; }
|
||||
public Dictionary<string, ClassContext> Classes { get; }
|
||||
public Dictionary<string, FunctionContext> Functions { get; }
|
||||
|
||||
public PackageContext(
|
||||
string path,
|
||||
Dictionary<string, PackageContext> subPackages,
|
||||
Dictionary<string, ClassContext> classes,
|
||||
Dictionary<string, FunctionContext> functions)
|
||||
{
|
||||
Path = path;
|
||||
SubPackages = subPackages;
|
||||
Classes = classes;
|
||||
Functions = functions;
|
||||
}
|
||||
|
||||
public PackageContext(
|
||||
string path)
|
||||
{
|
||||
Path = path;
|
||||
SubPackages = new Dictionary<string, PackageContext>();
|
||||
Classes = new Dictionary<string, ClassContext>();
|
||||
Functions = new Dictionary<string, FunctionContext>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Parser\Parser.csproj" />
|
7
Semantics/Variable.cs
Normal file
7
Semantics/Variable.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Semantics
|
||||
{
|
||||
public class Variable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
30
Semantics/VariableAssignments.cs
Normal file
30
Semantics/VariableAssignments.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semantics
|
||||
{
|
||||
public class VariableAssignments
|
||||
{
|
||||
public Context Context { get; }
|
||||
|
||||
private readonly Dictionary<string, Variable> _variables;
|
||||
|
||||
public VariableAssignments()
|
||||
{
|
||||
_variables = new Dictionary<string, Variable>();
|
||||
}
|
||||
|
||||
public Variable Find(string name)
|
||||
{
|
||||
if (_variables.ContainsKey(name))
|
||||
{
|
||||
return _variables[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Add(string name, Variable variable)
|
||||
{
|
||||
_variables[name] = variable;
|
||||
}
|
||||
}
|
||||
}
|
26
Solution.sln
26
Solution.sln
@ -3,15 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2026
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectConsole", "ProjectConsole\ProjectConsole.csproj", "{5025FD8F-0F1A-43E5-A996-7753BC703D62}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleDemo", "ConsoleDemo\ConsoleDemo.csproj", "{5025FD8F-0F1A-43E5-A996-7753BC703D62}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parser", "Parser\Parser.csproj", "{B20EDC10-E6E6-4430-8527-B95206DEF941}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Parser", "Parser\Parser.csproj", "{1B8E5BBC-E5CD-427B-A6C7-F30047AA4A39}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Parser.Tests", "Parser.Tests\Parser.Tests.csproj", "{83008C72-2EFC-41EB-AC8D-023C6AE1709F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Parser.Tests", "Parser.Tests\Parser.Tests.csproj", "{7BFEAD86-EAC3-43C8-9388-EBAB377938D4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lexer", "Lexer\Lexer.csproj", "{1B8E5BBC-E5CD-427B-A6C7-F30047AA4A39}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyntaxGenerator", "SyntaxGenerator\SyntaxGenerator.csproj", "{03487753-C1F5-4753-B576-593294ED86D1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lexer.Tests", "Lexer.Tests\Lexer.Tests.csproj", "{7BFEAD86-EAC3-43C8-9388-EBAB377938D4}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semantics", "Semantics\Semantics.csproj", "{4595633B-7F9A-4771-B348-F12BB9DD7ABC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -23,14 +23,6 @@ Global
|
||||
{5025FD8F-0F1A-43E5-A996-7753BC703D62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5025FD8F-0F1A-43E5-A996-7753BC703D62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5025FD8F-0F1A-43E5-A996-7753BC703D62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B20EDC10-E6E6-4430-8527-B95206DEF941}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B20EDC10-E6E6-4430-8527-B95206DEF941}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B20EDC10-E6E6-4430-8527-B95206DEF941}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B20EDC10-E6E6-4430-8527-B95206DEF941}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{83008C72-2EFC-41EB-AC8D-023C6AE1709F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{83008C72-2EFC-41EB-AC8D-023C6AE1709F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{83008C72-2EFC-41EB-AC8D-023C6AE1709F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{83008C72-2EFC-41EB-AC8D-023C6AE1709F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1B8E5BBC-E5CD-427B-A6C7-F30047AA4A39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1B8E5BBC-E5CD-427B-A6C7-F30047AA4A39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1B8E5BBC-E5CD-427B-A6C7-F30047AA4A39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -39,6 +31,14 @@ Global
|
||||
{7BFEAD86-EAC3-43C8-9388-EBAB377938D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7BFEAD86-EAC3-43C8-9388-EBAB377938D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7BFEAD86-EAC3-43C8-9388-EBAB377938D4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{03487753-C1F5-4753-B576-593294ED86D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{03487753-C1F5-4753-B576-593294ED86D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{03487753-C1F5-4753-B576-593294ED86D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{03487753-C1F5-4753-B576-593294ED86D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4595633B-7F9A-4771-B348-F12BB9DD7ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4595633B-7F9A-4771-B348-F12BB9DD7ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4595633B-7F9A-4771-B348-F12BB9DD7ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4595633B-7F9A-4771-B348-F12BB9DD7ABC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
360
SyntaxGenerator/GenerateSyntax.cs
Normal file
360
SyntaxGenerator/GenerateSyntax.cs
Normal file
@ -0,0 +1,360 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SyntaxGenerator
|
||||
{
|
||||
public class GenerateSyntax
|
||||
{
|
||||
private const string SyntaxTokenClassName = "SyntaxToken";
|
||||
private const string InternalNamespace = "Parser.Internal";
|
||||
private const string OuterNamespace = "Parser";
|
||||
|
||||
private static readonly List<(string visitorMethodName, string className)> Visitors = new List<(string, string)>();
|
||||
|
||||
private static string _outputPath;
|
||||
|
||||
static GenerateSyntax()
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
_outputPath = Path.Combine("..", "Parser");
|
||||
break;
|
||||
default:
|
||||
_outputPath = Path.Combine("..", "..", "..", "..", "Parser");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestOutput()
|
||||
{
|
||||
var field1 = new FieldDescription
|
||||
{
|
||||
FieldType = "SyntaxToken",
|
||||
FieldName = "functionKeyword"
|
||||
};
|
||||
var field2 = new FieldDescription
|
||||
{
|
||||
FieldType = "FunctionOutputDescriptionSyntaxNode",
|
||||
FieldName = "outputDescription"
|
||||
};
|
||||
var syntaxNode = new SyntaxNodeDescription
|
||||
{
|
||||
ClassName = "FunctionDeclarationSyntaxNode",
|
||||
BaseClassName = "StatementSyntaxNode",
|
||||
Fields = new[] {field1, field2}
|
||||
};
|
||||
var syntax = new SyntaxDescription
|
||||
{
|
||||
Nodes = new[] {syntaxNode, syntaxNode}
|
||||
};
|
||||
var serializer = new XmlSerializer(typeof(SyntaxDescription));
|
||||
using (var writer = new StreamWriter("output.xml"))
|
||||
{
|
||||
serializer.Serialize(writer, syntax);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateInternalFieldDeclaration(FieldDescription field)
|
||||
{
|
||||
return $" internal readonly {field.FieldType} _{field.FieldName};\n";
|
||||
}
|
||||
|
||||
private static string GeneratePrivateFieldDeclaration(FieldDescription field)
|
||||
{
|
||||
return $" private SyntaxNode _{field.FieldName};\n";
|
||||
}
|
||||
|
||||
private static string GenerateFieldAssignmentInsideConstructor(FieldDescription field)
|
||||
{
|
||||
return $" _{field.FieldName} = {field.FieldName};\n";
|
||||
}
|
||||
|
||||
private static string GenerateInternalConstructor(SyntaxNodeDescription node)
|
||||
{
|
||||
var arguments = string.Join(
|
||||
",",
|
||||
node.Fields.Select(field => $"\n {field.FieldType} {field.FieldName}"));
|
||||
|
||||
var header =
|
||||
$" internal {node.ClassName}({arguments}) : base(TokenKind.{node.TokenKindName}, {node.Fields.Length})\n";
|
||||
var assignments = string.Join(
|
||||
"",
|
||||
node.Fields.Select(GenerateFieldAssignmentInsideConstructor));
|
||||
return header + " {\n" + assignments + " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateConstructor(SyntaxNodeDescription node)
|
||||
{
|
||||
var arguments = "SyntaxNode parent, Internal.GreenNode green";
|
||||
var header =
|
||||
$" internal {node.ClassName}({arguments}) : base(parent, green)\n";
|
||||
return header + " {\n }\n";
|
||||
}
|
||||
|
||||
private static string GenerateInternalGetSlot(SyntaxNodeDescription node)
|
||||
{
|
||||
var header = $" public override GreenNode GetSlot(int i)\n";
|
||||
var cases = string.Join(
|
||||
"",
|
||||
node.Fields.Select((f, i) => $" case {i}: return _{f.FieldName};\n"));
|
||||
var defaultCase = " default: return null;\n";
|
||||
return header
|
||||
+ " {\n switch (i)\n {\n"
|
||||
+ cases
|
||||
+ defaultCase
|
||||
+ " }\n"
|
||||
+ " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateGetSlot(SyntaxNodeDescription node, List<(FieldDescription field, int index)> pairs)
|
||||
{
|
||||
var header = $" internal override SyntaxNode GetNode(int i)\n";
|
||||
var cases = string.Join(
|
||||
"",
|
||||
pairs.Select(pair => $" case {pair.index}: return GetRed(ref _{pair.field.FieldName}, {pair.index});\n"));
|
||||
var defaultCase = " default: return null;\n";
|
||||
return header
|
||||
+ " {\n switch (i)\n {\n"
|
||||
+ cases
|
||||
+ defaultCase
|
||||
+ " }\n"
|
||||
+ " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateCreateRed(SyntaxNodeDescription node)
|
||||
{
|
||||
var header = $" internal override {OuterNamespace}.SyntaxNode CreateRed({OuterNamespace}.SyntaxNode parent)\n";
|
||||
var text = $" return new {OuterNamespace}.{node.ClassName}(parent, this);\n";
|
||||
return header + " {\n" + text + " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateInternalClass(SyntaxNodeDescription node)
|
||||
{
|
||||
var header = $" internal class {node.ClassName}";
|
||||
if (node.BaseClassName != null)
|
||||
{
|
||||
header += $" : {node.BaseClassName}";
|
||||
}
|
||||
|
||||
var fields = string.Join(
|
||||
"",
|
||||
node.Fields.Select(GenerateInternalFieldDeclaration));
|
||||
var constructor = GenerateInternalConstructor(node);
|
||||
var getSlot = GenerateInternalGetSlot(node);
|
||||
var createRed = GenerateCreateRed(node);
|
||||
return
|
||||
header
|
||||
+ "\n {\n"
|
||||
+ fields + "\n"
|
||||
+ constructor + "\n"
|
||||
+ createRed + "\n"
|
||||
+ getSlot + " }\n";
|
||||
}
|
||||
|
||||
private static string Capitalize(string s)
|
||||
{
|
||||
return s[0].ToString().ToUpper() + s.Substring(1, s.Length - 1);
|
||||
}
|
||||
|
||||
private static string GenerateTokenAccessor(SyntaxNodeDescription node, FieldDescription field)
|
||||
{
|
||||
var header = $" public SyntaxToken {Capitalize(field.FieldName)}\n";
|
||||
var text =
|
||||
$" get {{ return new SyntaxToken(this, (({InternalNamespace}.{node.ClassName})_green)._{field.FieldName}); }}";
|
||||
return header + " {\n" + text + "\n }\n";
|
||||
}
|
||||
|
||||
private static bool IsList(string type)
|
||||
{
|
||||
return type.StartsWith("SyntaxList");
|
||||
}
|
||||
|
||||
private static string GenerateNodeAccessor(SyntaxNodeDescription node, FieldDescription field, int index)
|
||||
{
|
||||
var type = field.FieldType;
|
||||
if (IsList(type))
|
||||
{
|
||||
type = "SyntaxNodeOrTokenList";
|
||||
}
|
||||
var header = $" public {type} {Capitalize(field.FieldName)}\n {{\n get\n {{\n";
|
||||
var text =
|
||||
$" var red = this.GetRed(ref this._{field.FieldName}, {index});\n"
|
||||
+ $" if (red != null)\n"
|
||||
+ $" return ({type})red;\n\n"
|
||||
+ $" return default({type});\n";
|
||||
return header + text + " }\n }\n";
|
||||
}
|
||||
|
||||
private static string ConvertClassNameToVisitorName(string name)
|
||||
{
|
||||
if (name.EndsWith("SyntaxNode"))
|
||||
{
|
||||
name = name.Substring(0, name.Length - "SyntaxNode".Length);
|
||||
}
|
||||
|
||||
return "Visit" + name;
|
||||
}
|
||||
|
||||
private static string GenerateAccessMethod(SyntaxNodeDescription node)
|
||||
{
|
||||
var visitorName = ConvertClassNameToVisitorName(node.ClassName);
|
||||
Visitors.Add((visitorName, node.ClassName));
|
||||
var header = $" public override void Accept(SyntaxVisitor visitor)\n";
|
||||
var body = $" visitor.{visitorName}(this);\n";
|
||||
return header + " {\n" + body + " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateClass(SyntaxNodeDescription node)
|
||||
{
|
||||
var header = $" public class {node.ClassName}";
|
||||
if (node.BaseClassName != null)
|
||||
{
|
||||
header += $" : {node.BaseClassName}";
|
||||
}
|
||||
|
||||
var tokenSlots = node.Fields
|
||||
.Select((f, i) => (field: f, index: i))
|
||||
.Where(pair => pair.field.FieldType == SyntaxTokenClassName)
|
||||
.ToList();
|
||||
var nodeSlots = node.Fields
|
||||
.Select((f, i) => (field: f, index: i))
|
||||
.Where(pair => pair.field.FieldType != SyntaxTokenClassName)
|
||||
.ToList();
|
||||
var fields = string.Join(
|
||||
"",
|
||||
nodeSlots.Select(pair => GeneratePrivateFieldDeclaration(pair.field)));
|
||||
var constructor = GenerateConstructor(node);
|
||||
var tokenAccessors =
|
||||
string.Join(
|
||||
"\n",
|
||||
tokenSlots.Select(pair => GenerateTokenAccessor(node, pair.field)));
|
||||
var nodeAccessors =
|
||||
string.Join(
|
||||
"\n",
|
||||
nodeSlots.Select(pair => GenerateNodeAccessor(node, pair.field, pair.index)));
|
||||
var getSlot = GenerateGetSlot(node, nodeSlots);
|
||||
var access = GenerateAccessMethod(node);
|
||||
return
|
||||
header
|
||||
+ "\n {\n"
|
||||
+ fields + "\n"
|
||||
+ constructor + "\n"
|
||||
+ tokenAccessors + "\n"
|
||||
+ nodeAccessors + "\n"
|
||||
+ getSlot + "\n"
|
||||
+ access + "\n"
|
||||
+ " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateInternalSyntaxNodeFile(SyntaxDescription syntax)
|
||||
{
|
||||
var header = $"namespace {InternalNamespace}\n";
|
||||
var classes = string.Join(
|
||||
"\n",
|
||||
syntax.Nodes.Select(GenerateInternalClass)
|
||||
);
|
||||
return header + "{\n" + classes + "}\n";
|
||||
}
|
||||
|
||||
private static string GenerateSyntaxNodeFile(SyntaxDescription syntax)
|
||||
{
|
||||
var header = $"namespace {OuterNamespace}\n";
|
||||
var classes = string.Join(
|
||||
"\n",
|
||||
syntax.Nodes.Select(GenerateClass)
|
||||
);
|
||||
return header + "{\n" + classes + "}\n";
|
||||
}
|
||||
|
||||
private static string FactoryMethodNameFromClassName(string className)
|
||||
{
|
||||
if (className.EndsWith("Node"))
|
||||
{
|
||||
return className.Substring(0, className.Length - 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateFactoryMethod(SyntaxNodeDescription node)
|
||||
{
|
||||
var methodName = FactoryMethodNameFromClassName(node.ClassName);
|
||||
var header = $" public {node.ClassName} {methodName}";
|
||||
var arguments = string.Join(
|
||||
", ",
|
||||
node.Fields.Select(field => $"\n {field.FieldType} {field.FieldName}"));
|
||||
var constructorParameters = string.Join(
|
||||
", ",
|
||||
node.Fields.Select(field => $"\n {field.FieldName}"));
|
||||
var returnStatement =
|
||||
$" return new {node.ClassName}({constructorParameters});\n";
|
||||
|
||||
return header + "(" + arguments + ")\n {\n" + returnStatement + " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateSyntaxFactoryFile(SyntaxDescription syntax)
|
||||
{
|
||||
var header = $"namespace {InternalNamespace}\n{{\n internal partial class SyntaxFactory\n";
|
||||
var methods = string.Join(
|
||||
"\n",
|
||||
syntax.Nodes.Select(GenerateFactoryMethod)
|
||||
);
|
||||
return header + " {\n" + methods + " }\n}";
|
||||
}
|
||||
|
||||
private static string GenerateVisitor((string visitorMethodName, string className) info)
|
||||
{
|
||||
var header = $" public virtual void {info.visitorMethodName}({info.className} node)\n";
|
||||
var body = $" DefaultVisit(node);\n";
|
||||
return header + " {\n" + body + " }\n";
|
||||
}
|
||||
|
||||
private static string GenerateSyntaxVisitorFile(SyntaxDescription syntax)
|
||||
{
|
||||
var header = $"namespace {OuterNamespace}\n{{\n public partial class SyntaxVisitor\n";
|
||||
var methods = string.Join(
|
||||
"\n",
|
||||
Visitors.Select(GenerateVisitor));
|
||||
return header + " {\n" + methods + " }\n};";
|
||||
}
|
||||
|
||||
|
||||
public static void Input()
|
||||
{
|
||||
var serializer = new XmlSerializer(typeof(SyntaxDescription));
|
||||
using (var stream = new FileStream("input.xml", FileMode.Open))
|
||||
{
|
||||
var syntax = serializer.Deserialize(stream) as SyntaxDescription;
|
||||
if (syntax == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't deserialize syntax.");
|
||||
return;
|
||||
}
|
||||
|
||||
var internalSyntaxNodePath = Path.Combine(_outputPath, "Internal", "SyntaxNode.Generated.cs");
|
||||
File.WriteAllText(internalSyntaxNodePath, GenerateInternalSyntaxNodeFile(syntax));
|
||||
var internalSyntaxFactoryPath = Path.Combine(_outputPath, "Internal", "SyntaxFactory.Generated.cs");
|
||||
File.WriteAllText(internalSyntaxFactoryPath, GenerateSyntaxFactoryFile(syntax));
|
||||
var syntaxNodePath = Path.Combine(_outputPath, "SyntaxNode.Generated.cs");
|
||||
File.WriteAllText(syntaxNodePath, GenerateSyntaxNodeFile(syntax));
|
||||
var syntaxVisitorPath = Path.Combine(_outputPath, "SyntaxVisitor.Generated.cs");
|
||||
File.WriteAllText(syntaxVisitorPath, GenerateSyntaxVisitorFile(syntax));
|
||||
}
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.Write("Generating syntax...");
|
||||
Input();
|
||||
Console.WriteLine("Done.");
|
||||
}
|
||||
}
|
||||
}
|
11
SyntaxGenerator/SyntaxGenerator.csproj
Normal file
11
SyntaxGenerator/SyntaxGenerator.csproj
Normal file
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Update="input.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
38
SyntaxGenerator/SyntaxNodeDescription.cs
Normal file
38
SyntaxGenerator/SyntaxNodeDescription.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SyntaxGenerator
|
||||
{
|
||||
[XmlRoot(ElementName = "Syntax")]
|
||||
public class SyntaxDescription
|
||||
{
|
||||
[XmlElement(ElementName = "Class")]
|
||||
public SyntaxNodeDescription[] Nodes { get; set; }
|
||||
}
|
||||
|
||||
public class SyntaxNodeDescription
|
||||
{
|
||||
[XmlAttribute("Name")]
|
||||
public string ClassName { get; set; }
|
||||
[XmlAttribute("BaseClass")]
|
||||
public string BaseClassName { get; set; }
|
||||
[XmlAttribute("Kind")]
|
||||
public string TokenKindName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Field")]
|
||||
public FieldDescription[] Fields
|
||||
{
|
||||
get => _fields;
|
||||
set => _fields = value ?? new FieldDescription[0];
|
||||
}
|
||||
|
||||
private FieldDescription[] _fields = new FieldDescription[0];
|
||||
}
|
||||
|
||||
public class FieldDescription
|
||||
{
|
||||
[XmlAttribute("Type")]
|
||||
public string FieldType { get; set; }
|
||||
[XmlAttribute("Name")]
|
||||
public string FieldName { get; set; }
|
||||
}
|
||||
}
|
253
SyntaxGenerator/input.xml
Normal file
253
SyntaxGenerator/input.xml
Normal file
@ -0,0 +1,253 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Syntax xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Class Name="FileSyntaxNode" BaseClass="SyntaxNode" Kind="File">
|
||||
<Field Type="SyntaxList" Name="statementList" />
|
||||
<Field Type="SyntaxToken" Name="endOfFile" />
|
||||
</Class>
|
||||
<Class Name="FunctionDeclarationSyntaxNode" BaseClass="StatementSyntaxNode" Kind="FunctionDeclaration">
|
||||
<Field Type="SyntaxToken" Name="functionKeyword" />
|
||||
<Field Type="FunctionOutputDescriptionSyntaxNode" Name="outputDescription" />
|
||||
<Field Type="SyntaxToken" Name="name" />
|
||||
<Field Type="FunctionInputDescriptionSyntaxNode" Name="inputDescription" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="commas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="FunctionOutputDescriptionSyntaxNode" BaseClass="SyntaxNode" Kind="FunctionOutputDescription">
|
||||
<Field Type="SyntaxList" Name="outputList" />
|
||||
<Field Type="SyntaxToken" Name="assignmentSign" />
|
||||
</Class>
|
||||
<Class Name="FunctionInputDescriptionSyntaxNode" BaseClass="SyntaxNode" Kind="FunctionInputDescription">
|
||||
<Field Type="SyntaxToken" Name="openingBracket" />
|
||||
<Field Type="SyntaxList" Name="parameterList" />
|
||||
<Field Type="SyntaxToken" Name="closingBracket" />
|
||||
</Class>
|
||||
<Class Name="SwitchStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="SwitchStatement">
|
||||
<Field Type="SyntaxToken" Name="switchKeyword" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="switchExpression" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="optionalCommas" />
|
||||
<Field Type="SyntaxList<SwitchCaseSyntaxNode>" Name="cases" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="SwitchCaseSyntaxNode" BaseClass="SyntaxNode" Kind="SwitchCase">
|
||||
<Field Type="SyntaxToken" Name="caseKeyword" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="caseIdentifier" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="optionalCommas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
</Class>
|
||||
<Class Name="WhileStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="WhileStatement">
|
||||
<Field Type="SyntaxToken" Name="whileKeyword" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="condition" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="optionalCommas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="ElseifClause" BaseClass="SyntaxNode" Kind="ElseifClause">
|
||||
<Field Type="SyntaxToken" Name="elseifKeyword" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="condition" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="optionalCommas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
</Class>
|
||||
<Class Name="ElseClause" BaseClass="SyntaxNode" Kind="ElseClause">
|
||||
<Field Type="SyntaxToken" Name="elseKeyword" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
</Class>
|
||||
<Class Name="IfStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="IfStatement">
|
||||
<Field Type="SyntaxToken" Name="ifKeyword" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="condition" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="optionalCommas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
<Field Type="SyntaxList<ElseifClause>" Name="elseifClauses" />
|
||||
<Field Type="ElseClause" Name="elseClause" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="ForStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="ForStatement">
|
||||
<Field Type="SyntaxToken" Name="forKeyword" />
|
||||
<Field Type="AssignmentExpressionSyntaxNode" Name="assignment" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="optionalCommas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="AssignmentExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="AssignmentExpression">
|
||||
<Field Type="ExpressionSyntaxNode" Name="lhs" />
|
||||
<Field Type="SyntaxToken" Name="assignmentSign" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="rhs" />
|
||||
</Class>
|
||||
<Class Name="CatchClauseSyntaxNode" BaseClass="SyntaxNode" Kind="CatchClause">
|
||||
<Field Type="SyntaxToken" Name="catchKeyword" />
|
||||
<Field Type="SyntaxList" Name="catchBody" />
|
||||
</Class>
|
||||
<Class Name="TryCatchStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="TryCatchStatement">
|
||||
<Field Type="SyntaxToken" Name="tryKeyword" />
|
||||
<Field Type="SyntaxList" Name="tryBody" />
|
||||
<Field Type="CatchClauseSyntaxNode" Name="catchClause" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="ExpressionStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="ExpressionStatement">
|
||||
<Field Type="ExpressionSyntaxNode" Name="expression" />
|
||||
</Class>
|
||||
<Class Name="EmptyStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="EmptyStatement">
|
||||
<Field Type="SyntaxToken" Name="semicolon" />
|
||||
</Class>
|
||||
<Class Name="EmptyExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="EmptyExpression">
|
||||
</Class>
|
||||
<Class Name="UnaryPrefixOperationExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="UnaryPrefixOperationExpression">
|
||||
<Field Type="SyntaxToken" Name="operation" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="operand" />
|
||||
</Class>
|
||||
<Class Name="CompoundNameSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="CompoundName">
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
</Class>
|
||||
<Class Name="NamedFunctionHandleSyntaxNode" BaseClass="FunctionHandleSyntaxNode" Kind="NamedFunctionHandle">
|
||||
<Field Type="SyntaxToken" Name="atSign" />
|
||||
<Field Type="CompoundNameSyntaxNode" Name="functionName" />
|
||||
</Class>
|
||||
<Class Name="LambdaSyntaxNode" BaseClass="FunctionHandleSyntaxNode" Kind="Lambda">
|
||||
<Field Type="SyntaxToken" Name="atSign" />
|
||||
<Field Type="FunctionInputDescriptionSyntaxNode" Name="input" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="body" />
|
||||
</Class>
|
||||
<Class Name="BinaryOperationExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="BinaryOperation">
|
||||
<Field Type="ExpressionSyntaxNode" Name="lhs" />
|
||||
<Field Type="SyntaxToken" Name="operation" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="rhs" />
|
||||
</Class>
|
||||
<Class Name="IdentifierNameSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="IdentifierName">
|
||||
<Field Type="SyntaxToken" Name="name" />
|
||||
</Class>
|
||||
<Class Name="NumberLiteralSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="NumberLiteralExpression">
|
||||
<Field Type="SyntaxToken" Name="number" />
|
||||
</Class>
|
||||
<Class Name="StringLiteralSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="StringLiteralExpression">
|
||||
<Field Type="SyntaxToken" Name="stringToken" />
|
||||
</Class>
|
||||
<Class Name="DoubleQuotedStringLiteralSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="DoubleQuotedStringLiteralExpression">
|
||||
<Field Type="SyntaxToken" Name="stringToken" />
|
||||
</Class>
|
||||
<Class Name="UnquotedStringLiteralSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="UnquotedStringLiteralExpression">
|
||||
<Field Type="SyntaxToken" Name="stringToken" />
|
||||
</Class>
|
||||
<Class Name="ArrayLiteralExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="ArrayLiteralExpression">
|
||||
<Field Type="SyntaxToken" Name="openingSquareBracket" />
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
<Field Type="SyntaxToken" Name="closingSquareBracket" />
|
||||
</Class>
|
||||
<Class Name="CellArrayLiteralExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="CellArrayLiteralExpression">
|
||||
<Field Type="SyntaxToken" Name="openingBrace" />
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
<Field Type="SyntaxToken" Name="closingBrace" />
|
||||
</Class>
|
||||
<Class Name="ParenthesizedExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="ParenthesizedExpression">
|
||||
<Field Type="SyntaxToken" Name="openingBracket" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="expression" />
|
||||
<Field Type="SyntaxToken" Name="closingBracket" />
|
||||
</Class>
|
||||
<Class Name="CellArrayElementAccessExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="CellArrayElementAccess">
|
||||
<Field Type="ExpressionSyntaxNode" Name="expression" />
|
||||
<Field Type="SyntaxToken" Name="openingBrace" />
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
<Field Type="SyntaxToken" Name="closingBrace" />
|
||||
</Class>
|
||||
<Class Name="FunctionCallExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="FunctionCall">
|
||||
<Field Type="ExpressionSyntaxNode" Name="functionName" />
|
||||
<Field Type="SyntaxToken" Name="openingBracket" />
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
<Field Type="SyntaxToken" Name="closingBracket" />
|
||||
</Class>
|
||||
<Class Name="MemberAccessSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="MemberAccess">
|
||||
<Field Type="SyntaxNode" Name="leftOperand" />
|
||||
<Field Type="SyntaxToken" Name="dot" />
|
||||
<Field Type="SyntaxNode" Name="rightOperand" />
|
||||
</Class>
|
||||
<Class Name="UnaryPostixOperationExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="UnaryPostfixOperationExpression">
|
||||
<Field Type="ExpressionSyntaxNode" Name="operand" />
|
||||
<Field Type="SyntaxToken" Name="operation" />
|
||||
</Class>
|
||||
<Class Name="IndirectMemberAccessSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="IndirectMemberAccess">
|
||||
<Field Type="SyntaxToken" Name="openingBracket" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="expression" />
|
||||
<Field Type="SyntaxToken" Name="closingBracket" />
|
||||
</Class>
|
||||
<Class Name="CommandExpressionSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="Command">
|
||||
<Field Type="IdentifierNameSyntaxNode" Name="commandName" />
|
||||
<Field Type="SyntaxList<UnquotedStringLiteralSyntaxNode>" Name="arguments" />
|
||||
</Class>
|
||||
<Class Name="BaseClassInvokationSyntaxNode" BaseClass="ExpressionSyntaxNode" Kind="ClassInvokation">
|
||||
<Field Type="ExpressionSyntaxNode" Name="methodName" />
|
||||
<Field Type="SyntaxToken" Name="atSign" />
|
||||
<Field Type="ExpressionSyntaxNode" Name="baseClassNameAndArguments" />
|
||||
</Class>
|
||||
<Class Name="AttributeAssignmentSyntaxNode" BaseClass="SyntaxNode" Kind="AttributeAssignment">
|
||||
<Field Type="SyntaxToken" Name="assignmentSign"/>
|
||||
<Field Type="ExpressionSyntaxNode" Name="value" />
|
||||
</Class>
|
||||
<Class Name="AttributeSyntaxNode" BaseClass="SyntaxNode" Kind="Attribute">
|
||||
<Field Type="IdentifierNameSyntaxNode" Name="name" />
|
||||
<Field Type="AttributeAssignmentSyntaxNode" Name="assignment" />
|
||||
</Class>
|
||||
<Class Name="AttributeListSyntaxNode" BaseClass="SyntaxNode" Kind="AttributeList">
|
||||
<Field Type="SyntaxToken" Name="openingBracket" />
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
<Field Type="SyntaxToken" Name="closingBracket" />
|
||||
</Class>
|
||||
<Class Name="MethodDefinitionSyntaxNode" BaseClass="MethodDeclarationSyntaxNode" Kind="MethodDefinition">
|
||||
<Field Type="SyntaxToken" Name="functionKeyword" />
|
||||
<Field Type="FunctionOutputDescriptionSyntaxNode" Name="outputDescription" />
|
||||
<Field Type="CompoundNameSyntaxNode" Name="name" />
|
||||
<Field Type="FunctionInputDescriptionSyntaxNode" Name="inputDescription" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="commas" />
|
||||
<Field Type="SyntaxList" Name="body" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="AbstractMethodDeclarationSyntaxNode" BaseClass="MethodDeclarationSyntaxNode" Kind="AbstractMethodDeclaration">
|
||||
<Field Type="FunctionOutputDescriptionSyntaxNode" Name="outputDescription" />
|
||||
<Field Type="CompoundNameSyntaxNode" Name="name" />
|
||||
<Field Type="FunctionInputDescriptionSyntaxNode" Name="inputDescription" />
|
||||
</Class>
|
||||
<Class Name="MethodsListSyntaxNode" BaseClass="SyntaxNode" Kind="MethodsList">
|
||||
<Field Type="SyntaxToken" Name="methodsKeyword" />
|
||||
<Field Type="AttributeListSyntaxNode" Name="attributes" />
|
||||
<Field Type="SyntaxList" Name="methods" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="PropertiesListSyntaxNode" BaseClass="SyntaxNode" Kind="PropertiesList">
|
||||
<Field Type="SyntaxToken" Name="propertiesKeyword" />
|
||||
<Field Type="AttributeListSyntaxNode" Name="attributes" />
|
||||
<Field Type="SyntaxList" Name="properties" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="BaseClassListSyntaxNode" BaseClass="SyntaxNode" Kind="BaseClassList">
|
||||
<Field Type="SyntaxToken" Name="lessSign" />
|
||||
<Field Type="SyntaxList" Name="baseClasses" />
|
||||
</Class>
|
||||
<Class Name="ClassDeclarationSyntaxNode" BaseClass="StatementSyntaxNode" Kind="ClassDeclaration">
|
||||
<Field Type="SyntaxToken" Name="classdefKeyword" />
|
||||
<Field Type="AttributeListSyntaxNode" Name="attributes" />
|
||||
<Field Type="IdentifierNameSyntaxNode" Name="className" />
|
||||
<Field Type="BaseClassListSyntaxNode" Name="baseClassList" />
|
||||
<Field Type="SyntaxList" Name="nodes" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="EnumerationItemValueSyntaxNode" BaseClass="SyntaxNode" Kind="EnumerationItemValue">
|
||||
<Field Type="SyntaxToken" Name="openingBracket" />
|
||||
<Field Type="SyntaxList" Name="values" />
|
||||
<Field Type="SyntaxToken" Name="closingBracket" />
|
||||
</Class>
|
||||
<Class Name="EnumerationItemSyntaxNode" BaseClass="SyntaxNode" Kind="EnumerationItem">
|
||||
<Field Type="IdentifierNameSyntaxNode" Name="name" />
|
||||
<Field Type="EnumerationItemValueSyntaxNode" Name="values" />
|
||||
<Field Type="SyntaxList<SyntaxToken>" Name="commas" />
|
||||
</Class>
|
||||
<Class Name="EnumerationListSyntaxNode" BaseClass="SyntaxNode" Kind="EnumerationList">
|
||||
<Field Type="SyntaxToken" Name="enumerationKeyword" />
|
||||
<Field Type="AttributeListSyntaxNode" Name="attributes" />
|
||||
<Field Type="SyntaxList<EnumerationItemSyntaxNode>" Name="items" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
<Class Name="EventsListSyntaxNode" BaseClass="SyntaxNode" Kind="EventsList">
|
||||
<Field Type="SyntaxToken" Name="eventsKeyword" />
|
||||
<Field Type="AttributeListSyntaxNode" Name="attributes" />
|
||||
<Field Type="SyntaxList" Name="events" />
|
||||
<Field Type="SyntaxToken" Name="endKeyword" />
|
||||
</Class>
|
||||
</Syntax>
|
Loading…
x
Reference in New Issue
Block a user