Add diagnostics

This commit is contained in:
Alexander Luzgarev 2019-04-10 22:10:03 +02:00
parent 8be2aec0ab
commit fe5012bd29
19 changed files with 1424 additions and 23 deletions

View File

@ -15,7 +15,9 @@ namespace Parser.Tests
} }
[Theory] [Theory]
#pragma warning disable xUnit1019 // MemberData must reference a member providing a valid data type
[MemberData(nameof(SingleTokensData))] [MemberData(nameof(SingleTokensData))]
#pragma warning restore xUnit1019 // MemberData must reference a member providing a valid data type
public void MLexerGreen_Parses_Token(TokenKind kind, string text) public void MLexerGreen_Parses_Token(TokenKind kind, string text)
{ {
var tokens = ParseText(text); var tokens = ParseText(text);
@ -24,7 +26,9 @@ namespace Parser.Tests
} }
[Theory] [Theory]
#pragma warning disable xUnit1019 // MemberData must reference a member providing a valid data type
[MemberData(nameof(PairTokensData))] [MemberData(nameof(PairTokensData))]
#pragma warning restore xUnit1019 // MemberData must reference a member providing a valid data type
public void MLexerGreen_Parses_PairOfTokens(TokenKind kind1, string text1, TokenKind kind2, string text2) public void MLexerGreen_Parses_PairOfTokens(TokenKind kind1, string text1, TokenKind kind2, string text2)
{ {
var text = text1 + text2; var text = text1 + text2;
@ -35,7 +39,9 @@ namespace Parser.Tests
} }
[Theory] [Theory]
#pragma warning disable xUnit1019 // MemberData must reference a member providing a valid data type
[MemberData(nameof(PairTokensWithSeparatorData))] [MemberData(nameof(PairTokensWithSeparatorData))]
#pragma warning restore xUnit1019 // MemberData must reference a member providing a valid data type
public void MLexerGreen_Parses_PairOfTokensWithSeparator(TokenKind kind1, string text1, string separatorText, TokenKind kind2, string text2) public void MLexerGreen_Parses_PairOfTokensWithSeparator(TokenKind kind1, string text1, string separatorText, TokenKind kind2, string text2)
{ {
var text = text1 + separatorText + text2; var text = text1 + separatorText + text2;

View File

@ -19,6 +19,11 @@ namespace Parser.Tests
var actual = sut.Parse(); var actual = sut.Parse();
var assignment = actual.Root.StatementList[0].AsNode(); var assignment = actual.Root.StatementList[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(assignment); Assert.IsType<ExpressionStatementSyntaxNode>(assignment);
if (assignment is null)
{
throw new System.Exception();
}
Assert.IsType<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression); Assert.IsType<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression);
} }
@ -30,6 +35,11 @@ namespace Parser.Tests
var actual = sut.Parse(); var actual = sut.Parse();
var statement = actual.Root.StatementList[0].AsNode(); var statement = actual.Root.StatementList[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement); Assert.IsType<ExpressionStatementSyntaxNode>(statement);
if (statement is null)
{
throw new System.Exception();
}
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement).Expression); Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement).Expression);
} }
@ -41,6 +51,11 @@ namespace Parser.Tests
var actual = sut.Parse(); var actual = sut.Parse();
var assignment = actual.Root.StatementList[0].AsNode(); var assignment = actual.Root.StatementList[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(assignment); Assert.IsType<ExpressionStatementSyntaxNode>(assignment);
if (assignment is null)
{
throw new System.Exception();
}
Assert.IsType<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression); Assert.IsType<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression);
} }
@ -50,6 +65,7 @@ namespace Parser.Tests
{ {
var sut = GetSut(text); var sut = GetSut(text);
var actual = sut.Parse(); var actual = sut.Parse();
var diagnostics = actual.Root.GetDiagnostics();
Assert.Collection(actual.Diagnostics, item => Assert.Equal("Unexpected token 'SemicolonToken', expected 'IdentifierToken'.", item.Message)); Assert.Collection(actual.Diagnostics, item => Assert.Equal("Unexpected token 'SemicolonToken', expected 'IdentifierToken'.", item.Message));
} }
@ -60,8 +76,8 @@ namespace Parser.Tests
var sut = GetSut(text); var sut = GetSut(text);
var actual = sut.Parse(); var actual = sut.Parse();
var statement = actual.Root.StatementList[0].AsNode() as ExpressionStatementSyntaxNode; var statement = actual.Root.StatementList[0].AsNode() as ExpressionStatementSyntaxNode;
var expression = statement.Expression as BinaryOperationExpressionSyntaxNode; var expression = statement!.Expression as BinaryOperationExpressionSyntaxNode;
var lhs = expression.Lhs; var lhs = expression!.Lhs;
var operation = expression.Operation; var operation = expression.Operation;
var rhs = expression.Rhs; var rhs = expression.Rhs;
Assert.Equal(0, lhs.Position); Assert.Equal(0, lhs.Position);

