Add diagnostics to parser
This commit is contained in:
parent
4b6ee62f07
commit
6bc04ad659
@ -43,5 +43,14 @@ namespace Parser.Tests
|
|||||||
Assert.IsType<ExpressionStatementSyntaxNode>(assignment);
|
Assert.IsType<ExpressionStatementSyntaxNode>(assignment);
|
||||||
Assert.IsType<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression);
|
Assert.IsType<AssignmentExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)assignment).Expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("2 + ;")]
|
||||||
|
public void ParseStatement(string text)
|
||||||
|
{
|
||||||
|
var sut = GetSut(text);
|
||||||
|
var actual = sut.Parse();
|
||||||
|
Assert.Collection(actual.Diagnostics, item => Assert.Equal("Unexpected token 'SemicolonToken', expected 'IdentifierToken'.", item.Message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Parser.Internal
|
namespace Parser.Internal
|
||||||
{
|
{
|
||||||
@ -10,6 +11,11 @@ namespace Parser.Internal
|
|||||||
_diagnostics = new List<Diagnostic>();
|
_diagnostics = new List<Diagnostic>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DiagnosticsBag(IEnumerable<Diagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
_diagnostics = diagnostics.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private readonly List<Diagnostic> _diagnostics;
|
private readonly List<Diagnostic> _diagnostics;
|
||||||
|
|
||||||
public IReadOnlyCollection<Diagnostic> Diagnostics => _diagnostics.AsReadOnly();
|
public IReadOnlyCollection<Diagnostic> Diagnostics => _diagnostics.AsReadOnly();
|
||||||
@ -40,6 +46,11 @@ namespace Parser.Internal
|
|||||||
Report(span, $"Unknown symbol '{c}'.");
|
Report(span, $"Unknown symbol '{c}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ReportUnexpectedToken(TextSpan span, TokenKind expected, TokenKind actual)
|
||||||
|
{
|
||||||
|
Report(span, $"Unexpected token '{actual}', expected '{expected}'.");
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerator<Diagnostic> GetEnumerator()
|
public IEnumerator<Diagnostic> GetEnumerator()
|
||||||
{
|
{
|
||||||
return _diagnostics.GetEnumerator();
|
return _diagnostics.GetEnumerator();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Parser.Internal
|
namespace Parser.Internal
|
||||||
@ -11,13 +12,13 @@ namespace Parser.Internal
|
|||||||
private Position CurrentPosition => Pairs[_index].position;
|
private Position CurrentPosition => Pairs[_index].position;
|
||||||
private SyntaxToken PeekToken(int n) => Pairs[_index + n].token;
|
private SyntaxToken PeekToken(int n) => Pairs[_index + n].token;
|
||||||
private SyntaxFactory Factory { get; }
|
private SyntaxFactory Factory { get; }
|
||||||
private List<string> Errors { get; }
|
public DiagnosticsBag Diagnostics { get; }
|
||||||
|
|
||||||
public MParserGreen(List<(SyntaxToken, Position)> pairs, SyntaxFactory factory)
|
public MParserGreen(List<(SyntaxToken, Position)> pairs, SyntaxFactory factory)
|
||||||
{
|
{
|
||||||
Pairs = pairs;
|
Pairs = pairs;
|
||||||
Factory = factory;
|
Factory = factory;
|
||||||
Errors = new List<string>();
|
Diagnostics = new DiagnosticsBag();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SyntaxToken EatToken()
|
private SyntaxToken EatToken()
|
||||||
@ -32,7 +33,7 @@ namespace Parser.Internal
|
|||||||
var token = CurrentToken;
|
var token = CurrentToken;
|
||||||
if (token.Kind != kind)
|
if (token.Kind != kind)
|
||||||
{
|
{
|
||||||
Errors.Add($"Unexpected token \"{token.Text}\" instead of {kind} at {CurrentPosition}.");
|
Diagnostics.ReportUnexpectedToken(token.Span, kind, token.Kind);
|
||||||
return TokenFactory.CreateMissing(kind, null, null);
|
return TokenFactory.CreateMissing(kind, null, null);
|
||||||
}
|
}
|
||||||
_index++;
|
_index++;
|
||||||
@ -54,13 +55,13 @@ namespace Parser.Internal
|
|||||||
var token = CurrentToken;
|
var token = CurrentToken;
|
||||||
if (token.Kind != TokenKind.IdentifierToken)
|
if (token.Kind != TokenKind.IdentifierToken)
|
||||||
{
|
{
|
||||||
Errors.Add($"Unexpected token \"{token.Text}\" instead of {TokenKind.IdentifierToken} at {CurrentPosition}.");
|
Diagnostics.ReportUnexpectedToken(token.Span, TokenKind.IdentifierToken, token.Kind);
|
||||||
return TokenFactory.CreateMissing(TokenKind.IdentifierToken, null, null);
|
return TokenFactory.CreateMissing(TokenKind.IdentifierToken, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.Text != s)
|
if (token.Text != s)
|
||||||
{
|
{
|
||||||
Errors.Add($"Unexpected token \"{token.Text}\" instead of identifier {s} at {CurrentPosition}.");
|
Diagnostics.ReportUnexpectedToken(token.Span, TokenKind.IdentifierToken, token.Kind);
|
||||||
return TokenFactory.CreateMissing(TokenKind.IdentifierToken, null, null);
|
return TokenFactory.CreateMissing(TokenKind.IdentifierToken, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,19 +117,7 @@ namespace Parser.Internal
|
|||||||
SyntaxToken assignmentSign;
|
SyntaxToken assignmentSign;
|
||||||
var builder = new SyntaxListBuilder();
|
var builder = new SyntaxListBuilder();
|
||||||
|
|
||||||
if (CurrentToken.Kind == TokenKind.IdentifierToken)
|
if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken)
|
||||||
{
|
|
||||||
if (PeekToken(1).Kind == TokenKind.EqualsToken)
|
|
||||||
{
|
|
||||||
builder.Add(Factory.IdentifierNameSyntax(EatToken()));
|
|
||||||
assignmentSign = EatToken(TokenKind.EqualsToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken)
|
|
||||||
{
|
{
|
||||||
builder.Add(EatToken());
|
builder.Add(EatToken());
|
||||||
var outputs = ParseFunctionOutputList();
|
var outputs = ParseFunctionOutputList();
|
||||||
@ -140,10 +129,21 @@ namespace Parser.Internal
|
|||||||
builder.Add(EatToken(TokenKind.CloseSquareBracketToken));
|
builder.Add(EatToken(TokenKind.CloseSquareBracketToken));
|
||||||
assignmentSign = EatToken(TokenKind.EqualsToken);
|
assignmentSign = EatToken(TokenKind.EqualsToken);
|
||||||
}
|
}
|
||||||
|
else if (CurrentToken.Kind == TokenKind.IdentifierToken)
|
||||||
|
{
|
||||||
|
if (PeekToken(1).Kind == TokenKind.EqualsToken)
|
||||||
|
{
|
||||||
|
var identifierToken = EatIdentifier();
|
||||||
|
builder.Add(Factory.IdentifierNameSyntax(identifierToken));
|
||||||
|
assignmentSign = EatToken(TokenKind.EqualsToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Errors.Add(
|
|
||||||
$"Unexpected token {CurrentToken} during parsing function output description at {CurrentPosition}.");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,9 +292,6 @@ namespace Parser.Internal
|
|||||||
ExpressionSyntaxNode expression = null;
|
ExpressionSyntaxNode expression = null;
|
||||||
switch (token.Kind)
|
switch (token.Kind)
|
||||||
{
|
{
|
||||||
case TokenKind.IdentifierToken:
|
|
||||||
expression = Factory.IdentifierNameSyntax(EatToken());
|
|
||||||
break;
|
|
||||||
case TokenKind.NumberLiteralToken:
|
case TokenKind.NumberLiteralToken:
|
||||||
expression = Factory.NumberLiteralSyntax(EatToken());
|
expression = Factory.NumberLiteralSyntax(EatToken());
|
||||||
break;
|
break;
|
||||||
@ -316,6 +313,10 @@ namespace Parser.Internal
|
|||||||
case TokenKind.OpenParenthesisToken:
|
case TokenKind.OpenParenthesisToken:
|
||||||
expression = ParseParenthesizedExpression();
|
expression = ParseParenthesizedExpression();
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
var id = EatToken(TokenKind.IdentifierToken);
|
||||||
|
expression = Factory.IdentifierNameSyntax(id);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression == null)
|
if (expression == null)
|
||||||
@ -570,9 +571,16 @@ namespace Parser.Internal
|
|||||||
|
|
||||||
EatToken();
|
EatToken();
|
||||||
var rhs = ParseSubExpression(options, newPrecedence);
|
var rhs = ParseSubExpression(options, newPrecedence);
|
||||||
if (rhs == null && token.Kind == TokenKind.ColonToken) // for parsing things like a{:}
|
if (rhs == null)
|
||||||
{
|
{
|
||||||
rhs = Factory.EmptyExpressionSyntax();
|
if (token.Kind == TokenKind.ColonToken) // for parsing things like a{:}
|
||||||
|
{
|
||||||
|
rhs = Factory.EmptyExpressionSyntax();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rhs = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (token.Kind == TokenKind.EqualsToken)
|
if (token.Kind == TokenKind.EqualsToken)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,8 @@ namespace Parser.Internal
|
|||||||
{
|
{
|
||||||
internal abstract class SyntaxToken : GreenNode
|
internal abstract class SyntaxToken : GreenNode
|
||||||
{
|
{
|
||||||
|
public TextSpan Span { get; }
|
||||||
|
|
||||||
internal class SyntaxTokenWithTrivia : SyntaxToken
|
internal class SyntaxTokenWithTrivia : SyntaxToken
|
||||||
{
|
{
|
||||||
private readonly string _text;
|
private readonly string _text;
|
||||||
|
@ -11,5 +11,10 @@
|
|||||||
public int Start { get; }
|
public int Start { get; }
|
||||||
public int Length { get; }
|
public int Length { get; }
|
||||||
public int End => Start + Length;
|
public int End => Start + Length;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Start}--{End}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Parser.Internal;
|
using Parser.Internal;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Parser
|
namespace Parser
|
||||||
{
|
{
|
||||||
@ -16,12 +17,14 @@ namespace Parser
|
|||||||
public SyntaxTree Parse()
|
public SyntaxTree Parse()
|
||||||
{
|
{
|
||||||
var lexer = new Internal.MLexerGreen(_window);
|
var lexer = new Internal.MLexerGreen(_window);
|
||||||
var diagnostics = lexer.Diagnostics;
|
var lexerDiagnostics = lexer.Diagnostics;
|
||||||
var tokens = lexer.ParseAll();
|
var tokens = lexer.ParseAll();
|
||||||
var parser = new Internal.MParserGreen(tokens, new Internal.SyntaxFactory());
|
var parser = new Internal.MParserGreen(tokens, new Internal.SyntaxFactory());
|
||||||
var green = parser.ParseFile();
|
var green = parser.ParseFile();
|
||||||
|
var parserDiagnostics = parser.Diagnostics;
|
||||||
|
var totalDiagnostics = new DiagnosticsBag(lexerDiagnostics.Concat(parserDiagnostics));
|
||||||
var root = new FileSyntaxNode(null, green);
|
var root = new FileSyntaxNode(null, green);
|
||||||
return new SyntaxTree(root, diagnostics);
|
return new SyntaxTree(root, totalDiagnostics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,13 @@ namespace Repl
|
|||||||
var window = new TextWindowWithNull(line);
|
var window = new TextWindowWithNull(line);
|
||||||
var parser = new MParser(window);
|
var parser = new MParser(window);
|
||||||
var tree = parser.Parse();
|
var tree = parser.Parse();
|
||||||
|
if (tree.Diagnostics.Diagnostics.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var diagnostic in tree.Diagnostics.Diagnostics)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{diagnostic.Span}: {diagnostic.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
TreeRenderer.RenderTree(tree);
|
TreeRenderer.RenderTree(tree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user