Merge pull request #6 from mahalex/more-evaluate

More evaluate
This commit is contained in:
Alexander Luzgarev 2022-04-21 09:14:36 -07:00 committed by GitHub
commit e11476f614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 2616 additions and 348 deletions

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Parser\Parser.csproj" />

View File

@ -1,4 +1,6 @@
using Xunit;
using FluentAssertions;
using System;
namespace Parser.Tests
{
@ -37,12 +39,33 @@ namespace Parser.Tests
var actual = sut.Parse();
var statement = actual.Root.Body.Statements[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement);
if (statement is null)
{
throw new System.Exception();
}
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement!).Expression);
}
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement).Expression);
[Fact]
public void ParseExpressionStatementWithSemicolon()
{
var text = "2 + 3;";
var sut = GetSut(text);
var actual = sut.Parse();
Assert.Single(actual.Root.Body.Statements);
var statement = actual.Root.Body.Statements[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement);
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement!).Expression);
}
[Fact]
public void ParseExpressionStatementWithSemicolonAfterNewLine()
{
var text = "2 + 3\n;";
var sut = GetSut(text);
var actual = sut.Parse();
Assert.Equal(2, actual.Root.Body.Statements.Count);
var statement1 = actual.Root.Body.Statements[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement1);
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement1!).Expression);
var statement2 = actual.Root.Body.Statements[1].AsToken();
Assert.Equal(TokenKind.SemicolonToken, statement2.Kind);
}
[Fact]
@ -74,7 +97,7 @@ namespace Parser.Tests
[Fact]
public void ProvidePosition()
{
var text = "2 + 3";
var text = "% Comment\n 2 + 3";
var sut = GetSut(text);
var actual = sut.Parse();
var statement = actual.Root.Body.Statements[0].AsNode() as ExpressionStatementSyntaxNode;
@ -83,8 +106,79 @@ namespace Parser.Tests
var operation = expression.Operation;
var rhs = expression.Rhs;
Assert.Equal(0, lhs.Position);
Assert.Equal(2, operation.Position);
Assert.Equal(4, rhs.Position);
Assert.Equal(14, operation.Position);
Assert.Equal(16, rhs.Position);
}
[Fact]
public void ProvideFullSpan()
{
var text = "% Comment\n 2 + 3";
var sut = GetSut(text);
var actual = sut.Parse();
var statement = actual.Root.Body.Statements[0].AsNode() as ExpressionStatementSyntaxNode;
var expression = statement!.Expression as BinaryOperationExpressionSyntaxNode;
var lhs = expression!.Lhs;
var operation = expression.Operation;
var rhs = expression.Rhs;
expression.FullSpan.Start.Should().Be(0);
expression.FullSpan.End.Should().Be(17);
lhs.FullSpan.Start.Should().Be(0);
lhs.FullSpan.End.Should().Be(14);
operation.FullSpan.Start.Should().Be(14);
operation.FullSpan.End.Should().Be(16);
rhs.FullSpan.Start.Should().Be(16);
rhs.FullSpan.End.Should().Be(17);
}
[Fact]
public void ProvideSpan()
{
var text = "% Comment\n 2 + 3";
var sut = GetSut(text);
var actual = sut.Parse();
var statement = actual.Root.Body.Statements[0].AsNode() as ExpressionStatementSyntaxNode;
var expression = statement!.Expression as BinaryOperationExpressionSyntaxNode;
var lhs = expression!.Lhs;
var operation = expression.Operation;
var rhs = expression.Rhs;
expression.Span.Start.Should().Be(12);
expression.Span.End.Should().Be(17);
lhs.Span.Start.Should().Be(12);
lhs.Span.End.Should().Be(13);
operation.Span.Start.Should().Be(14);
operation.Span.End.Should().Be(15);
rhs.Span.Start.Should().Be(16);
rhs.Span.End.Should().Be(17);
}
[Fact]
public void NotHangOnUnknownSyntax()
{
var text = @"
classdef myClass
properties
Channel;
NodeID;
Node;
end
methods
function this = sendData(this, arg1, arg2)
arguments
this (1,1) myClass
arg1 (1,1) double {mustBeNonnegative}
arg2 (1,1) double {mustBeNonnegative}
end
If (arg1 = 0)
this.NodeID = 3;
end
end
function this = getData(this, arg1, arg2)
end
end";
var sut = GetSut(text);
Func<SyntaxTree> action = sut.Parse;
action.Should().Throw<ParsingException>();
}
}
}

View File

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Xunit;
namespace Parser.Tests
@ -56,7 +57,35 @@ namespace Parser.Tests
Assert.Equal(text, actualText);
Assert.Equal(text.Length, actualWidth);
}
[Theory]
[MemberData(nameof(FilesData))]
public void TestLeadingAndTrailingTrivia(string fileName)
{
var text = File.ReadAllText(fileName);
var window = new TextWindowWithNull(text, fileName);
var parser = CreateParser(window);
var tree = parser.Parse();
var sb = new StringBuilder();
var maybeLeadingTrivia = tree.Root.LeadingTrivia;
var maybeTrailingTrivia = tree.Root.TrailingTrivia;
if (maybeLeadingTrivia is SyntaxTriviaList leadingTrivia)
{
sb.Append(leadingTrivia.FullText);
}
sb.Append(tree.Root.Text);
if (maybeTrailingTrivia is SyntaxTriviaList trailingTrivia)
{
sb.Append(trailingTrivia.FullText);
}
var actualText = sb.ToString();
Assert.Equal(text, actualText);
}
public static IEnumerable<object[]> FilesData()
{
return Files().Select(fileName => new object[] { fileName });

View File

@ -13,24 +13,37 @@ namespace Parser.Binding
{
private readonly DiagnosticsBag _diagnostics = new DiagnosticsBag();
public static BoundProgram BindProgram(SyntaxTree syntaxTree)
private BoundProgram BindProgramInternal(SyntaxTree syntaxTree)
{
var binder = new Binder();
var boundRoot = binder.BindRoot(syntaxTree.NullRoot);
var boundRoot = BindRoot(syntaxTree.NullRoot);
var statements = ((BoundBlockStatement)boundRoot.File.Body).Statements;
var functionsBuilder = ImmutableDictionary.CreateBuilder<FunctionSymbol, LoweredFunction>();
var globalStatements = statements.Where(s => s.Kind != BoundNodeKind.FunctionDeclaration).ToArray();
var mainFunction = (FunctionSymbol?)null;
var scriptFunction = (FunctionSymbol?)null;
var functions = statements.OfType<BoundFunctionDeclaration>().ToArray();
if (globalStatements.Length > 0)
{
// we have to gather all bound expression statements into a "script" function.
scriptFunction = new FunctionSymbol("%script");
// we have to gather all bound expression statements into a "Main" function.
scriptFunction = new FunctionSymbol("Main");
foreach (var f in functions) {
if (f.Name == "Main")
{
_diagnostics.ReportMainIsNotAllowed(
f.Syntax.Span);
return new BoundProgram(
_diagnostics.ToImmutableArray(),
mainFunction: null,
scriptFunction: null,
functions: functionsBuilder.ToImmutable());
}
}
var body = Block(globalStatements[0].Syntax, globalStatements);
var loweredBody = Lowerer.Lower(body);
var declaration = new BoundFunctionDeclaration(
syntax: globalStatements[0].Syntax,
name: "%script",
name: "Main",
inputDescription: ImmutableArray<ParameterSymbol>.Empty,
outputDescription: ImmutableArray<ParameterSymbol>.Empty,
body: body);
@ -38,7 +51,7 @@ namespace Parser.Binding
functionsBuilder.Add(scriptFunction, loweredFunction);
}
var functions = statements.OfType<BoundFunctionDeclaration>().ToArray();
var first = true;
foreach (var function in functions)
{
@ -55,12 +68,18 @@ namespace Parser.Binding
}
return new BoundProgram(
binder._diagnostics.ToImmutableArray(),
_diagnostics.ToImmutableArray(),
mainFunction,
scriptFunction,
functionsBuilder.ToImmutable());
}
public static BoundProgram BindProgram(SyntaxTree syntaxTree)
{
var binder = new Binder();
return binder.BindProgramInternal(syntaxTree);
}
private static LoweredFunction LowerFunction(BoundFunctionDeclaration declaration)
{
var loweredBody = Lowerer.Lower(declaration.Body);
@ -101,7 +120,7 @@ namespace Parser.Binding
TokenKind.ExpressionStatement =>
BindExpressionStatement((ExpressionStatementSyntaxNode)node),
TokenKind.ForStatement =>
BindForStatement((ForStatementSyntaxNode)node),
BindForStatement((ForStatementSyntaxNode)node)!,
TokenKind.FunctionDeclaration =>
BindFunctionDeclaration((FunctionDeclarationSyntaxNode)node),
TokenKind.IfStatement =>
@ -117,11 +136,6 @@ namespace Parser.Binding
};
}
private BoundWhileStatement BindWhileStatement(WhileStatementSyntaxNode node)
{
throw new NotImplementedException();
}
private BoundTryCatchStatement BindTryCatchStatement(TryCatchStatementSyntaxNode node)
{
throw new NotImplementedException();
@ -250,15 +264,42 @@ namespace Parser.Binding
return new ParameterSymbol(parameter.Text);
}
private BoundForStatement BindForStatement(ForStatementSyntaxNode node)
private BoundForStatement? BindForStatement(ForStatementSyntaxNode node)
{
throw new NotImplementedException();
var loopVariable = BindLoopVariable(node.Assignment.Lhs);
if (loopVariable is null)
{
return null;
}
var loopedExpression = BindExpression(node.Assignment.Rhs);
var body = BindStatement(node.Body);
return ForStatement(node, loopVariable, loopedExpression, body);
}
private BoundIdentifierNameExpression? BindLoopVariable(ExpressionSyntaxNode node)
{
if (node.Kind != TokenKind.IdentifierNameExpression)
{
_diagnostics.ReportForLoopWithoutVariable(node.Span);
return null;
}
return Identifier(node, ((IdentifierNameExpressionSyntaxNode)node).Name.Text);
}
private BoundWhileStatement BindWhileStatement(WhileStatementSyntaxNode node)
{
var condition = BindConversion(BindExpression(node.Condition), TypeSymbol.Boolean);
var body = BindStatement(node.Body);
return WhileStatement(node, condition, body);
}
private BoundExpressionStatement BindExpressionStatement(ExpressionStatementSyntaxNode node)
{
var expression = BindExpression(node.Expression);
return ExpressionStatement(node, expression);
var discardResult = node.Semicolon is not null;
return ExpressionStatement(node, expression, discardResult);
}
private BoundExpression BindExpression(ExpressionSyntaxNode node)
@ -326,11 +367,37 @@ namespace Parser.Binding
return Assignment(node, left, right);
}
private BoundExpression BindConversion(BoundExpression expression, TypeSymbol targetType)
{
var conversion = Conversion.Classify(expression.Type, targetType);
if (!conversion.Exists)
{
return new BoundErrorExpression(expression.Syntax);
}
if (conversion.IsIdentity)
{
return expression;
}
return Conversion(expression.Syntax, targetType, expression);
}
private BoundBinaryOperationExpression BindBinaryOperationExpression(BinaryOperationExpressionSyntaxNode node)
{
var left = BindExpression(node.Lhs);
var right = BindExpression(node.Rhs);
return BinaryOperation(node, left, node.Operation.Kind, right);
var op = BoundBinaryOperator.GetOperator(node.Operation.Kind, left.Type, right.Type);
if (op is null)
{
throw new Exception($"Unknown binary operator '{node.Operation.Kind}'.");
}
return BinaryOperation(
node,
BindConversion(left, op.Left),
op,
BindConversion(right, op.Right));
}
private BoundCellArrayElementAccessExpression BindCellArrayElementAccessExpression(CellArrayElementAccessExpressionSyntaxNode node)
@ -403,7 +470,15 @@ namespace Parser.Binding
private BoundNumberLiteralExpression BindNumberLiteralExpression(NumberLiteralExpressionSyntaxNode node)
{
var value = (double)node.Number.Value!;
return NumberLiteral(node, value);
var intValue = (int)Math.Round(value);
if (intValue == value)
{
return NumberIntLiteral(node, intValue);
}
else
{
return NumberDoubleLiteral(node, value);
}
}
private BoundExpression BindParenthesizedExpression(ParenthesizedExpressionSyntaxNode node)
@ -420,7 +495,16 @@ namespace Parser.Binding
private BoundUnaryOperationExpression BindUnaryPrefixOperationExpression(UnaryPrefixOperationExpressionSyntaxNode node)
{
var operand = BindExpression(node.Operand);
return UnaryOperation(node, node.Operation.Kind, operand);
var op = BoundUnaryOperator.GetOperator(node.Operation.Kind, operand.Type);
if (op is null)
{
throw new Exception($"Unknown binary operator '{node.Operation.Kind}'.");
}
return UnaryOperation(
node,
op,
BindConversion(operand, op.Result));
}
private BoundUnaryOperationExpression BindUnaryPostfixOperationExpression(UnaryPostfixOperationExpressionSyntaxNode node)

View File