View File

@ -2,13 +2,16 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<NullableReferenceTypes>true</NullableReferenceTypes> <NullableContextOptions>enable</NullableContextOptions>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Parser\Parser.csproj" /> <ProjectReference Include="..\Parser\Parser.csproj" />

View File

@ -44,7 +44,9 @@ namespace Parser.Tests
} }
[Theory] [Theory]
#pragma warning disable xUnit1019 // MemberData must reference a member providing a valid data type
[MemberData(nameof(FilesData))] [MemberData(nameof(FilesData))]
#pragma warning restore xUnit1019 // MemberData must reference a member providing a valid data type
public void TestFile(string fileName) public void TestFile(string fileName)
{ {
var text = File.ReadAllText(fileName); var text = File.ReadAllText(fileName);

View File

@ -11,6 +11,9 @@ namespace Parser.Internal
public TokenKind Kind { get; } public TokenKind Kind { get; }
public int Slots { get; protected set; } public int Slots { get; protected set; }
public abstract GreenNode? GetSlot(int i); public abstract GreenNode? GetSlot(int i);
public bool HasDiagnostics { get; }
private static Dictionary<GreenNode, TokenDiagnostic[]> diagnosticsTable = new Dictionary<GreenNode, TokenDiagnostic[]>();
private static TokenDiagnostic[] emptyDiagnostics = Array.Empty<TokenDiagnostic>();
public GreenNode(TokenKind kind) public GreenNode(TokenKind kind)
{ {
@ -23,6 +26,21 @@ namespace Parser.Internal
_fullWidth = fullWidth; _fullWidth = fullWidth;
} }
public GreenNode(TokenKind kind, TokenDiagnostic[] diagnostics)
{
Kind = kind;
HasDiagnostics = true;
diagnosticsTable[this] = diagnostics;
}
public GreenNode(TokenKind kind, int fullWidth, TokenDiagnostic[] diagnostics)
{
Kind = kind;
_fullWidth = fullWidth;
HasDiagnostics = true;
diagnosticsTable[this] = diagnostics;
}
internal abstract Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position); internal abstract Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position);
protected int _fullWidth; protected int _fullWidth;
@ -225,5 +243,17 @@ namespace Parser.Internal
} }
} }
} }
public abstract GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics);
internal TokenDiagnostic[] GetDiagnostics()
{
if (diagnosticsTable.TryGetValue(this, out var diags))
{
return diags;
}
return emptyDiagnostics;
}
} }
} }

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Linq;
namespace Parser.Internal
{
internal static class GreenNodeExtensions
{
public static TNode WithDiagnostics<TNode>(this TNode node, params TokenDiagnostic[] diagnostics)
where TNode : GreenNode
{
return (TNode)node.SetDiagnostics(diagnostics.ToArray());
}
}
}

View File

