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

View File

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

View File

@ -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)
{ {

View File

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

View File

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

View File

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

View File

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