@ -4,45 +4,164 @@ namespace Parser.Binding
{
public class BoundBinaryOperator
{
private static BoundBinaryOperator[] _operators =
private static BoundBinaryOperator[] _specificOperators =
{
new BoundBinaryOperator(TokenKind.EqualsToken, BoundBinaryOperatorKind.Equals),
new BoundBinaryOperator(TokenKind.PipePipeToken, BoundBinaryOperatorKind.PipePipe),
new BoundBinaryOperator(TokenKind.AmpersandAmpersandToken, BoundBinaryOperatorKind.AmpersandAmpersand),
new BoundBinaryOperator(TokenKind.PipeToken, BoundBinaryOperatorKind.Pipe),
new BoundBinaryOperator(TokenKind.AmpersandToken, BoundBinaryOperatorKind.Ampersand),
new BoundBinaryOperator(TokenKind.LessToken, BoundBinaryOperatorKind.Less),
new BoundBinaryOperator(TokenKind.LessOrEqualsToken, BoundBinaryOperatorKind.LessOrEquals),
new BoundBinaryOperator(TokenKind.GreaterToken, BoundBinaryOperatorKind.Greater),
new BoundBinaryOperator(TokenKind.GreaterOrEqualsToken, BoundBinaryOperatorKind.GreaterOrEquals),
new BoundBinaryOperator(TokenKind.EqualsEqualsToken, BoundBinaryOperatorKind.EqualsEquals),
new BoundBinaryOperator(TokenKind.TildeEqualsToken, BoundBinaryOperatorKind.TildeEquals),
new BoundBinaryOperator(TokenKind.ColonToken, BoundBinaryOperatorKind.Colon),
new BoundBinaryOperator(TokenKind.PlusToken, BoundBinaryOperatorKind.Plus),
new BoundBinaryOperator(TokenKind.MinusToken, BoundBinaryOperatorKind.Minus),
new BoundBinaryOperator(TokenKind.StarToken, BoundBinaryOperatorKind.Star),
new BoundBinaryOperator(TokenKind.DotStarToken, BoundBinaryOperatorKind.DotStar),
new BoundBinaryOperator(TokenKind.SlashToken, BoundBinaryOperatorKind.Slash),
new BoundBinaryOperator(TokenKind.DotSlashToken, BoundBinaryOperatorKind.DotSlash),
new BoundBinaryOperator(TokenKind.BackslashToken, BoundBinaryOperatorKind.Backslash),
new BoundBinaryOperator(TokenKind.DotBackslashToken, BoundBinaryOperatorKind.DotBackslash),
new BoundBinaryOperator(TokenKind.TildeToken, BoundBinaryOperatorKind.Tilde),
new BoundBinaryOperator(TokenKind.CaretToken, BoundBinaryOperatorKind.Caret),
new BoundBinaryOperator(TokenKind.DotCaretToken, BoundBinaryOperatorKind.DotCaret),
new BoundBinaryOperator(
TokenKind.LessToken,
BoundBinaryOperatorKind.LessInt,
TypeSymbol.Int,
TypeSymbol.Boolean),
new BoundBinaryOperator(
TokenKind.PlusToken,
BoundBinaryOperatorKind.PlusInt,
TypeSymbol.Int),
};
public BoundBinaryOperator(TokenKind syntaxKind, BoundBinaryOperatorKind kind)
private static BoundBinaryOperator[] _defaultOperators =
{
new BoundBinaryOperator(
TokenKind.EqualsToken,
BoundBinaryOperatorKind.Equals,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.PipePipeToken,
BoundBinaryOperatorKind.PipePipe,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.AmpersandAmpersandToken,
BoundBinaryOperatorKind.AmpersandAmpersand,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.PipeToken,
BoundBinaryOperatorKind.Pipe,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.AmpersandToken,
BoundBinaryOperatorKind.Ampersand,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.LessToken,
BoundBinaryOperatorKind.Less,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.LessOrEqualsToken,
BoundBinaryOperatorKind.LessOrEquals,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.GreaterToken,
BoundBinaryOperatorKind.Greater,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.GreaterOrEqualsToken,
BoundBinaryOperatorKind.GreaterOrEquals,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.EqualsEqualsToken,
BoundBinaryOperatorKind.EqualsEquals,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.TildeEqualsToken,
BoundBinaryOperatorKind.TildeEquals,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.ColonToken,
BoundBinaryOperatorKind.Colon,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.PlusToken,
BoundBinaryOperatorKind.Plus,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.MinusToken,
BoundBinaryOperatorKind.Minus,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.StarToken,
BoundBinaryOperatorKind.Star,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.DotStarToken,
BoundBinaryOperatorKind.DotStar,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.SlashToken,
BoundBinaryOperatorKind.Slash,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.DotSlashToken,
BoundBinaryOperatorKind.DotSlash,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.BackslashToken,
BoundBinaryOperatorKind.Backslash,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.DotBackslashToken,
BoundBinaryOperatorKind.DotBackslash,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.TildeToken,
BoundBinaryOperatorKind.Tilde,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.CaretToken,
BoundBinaryOperatorKind.Caret,
TypeSymbol.MObject),
new BoundBinaryOperator(
TokenKind.DotCaretToken,
BoundBinaryOperatorKind.DotCaret,
TypeSymbol.MObject),
};
public BoundBinaryOperator(
TokenKind syntaxKind,
BoundBinaryOperatorKind kind,
TypeSymbol type)
{
SyntaxKind = syntaxKind;
Kind = kind;
Left = type;
Right = type;
Result = type;
}
public BoundBinaryOperator(
TokenKind syntaxKind,
BoundBinaryOperatorKind kind,
TypeSymbol operand,
TypeSymbol result)
{
SyntaxKind = syntaxKind;
Kind = kind;
Left = operand;
Right = operand;
Result = result;
}
public BoundBinaryOperator(
TokenKind syntaxKind,
BoundBinaryOperatorKind kind,
TypeSymbol left,
TypeSymbol right,
TypeSymbol result)
{
SyntaxKind = syntaxKind;
Kind = kind;
Left = left;
Right = right;
Result = result;
}
public TokenKind SyntaxKind { get; }
public BoundBinaryOperatorKind Kind { get; }
public TypeSymbol Left { get; }
public TypeSymbol Right { get; }
public TypeSymbol Result { get; }
internal static BoundBinaryOperator? GetOperator(TokenKind kind)
internal static BoundBinaryOperator? GetOperator(TokenKind kind, TypeSymbol left, TypeSymbol right)
{
return _operators.FirstOrDefault(op => op.SyntaxKind == kind);
return _specificOperators.FirstOrDefault(op => op.SyntaxKind == kind && op.Left == left && op.Right == right)
?? _defaultOperators.FirstOrDefault(op => op.SyntaxKind == kind);
}
}
}

View File

@ -25,5 +25,7 @@
Tilde,
Caret,
DotCaret,
LessInt,
PlusInt,
}
}

View File

@ -25,9 +25,17 @@ namespace Parser.Binding
return new BoundBlockStatement(syntax, statements);
}
public static BoundExpressionStatement ExpressionStatement(SyntaxNode syntax, BoundExpression expression)
public static BoundConversionExpression Conversion(SyntaxNode syntax, TypeSymbol targetType, BoundExpression expression)
{
return new BoundExpressionStatement(syntax, expression);
return new BoundConversionExpression(syntax, targetType, expression);
}
public static BoundExpressionStatement ExpressionStatement(
SyntaxNode syntax,
BoundExpression expression,
bool discardResult)
{
return new BoundExpressionStatement(syntax, expression, discardResult);
}
public static BoundIfStatement IfStatement(
@ -40,6 +48,15 @@ namespace Parser.Binding
return new BoundIfStatement(syntax, condition, body, elseifClauses, elseClause);
}
public static BoundForStatement ForStatement(
SyntaxNode syntax,
BoundIdentifierNameExpression loopVariable,
BoundExpression loopExpression,
BoundStatement body)
{
return new BoundForStatement(syntax, loopVariable, loopExpression, body);
}
public static BoundLabelStatement LabelStatement(
SyntaxNode syntax,
BoundLabel label)
@ -58,10 +75,9 @@ namespace Parser.Binding
public static BoundBinaryOperationExpression BinaryOperation(
SyntaxNode syntax,
BoundExpression left,
TokenKind kind,
BoundBinaryOperator op,
BoundExpression right)
{
var op = BindBinaryOperator(kind);
return new BoundBinaryOperationExpression(syntax, left, op, right);
}
@ -117,6 +133,14 @@ namespace Parser.Binding
return new BoundGotoStatement(syntax, label);
}
public static BoundWhileStatement WhileStatement(
SyntaxNode syntax,
BoundExpression condition,
BoundStatement body)
{
return new BoundWhileStatement(syntax, condition, body);
}
public static BoundIdentifierNameExpression Identifier(
SyntaxNode syntax,
string name)
@ -124,11 +148,18 @@ namespace Parser.Binding
return new BoundIdentifierNameExpression(syntax, name);
}
public static BoundNumberLiteralExpression NumberLiteral(
public static BoundNumberDoubleLiteralExpression NumberDoubleLiteral(
SyntaxNode syntax,
double value)
{
return new BoundNumberLiteralExpression(syntax, value);
return new BoundNumberDoubleLiteralExpression(syntax, value);
}
public static BoundNumberIntLiteralExpression NumberIntLiteral(
SyntaxNode syntax,
int value)
{
return new BoundNumberIntLiteralExpression(syntax, value);
}
public static BoundStringLiteralExpression StringLiteral(
@ -146,25 +177,38 @@ namespace Parser.Binding
return new BoundElseifClause(syntax, condition, body);
}
public static BoundTypedVariableDeclaration TypedVariableDeclaration(
SyntaxNode syntax,
TypedVariableSymbol variable,
BoundExpression initializer)
{
return new BoundTypedVariableDeclaration(syntax, variable, initializer);
}
public static BoundTypedVariableExpression TypedVariableExpression(
SyntaxNode syntax,
TypedVariableSymbol variable)
{
return new BoundTypedVariableExpression(syntax, variable);
}
public static BoundUnaryOperationExpression UnaryOperation(
SyntaxNode syntax,
TokenKind kind,
BoundUnaryOperator op,
BoundExpression operand)
{
var op = BindUnaryOperator(kind);
return new BoundUnaryOperationExpression(syntax, op, operand);
}
private static BoundUnaryOperator BindUnaryOperator(TokenKind kind)
public static BoundExpression TypedFunctionCall(
SyntaxNode syntax,
TypedFunctionSymbol function,
ImmutableArray<BoundExpression> arguments)
{
return BoundUnaryOperator.GetOperator(kind)
?? throw new Exception($"Unexpected unary operator kind {kind}.");
}
private static BoundBinaryOperator BindBinaryOperator(TokenKind kind)
{
return BoundBinaryOperator.GetOperator(kind)
?? throw new Exception($"Unexpected binary operator kind {kind}.");
return new BoundTypedFunctionCallExpression(
syntax,
function,
arguments);
}
}
}

View File

@ -21,6 +21,7 @@
LabelStatement,
SwitchStatement,
TryCatchStatement,
TypedVariableDeclaration,
WhileStatement,
// Expressions
@ -33,22 +34,27 @@
ClassInvokationExpression,
CommandExpression,
CompoundNameExpression,
ConversionExpression,
DoubleQuotedStringLiteralExpression,
EmptyExpression,
ErrorExpression,
FunctionCallExpression,
IdentifierNameExpression,
IndirectMemberAccessExpression,
LambdaExpression,
MemberAccessExpression,
NamedFunctionHandleExpression,
NumberLiteralExpression,
NumberDoubleLiteralExpression,
NumberIntLiteralExpression,
ParenthesizedExpression,
StringLiteralExpression,
TypedFunctionCallExpression,
TypedVariableExpression,
UnaryOperationExpression,
UnquotedStringLiteralExpression,
// Parts
ElseIfClause,
ElseClause
ElseClause,
}
}

View File

@ -118,24 +118,40 @@ namespace Parser.Binding
public class BoundExpressionStatement : BoundStatement
{
public BoundExpression Expression { get; }
public override BoundNodeKind Kind => BoundNodeKind.ExpressionStatement;
public BoundExpressionStatement(SyntaxNode syntax, BoundExpression expression)
public BoundExpressionStatement(SyntaxNode syntax, BoundExpression expression, bool discardResult)
: base(syntax)
{
Expression = expression;
DiscardResult = discardResult;
}
public BoundExpression Expression { get; }
public bool DiscardResult { get; }
public override BoundNodeKind Kind => BoundNodeKind.ExpressionStatement;
}
public class BoundForStatement : BoundStatement
{
public BoundForStatement(SyntaxNode syntax)
public BoundForStatement(
SyntaxNode syntax,
BoundIdentifierNameExpression loopVariable,
BoundExpression loopedExpression,
BoundStatement body)
: base(syntax)
{
LoopVariable = loopVariable;
LoopedExpression = loopedExpression;
Body = body;
}
public BoundIdentifierNameExpression LoopVariable { get; }
public BoundExpression LoopedExpression { get; }
public BoundStatement Body { get; }
public override BoundNodeKind Kind => BoundNodeKind.ForStatement;
}
@ -240,12 +256,18 @@ namespace Parser.Binding
public class BoundWhileStatement : BoundStatement
{
public BoundWhileStatement(SyntaxNode syntax)
public BoundWhileStatement(SyntaxNode syntax, BoundExpression condition, BoundStatement body)
: base(syntax)
{
Condition = condition;
Body = body;
}
public override BoundNodeKind Kind => BoundNodeKind.WhileStatement;
public BoundExpression Condition { get; }
public BoundStatement Body { get; }
}
public abstract class BoundExpression : BoundNode
@ -254,6 +276,8 @@ namespace Parser.Binding
: base(syntax)
{
}
public abstract TypeSymbol Type { get; }
}
public class BoundArrayLiteralExpression : BoundExpression
@ -264,6 +288,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.ArrayLiteralExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundAssignmentExpression : BoundExpression
@ -279,6 +305,8 @@ namespace Parser.Binding
public BoundExpression Right { get; }
public override BoundNodeKind Kind => BoundNodeKind.AssignmentExpression;
public override TypeSymbol Type => Right.Type;
}
public class BoundBinaryOperationExpression : BoundExpression
@ -296,6 +324,8 @@ namespace Parser.Binding
public BoundExpression Right { get; }
public override BoundNodeKind Kind => BoundNodeKind.BinaryOperationExpression;
public override TypeSymbol Type => Op.Result;
}
public class BoundCellArrayElementAccessExpression : BoundExpression
@ -306,6 +336,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.CellArrayElementAccessExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundCellArrayLiteralExpression : BoundExpression
@ -316,6 +348,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.CellArrayLiteralExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundClassInvokationExpression : BoundExpression
@ -326,6 +360,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.ClassInvokationExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundCommandExpression : BoundExpression
@ -336,6 +372,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.CommandExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundCompoundNameExpression : BoundExpression
@ -346,6 +384,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.CompoundNameExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundDoubleQuotedStringLiteralExpression : BoundExpression
@ -356,6 +396,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.DoubleQuotedStringLiteralExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundEmptyExpression : BoundExpression
@ -366,6 +408,20 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.EmptyExpression;
public override TypeSymbol Type => TypeSymbol.Null;
}
public class BoundErrorExpression : BoundExpression
{
public BoundErrorExpression(SyntaxNode syntax)
: base(syntax)
{
}
public override BoundNodeKind Kind => BoundNodeKind.ErrorExpression;
public override TypeSymbol Type => TypeSymbol.Error;
}
public class BoundFunctionCallExpression : BoundExpression
@ -380,6 +436,27 @@ namespace Parser.Binding
public BoundExpression Name { get; }
public ImmutableArray<BoundExpression> Arguments { get; }
public override BoundNodeKind Kind => BoundNodeKind.FunctionCallExpression;
public override TypeSymbol Type => TypeSymbol.MObject;
}
public class BoundTypedFunctionCallExpression : BoundExpression
{
public BoundTypedFunctionCallExpression(
SyntaxNode syntax,
TypedFunctionSymbol function,
ImmutableArray<BoundExpression> arguments)
: base(syntax)
{
Function = function;
Arguments = arguments;
}
public TypedFunctionSymbol Function { get; }
public ImmutableArray<BoundExpression> Arguments { get; }
public override BoundNodeKind Kind => BoundNodeKind.TypedFunctionCallExpression;
public override TypeSymbol Type => Function.ReturnType;
}
public class BoundIdentifierNameExpression : BoundExpression
@ -392,6 +469,8 @@ namespace Parser.Binding
public string Name { get; }
public override BoundNodeKind Kind => BoundNodeKind.IdentifierNameExpression;
public override TypeSymbol Type => TypeSymbol.MObject;
}
public class BoundIndirectMemberAccessExpression : BoundExpression
@ -402,6 +481,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.IndirectMemberAccessExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundLambdaExpression : BoundExpression
@ -412,6 +493,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.LambdaExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundMemberAccessExpression : BoundExpression
@ -422,6 +505,8 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.MemberAccessExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundNamedFunctionHandleExpression : BoundExpression
@ -432,18 +517,43 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.NamedFunctionHandleExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundNumberLiteralExpression : BoundExpression
public abstract class BoundNumberLiteralExpression : BoundExpression
{
public BoundNumberLiteralExpression(SyntaxNode syntax, double value)
protected BoundNumberLiteralExpression(SyntaxNode syntax) : base(syntax)
{
}
}
public class BoundNumberDoubleLiteralExpression : BoundNumberLiteralExpression
{
public BoundNumberDoubleLiteralExpression(SyntaxNode syntax, double value)
: base(syntax)
{
Value = value;
}
public double Value { get; }
public override BoundNodeKind Kind => BoundNodeKind.NumberLiteralExpression;
public override BoundNodeKind Kind => BoundNodeKind.NumberDoubleLiteralExpression;
public override TypeSymbol Type => TypeSymbol.Double;
}
public class BoundNumberIntLiteralExpression : BoundNumberLiteralExpression
{
public BoundNumberIntLiteralExpression(SyntaxNode syntax, int value)
: base(syntax)
{
Value = value;
}
public int Value { get; }
public override BoundNodeKind Kind => BoundNodeKind.NumberIntLiteralExpression;
public override TypeSymbol Type => TypeSymbol.Int;
}
public class BoundStringLiteralExpression : BoundExpression
@ -456,6 +566,39 @@ namespace Parser.Binding
public string Value { get; }
public override BoundNodeKind Kind => BoundNodeKind.StringLiteralExpression;
public override TypeSymbol Type => TypeSymbol.String;
}
public class BoundTypedVariableDeclaration : BoundStatement
{
public BoundTypedVariableDeclaration(SyntaxNode syntax, TypedVariableSymbol variable, BoundExpression initializer)
: base(syntax)
{
Variable = variable;
Initializer = initializer;
}
public TypedVariableSymbol Variable { get; }
public BoundExpression Initializer { get; }
public override BoundNodeKind Kind => BoundNodeKind.TypedVariableDeclaration;
}
public class BoundTypedVariableExpression : BoundExpression
{
public BoundTypedVariableExpression(SyntaxNode syntax, TypedVariableSymbol variable)
: base(syntax)
{
Variable = variable;
}
public TypedVariableSymbol Variable { get; }
public override BoundNodeKind Kind => BoundNodeKind.TypedVariableExpression;
public override TypeSymbol Type => Variable.Type;
}
public class BoundUnaryOperationExpression : BoundExpression
@ -471,6 +614,8 @@ namespace Parser.Binding
public BoundUnaryOperator Op { get; }
public BoundExpression Operand { get; }
public override TypeSymbol Type => Op.Result;
}
public class BoundUnquotedStringLiteralExpression : BoundExpression
@ -481,6 +626,26 @@ namespace Parser.Binding
}
public override BoundNodeKind Kind => BoundNodeKind.UnquotedStringLiteralExpression;
public override TypeSymbol Type => throw new System.NotImplementedException();
}
public class BoundConversionExpression : BoundExpression
{
public BoundConversionExpression(SyntaxNode syntax, TypeSymbol targetType, BoundExpression expression)
: base(syntax)
{
TargetType = targetType;
Expression = expression;
}
public TypeSymbol TargetType { get; }
public BoundExpression Expression { get; }
public override BoundNodeKind Kind => BoundNodeKind.ConversionExpression;
public override TypeSymbol Type => TargetType;
}
public class BoundElseifClause : BoundNode