@ -28,13 +28,20 @@ namespace Parser.Internal
return token; return token;
} }
private SyntaxToken CreateMissingToken(TokenKind kind)
{
return TokenFactory
.CreateMissing(kind, null, null)
.WithDiagnostics(TokenDiagnostic.MissingToken(kind));
}
private SyntaxToken EatToken(TokenKind kind) private SyntaxToken EatToken(TokenKind kind)
{ {
var token = CurrentToken; var token = CurrentToken;
if (token.Kind != kind) if (token.Kind != kind)
{ {
Diagnostics.ReportUnexpectedToken(kind, token.Kind); Diagnostics.ReportUnexpectedToken(kind, token.Kind);
return TokenFactory.CreateMissing(kind, null, null); return CreateMissingToken(kind);
} }
_index++; _index++;
return token; return token;

View File

@ -5,7 +5,7 @@ namespace Parser.Internal
{ {
internal class SyntaxList : SyntaxNode internal class SyntaxList : SyntaxNode
{ {
private readonly GreenNode[] _elements; internal readonly GreenNode[] _elements;
protected SyntaxList(GreenNode[] elements) : base(TokenKind.List) protected SyntaxList(GreenNode[] elements) : base(TokenKind.List)
{ {
@ -17,6 +17,19 @@ namespace Parser.Internal
} }
} }
protected SyntaxList(
GreenNode[] elements,
TokenDiagnostic[] diagnostics)
: base(TokenKind.List, diagnostics)
{
Slots = elements.Length;
_elements = elements;
foreach (var element in elements)
{
this.AdjustWidth(element);
}
}
public GreenNode GetListSlot(int i) public GreenNode GetListSlot(int i)
{ {
return _elements[i]; return _elements[i];
@ -40,5 +53,10 @@ namespace Parser.Internal
{ {
return new Parser.SyntaxNodeOrTokenList(parent, this, position); return new Parser.SyntaxNodeOrTokenList(parent, this, position);
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxList(_elements, diagnostics);
}
} }
} }

View File

@ -1,4 +1,6 @@
namespace Parser.Internal using System.Linq;
namespace Parser.Internal
{ {
internal class SyntaxList<T> : SyntaxNode where T : GreenNode internal class SyntaxList<T> : SyntaxNode where T : GreenNode
{ {
@ -14,6 +16,16 @@
} }
} }
protected SyntaxList(T[] list, TokenDiagnostic[] diagnostics) : base(TokenKind.List, diagnostics)
{
Slots = list.Length;
_list = SyntaxList.List(list);
foreach (var element in list)
{
this.AdjustWidth(element);
}
}
public override GreenNode? GetSlot(int i) public override GreenNode? GetSlot(int i)
{ {
return (T)_list.GetListSlot(i); return (T)_list.GetListSlot(i);
@ -32,5 +44,10 @@
{ {
return new Parser.SyntaxNodeOrTokenList(parent, this, position); return new Parser.SyntaxNodeOrTokenList(parent, this, position);
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxList<T>(_list._elements.Select(x => (T)x).ToArray(), diagnostics);
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,11 @@ namespace Parser.Internal
{ {
} }
protected SyntaxNode(TokenKind kind, TokenDiagnostic[] diagnostics)
: base(kind, diagnostics)
{
}
public IEnumerable<SyntaxToken> DescendantTokens => CalculateChildTokens(); public IEnumerable<SyntaxToken> DescendantTokens => CalculateChildTokens();
private IEnumerable<SyntaxToken> CalculateChildTokens() private IEnumerable<SyntaxToken> CalculateChildTokens()
@ -57,6 +62,10 @@ namespace Parser.Internal
protected StatementSyntaxNode(TokenKind kind) : base(kind) protected StatementSyntaxNode(TokenKind kind) : base(kind)
{ {
} }
protected StatementSyntaxNode(TokenKind kind, TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
}
} }
internal abstract class ExpressionSyntaxNode : SyntaxNode internal abstract class ExpressionSyntaxNode : SyntaxNode
@ -64,6 +73,10 @@ namespace Parser.Internal
protected ExpressionSyntaxNode(TokenKind kind) : base(kind) protected ExpressionSyntaxNode(TokenKind kind) : base(kind)
{ {
} }
protected ExpressionSyntaxNode(TokenKind kind, TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
}
} }
internal abstract class FunctionHandleSyntaxNode : ExpressionSyntaxNode internal abstract class FunctionHandleSyntaxNode : ExpressionSyntaxNode
@ -71,6 +84,10 @@ namespace Parser.Internal
protected FunctionHandleSyntaxNode(TokenKind kind) : base(kind) protected FunctionHandleSyntaxNode(TokenKind kind) : base(kind)
{ {
} }
protected FunctionHandleSyntaxNode(TokenKind kind, TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
}
} }
internal abstract class MethodDeclarationSyntaxNode : StatementSyntaxNode internal abstract class MethodDeclarationSyntaxNode : StatementSyntaxNode
@ -78,6 +95,10 @@ namespace Parser.Internal
protected MethodDeclarationSyntaxNode(TokenKind kind) : base(kind) protected MethodDeclarationSyntaxNode(TokenKind kind) : base(kind)
{ {
} }
protected MethodDeclarationSyntaxNode(TokenKind kind, TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
}
} }
internal class RootSyntaxNode : SyntaxNode internal class RootSyntaxNode : SyntaxNode
@ -91,6 +112,13 @@ namespace Parser.Internal
_file = file; _file = file;
} }
public RootSyntaxNode(FileSyntaxNode file, TokenDiagnostic[] diagnostics) : base(TokenKind.Root, diagnostics)
{
Slots = 1;
this.AdjustWidth(file);
_file = file;
}
public override GreenNode? GetSlot(int i) public override GreenNode? GetSlot(int i)
{ {
switch (i) switch (i)
@ -100,6 +128,11 @@ namespace Parser.Internal
} }
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new RootSyntaxNode(this._file, diagnostics);
}
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position) internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position)
{ {
return new Parser.RootSyntaxNode(this, position); return new Parser.RootSyntaxNode(this, position);

View File

@ -36,6 +36,31 @@ namespace Parser.Internal
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
} }
public SyntaxTokenWithTrivia(
TokenKind kind,
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> 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);
}
public SyntaxTokenWithTrivia(
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> 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);
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{ {
if (leading) if (leading)
@ -55,6 +80,11 @@ namespace Parser.Internal
} }
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxTokenWithTrivia(Kind, _text, LeadingTrivia, TrailingTrivia, diagnostics);
}
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; } public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; } public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
@ -75,10 +105,27 @@ namespace Parser.Internal
_fullWidth = text?.Length ?? 0; _fullWidth = text?.Length ?? 0;
} }
public SyntaxTokenWithValue(
TokenKind kind,
string text,
T value,
TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
_text = text;
_value = value;
_fullWidth = text?.Length ?? 0;
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{ {
writer.Write(_text); writer.Write(_text);
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxTokenWithValue<T>(Kind, _text, _value, diagnostics);
}
public T Value => _value; public T Value => _value;
} }
@ -96,6 +143,19 @@ namespace Parser.Internal
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
} }
public SyntaxTokenWithValueAndTrivia(
TokenKind kind,
string text,
T value,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> 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);
}
public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; } public override IReadOnlyList<SyntaxTrivia> LeadingTriviaCore { get; }
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; } public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore { get; }
@ -118,6 +178,11 @@ namespace Parser.Internal
} }
} }
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxTokenWithValueAndTrivia<T>(Kind, _text, Value, LeadingTrivia, TrailingTrivia, diagnostics);
}
} }
internal class SyntaxIdentifier : SyntaxToken internal class SyntaxIdentifier : SyntaxToken
@ -129,9 +194,21 @@ namespace Parser.Internal
writer.Write(_text); writer.Write(_text);
} }
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxIdentifier(_text, diagnostics);
}
public SyntaxIdentifier( public SyntaxIdentifier(
string text string text) : base(TokenKind.IdentifierToken)
) : base(TokenKind.IdentifierToken) {
_text = text;
_fullWidth = text?.Length ?? 0;
}
public SyntaxIdentifier(
string text,
TokenDiagnostic[] diagnostics) : base(TokenKind.IdentifierToken, diagnostics)
{ {
_text = text; _text = text;
_fullWidth = text?.Length ?? 0; _fullWidth = text?.Length ?? 0;
@ -157,6 +234,17 @@ namespace Parser.Internal
_fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0);
} }
public SyntaxIdentifierWithTrivia(
string text,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> 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);
}
public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing)
{ {
if (leading) if (leading)
@ -178,6 +266,11 @@ namespace Parser.Internal
public override bool IsToken => true; public override bool IsToken => true;
public override bool IsNode => false; public override bool IsNode => false;
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new SyntaxIdentifierWithTrivia(Text, _leadingTrivia, _trailingTrivia, diagnostics);
}
} }
internal class MissingTokenWithTrivia : SyntaxTokenWithTrivia internal class MissingTokenWithTrivia : SyntaxTokenWithTrivia
@ -191,13 +284,31 @@ namespace Parser.Internal
_isMissing = true; _isMissing = true;
} }
public MissingTokenWithTrivia(
TokenKind kind,
IReadOnlyList<SyntaxTrivia> leadingTrivia,
IReadOnlyList<SyntaxTrivia> trailingTrivia,
TokenDiagnostic[] diagnostics) : base(kind, leadingTrivia, trailingTrivia, diagnostics)
{
_isMissing = true;
}
public override string Text => ""; public override string Text => "";
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new MissingTokenWithTrivia(Kind, LeadingTrivia, TrailingTrivia, diagnostics);
}
} }
protected SyntaxToken(TokenKind kind) : base(kind) protected SyntaxToken(TokenKind kind) : base(kind)
{ {
} }
protected SyntaxToken(TokenKind kind, TokenDiagnostic[] diagnostics) : base(kind, diagnostics)
{
}
internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, s_EmptySyntaxTriviaList, s_EmptySyntaxTriviaList); internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, s_EmptySyntaxTriviaList, s_EmptySyntaxTriviaList);
public virtual int Width => Text.Length; public virtual int Width => Text.Length;

