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<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.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Parser.Internal
|
||||
{
|
||||
@ -10,6 +11,11 @@ namespace Parser.Internal
|
||||
_diagnostics = new List<Diagnostic>();
|
||||
}
|
||||
|
||||
public DiagnosticsBag(IEnumerable<Diagnostic> diagnostics)
|
||||
{
|
||||
_diagnostics = diagnostics.ToList();
|
||||
}
|
||||
|
||||
private readonly List<Diagnostic> _diagnostics;
|
||||
|
||||
public IReadOnlyCollection<Diagnostic> Diagnostics => _diagnostics.AsReadOnly();
|
||||
@ -40,6 +46,11 @@ namespace Parser.Internal
|
||||
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()
|
||||
{
|
||||
return _diagnostics.GetEnumerator();
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Parser.Internal
|
||||
@ -11,13 +12,13 @@ namespace Parser.Internal
|
||||
private Position CurrentPosition => Pairs[_index].position;
|
||||
private SyntaxToken PeekToken(int n) => Pairs[_index + n].token;
|
||||
private SyntaxFactory Factory { get; }
|
||||
private List<string> Errors { get; }
|
||||
public DiagnosticsBag Diagnostics { get; }
|
||||
|
||||
public MParserGreen(List<(SyntaxToken, Position)> pairs, SyntaxFactory factory)
|
||||
{
|
||||
Pairs = pairs;
|
||||
Factory = factory;
|
||||
Errors = new List<string>();
|
||||
Diagnostics = new DiagnosticsBag();
|
||||
}
|
||||
|
||||
private SyntaxToken EatToken()
|
||||
@ -32,7 +33,7 @@ namespace Parser.Internal
|
||||
var token = CurrentToken;
|
||||
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);
|
||||
}
|
||||
_index++;
|
||||
@ -54,13 +55,13 @@ namespace Parser.Internal
|
||||
var token = CurrentToken;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -116,19 +117,7 @@ namespace Parser.Internal
|
||||
SyntaxToken assignmentSign;
|
||||
var builder = new SyntaxListBuilder();
|
||||
|
||||
if (CurrentToken.Kind == TokenKind.IdentifierToken)
|
||||
{
|
||||
if (PeekToken(1).Kind == TokenKind.EqualsToken)
|
||||
{
|
||||
builder.Add(Factory.IdentifierNameSyntax(EatToken()));
|
||||
assignmentSign = EatToken(TokenKind.EqualsToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken)
|
||||
if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken)
|
||||
{
|
||||
builder.Add(EatToken());
|
||||
var outputs = ParseFunctionOutputList();
|
||||
@ -140,10 +129,21 @@ namespace Parser.Internal
|
||||
builder.Add(EatToken(TokenKind.CloseSquareBracketToken));
|
||||
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
|
||||
{
|
||||
Errors.Add(
|
||||
$"Unexpected token {CurrentToken} during parsing function output description at {CurrentPosition}.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -292,9 +292,6 @@ namespace Parser.Internal
|
||||
ExpressionSyntaxNode expression = null;
|
||||
switch (token.Kind)
|
||||
{
|
||||
case TokenKind.IdentifierToken:
|
||||
expression = Factory.IdentifierNameSyntax(EatToken());
|
||||
break;
|
||||
case TokenKind.NumberLiteralToken:
|
||||
expression = Factory.NumberLiteralSyntax(EatToken());
|
||||
break;
|
||||
@ -316,6 +313,10 @@ namespace Parser.Internal
|
||||
case TokenKind.OpenParenthesisToken:
|
||||
expression = ParseParenthesizedExpression();
|
||||
break;
|
||||
default:
|
||||
var id = EatToken(TokenKind.IdentifierToken);
|
||||
expression = Factory.IdentifierNameSyntax(id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (expression == null)
|
||||
@ -570,9 +571,16 @@ namespace Parser.Internal
|
||||
|
||||
EatToken();
|
||||
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)
|
||||
{
|
||||
|
@ -6,6 +6,8 @@ namespace Parser.Internal
|
||||
{
|
||||
internal abstract class SyntaxToken : GreenNode
|
||||
{
|
||||
public TextSpan Span { get; }
|
||||
|
||||
internal class SyntaxTokenWithTrivia : SyntaxToken
|
||||
{
|
||||
private readonly string _text;
|
||||
|
@ -11,5 +11,10 @@
|
||||
public int Start { get; }
|
||||
public int Length { get; }
|
||||
public int End => Start + Length;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Start}--{End}";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Parser.Internal;
|
||||
using System.Linq;
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
@ -16,12 +17,14 @@ namespace Parser
|
||||
public SyntaxTree Parse()
|
||||
{
|
||||
var lexer = new Internal.MLexerGreen(_window);
|
||||
var diagnostics = lexer.Diagnostics;
|
||||
var lexerDiagnostics = lexer.Diagnostics;
|
||||
var tokens = lexer.ParseAll();
|
||||
var parser = new Internal.MParserGreen(tokens, new Internal.SyntaxFactory());
|
||||
var green = parser.ParseFile();
|
||||
var parserDiagnostics = parser.Diagnostics;
|
||||
var totalDiagnostics = new DiagnosticsBag(lexerDiagnostics.Concat(parserDiagnostics));
|
||||
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 parser = new MParser(window);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user