View File

@ -38,6 +38,8 @@ namespace Parser.Binding
RewriteSwitchStatement((BoundSwitchStatement)node),
BoundNodeKind.TryCatchStatement =>
RewriteTryCatchStatement((BoundTryCatchStatement)node),
BoundNodeKind.TypedVariableDeclaration =>
RewriteTypedVariableDeclaration((BoundTypedVariableDeclaration)node),
BoundNodeKind.WhileStatement =>
RewriteWhileStatement((BoundWhileStatement)node),
_ =>
@ -45,6 +47,11 @@ namespace Parser.Binding
};
}
public virtual BoundStatement RewriteTypedVariableDeclaration(BoundTypedVariableDeclaration node)
{
return node;
}
public virtual BoundStatement RewriteGotoStatement(BoundGotoStatement node)
{
return node;
@ -63,7 +70,9 @@ namespace Parser.Binding
public virtual BoundStatement RewriteWhileStatement(BoundWhileStatement node)
{
throw new NotImplementedException();
var condition = RewriteExpression(node.Condition);
var body = RewriteStatement(node.Body);
return WhileStatement(node.Syntax, condition, body);
}
public virtual BoundStatement RewriteTryCatchStatement(BoundTryCatchStatement node)
@ -136,7 +145,7 @@ namespace Parser.Binding
public virtual BoundStatement RewriteForStatement(BoundForStatement node)
{
throw new NotImplementedException();
return node;
}
public virtual BoundStatement RewriteExpressionStatement(BoundExpressionStatement node)
@ -147,7 +156,7 @@ namespace Parser.Binding
return node;
}
return ExpressionStatement(node.Syntax, expression);
return ExpressionStatement(node.Syntax, expression, node.DiscardResult);
}
public virtual BoundStatement RewriteEmptyStatement(BoundEmptyStatement node)
@ -220,6 +229,8 @@ namespace Parser.Binding
RewriteCommandExpression((BoundCommandExpression)node),
BoundNodeKind.CompoundNameExpression =>
RewriteCompoundNameExpression((BoundCompoundNameExpression)node),
BoundNodeKind.ConversionExpression =>
RewriteConversionExpression((BoundConversionExpression)node),
BoundNodeKind.DoubleQuotedStringLiteralExpression =>
RewriteDoubleQuotedStringLiteralExpression((BoundDoubleQuotedStringLiteralExpression)node),
BoundNodeKind.EmptyExpression =>
@ -236,10 +247,14 @@ namespace Parser.Binding
RewriteMemberAccessExpression((BoundMemberAccessExpression)node),
BoundNodeKind.NamedFunctionHandleExpression =>
RewriteNamedFunctionHandleExpression((BoundNamedFunctionHandleExpression)node),
BoundNodeKind.NumberLiteralExpression =>
RewriteNumberLiteralExpression((BoundNumberLiteralExpression)node),
BoundNodeKind.NumberDoubleLiteralExpression =>
RewriteNumberDoubleLiteralExpression((BoundNumberDoubleLiteralExpression)node),
BoundNodeKind.NumberIntLiteralExpression =>
RewriteNumberIntLiteralExpression((BoundNumberIntLiteralExpression)node),
BoundNodeKind.StringLiteralExpression =>
RewriteStringLiteralExpression((BoundStringLiteralExpression)node),
BoundNodeKind.TypedVariableExpression =>
RewriteTypedVariableExpression((BoundTypedVariableExpression)node),
BoundNodeKind.UnaryOperationExpression =>
RewriteUnaryOperationExpression((BoundUnaryOperationExpression)node),
BoundNodeKind.UnquotedStringLiteralExpression =>
@ -249,6 +264,17 @@ namespace Parser.Binding
};
}
public virtual BoundExpression RewriteConversionExpression(BoundConversionExpression node)
{
var operand = RewriteExpression(node.Expression);
return Conversion(node.Syntax, node.TargetType, operand);
}
public virtual BoundExpression RewriteTypedVariableExpression(BoundTypedVariableExpression node)
{
return node;
}
public virtual BoundExpression RewriteUnquotedStringLiteralExpression(BoundUnquotedStringLiteralExpression node)
{
throw new NotImplementedException();
@ -265,7 +291,12 @@ namespace Parser.Binding
return node;
}
public virtual BoundExpression RewriteNumberLiteralExpression(BoundNumberLiteralExpression node)
public virtual BoundExpression RewriteNumberDoubleLiteralExpression(BoundNumberDoubleLiteralExpression node)
{
return node;
}
public virtual BoundExpression RewriteNumberIntLiteralExpression(BoundNumberIntLiteralExpression node)
{
return node;
}

View File

@ -1,26 +1,42 @@
using System.Linq;
using System;
using System.Linq;
namespace Parser.Binding
{
public class BoundUnaryOperator
{
private static BoundUnaryOperator[] _operators =
private static BoundUnaryOperator[] _specificOperators = Array.Empty<BoundUnaryOperator>();
private static BoundUnaryOperator[] _defaultOperators =
{
new BoundUnaryOperator(TokenKind.MinusToken, BoundUnaryOperatorKind.Minus),
new BoundUnaryOperator(TokenKind.MinusToken, BoundUnaryOperatorKind.Minus, TypeSymbol.MObject),
};
public BoundUnaryOperator(TokenKind syntaxKind, BoundUnaryOperatorKind kind)
public BoundUnaryOperator(TokenKind syntaxKind, BoundUnaryOperatorKind kind, TypeSymbol type)
{
SyntaxKind = syntaxKind;
Kind = kind;
Operand = type;
Result = type;
}
public BoundUnaryOperator(TokenKind syntaxKind, BoundUnaryOperatorKind kind, TypeSymbol operand, TypeSymbol result)
{
SyntaxKind = syntaxKind;
Kind = kind;
Operand = operand;
Result = result;
}
public TokenKind SyntaxKind { get; }
public BoundUnaryOperatorKind Kind { get; }
public TypeSymbol Operand { get; }
public TypeSymbol Result { get; }
internal static BoundUnaryOperator? GetOperator(TokenKind kind)
internal static BoundUnaryOperator? GetOperator(TokenKind kind, TypeSymbol operand)
{
return _operators.FirstOrDefault(op => op.SyntaxKind == kind);
return _specificOperators.FirstOrDefault(op => op.SyntaxKind == kind && op.Operand == operand)
?? _defaultOperators.FirstOrDefault(op => op.SyntaxKind == kind);
}
}
}

View File

@ -0,0 +1,39 @@
namespace Parser.Binding
{
internal class Conversion
{
public static Conversion None = new Conversion(exists: false, isIdentity: false);
public static Conversion Identity = new Conversion(exists: true, isIdentity: true);
public static Conversion Implicit = new Conversion(exists: true, isIdentity: false);
private Conversion(bool exists, bool isIdentity)
{
Exists = exists;
IsIdentity = isIdentity;
}
public bool Exists { get; }
public bool IsIdentity { get; }
public static Conversion Classify(TypeSymbol from, TypeSymbol to)
{
if (from == to)
{
return Identity;
}
if (to == TypeSymbol.MObject)
{
return Implicit;
}
if (to == TypeSymbol.Boolean)
{
return Implicit;
}
return None;
}
}
}

View File

@ -0,0 +1,26 @@
namespace Parser.Binding
{
public class TypeSymbol
{
public static readonly TypeSymbol Error = new TypeSymbol("error");
public static readonly TypeSymbol Null = new TypeSymbol("null");
public static readonly TypeSymbol Boolean = new TypeSymbol("bool");
public static readonly TypeSymbol Double = new TypeSymbol("double");
public static readonly TypeSymbol Int = new TypeSymbol("int");
public static readonly TypeSymbol String = new TypeSymbol("string");
public static readonly TypeSymbol MObject = new TypeSymbol("mobject");
public static readonly TypeSymbol Void = new TypeSymbol("void");
private TypeSymbol(string name)
{
Name = name;
}
public string Name { get; }
public override string ToString()
{
return Name;
}
}
}

View File

@ -0,0 +1,23 @@
using System.Collections.Immutable;
namespace Parser.Binding
{
public class TypedFunctionSymbol
{
public TypedFunctionSymbol(
string name,
ImmutableArray<TypedParameterSymbol> parameters,
TypeSymbol returnType)
{
Name = name;
Parameters = parameters;
ReturnType = returnType;
}
public string Name { get; }
public ImmutableArray<TypedParameterSymbol> Parameters { get; }
public TypeSymbol ReturnType { get; }
}
}

View File

@ -0,0 +1,14 @@
namespace Parser.Binding
{
public class TypedParameterSymbol
{
public TypedParameterSymbol(string name, TypeSymbol type)
{
Name = name;
Type = type;
}
public string Name { get; }
public TypeSymbol Type { get; }
}
}

View File

@ -0,0 +1,15 @@
namespace Parser.Binding
{
public class TypedVariableSymbol
{
public TypedVariableSymbol(string name, TypeSymbol type)
{
Name = name;
Type = type;
}
public string Name { get; }
public TypeSymbol Type { get; }
}
}

View File

