commit
e11476f614
@ -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" />
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
@ -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">
|
||||
|
@ -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 });
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,5 +25,7 @@
|
||||
Tilde,
|
||||
Caret,
|
||||
DotCaret,
|
||||
LessInt,
|
||||
PlusInt,
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
Parser/Binding/Conversion.cs
Normal file
39
Parser/Binding/Conversion.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
26
Parser/Binding/TypeSymbol.cs
Normal file
26
Parser/Binding/TypeSymbol.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
23
Parser/Binding/TypedFunctionSymbol.cs
Normal file
23
Parser/Binding/TypedFunctionSymbol.cs
Normal 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; }
|
||||
}
|
||||
}
|
14
Parser/Binding/TypedParameterSymbol.cs
Normal file
14
Parser/Binding/TypedParameterSymbol.cs
Normal 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; }
|
||||
}
|
||||
}
|
15
Parser/Binding/TypedVariableSymbol.cs
Normal file
15
Parser/Binding/TypedVariableSymbol.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -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
840
Parser/Emitting/Emitter.cs
Normal 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}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
Parser/Emitting/MethodInterfaceDescription.cs
Normal file
22
Parser/Emitting/MethodInterfaceDescription.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -58,5 +58,7 @@ namespace Parser.Internal
|
||||
{
|
||||
return new SyntaxList(_elements, diagnostics);
|
||||
}
|
||||
|
||||
public int Length => _elements.Length;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
40
Parser/MFunctions/MHelpers.cs
Normal file
40
Parser/MFunctions/MHelpers.cs
Normal 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()}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
48
Parser/Objects/MDoubleMatrix.cs
Normal file
48
Parser/Objects/MDoubleMatrix.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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>
|
@ -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
27
Parser/SyntaxNavigator.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
54
Parser/SyntaxTriviaList.cs
Normal file
54
Parser/SyntaxTriviaList.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -83,6 +83,11 @@ namespace Parser
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitTrailingSemicolon(TrailingSemicolonSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
|
||||
public virtual void VisitEmptyStatement(EmptyStatementSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
|
@ -15,5 +15,10 @@
|
||||
{
|
||||
DefaultVisit(list);
|
||||
}
|
||||
|
||||
public virtual void VisitRoot(RootSyntaxNode node)
|
||||
{
|
||||
DefaultVisit(node);
|
||||
}
|
||||
}
|
||||
}
|
@ -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".
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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
|
||||
|
@ -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
60
cmc/Program.cs
Normal 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
18
cmc/cmc.csproj
Normal 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>
|
@ -4,7 +4,6 @@ using System.IO;
|
||||
|
||||
namespace cmi
|
||||
{
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
|
@ -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)
|
||||
{
|
||||
|
7
examples/Directory.Build.props
Normal file
7
examples/Directory.Build.props
Normal file
@ -0,0 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<DefaultLanguageSourceExtension>.m</DefaultLanguageSourceExtension>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
18
examples/Directory.Build.targets
Normal file
18
examples/Directory.Build.targets
Normal 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 "$(MSBuildThisFileDirectory)\..\cmc\cmc.csproj" -- @(Compile->'"%(Identity)"', ' ') /o "@(IntermediateAssembly)" @(ReferencePath->'/r "%(Identity)"', ' ')"
|
||||
WorkingDirectory="$(MSBuildProjectDirectory)" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
@ -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
|
12
examples/helloworld/helloworld.cmproj
Normal file
12
examples/helloworld/helloworld.cmproj
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user