Add diagnostics to parser

This commit is contained in:
Alexander Luzgarev 2018-12-10 21:04:42 +01:00
parent 4b6ee62f07
commit 6bc04ad659
7 changed files with 73 additions and 28 deletions

View File

@ -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));
}
}
}

View File

@ -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();

View File

@ -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,10 +571,17 @@ namespace Parser.Internal
EatToken();
var rhs = ParseSubExpression(options, newPrecedence);
if (rhs == null && token.Kind == TokenKind.ColonToken) // for parsing things like a{:}
if (rhs == null)
{
if (token.Kind == TokenKind.ColonToken) // for parsing things like a{:}
{
rhs = Factory.EmptyExpressionSyntax();
}
else
{
rhs = null;
}
}
if (token.Kind == TokenKind.EqualsToken)
{
lhs = Factory.AssignmentExpressionSyntax(lhs, token, rhs);

View File

@ -6,6 +6,8 @@ namespace Parser.Internal
{
internal abstract class SyntaxToken : GreenNode
{
public TextSpan Span { get; }
internal class SyntaxTokenWithTrivia : SyntaxToken
{
private readonly string _text;

View File

@ -11,5 +11,10 @@
public int Start { get; }
public int Length { get; }
public int End => Start + Length;
public override string ToString()
{
return $"{Start}--{End}";
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}