@ -1,4 +1,5 @@
using Parser.Binding;
using Parser.Emitting;
namespace Parser
{
@ -16,6 +17,13 @@ namespace Parser
return new Compilation(syntaxTree);
}
public void Emit(string[] references, string outputPath)
{
var emitter = new Emitter();
var boundProgram = GetBoundProgram();
emitter.Emit(boundProgram, references, outputPath);
}
private BoundProgram GetBoundProgram()
{
return Binder.BindProgram(_syntaxTree);

840
Parser/Emitting/Emitter.cs Normal file
View File

@ -0,0 +1,840 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Parser.Binding;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
namespace Parser.Emitting
{
public static class BuiltInFunctions
{
public static readonly TypedFunctionSymbol Disp = new TypedFunctionSymbol(
"disp",
ImmutableArray.Create(
new TypedParameterSymbol("text", TypeSymbol.MObject)),
TypeSymbol.Void);
}
public class Emitter
{
private Dictionary<string, TypeReference> _knownTypes = new Dictionary<string, TypeReference>();
private Dictionary<TypeSymbol, TypeReference> _resolvedTypes = new Dictionary<TypeSymbol, TypeReference>();
private Dictionary<string, MethodInterfaceDescription> _functions = new Dictionary<string, MethodInterfaceDescription>();
private MethodReference? _consoleWriteLineReference;
private MethodReference? _dispReference;
private MethodReference? _lenReference;
private MethodReference? _arraySliceReference;
private MethodReference? _mObjectToBool;
private MethodReference? _stringToMObject;
private MethodReference? _doubleToMObject;
private MethodReference? _intToMObject;
private MethodReference? _getItemFromDictionary;
private MethodReference? _putItemIntoDictionary;
private TypeReference? _mObjectType;
private ArrayType? _mObjectArrayType;
private Dictionary<string, VariableDefinition> _currentOutputVariables = new Dictionary<string, VariableDefinition>();
private VariableDefinition? _currentLocals = null;
private TypeSpecification? _stringMObjectDictionary = null;
private MethodReference? _dictionaryCtorReference = null;
private Dictionary<BoundBinaryOperatorKind, MethodReference> _binaryOperations = new Dictionary<BoundBinaryOperatorKind, MethodReference>();
private Dictionary<BoundUnaryOperatorKind, MethodReference> _unaryOperations = new Dictionary<BoundUnaryOperatorKind, MethodReference>();
private Dictionary<BoundLabel, int> _labels = new Dictionary<BoundLabel, int>();
private Dictionary<int, BoundLabel> _forwardLabelsToFix = new Dictionary<int, BoundLabel>();
private Dictionary<TypedVariableSymbol, VariableDefinition> _typedLocals = new Dictionary<TypedVariableSymbol, VariableDefinition>();
private Dictionary<TypedFunctionSymbol, MethodReference> _builtInFunctions = new Dictionary<TypedFunctionSymbol, MethodReference>();
private static TypeReference ResolveAndImportType(
string typeName,
List<AssemblyDefinition> assemblies,
AssemblyDefinition assemblyDefinition)
{
var foundTypes = assemblies.SelectMany(a => a.Modules)
.SelectMany(m => m.Types)
.Where(t => t.FullName == typeName)
.ToArray();
if (foundTypes.Length == 1)
{
var typeReference = assemblyDefinition.MainModule.ImportReference(foundTypes[0]);
return typeReference;
}
else if (foundTypes.Length == 0)
{
throw new Exception($"Cannot find type {typeName}");
}
else
{
throw new Exception($"Ambiguous type {typeName}");
}
}
private static MethodReference ResolveAndImportMethod(
string typeName,
string methodName,
string[] parameterTypeNames,
List<AssemblyDefinition> assemblies,
AssemblyDefinition assemblyDefinition)
{
var foundTypes = assemblies.SelectMany(a => a.Modules)
.SelectMany(m => m.Types)
.Where(t => t.FullName == typeName)
.ToArray();
if (foundTypes.Length == 1)
{
var foundType = foundTypes[0];
var methods = foundType.Methods.Where(m => m.Name == methodName);
foreach (var method in methods)
{
if (method.Parameters.Count != parameterTypeNames.Length)
continue;
var allParametersMatch = true;
for (var i = 0; i < parameterTypeNames.Length; i++)
{
if (method.Parameters[i].ParameterType.FullName != parameterTypeNames[i])
{
allParametersMatch = false;
break;
}
}
if (!allParametersMatch)
continue;
return assemblyDefinition.MainModule.ImportReference(method);
}
throw new Exception($"Required method {typeName}.{methodName} not found.");
}
else if (foundTypes.Length == 0)
{
throw new Exception($"Required type {typeName} not found.");
}
else
{
throw new Exception($"Required type {typeName} is ambiguous.");
}
}
public void Emit(BoundProgram program, string[] references, string outputFileName)
{
var assemblies = new List<AssemblyDefinition>();
foreach (var reference in references)
{
try
{
var assembly = AssemblyDefinition.ReadAssembly(reference);
assemblies.Add(assembly);
}
catch (BadImageFormatException)
{
throw new Exception($"Invalid reference '{reference}'.");
}
}
var moduleName = Path.GetFileNameWithoutExtension(outputFileName);
var assemblyName = new AssemblyNameDefinition(
name: moduleName,
version: new Version(1, 0));
var assemblyDefinition = AssemblyDefinition.CreateAssembly(
assemblyName: assemblyName,
moduleName: moduleName,
kind: ModuleKind.Console);
var builtInTypes = new[]
{
"System.Int32",
"System.Object",
"System.Void",
"System.String",
"System.Collections.Generic.Dictionary`2",
"Parser.Objects.MObject"
};
var typeSymbolToKnownType = new Dictionary<TypeSymbol, string>
{
[TypeSymbol.Int] = "System.Int32",
[TypeSymbol.MObject] = "Parser.Objects.MObject",
};
// Resolve built-in types and methods.
foreach (var typeName in builtInTypes)
{
var typeReference = ResolveAndImportType(typeName, assemblies, assemblyDefinition);
_knownTypes.Add(typeName, typeReference);
}
foreach (var (typeSymbol, knownTypeName) in typeSymbolToKnownType)
{
_resolvedTypes.Add(typeSymbol, _knownTypes[knownTypeName]);
}
var objectType = _knownTypes["System.Object"];
var voidType = _knownTypes["System.Void"];
var stringType = _knownTypes["System.String"];
var dictionaryType = _knownTypes["System.Collections.Generic.Dictionary`2"];
_mObjectType = _knownTypes["Parser.Objects.MObject"];
_mObjectArrayType = _mObjectType.MakeArrayType();
var stringMObjectDictionary = new GenericInstanceType(dictionaryType);
stringMObjectDictionary.GenericArguments.Add(stringType);
stringMObjectDictionary.GenericArguments.Add(_mObjectType);
_stringMObjectDictionary = stringMObjectDictionary;
_dictionaryCtorReference = ResolveAndImportMethod(
typeName: "System.Collections.Generic.Dictionary`2",
methodName: ".ctor",
parameterTypeNames: Array.Empty<string>(),
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_dictionaryCtorReference.DeclaringType = _stringMObjectDictionary;
_getItemFromDictionary = ResolveAndImportMethod(
typeName: "System.Collections.Generic.Dictionary`2",
methodName: "get_Item",
parameterTypeNames: new[] { "TKey" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_getItemFromDictionary.DeclaringType = _stringMObjectDictionary;
_putItemIntoDictionary = ResolveAndImportMethod(
typeName: "System.Collections.Generic.Dictionary`2",
methodName: "set_Item",
parameterTypeNames: new[] { "TKey", "TValue" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_putItemIntoDictionary.DeclaringType = _stringMObjectDictionary;
_consoleWriteLineReference = ResolveAndImportMethod(
typeName: "System.Console",
methodName: "WriteLine",
parameterTypeNames: new[] { "System.Object" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_dispReference = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MHelpers",
methodName: "Disp",
parameterTypeNames: new[] { "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_lenReference = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MHelpers",
methodName: "Len",
parameterTypeNames: new[] { "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_arraySliceReference = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MOperations",
methodName: "ArraySlice",
parameterTypeNames: new[] { "Parser.Objects.MObject", "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_mObjectToBool = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MHelpers",
methodName: "ToBool",
parameterTypeNames: new[] { "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_stringToMObject = ResolveAndImportMethod(
typeName: "Parser.Objects.MObject",
methodName: "CreateCharArray",
parameterTypeNames: new[] { "System.String" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_doubleToMObject = ResolveAndImportMethod(
typeName: "Parser.Objects.MObject",
methodName: "CreateDoubleNumber",
parameterTypeNames: new[] { "System.Double" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_intToMObject = ResolveAndImportMethod(
typeName: "Parser.Objects.MObject",
methodName: "CreateIntNumber",
parameterTypeNames: new[] { "System.Int32" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
var binaryOperationNames = new Dictionary<BoundBinaryOperatorKind, string>
{
[BoundBinaryOperatorKind.Colon] = "Colon",
[BoundBinaryOperatorKind.Plus] = "Plus",
[BoundBinaryOperatorKind.Minus] = "Minus",
[BoundBinaryOperatorKind.Star] = "Star",
[BoundBinaryOperatorKind.Slash] = "Slash",
[BoundBinaryOperatorKind.Greater] = "Greater",
[BoundBinaryOperatorKind.GreaterOrEquals] = "GreaterOrEquals",
[BoundBinaryOperatorKind.Less] = "Less",
[BoundBinaryOperatorKind.LessOrEquals] = "LessOrEquals",
};
var unaryOperationNames = new Dictionary<BoundUnaryOperatorKind, string>
{
[BoundUnaryOperatorKind.Minus] = "Minus",
};
_builtInFunctions.Add(
BuiltInFunctions.Disp,
_dispReference);
foreach (var (op, opName) in binaryOperationNames)
{
_binaryOperations[op] = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MOperations",
methodName: opName,
parameterTypeNames: new[] { "Parser.Objects.MObject", "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
}
foreach (var (op, opName) in unaryOperationNames)
{
_unaryOperations[op] = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MOperations",
methodName: opName,
parameterTypeNames: new[] { "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
}
// Create type.
var typeDefinition = new TypeDefinition(
@namespace: "",
name: "Program",
attributes: TypeAttributes.Abstract | TypeAttributes.Sealed,
baseType: objectType);
assemblyDefinition.MainModule.Types.Add(typeDefinition);
// Generate method definitions for all functions first.
foreach (var (name, body) in program.Functions)
{
var methodDefinition = new MethodDefinition(
name: name.Name,
attributes: MethodAttributes.Static | MethodAttributes.Private,
returnType: body.OutputDescription.Length == 0 ? voidType : _mObjectArrayType);
if (body.InputDescription.Length > 0)
{
foreach (var inputDescription in body.InputDescription)
{
var parameter = new ParameterDefinition(
name: inputDescription.Name,
attributes: ParameterAttributes.None,
parameterType: _mObjectType);
methodDefinition.Parameters.Add(parameter);
}
}
_functions[name.Name] = new MethodInterfaceDescription(
inputDescription: body.InputDescription,
outputDescription: body.OutputDescription,
method: methodDefinition);
}
// Emit functions.
foreach (var (name, body) in program.Functions)
{
var methodDefinition = _functions[name.Name];
EmitFunction(body, methodDefinition.Method);
typeDefinition.Methods.Add(methodDefinition.Method);
}
// Set entry point.
if (program.ScriptFunction is { } scriptFunction)
{
assemblyDefinition.EntryPoint = _functions[scriptFunction.Name].Method;
}
else if (program.MainFunction is { } mainFunction)
{
assemblyDefinition.EntryPoint = _functions[mainFunction.Name].Method;
}
assemblyDefinition.Write(outputFileName);
}
private void EmitFunction(LoweredFunction function, MethodDefinition methodDefinition)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
_labels.Clear();
_forwardLabelsToFix.Clear();
_typedLocals.Clear();
// Local #0 is the dictionary with actual local variables.
_currentLocals = new VariableDefinition(_stringMObjectDictionary);
ilProcessor.Body.Variables.Add(_currentLocals);
ilProcessor.Emit(OpCodes.Newobj, _dictionaryCtorReference);
ilProcessor.Emit(OpCodes.Stloc_0);
var counter = 0;
foreach (var inputDescription in function.InputDescription)
{
var name = inputDescription.Name;
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, name);
ilProcessor.Emit(OpCodes.Ldarg, methodDefinition.Parameters[counter]);
ilProcessor.Emit(OpCodes.Callvirt, _putItemIntoDictionary);
counter++;
}
// The following locals are "output variables".
_currentOutputVariables.Clear();
if (function.OutputDescription.Length > 0)
{
foreach (var outputDescription in function.OutputDescription)
{
var outputVariable = new VariableDefinition(_mObjectArrayType);
ilProcessor.Body.Variables.Add(outputVariable);
_currentOutputVariables.Add(outputDescription.Name, outputVariable);
}
}
EmitBlockStatement(function.Body, methodDefinition);
// Copy output variables to the output array.
if (function.OutputDescription.Length > 0)
{
ilProcessor.Emit(OpCodes.Ldc_I4, function.OutputDescription.Length);
ilProcessor.Emit(OpCodes.Newarr, _mObjectType);
for (var i = 0; i < function.OutputDescription.Length; i++)
{
ilProcessor.Emit(OpCodes.Dup);
ilProcessor.Emit(OpCodes.Ldc_I4, i);
ilProcessor.Emit(OpCodes.Ldloc, i + 1);
ilProcessor.Emit(OpCodes.Stelem_Ref);
}
}
ilProcessor.Emit(OpCodes.Ret);
foreach (var (index, target) in _forwardLabelsToFix)
{
var targetIndex = _labels[target];
var left = ilProcessor.Body.Instructions[index];
var right = ilProcessor.Body.Instructions[targetIndex];
left.Operand = right;
}
}
private void EmitBlockStatement(BoundBlockStatement block, MethodDefinition methodDefinition)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
foreach (var statement in block.Statements)
{
switch (statement.Kind)
{
case BoundNodeKind.GotoStatement:
EmitGoto((BoundGotoStatement)statement, ilProcessor);
break;
case BoundNodeKind.ConditionalGotoStatement:
EmitConditionalGoto((BoundConditionalGotoStatement)statement, ilProcessor);
break;
case BoundNodeKind.LabelStatement:
EmitLabelStatement((BoundLabelStatement)statement, ilProcessor);
break;
default:
EmitStatement(statement, ilProcessor);
break;
}
}
}
private void EmitLabelStatement(BoundLabelStatement node, ILProcessor ilProcessor)
{
_labels[node.Label] = ilProcessor.Body.Instructions.Count;
}
private void EmitGoto(BoundGotoStatement node, ILProcessor ilProcessor)
{
if (_labels.TryGetValue(node.Label, out var target))
{
ilProcessor.Emit(OpCodes.Br, ilProcessor.Body.Instructions[target]);
}
else
{
_forwardLabelsToFix.Add(ilProcessor.Body.Instructions.Count, node.Label);
ilProcessor.Emit(OpCodes.Br, Instruction.Create(OpCodes.Nop));
}
}
private void EmitConditionalGoto(BoundConditionalGotoStatement node, ILProcessor ilProcessor)
{
var condition = TryConvertExpression(node.Condition, TypeSymbol.Boolean);
if (condition is null)
{
throw new Exception("Cannot cast a condition in GOTO to boolean.");
}
EmitExpression(condition, ilProcessor);
var instruction = node.GotoIfTrue ? OpCodes.Brtrue : OpCodes.Brfalse;
if (_labels.TryGetValue(node.Label, out var target))
{
ilProcessor.Emit(instruction, ilProcessor.Body.Instructions[target]);
}
else
{
_forwardLabelsToFix.Add(ilProcessor.Body.Instructions.Count, node.Label);
ilProcessor.Emit(instruction, Instruction.Create(OpCodes.Nop));
}
}
private void EmitStatement(BoundStatement node, ILProcessor ilProcessor)
{
switch (node.Kind)
{
case BoundNodeKind.EmptyStatement:
break;
case BoundNodeKind.ExpressionStatement:
EmitExpressionStatement((BoundExpressionStatement)node, ilProcessor);
break;
case BoundNodeKind.TypedVariableDeclaration:
EmitTypedVariableDeclaration((BoundTypedVariableDeclaration)node, ilProcessor);
break;
default:
throw new NotImplementedException($"Invalid statement kind '{node.Kind}'.");
};
}
private void EmitTypedVariableDeclaration(BoundTypedVariableDeclaration node, ILProcessor ilProcessor)
{
var typeReference = _resolvedTypes[node.Variable.Type];
var variableDefinition = new VariableDefinition(typeReference);
_typedLocals.Add(node.Variable, variableDefinition);
ilProcessor.Body.Variables.Add(variableDefinition);
EmitExpression(node.Initializer, ilProcessor);
ilProcessor.Emit(OpCodes.Stloc, variableDefinition);
}
private void EmitExpressionStatement(BoundExpressionStatement node, ILProcessor ilProcessor)
{
EmitExpression(node.Expression, ilProcessor);
if (node.DiscardResult)
{
ilProcessor.Emit(OpCodes.Pop);
}
else
{
ilProcessor.Emit(OpCodes.Call, _dispReference);
}
}
private void EmitExpression(BoundExpression node, ILProcessor ilProcessor)
{
switch (node.Kind) {
case BoundNodeKind.AssignmentExpression:
EmitAssignmentExpression((BoundAssignmentExpression)node, ilProcessor);
break;
case BoundNodeKind.BinaryOperationExpression:
EmitBinaryOperationExpression((BoundBinaryOperationExpression)node, ilProcessor);
break;
case BoundNodeKind.ConversionExpression:
EmitConversionExpression((BoundConversionExpression)node, ilProcessor);
break;
case BoundNodeKind.FunctionCallExpression:
EmitFunctionCallExpression((BoundFunctionCallExpression)node, ilProcessor);
break;
case BoundNodeKind.IdentifierNameExpression:
EmitIdentifierNameExpression((BoundIdentifierNameExpression)node, ilProcessor);
break;
case BoundNodeKind.NumberDoubleLiteralExpression:
EmitNumberDoubleLiteralExpression((BoundNumberDoubleLiteralExpression)node, ilProcessor);
break;
case BoundNodeKind.NumberIntLiteralExpression:
EmitNumberIntLiteralExpression((BoundNumberIntLiteralExpression)node, ilProcessor);
break;
case BoundNodeKind.StringLiteralExpression:
EmitStringLiteralExpression((BoundStringLiteralExpression)node, ilProcessor);
break;
case BoundNodeKind.TypedFunctionCallExpression:
EmitTypedFunctionCallExpression((BoundTypedFunctionCallExpression)node, ilProcessor);
break;
case BoundNodeKind.TypedVariableExpression:
EmitTypedVariableExpression((BoundTypedVariableExpression)node, ilProcessor);
break;
default:
throw new NotImplementedException($"Invalid node kind '{node.Kind}'.");
}
}
private void EmitConversionExpression(BoundConversionExpression node, ILProcessor ilProcessor)
{
var fromType = node.Expression.Type;
var toType = node.TargetType;
EmitExpression(node.Expression, ilProcessor);
if ((fromType, toType) == (TypeSymbol.Double, TypeSymbol.MObject))
{
ilProcessor.Emit(OpCodes.Call, _doubleToMObject);
}
else if ((fromType, toType) == (TypeSymbol.Int, TypeSymbol.MObject))
{
ilProcessor.Emit(OpCodes.Call, _intToMObject);
}
else if ((fromType, toType) == (TypeSymbol.String, TypeSymbol.MObject))
{
ilProcessor.Emit(OpCodes.Call, _stringToMObject);
}
else if ((fromType, toType) == (TypeSymbol.MObject, TypeSymbol.Boolean))
{
ilProcessor.Emit(OpCodes.Call, _mObjectToBool);
}
else
{
throw new NotImplementedException($"Conversion of '{fromType}' to '{toType}' is not implemented.");
}
}
private void EmitTypedVariableExpression(BoundTypedVariableExpression node, ILProcessor ilProcessor)
{
var variableDefinition = _typedLocals[node.Variable];
ilProcessor.Emit(OpCodes.Ldloc, variableDefinition);
}
private void EmitBinaryOperationExpression(BoundBinaryOperationExpression node, ILProcessor ilProcessor)
{
if (_binaryOperations.TryGetValue(node.Op.Kind, out var method))
{
EmitExpression(node.Left, ilProcessor);
EmitExpression(node.Right, ilProcessor);
ilProcessor.Emit(OpCodes.Call, method);
} else
{
if (node.Op.Kind == BoundBinaryOperatorKind.LessInt)
{
EmitExpression(node.Left, ilProcessor);
EmitExpression(node.Right, ilProcessor);
ilProcessor.Emit(OpCodes.Clt);
}
else if (node.Op.Kind == BoundBinaryOperatorKind.PlusInt)
{
EmitExpression(node.Left, ilProcessor);
EmitExpression(node.Right, ilProcessor);
ilProcessor.Emit(OpCodes.Add);
}
else
{
throw new Exception($"Binary operation '{node.Op.Kind}' not implemented.");
}
}
}
private void EmitAssignmentExpression(BoundAssignmentExpression node, ILProcessor ilProcessor)
{
var leftType = node.Left.Kind switch
{
BoundNodeKind.IdentifierNameExpression => TypeSymbol.MObject,
BoundNodeKind.TypedVariableExpression => ((BoundTypedVariableExpression)node.Left).Type,
_ => throw new Exception($"Assignment to lvalue of kind {node.Left.Kind} is not supported."),
};
var rewrittenRight = TryConvertExpression(node.Right, leftType);
if (rewrittenRight is null)
{
throw new Exception($"Cannot convert an expression of type '{node.Right.Type}' to '{leftType}'.");
}
if (node.Left.Kind == BoundNodeKind.IdentifierNameExpression)
{
var left = ((BoundIdentifierNameExpression)node.Left);
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, left.Name);
EmitExpression(rewrittenRight, ilProcessor);
ilProcessor.Emit(OpCodes.Callvirt, _putItemIntoDictionary);
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, left.Name);
ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary);
}
else if (node.Left.Kind == BoundNodeKind.TypedVariableExpression)
{
var typedVariableExpression = (BoundTypedVariableExpression)node.Left;
EmitExpression(rewrittenRight, ilProcessor);
ilProcessor.Emit(OpCodes.Dup);
ilProcessor.Emit(OpCodes.Stloc, _typedLocals[typedVariableExpression.Variable]);
}
else
{
throw new Exception($"Assignment to lvalue of kind {node.Left.Kind} is not supported.");
}
}
private void EmitIdentifierNameExpression(BoundIdentifierNameExpression node, ILProcessor ilProcessor)
{
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, node.Name);
ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary);
}
private void EmitNumberDoubleLiteralExpression(BoundNumberDoubleLiteralExpression node, ILProcessor ilProcessor)
{
ilProcessor.Emit(OpCodes.Ldc_R8, node.Value);
}
private void EmitNumberIntLiteralExpression(BoundNumberIntLiteralExpression node, ILProcessor ilProcessor)
{
ilProcessor.Emit(OpCodes.Ldc_I4, node.Value);
}
private void EmitStringLiteralExpression(BoundStringLiteralExpression node, ILProcessor ilProcessor)
{
ilProcessor.Emit(OpCodes.Ldstr, node.Value);
}
private BoundExpression? TryConvertExpression(BoundExpression expression, TypeSymbol targetType)
{
var conversion = Conversion.Classify(expression.Type, targetType);
if (!conversion.Exists)
{
return null;
}
if (conversion.IsIdentity)
{
return expression;
}
else
{
return BoundNodeFactory.Conversion(expression.Syntax, targetType, expression);
}
}
private BoundExpression ConvertExpression(BoundExpression expression, TypeSymbol targetType)
{
return TryConvertExpression(expression, targetType)
?? throw new Exception($"Conversion from '{expression.Type}' to '{targetType}' failed.");
}
private BoundExpression RewriteFunctionCall(BoundFunctionCallExpression node, TypedFunctionSymbol function)
{
var numberOfArguments = node.Arguments.Length;
if (numberOfArguments != function.Parameters.Length)
{
throw new NotImplementedException($"Function '{function.Name}' expected {function.Parameters.Length} arguments, but was called with {numberOfArguments}.");
}
var rewrittenArguments = ImmutableArray.CreateBuilder<BoundExpression>(numberOfArguments);
for (var i = 0; i < numberOfArguments; i++)
{
var argument = node.Arguments[i];
var parameter = function.Parameters[i];
var rewrittenArgument = TryConvertExpression(argument, parameter.Type);
if (rewrittenArgument is null)
{
throw new NotImplementedException($"Argument number {i + 1} of function '{function.Name}' expects {parameter.Type}, but got {argument.Type}, and no conversion exists.");
}
rewrittenArguments.Add(rewrittenArgument);
}
return BoundNodeFactory.TypedFunctionCall(node.Syntax, function, rewrittenArguments.MoveToImmutable());
}
private void EmitTypedFunctionCallExpression(BoundTypedFunctionCallExpression node, ILProcessor ilProcessor)
{
var function = _builtInFunctions[node.Function];
foreach (var argument in node.Arguments)
{
EmitExpression(argument, ilProcessor);
}
ilProcessor.Emit(OpCodes.Call, function);
if (node.Function.ReturnType == TypeSymbol.Void)
{
ilProcessor.Emit(OpCodes.Ldnull);
}
else if (node.Function.ReturnType == TypeSymbol.MObject)
{
}
else
{
throw new Exception("Don't know how to cast function output.");
}
}
private void EmitFunctionCallExpression(BoundFunctionCallExpression node, ILProcessor ilProcessor)
{
if (node.Name.Kind == BoundNodeKind.IdentifierNameExpression
&& ((BoundIdentifierNameExpression)node.Name).Name == "disp")
{
var dispFunctionSymbol = BuiltInFunctions.Disp;
var rewrittenCall = RewriteFunctionCall(node, dispFunctionSymbol);
EmitExpression(rewrittenCall, ilProcessor);
//ilProcessor.Emit(OpCodes.Call, _dispReference);
//ilProcessor.Emit(OpCodes.Ldnull);
} else if (node.Name.Kind == BoundNodeKind.IdentifierNameExpression
&& ((BoundIdentifierNameExpression)node.Name).Name == "len")
{
EmitExpression(node.Arguments[0], ilProcessor);
ilProcessor.Emit(OpCodes.Call, _lenReference);
}
else if (node.Name.Kind == BoundNodeKind.TypedVariableExpression)
{
if (node.Arguments.Length > 1)
{
throw new Exception("Multi-dimensional array slicing is not supported.");
}
var typedVariableExpression = ConvertExpression((BoundTypedVariableExpression)node.Name, TypeSymbol.MObject);
EmitExpression(typedVariableExpression, ilProcessor);
var indexExpression = ConvertExpression(node.Arguments[0], TypeSymbol.MObject);
EmitExpression(indexExpression, ilProcessor);
ilProcessor.Emit(OpCodes.Call, _arraySliceReference);
}
else
{
var function = ResolveFunction(node.Name);
for (var i = 0; i < function.InputDescription.Length; i++)
{
if (i < node.Arguments.Length)
{
EmitExpression(node.Arguments[i], ilProcessor);
}
else
{
ilProcessor.Emit(OpCodes.Ldnull);
}
}
ilProcessor.Emit(OpCodes.Call, function.Method);
if (function.OutputDescription.Length == 0)
{
ilProcessor.Emit(OpCodes.Ldnull);
}
else if (function.OutputDescription.Length == 1)
{
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Ldelem_Ref);
}
else
{
throw new NotImplementedException("Functions with multiple output are not supported.");
}
}
}
private MethodInterfaceDescription ResolveFunction(BoundExpression node)
{
if (node.Kind == BoundNodeKind.IdentifierNameExpression)
{
var name = ((BoundIdentifierNameExpression)node).Name;
if (_functions.TryGetValue(name, out var result))
{
return result;
} else
{
throw new Exception($"Function '{name}' not found.");
}
}
else
{
throw new NotImplementedException($"Dynamic functions calling not supported. Failed to resolve function call expression with kind '{node.Kind}'.");
}
}
}
}

View File

@ -0,0 +1,22 @@
using Mono.Cecil;
using Parser.Binding;
using System.Collections.Immutable;
namespace Parser.Emitting
{
public class MethodInterfaceDescription
{
public MethodInterfaceDescription(ImmutableArray<ParameterSymbol> inputDescription, ImmutableArray<ParameterSymbol> outputDescription, MethodDefinition method)
{
InputDescription = inputDescription;
OutputDescription = outputDescription;
Method = method;
}
public ImmutableArray<ParameterSymbol> InputDescription { get; }
public ImmutableArray<ParameterSymbol> OutputDescription { get; }
public MethodDefinition Method { get; }
}
}

View File

@ -39,7 +39,7 @@ namespace Parser
if (mainFunction.InputDescription.Length > 0)
{
_diagnostics.ReportNotEnoughInputs(
new TextSpan(mainFunction.Body.Syntax.Position, mainFunction.Body.Syntax.Position + mainFunction.Body.Syntax.FullWidth),
mainFunction.Body.Syntax.Span,
mainFunction.Name);
return new EvaluationResult(null, _diagnostics.ToImmutableArray());
}
@ -210,7 +210,8 @@ namespace Parser
private MObject? EvaluateExpressionStatement(BoundExpressionStatement node)
{
return EvaluateExpression(node.Expression);
var result = EvaluateExpression(node.Expression);
return node.DiscardResult ? null : result;
}
private MObject? EvaluateExpression(BoundExpression node)
@ -249,8 +250,10 @@ namespace Parser
EvaluateMemberAccess((BoundMemberAccessExpression)node),
BoundNodeKind.NamedFunctionHandleExpression =>
EvaluateNamedFunctionHandleExpression((BoundNamedFunctionHandleExpression)node),
BoundNodeKind.NumberLiteralExpression =>
EvaluateNumberLiteralExpression((BoundNumberLiteralExpression)node),
BoundNodeKind.NumberDoubleLiteralExpression =>
EvaluateNumberDoubleLiteralExpression((BoundNumberDoubleLiteralExpression)node),
BoundNodeKind.NumberIntLiteralExpression =>
EvaluateNumberIntLiteralExpression((BoundNumberIntLiteralExpression)node),
BoundNodeKind.StringLiteralExpression =>
EvaluateStringLiteralExpression((BoundStringLiteralExpression)node),
BoundNodeKind.UnaryOperationExpression =>
@ -288,10 +291,9 @@ namespace Parser
foreach (var argument in node.Arguments)
{
var evaluatedArgument = EvaluateExpression(argument);
if (argument is null)
if (evaluatedArgument is null)
{
_diagnostics.ReportCannotEvaluateExpression(
new TextSpan(argument.Syntax.Position, argument.Syntax.FullWidth));
_diagnostics.ReportCannotEvaluateExpression(argument.Syntax.Span);
allGood = false;
}
else
@ -316,9 +318,7 @@ namespace Parser
if (resolvedFunction is null)
{
_diagnostics.ReportFunctionNotFound(
new TextSpan(
node.Name.Syntax.Position,
node.Name.Syntax.Position + node.Name.Syntax.FullWidth),
node.Name.Syntax.Span,
function.Name);
return null;
}
@ -340,10 +340,8 @@ namespace Parser
if (counter < arguments.Count)
{
_diagnostics.ReportTooManyInputs(
new TextSpan(
node.Arguments[counter].Syntax.Position,
node.Arguments[counter].Syntax.Position + node.Arguments[counter].Syntax.FullWidth),
function.Name);
node.Arguments[counter].Syntax.Span,
function.Name);
return null;
}
_scopeStack.Push(newScope);
@ -419,7 +417,12 @@ namespace Parser
};
}
private MObject? EvaluateNumberLiteralExpression(BoundNumberLiteralExpression node)
private MObject? EvaluateNumberDoubleLiteralExpression(BoundNumberDoubleLiteralExpression node)
{
return MObject.CreateDoubleNumber(node.Value);
}
private MObject? EvaluateNumberIntLiteralExpression(BoundNumberIntLiteralExpression node)
{
return MObject.CreateDoubleNumber(node.Value);
}
@ -431,7 +434,7 @@ namespace Parser
if (maybeValue is null)
{
_diagnostics.ReportVariableNotFound(
new TextSpan(node.Syntax.Position, node.Syntax.FullWidth),
node.Syntax.Span,
variableName);
}
@ -497,7 +500,7 @@ namespace Parser
if (rightValue is null)
{
_diagnostics.ReportCannotEvaluateExpression(
new TextSpan(node.Right.Syntax.Position, node.Right.Syntax.Position + node.Right.Syntax.FullWidth));
node.Right.Syntax.Span);
return null;
}

View File

@ -48,6 +48,11 @@ namespace Parser.Internal
Report(span, $"Unexpected character '{c}' while parsing a number.");
}
internal void ReportMainIsNotAllowed(TextSpan span)
{
Report(span, $"Function name 'Main' is not allowed in scripts.");
}
internal void ReportUnexpectedEOLWhileParsingString(TextSpan span)
{
Report(span, "Unexpected end of line while parsing a string literal.");
@ -102,5 +107,10 @@ namespace Parser.Internal
{
Report(span, $"Too many inputs in the call to '{functionName}'.");
}
internal void ReportForLoopWithoutVariable(TextSpan span)
{
Report(span, $"A 'for' loop must have a loop variable.");
}
}
}

View File

@ -191,11 +191,11 @@ namespace Parser.Internal
}
}
public virtual IReadOnlyList<SyntaxTrivia> LeadingTrivia => GetFirstTerminal()?.LeadingTriviaCore ?? new List<SyntaxTrivia>();
public virtual IReadOnlyList<SyntaxTrivia> TrailingTrivia => GetLastTerminal()?.TrailingTriviaCore ?? new List<SyntaxTrivia>();
public virtual GreenNode? LeadingTrivia => GetFirstTerminal()?.LeadingTriviaCore;
public virtual GreenNode? TrailingTrivia => GetLastTerminal()?.TrailingTriviaCore;
public abstract IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
public abstract IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
public virtual GreenNode? LeadingTriviaCore => null;
public virtual GreenNode? TrailingTriviaCore => null;
public virtual string FullText
{
@ -209,7 +209,6 @@ namespace Parser.Internal
}
return sb.ToString();
}
}

View File

@ -493,7 +493,7 @@ namespace Parser.Internal
if (TokensSinceNewLine == 1
&& !TokenStack.Any()
&& LastToken.Kind == TokenKind.IdentifierToken
&& LastToken.TrailingTrivia.Any()
&& LastToken.TrailingTrivia is not null
&& character != '='
&& character != '('
&& !SyntaxFacts.Keywords.Contains(LastToken.Text))
@ -773,7 +773,7 @@ namespace Parser.Internal
|| LastToken.Kind == TokenKind.CloseSquareBracketToken
|| LastToken.Kind == TokenKind.IdentifierToken))
{
if (LastToken.TrailingTrivia.Count == 0 && leadingTrivia.Count == 0)
if (LastToken.TrailingTrivia is null && leadingTrivia.Count == 0)
{
Window.ConsumeChar();
tokenInfo.Kind = TokenKind.ApostropheToken;
@ -858,46 +858,40 @@ namespace Parser.Internal
List<SyntaxTrivia> leadingTrivia,
List<SyntaxTrivia> trailingTrivia)
{
switch (tokenInfo.Kind)
return tokenInfo.Kind switch
{
case TokenKind.IdentifierToken:
return TokenFactory.CreateIdentifier(
tokenInfo.Text,
leadingTrivia,
trailingTrivia);
case TokenKind.UnquotedStringLiteralToken:
return TokenFactory.CreateUnquotedStringLiteral(
tokenInfo.Text,
tokenInfo.StringValue,
leadingTrivia,
trailingTrivia);
case TokenKind.NumberLiteralToken:
return TokenFactory.CreateTokenWithValueAndTrivia<double>(
tokenInfo.Kind,
tokenInfo.Text,
tokenInfo.DoubleValue,
leadingTrivia,
trailingTrivia);
case TokenKind.StringLiteralToken:
return TokenFactory.CreateTokenWithValueAndTrivia<string>(
tokenInfo.Kind,
tokenInfo.Text,
tokenInfo.StringValue,
leadingTrivia,
trailingTrivia);
case TokenKind.DoubleQuotedStringLiteralToken:
return TokenFactory.CreateTokenWithValueAndTrivia<string>(
tokenInfo.Kind,
tokenInfo.Text,
tokenInfo.StringValue,
leadingTrivia,
trailingTrivia);
default:
return TokenFactory.CreateTokenWithTrivia(
tokenInfo.Kind,
leadingTrivia,
trailingTrivia);
}
TokenKind.IdentifierToken => TokenFactory.CreateIdentifier(
tokenInfo.Text,
leadingTrivia,
trailingTrivia),
TokenKind.UnquotedStringLiteralToken => TokenFactory.CreateUnquotedStringLiteral(
tokenInfo.Text,
tokenInfo.StringValue,
leadingTrivia,
trailingTrivia),
TokenKind.NumberLiteralToken => TokenFactory.CreateTokenWithValueAndTrivia<double>(
tokenInfo.Kind,
tokenInfo.Text,
tokenInfo.DoubleValue,
leadingTrivia,
trailingTrivia),
TokenKind.StringLiteralToken => TokenFactory.CreateTokenWithValueAndTrivia<string>(
tokenInfo.Kind,
tokenInfo.Text,
tokenInfo.StringValue,
leadingTrivia,
trailingTrivia),
TokenKind.DoubleQuotedStringLiteralToken => TokenFactory.CreateTokenWithValueAndTrivia<string>(
tokenInfo.Kind,
tokenInfo.Text,
tokenInfo.StringValue,
leadingTrivia,
trailingTrivia),
_ => TokenFactory.CreateTokenWithTrivia(
tokenInfo.Kind,
leadingTrivia,
trailingTrivia),
};
}
public List<(SyntaxToken, Position)> ParseAll()