View File

@ -14,6 +14,11 @@ namespace Parser.Internal
_text = text; _text = text;
} }
public SyntaxTrivia(TokenKind kind, string text, TokenDiagnostic[] diagnostics) : base(kind, text.Length, diagnostics)
{
_text = text;
}
public override string Text => _text; public override string Text => _text;
public int Width => _text.Length; public int Width => _text.Length;
@ -35,6 +40,11 @@ namespace Parser.Internal
writer.Write(_text); 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> LeadingTriviaCore => new List<SyntaxTrivia>();
public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => new List<SyntaxTrivia>(); public override IReadOnlyList<SyntaxTrivia> TrailingTriviaCore => new List<SyntaxTrivia>();
} }

View File

@ -0,0 +1,24 @@
namespace Parser.Internal
{
public class TokenDiagnostic
{
protected TokenDiagnostic()
{
}
public static TokenDiagnostic MissingToken(TokenKind kind)
{
return new MissingTokenDiagnostic(kind);
}
}
public class MissingTokenDiagnostic : TokenDiagnostic
{
internal MissingTokenDiagnostic(TokenKind kind)
{
Kind = kind;
}
public TokenKind Kind { get; }
}
}

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<NullableContextOptions>enable</NullableContextOptions> <NullableContextOptions>enable</NullableContextOptions>
</PropertyGroup> </PropertyGroup>