View File

@ -76,17 +76,6 @@ namespace Parser.Internal
return token;
}
private SyntaxToken? PossiblyEatIdentifier(string s)
{
var token = CurrentToken;
if (token.Kind == TokenKind.IdentifierToken && token.Text == s)
{
return EatToken();
}
return null;
}
private SyntaxToken EatPossiblyMissingIdentifier(string s)
{
var token = CurrentToken;
@ -181,6 +170,10 @@ namespace Parser.Internal
{
var identifierToken = EatToken(TokenKind.IdentifierToken);
builder.Add(Factory.IdentifierNameExpressionSyntax(identifierToken));
if (identifierToken.IsMissing)
{
break;
}
}
}
@ -300,43 +293,20 @@ namespace Parser.Internal
return builder.ToList();
}
private ExpressionSyntaxNode? ParseTerm(ParseOptions options)
private ExpressionSyntaxNode ParseTerm(ParseOptions options)
{
var token = CurrentToken;
ExpressionSyntaxNode? expression = null;
switch (token.Kind)
ExpressionSyntaxNode expression = token.Kind switch
{
case TokenKind.NumberLiteralToken:
expression = Factory.NumberLiteralExpressionSyntax(EatToken());
break;
case TokenKind.StringLiteralToken:
expression = Factory.StringLiteralExpressionSyntax(EatToken());
break;
case TokenKind.DoubleQuotedStringLiteralToken:
expression = Factory.DoubleQuotedStringLiteralExpressionSyntax(EatToken());
break;
case TokenKind.OpenSquareBracketToken:
expression = ParseArrayLiteral();
break;
case TokenKind.OpenBraceToken:
expression = ParseCellArrayLiteral();
break;
case TokenKind.ColonToken:
expression = Factory.EmptyExpressionSyntax();
break;
case TokenKind.OpenParenthesisToken:
expression = ParseParenthesizedExpression();
break;
default:
var id = EatToken(TokenKind.IdentifierToken);
expression = Factory.IdentifierNameExpressionSyntax(id);
break;
}
if (expression == null)
{
return null;
}
TokenKind.NumberLiteralToken => Factory.NumberLiteralExpressionSyntax(EatToken()),
TokenKind.StringLiteralToken => Factory.StringLiteralExpressionSyntax(EatToken()),
TokenKind.DoubleQuotedStringLiteralToken => Factory.DoubleQuotedStringLiteralExpressionSyntax(EatToken()),
TokenKind.OpenSquareBracketToken => ParseArrayLiteral(),
TokenKind.OpenBraceToken => ParseCellArrayLiteral(),
TokenKind.ColonToken => Factory.EmptyExpressionSyntax(),
TokenKind.OpenParenthesisToken => ParseParenthesizedExpression(),
_ => Factory.IdentifierNameExpressionSyntax(EatToken(TokenKind.IdentifierToken)),
};
return ParsePostfix(options, expression);
}
@ -348,7 +318,7 @@ namespace Parser.Internal
switch (token.Kind)
{
case TokenKind.OpenBraceToken: // cell array element access
if (options.ParsingArrayElements && expression.TrailingTrivia.Any())
if (options.ParsingArrayElements && expression.TrailingTrivia is not null)
{
return expression;
}
@ -363,7 +333,7 @@ namespace Parser.Internal
);
break;
case TokenKind.OpenParenthesisToken: // function call
if (options.ParsingArrayElements && expression.TrailingTrivia.Any())
if (options.ParsingArrayElements && expression.TrailingTrivia is not null)
{
return expression;
}
@ -401,7 +371,7 @@ namespace Parser.Internal
case TokenKind.UnquotedStringLiteralToken:
return ParseCommandExpression(expression);
case TokenKind.AtToken:
if (expression.TrailingTrivia.Any())
if (expression.TrailingTrivia is not null)
{
return expression;
}
@ -435,7 +405,7 @@ namespace Parser.Internal
private ClassInvokationExpressionSyntaxNode ParseBaseClassInvokation(ExpressionSyntaxNode expression)
{
if (expression is IdentifierNameExpressionSyntaxNode methodName
&& !expression.TrailingTrivia.Any())
&& expression.TrailingTrivia is null)
{
var atToken = EatToken();
var baseClassNameWithArguments = ParseExpression();
@ -446,7 +416,7 @@ namespace Parser.Internal
return Factory.ClassInvokationExpressionSyntax(methodName, atToken, baseClassNameWithArguments);
}
if (expression is MemberAccessExpressionSyntaxNode memberAccess
&& !expression.TrailingTrivia.Any())
&& expression.TrailingTrivia is null)
{
var atToken = EatToken();
var baseClassNameWithArguments = ParseExpression();
@ -530,7 +500,7 @@ namespace Parser.Internal
var builder = new SyntaxListBuilder();
builder.Add(firstName);
while (CurrentToken.Kind == TokenKind.DotToken
&& !lastToken.TrailingTrivia.Any())
&& lastToken.TrailingTrivia is null)
{
var dot = EatToken();
builder.Add(dot);
@ -599,10 +569,6 @@ namespace Parser.Internal
else
{
lhs = ParseTerm(options);
if (lhs is null)
{
throw new Exception("Left-hand side in subexpression cannot be empty.");
}
}
while (true)
@ -845,7 +811,34 @@ namespace Parser.Internal
throw new Exception("Expression statement cannot be empty.");
}
return Factory.ExpressionStatementSyntax(expression);
if (CurrentToken.Kind == TokenKind.SemicolonToken && !TriviaContainsNewLine(expression.TrailingTrivia))
{
var semicolon = EatToken();
return Factory.ExpressionStatementSyntax(
expression,
Factory.TrailingSemicolonSyntax(semicolon));
}
return Factory.ExpressionStatementSyntax(expression, semicolon: null);
}
private bool TriviaContainsNewLine(GreenNode? trivia)
{
if (trivia is not SyntaxList<SyntaxTrivia> triviaList)
{
return false;
}
for (var i = 0; i < triviaList.Length; i++)
{
var text = triviaList[i].Text;
if (text.Contains('\n'))
{
return true;
}
}
return false;
}
private AttributeAssignmentSyntaxNode? ParseAttributeAssignment()
@ -1179,11 +1172,6 @@ namespace Parser.Internal
}
}
if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken)
{
return ParseExpressionStatement();
}
if (CurrentToken.Kind == TokenKind.SemicolonToken)
{
return Factory.EmptyStatementSyntax(EatToken());
@ -1191,9 +1179,14 @@ namespace Parser.Internal
return ParseExpressionStatement();
}
private BlockStatementSyntaxNode ParseBlockStatement()
private BlockStatementSyntaxNode? ParseBlockStatement()
{
var statements = ParseStatementList();
if (statements.Length == 0)
{
return null;
}
return Factory.BlockStatementSyntax(statements);
}

View File

@ -3,7 +3,7 @@ namespace Parser.Internal
{
internal partial class SyntaxFactory
{
public FileSyntaxNode FileSyntax(BlockStatementSyntaxNode body, SyntaxToken endOfFile)
public FileSyntaxNode FileSyntax(BlockStatementSyntaxNode? body, SyntaxToken endOfFile)
{
return new FileSyntaxNode(body, endOfFile);
}
@ -78,9 +78,14 @@ namespace Parser.Internal
return new TryCatchStatementSyntaxNode(tryKeyword, tryBody, catchClause, endKeyword);
}
public ExpressionStatementSyntaxNode ExpressionStatementSyntax(ExpressionSyntaxNode expression)
public ExpressionStatementSyntaxNode ExpressionStatementSyntax(ExpressionSyntaxNode expression, TrailingSemicolonSyntaxNode? semicolon)
{
return new ExpressionStatementSyntaxNode(expression);
return new ExpressionStatementSyntaxNode(expression, semicolon);
}
public TrailingSemicolonSyntaxNode TrailingSemicolonSyntax(SyntaxToken semicolon)
{
return new TrailingSemicolonSyntaxNode(semicolon);
}
public EmptyStatementSyntaxNode EmptyStatementSyntax(SyntaxToken semicolon)

View File

@ -58,5 +58,7 @@ namespace Parser.Internal
{
return new SyntaxList(_elements, diagnostics);
}
public int Length => _elements.Length;
}
}

View File

@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
namespace Parser.Internal
{
@ -31,11 +32,18 @@ namespace Parser.Internal
return (T)_list.GetListSlot(i);
}
public T this[int i] => (T)_list.GetListSlot(i);
public static SyntaxList<T> List(T[] elements)
{
return new SyntaxList<T>(elements);
}
public static SyntaxList<T> List(IReadOnlyList<T> elements)
{
return new SyntaxList<T>(elements.ToArray());
}
public static SyntaxList<T> Empty => new SyntaxList<T>(new T[] { });
public override bool IsList => true;
@ -49,5 +57,7 @@ namespace Parser.Internal
{
return new SyntaxList<T>(_list._elements.Select(x => (T)x).ToArray(), diagnostics);
}
public int Length => _list.Length;
}
}

View File

@ -3,9 +3,9 @@ namespace Parser.Internal
{
internal class FileSyntaxNode : SyntaxNode
{
internal readonly BlockStatementSyntaxNode _body;
internal readonly BlockStatementSyntaxNode? _body;
internal readonly SyntaxToken _endOfFile;
internal FileSyntaxNode(BlockStatementSyntaxNode body, SyntaxToken endOfFile): base(TokenKind.File)
internal FileSyntaxNode(BlockStatementSyntaxNode? body, SyntaxToken endOfFile): base(TokenKind.File)
{
Slots = 2;
this.AdjustWidth(body);
@ -14,7 +14,7 @@ namespace Parser.Internal
_endOfFile = endOfFile;
}
internal FileSyntaxNode(BlockStatementSyntaxNode body, SyntaxToken endOfFile, TokenDiagnostic[] diagnostics): base(TokenKind.File, diagnostics)
internal FileSyntaxNode(BlockStatementSyntaxNode? body, SyntaxToken endOfFile, TokenDiagnostic[] diagnostics): base(TokenKind.File, diagnostics)
{
Slots = 2;
this.AdjustWidth(body);
@ -779,18 +779,23 @@ namespace Parser.Internal
internal class ExpressionStatementSyntaxNode : StatementSyntaxNode
{
internal readonly ExpressionSyntaxNode _expression;
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression): base(TokenKind.ExpressionStatement)
internal readonly TrailingSemicolonSyntaxNode? _semicolon;
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression, TrailingSemicolonSyntaxNode? semicolon): base(TokenKind.ExpressionStatement)
{
Slots = 1;
Slots = 2;
this.AdjustWidth(expression);
_expression = expression;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
}
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression, TokenDiagnostic[] diagnostics): base(TokenKind.ExpressionStatement, diagnostics)
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression, TrailingSemicolonSyntaxNode? semicolon, TokenDiagnostic[] diagnostics): base(TokenKind.ExpressionStatement, diagnostics)
{
Slots = 1;
Slots = 2;
this.AdjustWidth(expression);
_expression = expression;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
}
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position)
@ -800,14 +805,52 @@ namespace Parser.Internal
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new ExpressionStatementSyntaxNode(_expression, diagnostics);
return new ExpressionStatementSyntaxNode(_expression, _semicolon, diagnostics);
}
public override GreenNode? GetSlot(int i)
{
return i switch
{
0 => _expression, _ => null
0 => _expression, 1 => _semicolon, _ => null
}
;
}
}
internal class TrailingSemicolonSyntaxNode : SyntaxNode
{
internal readonly SyntaxToken _semicolon;
internal TrailingSemicolonSyntaxNode(SyntaxToken semicolon): base(TokenKind.TrailingSemicolon)
{
Slots = 1;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
}
internal TrailingSemicolonSyntaxNode(SyntaxToken semicolon, TokenDiagnostic[] diagnostics): base(TokenKind.TrailingSemicolon, diagnostics)
{
Slots = 1;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
}
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position)
{
return new Parser.TrailingSemicolonSyntaxNode(parent, this, position);
}
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new TrailingSemicolonSyntaxNode(_semicolon, diagnostics);
}
public override GreenNode? GetSlot(int i)
{
return i switch
{
0 => _semicolon, _ => null
}
;

View File

@ -51,10 +51,10 @@ namespace Parser.Internal
return builder.ToString();
}
public override string FullText => CollectFullText();
//public override string FullText => CollectFullText();
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => throw new NotImplementedException();
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => throw new NotImplementedException();
public override GreenNode? LeadingTriviaCore => throw new NotImplementedException();
public override GreenNode? TrailingTriviaCore => throw new NotImplementedException();
}
internal abstract class StatementSyntaxNode : SyntaxNode
@ -121,11 +121,11 @@ namespace Parser.Internal
public override GreenNode? GetSlot(int i)
{
switch (i)
return i switch
{
case 0: return _file;
default: return null;
}
0 => _file,
_ => null,
};
}
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Parser.Internal
{
@ -16,66 +15,66 @@ namespace Parser.Internal
public SyntaxTokenWithTrivia(
TokenKind kind,
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia) : base(kind)
GreenNode? leadingTrivia,
GreenNode? trailingTrivia) : base(kind)
{
_text = text;
LeadingTriviaCore = leadingTrivia;
TrailingTriviaCore = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public SyntaxTokenWithTrivia(
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia) : base(kind)
GreenNode? leadingTrivia,
GreenNode? trailingTrivia) : base(kind)
{
_text = base.Text;
LeadingTriviaCore = leadingTrivia;
TrailingTriviaCore = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public SyntaxTokenWithTrivia(
TokenKind kind,
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia,
TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
_text = text;
LeadingTriviaCore = leadingTrivia;
TrailingTriviaCore = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public SyntaxTokenWithTrivia(
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia,
TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
_text = base.Text;
LeadingTriviaCore = leadingTrivia;
TrailingTriviaCore = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
if (leading)
if (leading && LeadingTrivia is SyntaxList<SyntaxTrivia> leadingTrivia)
{
foreach (var trivia in LeadingTrivia)
for (var i = 0; i < leadingTrivia.Length; i++)
{
writer.Write(trivia.Text);
leadingTrivia[i].WriteTriviaTo(writer);
}
}
base.WriteTokenTo(writer, leading, trailing);
if (trailing)
if (trailing && TrailingTrivia is SyntaxList<SyntaxTrivia> trailingTrivia)
{
foreach (var trivia in TrailingTrivia)
for (var i = 0; i < trailingTrivia.Length; i++)
{
writer.Write(trivia.Text);
trailingTrivia[i].WriteTriviaTo(writer);
}
}
}
@ -85,9 +84,9 @@ namespace Parser.Internal
return new SyntaxTokenWithTrivia(Kind, _text, LeadingTrivia, TrailingTrivia, diagnostics);
}
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
public override GreenNode? LeadingTriviaCore { get; }
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
public override GreenNode? TrailingTriviaCore { get; }
}
internal class SyntaxTokenWithValue<T> : SyntaxToken
@ -138,46 +137,47 @@ namespace Parser.Internal
TokenKind kind,
string text,
T value,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia) : base(kind, text, value)
GreenNode? leadingTrivia,
GreenNode? trailingTrivia)
: base(kind, text, value)
{
LeadingTriviaCore = leadingTrivia;
TrailingTriviaCore = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public SyntaxTokenWithValueAndTrivia(
TokenKind kind,
string text,
T value,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia,
TokenDiagnostic[] diagnostics) : base(kind, text, value, diagnostics)
{
LeadingTriviaCore = leadingTrivia;
TrailingTriviaCore = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
public override GreenNode? LeadingTriviaCore { get; }
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
public override GreenNode? TrailingTriviaCore { get; }
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
if (leading)
if (leading && LeadingTrivia is SyntaxList<SyntaxTrivia> leadingTrivia)
{
foreach (var trivia in LeadingTrivia)
for (var i = 0; i < leadingTrivia.Length; i++)
{
writer.Write(trivia.Text);
leadingTrivia[i].WriteTriviaTo(writer);
}
}
base.WriteTokenTo(writer, leading, trailing);
if (trailing)
if (trailing && TrailingTrivia is SyntaxList<SyntaxTrivia> trailingTrivia)
{
foreach (var trivia in TrailingTrivia)
for (var i = 0; i < trailingTrivia.Length; i++)
{
writer.Write(trivia.Text);
trailingTrivia[i].WriteTriviaTo(writer);
}
}
}
@ -220,49 +220,49 @@ namespace Parser.Internal
internal class SyntaxIdentifierWithTrivia : SyntaxIdentifier
{
private readonly IReadOnlyList<SyntaxTrivia> _leadingTrivia;
private readonly IReadOnlyList<SyntaxTrivia> _trailingTrivia;
private readonly GreenNode? _leadingTrivia;
private readonly GreenNode? _trailingTrivia;
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => _leadingTrivia;
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => _trailingTrivia;
public override GreenNode? LeadingTriviaCore => _leadingTrivia;
public override GreenNode? TrailingTriviaCore => _trailingTrivia;
public SyntaxIdentifierWithTrivia(
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia
) : base(text)
GreenNode? leadingTrivia,
GreenNode? trailingTrivia)
: base(text)
{
_leadingTrivia = leadingTrivia;
_trailingTrivia = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public SyntaxIdentifierWithTrivia(
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia,
TokenDiagnostic[] diagnostics) : base(text, diagnostics)
{
_leadingTrivia = leadingTrivia;
_trailingTrivia = trailingTrivia;
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
_fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0);
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
if (leading)
if (leading && LeadingTrivia is SyntaxList<SyntaxTrivia> leadingTrivia)
{
foreach (var trivia in LeadingTrivia)
for (var i = 0; i < leadingTrivia.Length; i++)
{
writer.Write(trivia.Text);
leadingTrivia[i].WriteTriviaTo(writer);
}
}
base.WriteTokenTo(writer, leading, trailing);
if (trailing)
if (trailing && TrailingTrivia is SyntaxList<SyntaxTrivia> trailingTrivia)
{
foreach (var trivia in TrailingTrivia)
for (var i = 0; i < trailingTrivia.Length; i++)
{
writer.Write(trivia.Text);
trailingTrivia[i].WriteTriviaTo(writer);
}
}
}
@ -280,8 +280,8 @@ namespace Parser.Internal
{
public MissingTokenWithTrivia(
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia
GreenNode? leadingTrivia,
GreenNode? trailingTrivia
) : base(kind, leadingTrivia, trailingTrivia)
{
_isMissing = true;
@ -289,8 +289,8 @@ namespace Parser.Internal
public MissingTokenWithTrivia(
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia,
TokenDiagnostic[] diagnostics) : base(kind, leadingTrivia, trailingTrivia, diagnostics)
{
_isMissing = true;
@ -312,7 +312,7 @@ namespace Parser.Internal
{
}
internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, s_EmptySyntaxTriviaList, s_EmptySyntaxTriviaList);
internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, null, null);
public virtual object? Value => null;
@ -320,8 +320,8 @@ namespace Parser.Internal
public virtual int Width => Text.Length;
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => s_EmptySyntaxTriviaList;
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => s_EmptySyntaxTriviaList;
public override GreenNode? LeadingTriviaCore => null;
public override GreenNode? TrailingTriviaCore => null;
public override GreenNode? GetSlot(int i)
{

View File

@ -20,6 +20,7 @@ namespace Parser.Internal
}
public override string Text => _text;
public override string FullText => _text;
public int Width => _text.Length;
public override GreenNode? GetSlot(int i)
@ -40,12 +41,17 @@ namespace Parser.Internal
writer.Write(_text);
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{
writer.Write(_text);
}
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxTrivia(Kind, _text, diagnostics);
}
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore => new List<SyntaxTrivia>();
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => new List<SyntaxTrivia>();
public override GreenNode? LeadingTriviaCore => null;
public override GreenNode? TrailingTriviaCore => null;
}
}