View File

@ -96,6 +96,67 @@ namespace Parser
} }
public abstract void Accept(SyntaxVisitor visitor); public abstract void Accept(SyntaxVisitor visitor);
public SyntaxDiagnostic[] GetDiagnostics()
{
return GetDiagnosticsRecursive(_green, Position).ToArray();
}
private static IEnumerable<SyntaxDiagnostic> GetDiagnosticsRecursive(Internal.GreenNode node, int position)
{
if (node.HasDiagnostics)
{
foreach (var diagnostic in node.GetDiagnostics())
{
yield return SyntaxDiagnostic.From(diagnostic, position);
}
}
for (var i = 0; i < node.Slots; i++)
{
var maybeChild = node.GetSlot(i);
if (maybeChild is Internal.GreenNode child) {
foreach (var diagnostic in GetDiagnosticsRecursive(child, position))
{
yield return diagnostic;
}
position += child.FullWidth;
}
}
}
}
public class SyntaxDiagnostic
{
public int Position { get; }
public static SyntaxDiagnostic From(Internal.TokenDiagnostic diagnostic, int Position)
{
switch (diagnostic)
{
case Internal.MissingTokenDiagnostic missingToken:
return new MissingTokenSyntaxDiagnostic(Position, missingToken.Kind);
}
throw new System.ArgumentOutOfRangeException(nameof(diagnostic));
}
protected SyntaxDiagnostic(int position)
{
Position = position;
}
}
public class MissingTokenSyntaxDiagnostic : SyntaxDiagnostic
{
public TokenKind Kind { get; }
internal MissingTokenSyntaxDiagnostic(int position, TokenKind tokenKind)
: base(position)
{
Kind = tokenKind;
}
} }
public abstract class StatementSyntaxNode : SyntaxNode public abstract class StatementSyntaxNode : SyntaxNode