View File

@ -13,6 +13,16 @@ namespace Parser.Internal
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia)
{
var leading = leadingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(leadingTrivia) : null;
var trailing = trailingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(trailingTrivia) : null;
return new SyntaxToken.SyntaxTokenWithTrivia(kind, leading, trailing);
}
public static SyntaxToken CreateTokenWithTrivia(
TokenKind kind,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia)
{
return new SyntaxToken.SyntaxTokenWithTrivia(kind, leadingTrivia, trailingTrivia);
}
@ -21,6 +31,16 @@ namespace Parser.Internal
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia)
{
var leading = leadingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(leadingTrivia) : null;
var trailing = trailingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(trailingTrivia) : null;
return new SyntaxToken.SyntaxIdentifierWithTrivia(text, leading, trailing);
}
public static SyntaxToken CreateIdentifier(
string text,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia)
{
return new SyntaxToken.SyntaxIdentifierWithTrivia(text, leadingTrivia, trailingTrivia);
}
@ -31,6 +51,18 @@ namespace Parser.Internal
T value,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia)
{
var leading = leadingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(leadingTrivia) : null;
var trailing = trailingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(trailingTrivia) : null;
return new SyntaxToken.SyntaxTokenWithValueAndTrivia<T>(kind, text, value, leading, trailing);
}
public static SyntaxToken CreateTokenWithValueAndTrivia<T>(
TokenKind kind,
string text,
T value,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia)
{
return new SyntaxToken.SyntaxTokenWithValueAndTrivia<T>(kind, text, value, leadingTrivia, trailingTrivia);
}
@ -40,6 +72,22 @@ namespace Parser.Internal
string value,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia)
{
var leading = leadingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(leadingTrivia) : null;
var trailing = trailingTrivia.Count > 0 ? SyntaxList<SyntaxTrivia>.List(trailingTrivia) : null;
return new SyntaxToken.SyntaxTokenWithValueAndTrivia<string>(
TokenKind.UnquotedStringLiteralToken,
text,
value,
leading,
trailing);
}
public static SyntaxToken CreateUnquotedStringLiteral(
string text,
string value,
GreenNode? leadingTrivia,
GreenNode? trailingTrivia)
{
return new SyntaxToken.SyntaxTokenWithValueAndTrivia<string>(
TokenKind.UnquotedStringLiteralToken,
@ -54,7 +102,9 @@ namespace Parser.Internal
IReadOnlyList<SyntaxTrivia>? leadingTrivia,
IReadOnlyList<SyntaxTrivia>? trailingTrivia)
{
return new SyntaxToken.MissingTokenWithTrivia(kind, leadingTrivia ?? SyntaxToken.s_EmptySyntaxTriviaList, trailingTrivia ?? SyntaxToken.s_EmptySyntaxTriviaList);
var leading = (leadingTrivia is { } l && l.Count > 0) ? SyntaxList<SyntaxTrivia>.List(leadingTrivia) : null;
var trailing = (trailingTrivia is { } c && c.Count > 0) ? SyntaxList<SyntaxTrivia>.List(trailingTrivia) : null;
return new SyntaxToken.MissingTokenWithTrivia(kind, leading, trailing);
}
}
}

View File

@ -1,5 +1,5 @@
using Parser.Binding;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@ -10,6 +10,7 @@ namespace Parser.Lowering
internal class Lowerer : BoundTreeRewriter
{
private int _labelNumber = 0;
private int _localVariableNumber = 0;
private Lowerer()
{
@ -21,6 +22,12 @@ namespace Parser.Lowering
return new BoundLabel(name);
}
private TypedVariableSymbol GenerateTypedLocalVariable(TypeSymbol typeSymbol)
{
var name = $"#{++_localVariableNumber}";
return new TypedVariableSymbol(name, typeSymbol);
}
public override BoundStatement RewriteIfStatement(BoundIfStatement node)
{
// if cond
@ -80,6 +87,116 @@ namespace Parser.Lowering
return RewriteBlockStatement(Block(node.Syntax, builder.ToArray()));
}
public override BoundStatement RewriteWhileStatement(BoundWhileStatement node)
{
// while cond
// body
// end
//
// |
// |
// V
//
// LabelLoop:
// gotoFalse cond LabelEnd
// body
// goto LabelLoop
// LabelEnd:
var labelLoop = GenerateLabel();
var labelEnd = GenerateLabel();
var result = Block(
node.Syntax,
// LabelLoop:
LabelStatement(node.Syntax, labelLoop),
// gotoFalse cond LabelEnd
GotoIfFalse(
node.Syntax,
node.Condition,
labelEnd),
// body
node.Body,
// goto LabelLoop
Goto(node.Syntax, labelLoop),
// LabelEnd:
LabelStatement(node.Syntax, labelEnd));
return RewriteBlockStatement(result);
}
public override BoundStatement RewriteForStatement(BoundForStatement node)
{
// for i = expr
// body
// end
//
// |
// |
// V
//
// #array = expr
// #length = len(#array)
// #index = 0
// while #index < #length
// i = #array(#index);
// body
// #index = #index + 1
var localArray = GenerateTypedLocalVariable(TypeSymbol.MObject);
var localLength = GenerateTypedLocalVariable(TypeSymbol.Int);
var localIndex = GenerateTypedLocalVariable(TypeSymbol.Int);
var whileBody = Block(
node.Syntax,
// i = #array(#index);
ExpressionStatement(
node.Syntax,
Assignment(
node.Syntax,
node.LoopVariable,
FunctionCall(
node.Syntax,
TypedVariableExpression(node.Syntax, localArray),
new[] { (BoundExpression)TypedVariableExpression(node.Syntax, localIndex) }.ToImmutableArray())),
discardResult: true),
// body
node.Body,
// #index = #index + 1;
ExpressionStatement(
node.Syntax,
Assignment(
node.Syntax,
TypedVariableExpression(node.Syntax, localIndex),
BinaryOperation(
node.Syntax,
TypedVariableExpression(node.Syntax, localIndex),
BoundBinaryOperator.GetOperator(TokenKind.PlusToken, TypeSymbol.Int, TypeSymbol.Int)!,
NumberIntLiteral(node.Syntax, 1))),
discardResult: true));
var result = Block(
node.Syntax,
// #array = expr
TypedVariableDeclaration(node.Syntax, localArray, node.LoopedExpression),
// #length = len(#array)
TypedVariableDeclaration(
node.Syntax,
localLength,
FunctionCall(
node.Syntax,
Identifier(node.Syntax, "len"),
new[] { (BoundExpression)TypedVariableExpression(node.Syntax, localArray) }.ToImmutableArray())),
// #index = 0
TypedVariableDeclaration(node.Syntax, localIndex, NumberIntLiteral(node.Syntax, 0)),
// while #index < #length
// whileBody
WhileStatement(
node.Syntax,
BinaryOperation(
node.Syntax,
TypedVariableExpression(node.Syntax, localIndex),
BoundBinaryOperator.GetOperator(TokenKind.LessToken, TypeSymbol.Int, TypeSymbol.Int)!,
TypedVariableExpression(node.Syntax, localLength)),
whileBody));
return RewriteBlockStatement(result);
}
public static BoundBlockStatement Lower(BoundStatement statement)
{
var lowerer = new Lowerer();

View File

@ -0,0 +1,40 @@
using Parser.Objects;
using System;
namespace Parser.MFunctions
{
public static class MHelpers
{
public static void Disp(MObject? obj)
{
if (obj is not null)
{
Console.WriteLine(obj);
}
}
public static int Len(MObject obj)
{
return obj switch
{
MDoubleMatrix m => m.RowCount * m.ColumnCount,
MDoubleNumber _ => 1,
MLogical _ => 1,
MCharArray c => c.Chars.Length,
_ => throw new System.Exception($"Unknown MObject type {obj.GetType()}"),
};
}
public static bool ToBool(MObject operand)
{
return operand switch
{
MDoubleNumber { Value: var value } => value != 0.0,
MLogical { Value: var value } => value,
MCharArray { Chars: var value } => value.Length > 0,
MDoubleMatrix m => m.RowCount > 0 && m.ColumnCount > 0,
_ => throw new System.Exception($"Unknown MObject type {operand.GetType()}"),
};
}
}
}

View File

@ -1,9 +1,34 @@
using Parser.Objects;
using System;
namespace Parser.MFunctions
{
public static class MOperations
{
public static MObject? Colon(MObject left, MObject right)
{
if (left is MDoubleNumber { Value: var lValue }
&& right is MDoubleNumber { Value: var rValue })
{
var array = CreateRangeMatrix(lValue, rValue);
return MObject.CreateDoubleMatrix(array);
}
return null;
}
private static double[,] CreateRangeMatrix(double lValue, double rValue)
{
var length = (int)Math.Round(rValue - lValue) + 1;
var result = new double[1, length];
for (var i = 0; i < length; i++)
{
result[0, i] = lValue + i;
}
return result;
}
public static MObject? Plus(MObject left, MObject right)
{
if (left is MDoubleNumber { Value: var lValue }
@ -101,5 +126,17 @@ namespace Parser.MFunctions
return null;
}
public static MObject? ArraySlice(MObject array, MObject range)
{
if (array is MDoubleMatrix m
&& range is MDoubleNumber { Value: var doubleIndex })
{
var index = (int)doubleIndex;
return MObject.CreateDoubleNumber(m[index]);
}
return null;
}
}
}

View File

@ -0,0 +1,48 @@
using System.Text;
namespace Parser.Objects
{
public class MDoubleMatrix : MObject
{
private MDoubleMatrix(double[,] matrix)
{
Matrix = matrix;
RowCount = matrix.GetLength(0);
ColumnCount = matrix.GetLength(1);
}
public double[,] Matrix { get; }
public int RowCount { get; }
public int ColumnCount { get; }
public ref double this[int i, int j] => ref Matrix[i, j];
public ref double this[int i] => ref Matrix[i % RowCount, i / RowCount];
public override string ToString()
{
var sb = new StringBuilder();
for (var i = 0; i < RowCount; i++)
{
for (var j = 0; j < ColumnCount; j++)
{
if (j > 0)
{
sb.Append(' ');
}
sb.Append(Matrix[i, j]);
}
sb.AppendLine();
}
return sb.ToString();
}
public static MDoubleMatrix Create(double[,] matrix)
{
return new MDoubleMatrix(matrix);
}
}
}

View File

@ -1,4 +1,6 @@
namespace Parser.Objects
using System;
namespace Parser.Objects
{
public abstract class MObject
{
@ -7,11 +9,26 @@
return MDoubleNumber.Create(value);
}
public static MDoubleNumber CreateIntNumber(int value)
{
return MDoubleNumber.Create(value);
}
public static MDoubleMatrix CreateDoubleMatrix(double[,] matrix)
{
return MDoubleMatrix.Create(matrix);
}
public static MCharArray CreateCharArray(char[] chars)
{
return MCharArray.Create(chars);
}
public static MCharArray CreateCharArray(string s)
{
return MCharArray.Create(s.ToCharArray());
}
public static MLogical CreateLogical(bool value)
{
return MLogical.Create(value);

View File

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
<?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="BlockStatementSyntaxNode" Name="body" />
<Field Type="BlockStatementSyntaxNode" Name="body" Nullable="true"/>
<Field Type="SyntaxToken" Name="endOfFile" />
</Class>
<Class Name="BlockStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="BlockStatement">
@ -88,6 +88,10 @@
</Class>
<Class Name="ExpressionStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="ExpressionStatement">
<Field Type="ExpressionSyntaxNode" Name="expression" />
<Field Type="TrailingSemicolonSyntaxNode" Name="semicolon" Nullable="true" />
</Class>
<Class Name="TrailingSemicolonSyntaxNode" BaseClass="SyntaxNode" Kind="TrailingSemicolon">
<Field Type="SyntaxToken" Name="semicolon" />
</Class>
<Class Name="EmptyStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="EmptyStatement">
<Field Type="SyntaxToken" Name="semicolon" />

27
Parser/SyntaxNavigator.cs Normal file
View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace Parser
{
public class SyntaxNavigator
{
public static SyntaxNavigator Singleton = new SyntaxNavigator();
public IEnumerable<SyntaxToken> EnumerateTokens(SyntaxNode node)
{
foreach (var child in node.GetChildNodesAndTokens())
{
if (child.IsNode)
{
foreach (var token in EnumerateTokens(child.AsNode()!))
{
yield return token;
}
}
if (child.IsToken)
{
yield return child.AsToken();
}
}
}
}
}

View File

@ -16,12 +16,12 @@ namespace Parser
}
}
public BlockStatementSyntaxNode Body
public BlockStatementSyntaxNode? Body
{
get
{
var red = this.GetRed(ref this._body!, 0);
return red is null ? throw new System.Exception("body cannot be null.") : (BlockStatementSyntaxNode)red;
var red = this.GetRed(ref this._body, 0);
return red is null ? default : (BlockStatementSyntaxNode)red;
}
}
@ -29,7 +29,7 @@ namespace Parser
{
return i switch
{
0 => GetRed(ref _body!, 0), _ => null
0 => GetRed(ref _body, 0), _ => null
}
;
@ -852,6 +852,7 @@ namespace Parser
public class ExpressionStatementSyntaxNode : StatementSyntaxNode
{
private SyntaxNode? _expression;
private SyntaxNode? _semicolon;
internal ExpressionStatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position)
{
}
@ -865,11 +866,20 @@ namespace Parser
}
}
public TrailingSemicolonSyntaxNode? Semicolon
{
get
{
var red = this.GetRed(ref this._semicolon, 1);
return red is null ? default : (TrailingSemicolonSyntaxNode)red;
}
}
internal override SyntaxNode? GetNode(int i)
{
return i switch
{
0 => GetRed(ref _expression!, 0), _ => null
0 => GetRed(ref _expression!, 0), 1 => GetRed(ref _semicolon, 1), _ => null
}
;
@ -881,6 +891,36 @@ namespace Parser
}
}
public class TrailingSemicolonSyntaxNode : SyntaxNode
{
internal TrailingSemicolonSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position)
{
}
public SyntaxToken Semicolon
{
get
{
return new SyntaxToken(this, ((Parser.Internal.TrailingSemicolonSyntaxNode)_green)._semicolon, this.GetChildPosition(0));
}
}
internal override SyntaxNode? GetNode(int i)
{
return i switch
{
_ => null
}
;
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitTrailingSemicolon(this);
}
}
public class EmptyStatementSyntaxNode : StatementSyntaxNode
{
internal EmptyStatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position)

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Parser.Internal;
using System.Collections.Generic;
using System.Linq;
namespace Parser
@ -13,6 +13,7 @@ namespace Parser
_parent = parent;
_green = green;
Position = position;
FullSpan = new TextSpan(Position, green.FullWidth);
}
private protected SyntaxNode(Internal.GreenNode green, int position)
@ -20,6 +21,7 @@ namespace Parser
_parent = this;
_green = green;
Position = position;
FullSpan = new TextSpan(Position, green.FullWidth);
}
public TokenKind Kind => _green.Kind;
@ -35,6 +37,19 @@ namespace Parser
public int Position { get; }
public TextSpan FullSpan { get; }
public TextSpan Span => CalculateSpan();
private TextSpan CalculateSpan()
{
var leadingTriviaWidth = LeadingTrivia?.Width ?? 0;
var trailingTriviaWidth = TrailingTrivia?.Width ?? 0;
return new TextSpan(Position + leadingTriviaWidth, _green.FullWidth - leadingTriviaWidth - trailingTriviaWidth);
}
public int FullWidth => _green.FullWidth;
internal int GetChildPosition(int slot)
{
var result = Position;
@ -75,26 +90,32 @@ namespace Parser
public virtual string FullText => _green.FullText;
public int FullWidth => _green.FullWidth;
public virtual IReadOnlyList<SyntaxTrivia> LeadingTrivia
public virtual SyntaxTriviaList? LeadingTrivia
{
get
{
var p = Parent;
return _green.LeadingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
return GetFirstToken()?.LeadingTrivia;
}
}
public virtual IReadOnlyList<SyntaxTrivia> TrailingTrivia
public virtual SyntaxTriviaList? TrailingTrivia
{
get
{
var p = Parent;
return _green.TrailingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
return GetLastToken()?.TrailingTrivia;
}
}
public SyntaxToken? GetFirstToken()
{
return SyntaxNavigator.Singleton.EnumerateTokens(this).Select(t => (SyntaxToken?)t).FirstOrDefault();
}
public SyntaxToken? GetLastToken()
{
return SyntaxNavigator.Singleton.EnumerateTokens(this).Select(t => (SyntaxToken?)t).LastOrDefault();
}
public abstract void Accept(SyntaxVisitor visitor);
public SyntaxDiagnostic[] GetDiagnostics()
@ -165,12 +186,16 @@ namespace Parser
internal override SyntaxNode? GetNode(int index)
{
throw new System.NotImplementedException();
return index switch
{
0 => GetRed(ref _file!, 0),
_ => null,
};
}
public override void Accept(SyntaxVisitor visitor)
{
throw new System.NotImplementedException();
visitor.VisitRoot(this);
}

View File

@ -1,4 +1,5 @@
using System;
using Parser.Internal;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@ -22,6 +23,7 @@ namespace Parser
_parent = parent;
_token = token ?? throw new ArgumentNullException(nameof(token));
Position = position;
FullSpan = new TextSpan(Position, token.FullWidth);
}
public SyntaxNode Parent => _parent;
@ -29,6 +31,17 @@ namespace Parser
public int Position { get; }
public TextSpan FullSpan { get; }
public TextSpan Span => CalculateSpan();
public TextSpan CalculateSpan()
{
var leadingTriviaWidth = LeadingTrivia.Width;
var trailingTriviaWidth = TrailingTrivia.Width;
return new TextSpan(Position + leadingTriviaWidth, FullWidth - leadingTriviaWidth - trailingTriviaWidth);
}
public object? Value => _token.GetValue();
public bool Equals(SyntaxToken other)
@ -38,7 +51,7 @@ namespace Parser
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj is null) return false;
return obj is SyntaxToken token && Equals(token);
}
@ -65,21 +78,21 @@ namespace Parser
public int FullWidth => _token.FullWidth;
public bool IsMissing => _token.IsMissing;
public IReadOnlyList<SyntaxTrivia> LeadingTrivia
public SyntaxTriviaList LeadingTrivia
{
get
{
var p = _parent;
return _token.LeadingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
return new SyntaxTriviaList(this, Token.LeadingTriviaCore, this.Position);
}
}
public IReadOnlyList<SyntaxTrivia> TrailingTrivia
public SyntaxTriviaList TrailingTrivia
{
get
{
var p = _parent;
return _token.TrailingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList();
var trailingGreen = Token.TrailingTriviaCore;
var trailingTriviaWidth = trailingGreen?.FullWidth ?? 0;
return new SyntaxTriviaList(this, trailingGreen, this.Position + this.FullWidth - trailingTriviaWidth);
}
}
}

View File

@ -0,0 +1,54 @@
using Parser.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Parser
{
public struct SyntaxTriviaList : IReadOnlyList<SyntaxTrivia>
{
internal GreenNode? Node { get; }
internal SyntaxTriviaList(SyntaxToken token, GreenNode? node, int position)
{
Node = node;
Token = token;
Position = position;
}
public SyntaxToken Token { get; }
public int Position { get; }
public int Count => (Node as SyntaxList<Internal.SyntaxTrivia>)?.Length ?? 0;
public int Width => Node?.FullWidth ?? 0;
public SyntaxTrivia this[int index]
{
get
{
return Node switch
{
SyntaxList<Internal.SyntaxTrivia> triviaList => new SyntaxTrivia(Token.Parent, triviaList[index]),
_ => throw new IndexOutOfRangeException(),
};
}
}
public string FullText => Node?.FullText ?? string.Empty;
public IEnumerator<SyntaxTrivia> GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -83,6 +83,11 @@ namespace Parser
DefaultVisit(node);
}
public virtual void VisitTrailingSemicolon(TrailingSemicolonSyntaxNode node)
{
DefaultVisit(node);
}
public virtual void VisitEmptyStatement(EmptyStatementSyntaxNode node)
{
DefaultVisit(node);

View File

@ -15,5 +15,10 @@
{
DefaultVisit(list);
}
public virtual void VisitRoot(RootSyntaxNode node)
{
DefaultVisit(node);
}
}
}

View File

@ -135,6 +135,9 @@
// a list of syntax nodes and/or tokens.
List,
// a semicolon that marks expression statements with discarded results.
TrailingSemicolon,
// STATEMENTS
// The name ends with "Declaration" or "Statement".

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View File

@ -11,14 +11,14 @@ namespace Semantics
{
var name = methodDefinition.Name.Text;
var description = "";
description += string.Join("", methodDefinition.LeadingTrivia.Select(x => x.FullText));
description += string.Join("", methodDefinition.LeadingTrivia?.Select(x => x.FullText));
if (methodDefinition.Body == null)
{
description += string.Join("", methodDefinition.EndKeyword.LeadingTrivia.Select(x => x.FullText));
description += string.Join("", methodDefinition.EndKeyword.LeadingTrivia?.Select(x => x.FullText));
}
else
{
description += string.Join("", methodDefinition.Body.LeadingTrivia.Select(x => x.FullText));
description += string.Join("", methodDefinition.Body.LeadingTrivia?.Select(x => x.FullText));
}
return new MMethod(name, description);
@ -28,7 +28,7 @@ namespace Semantics
{
var name = methodDeclaration.Name.Text;
var description = "";
description += string.Join("", methodDeclaration.LeadingTrivia.Select(x => x.FullText));
description += string.Join("", methodDeclaration.LeadingTrivia?.Select(x => x.FullText));
return new MMethod(name, description);
}
@ -58,8 +58,7 @@ namespace Semantics
public static MClass FromTree(FileSyntaxNode tree, string fileName)
{
var classDeclaration = tree.Body.Statements[0].AsNode() as ClassDeclarationSyntaxNode;
if (classDeclaration == null)
if (tree.Body.Statements[0].AsNode() is not ClassDeclarationSyntaxNode classDeclaration)
{
return null;
}

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Parser\Parser.csproj" />

View File

@ -23,7 +23,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Repl", "Repl\Repl.csproj",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MApplication", "MApplication\MApplication.csproj", "{A7EE271C-8822-43EA-BA13-5D6D5DC5B581}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmi", "cmi\cmi.csproj", "{C2447F0B-733D-4755-A104-5B82E24D3F47}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmi", "cmi\cmi.csproj", "{C2447F0B-733D-4755-A104-5B82E24D3F47}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmc", "cmc\cmc.csproj", "{4200B289-ED2B-4C6F-AFDF-EC91FB3837B3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -63,6 +65,10 @@ Global
{C2447F0B-733D-4755-A104-5B82E24D3F47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2447F0B-733D-4755-A104-5B82E24D3F47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2447F0B-733D-4755-A104-5B82E24D3F47}.Release|Any CPU.Build.0 = Release|Any CPU
{4200B289-ED2B-4C6F-AFDF-EC91FB3837B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4200B289-ED2B-4C6F-AFDF-EC91FB3837B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4200B289-ED2B-4C6F-AFDF-EC91FB3837B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4200B289-ED2B-4C6F-AFDF-EC91FB3837B3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.7.0-3.final" />

60
cmc/Program.cs Normal file
View File

@ -0,0 +1,60 @@
using Parser;
using System;
using System.IO;
using Mono.Options;
using System.Collections.Generic;
namespace cmc
{
class Program
{
static int Main(string[] args)
{
var referencePaths = new List<string>();
var outputPath = (string?)null;
var moduleName = (string?)null;
var helpRequested = false;
var sourcePaths = new List<string>();
var options = new OptionSet
{
"usage: cmc <source-paths> [options]",
{ "r=", "The {path} of an assembly to reference", v => referencePaths.Add(v) },
{ "o=", "The output {path} of the assembly to create", v => outputPath = v },
{ "m=", "The {name} of the module", v => moduleName = v },
{ "?|h|help", "Prints help", v => helpRequested = true },
{ "<>", v => sourcePaths.Add(v) }
};
options.Parse(args);
if (helpRequested)
{
options.WriteOptionDescriptions(Console.Out);
return 0;
}
if (sourcePaths.Count > 1)
{
Console.Error.WriteLine("Cannot compile more than one file.");
return -1;
}
if (outputPath == null)
{
outputPath = Path.ChangeExtension(sourcePaths[0], ".exe");
}
if (moduleName == null)
{
moduleName = Path.GetFileNameWithoutExtension(outputPath);
}
var sourcePath = sourcePaths[0];
var text = File.ReadAllText(sourcePath);
var tree = SyntaxTree.Parse(text);
var compilation = Compilation.Create(tree);
compilation.Emit(referencePaths.ToArray(), outputPath);
return 0;
}
}
}

18
cmc/cmc.csproj Normal file
View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Parser\Parser.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
</ItemGroup>
</Project>

View File

@ -4,7 +4,6 @@ using System.IO;
namespace cmi
{
class Program
{
static void Main(string[] args)

View File

@ -27,7 +27,7 @@ namespace cmi
var child = children[index];
if (child.IsNode)
{
RenderNode(child.AsNode(), indent, index == last);
RenderNode(child.AsNode()!, indent, index == last);
}
else if (child.IsToken)
{

View File

@ -0,0 +1,7 @@
<Project>
<PropertyGroup>
<DefaultLanguageSourceExtension>.m</DefaultLanguageSourceExtension>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,18 @@
<Project>
<Target Name="CreateManifestResourceNames" />
<Target Name="CoreCompile" DependsOnTargets="$(CoreCompileDependsOn)">
<ItemGroup>
<ReferencePath Remove="@(ReferencePath)"
Condition="'%(FileName)' != 'System.Console' AND
'%(FileName)' != 'System.Collections' AND
'%(FileName)' != 'System.Runtime'" />
<ReferencePath Include="$(MSBuildThisFileDirectory)\..\Parser\bin\Debug\net5.0\Parser.dll" />
</ItemGroup>
<Message Importance="high" Text="ReferencePath: @(ReferencePath)" />
<Exec Command="dotnet run --project &quot;$(MSBuildThisFileDirectory)\..\cmc\cmc.csproj&quot; -- @(Compile->'&quot;%(Identity)&quot;', ' ') /o &quot;@(IntermediateAssembly)&quot; @(ReferencePath->'/r &quot;%(Identity)&quot;', ' ')"
WorkingDirectory="$(MSBuildProjectDirectory)" />
</Target>
</Project>

View File

@ -1,11 +1,31 @@
x = 2;
f(x);
x = 5;
f(x);
x = 3;
f(x);
% i = 1;
% while i <= 10
% disp(i);
% i = i + 1;
% end
for i = 1:10
disp(i);
end
function f(x)
disp('X was');
disp(x);
if x > 3
disp('greater than 3!');
elseif x < 3
disp('less than 3!');
else
disp('exactly 3!');
end
x = x + 1;
disp('X is')
disp('X + 1 is');
disp(x);
end

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Parser\Parser.csproj" />
</ItemGroup>
</Project>