View File

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

View File

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

View File

@ -79,7 +79,7 @@ namespace SyntaxGenerator
return widthAdjustment + fieldAssignment; return widthAdjustment + fieldAssignment;
} }
private static string GenerateInternalConstructor(SyntaxNodeDescription node) private static string GenerateInternalConstructorSimple(SyntaxNodeDescription node)
{ {
var arguments = string.Join( var arguments = string.Join(
",", ",",
@ -94,6 +94,27 @@ namespace SyntaxGenerator
return header + "\n {\n" + slotsAssignment + "\n" + assignments + " }\n"; return header + "\n {\n" + slotsAssignment + "\n" + assignments + " }\n";
} }
private static string GenerateInternalConstructorWithDiagnostics(SyntaxNodeDescription node)
{
var arguments = string.Join(
",",
node.Fields
.Select(field => $"\n {FullFieldType(field)} {field.FieldName}")
.Concat(new[] { $"\n TokenDiagnostic[] diagnostics" }));
var header =
$" internal {node.ClassName}({arguments}) : base(TokenKind.{node.TokenKindName}, diagnostics)";
var slotsAssignment = $" Slots = {node.Fields.Length};";
var assignments = string.Join(
"",
node.Fields.Select(GenerateFieldAssignmentInsideConstructor));
return header + "\n {\n" + slotsAssignment + "\n" + assignments + " }\n";
}
private static string GenerateInternalConstructors(SyntaxNodeDescription node)
{
return GenerateInternalConstructorSimple(node) + "\n" + GenerateInternalConstructorWithDiagnostics(node);
}
private static string GenerateConstructor(SyntaxNodeDescription node) private static string GenerateConstructor(SyntaxNodeDescription node)
{ {
var arguments = "SyntaxNode parent, Internal.GreenNode green, int position"; var arguments = "SyntaxNode parent, Internal.GreenNode green, int position";
@ -102,6 +123,18 @@ namespace SyntaxGenerator
return header + " {\n }\n"; return header + " {\n }\n";
} }
private static string GenerateInternalSetDiagnostics(SyntaxNodeDescription node)
{
var header = $" public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)";
var arguments = string.Join(
", ",
node.Fields
.Select(field => "_" + field.FieldName)
.Concat(new[] { "diagnostics" }));
var text = $" return new {node.ClassName}({arguments});";
return header + "\n {\n" + text + "\n }\n";
}
private static string GenerateInternalGetSlot(SyntaxNodeDescription node) private static string GenerateInternalGetSlot(SyntaxNodeDescription node)
{ {
var header = $" public override GreenNode? GetSlot(int i)\n"; var header = $" public override GreenNode? GetSlot(int i)\n";
@ -156,15 +189,17 @@ namespace SyntaxGenerator
var fields = string.Join( var fields = string.Join(
"", "",
node.Fields.Select(GenerateInternalFieldDeclaration)); node.Fields.Select(GenerateInternalFieldDeclaration));
var constructor = GenerateInternalConstructor(node); var constructor = GenerateInternalConstructors(node);
var getSlot = GenerateInternalGetSlot(node); var getSlot = GenerateInternalGetSlot(node);
var createRed = GenerateCreateRed(node); var createRed = GenerateCreateRed(node);
var setDiagnostics = GenerateInternalSetDiagnostics(node);
return return
header header
+ "\n {\n" + "\n {\n"
+ fields + "\n" + fields + "\n"
+ constructor + "\n" + constructor + "\n"
+ createRed + "\n" + createRed + "\n"
+ setDiagnostics + "\n"
+ getSlot + " }\n"; + getSlot + " }\n";
} }