From cdbb0ffb72577e77f2ca5328580dcdf69e5981f7 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Wed, 17 Oct 2018 20:08:16 +0200 Subject: [PATCH] Add diagnostics --- ConsoleDemo/Program.cs | 31 +++++++++++++++++-------- Parser/Internal/Diagnostic.cs | 16 +++++++++++++ Parser/Internal/DiagnosticsBag.cs | 38 +++++++++++++++++++++++++++++++ Parser/Internal/MLexerGreen.cs | 7 ++++-- Parser/Internal/TextSpan.cs | 15 ++++++++++++ Parser/MParser.cs | 26 +++++++++++++++++---- Parser/Position.cs | 1 + Parser/TextWindow.cs | 25 +++++++++----------- Parser/TextWindowWithNull.cs | 2 +- 9 files changed, 130 insertions(+), 31 deletions(-) create mode 100644 Parser/Internal/Diagnostic.cs create mode 100644 Parser/Internal/DiagnosticsBag.cs create mode 100644 Parser/Internal/TextSpan.cs diff --git a/ConsoleDemo/Program.cs b/ConsoleDemo/Program.cs index 16537f1..18982a0 100644 --- a/ConsoleDemo/Program.cs +++ b/ConsoleDemo/Program.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using Parser; using ProjectConsole; using Semantics; @@ -40,9 +41,9 @@ namespace ConsoleDemo } } - public static void Render(SyntaxNode node) + public static void RenderTree(SyntaxTree tree) { - RenderNode(node, "", true); + RenderNode(tree.Root, "", true); } } @@ -79,15 +80,24 @@ namespace ConsoleDemo function [a, b c] = functionName(d, e, f) a = d + e; end +%{ +comment "; var window = new TextWindowWithNull(text, "noname"); var parser = CreateParser(window); var tree = parser.Parse(); - TreeRenderer.Render(tree); + TreeRenderer.RenderTree(tree); + if (tree.Diagnostics.Any()) + { + foreach (var diagnostic in tree.Diagnostics) + { + Console.WriteLine($"ERROR: {diagnostic.Message} at position {diagnostic.Span.Start}"); + } + } Console.ReadKey(); } - private static FileSyntaxNode GetTree(string fileName) + private static SyntaxTree GetTree(string fileName) { var text = File.ReadAllText(fileName); var window = new TextWindowWithNull(text, fileName); @@ -104,10 +114,11 @@ namespace ConsoleDemo "@table", "table.m"); var tree = GetTree(fileName); - var childNodesAndTokens = tree.GetChildNodesAndTokens(); + var root = tree.Root; + var childNodesAndTokens = root.GetChildNodesAndTokens(); var node = childNodesAndTokens[0].AsNode(); var classChildNodesAndTokens = node.GetChildNodesAndTokens(); - var c = GetClass.FromTree(tree, fileName); + var c = GetClass.FromTree(root, fileName); Console.WriteLine(c.Name); foreach (var m in c.Methods) { @@ -135,7 +146,7 @@ namespace ConsoleDemo "heatmap.m"); var tree = GetTree(fileName); var printer = new DumbWalker(context); - printer.Visit(tree); + printer.Visit(tree.Root); } public static void UsageDemo() @@ -148,16 +159,16 @@ namespace ConsoleDemo "heatmap.m"); var tree = GetTree(fileName); var printer = new UsageGathering(context); - printer.Visit(tree); + printer.Visit(tree.Root); } public static void Main(string[] args) { - //ParserDemo(); + ParserDemo(); //SemanticsDemo(); //ContextDemo(); //DumbPrinterDemo(); - UsageDemo(); + //UsageDemo(); Console.ReadKey(); } } diff --git a/Parser/Internal/Diagnostic.cs b/Parser/Internal/Diagnostic.cs new file mode 100644 index 0000000..cb20f77 --- /dev/null +++ b/Parser/Internal/Diagnostic.cs @@ -0,0 +1,16 @@ +using System; + +namespace Parser.Internal +{ + public class Diagnostic + { + public TextSpan Span { get; } + public string Message { get; } + + public Diagnostic(TextSpan span, string message) + { + Span = span; + Message = message ?? throw new ArgumentNullException(nameof(message)); + } + } +} \ No newline at end of file diff --git a/Parser/Internal/DiagnosticsBag.cs b/Parser/Internal/DiagnosticsBag.cs new file mode 100644 index 0000000..1e2bdc6 --- /dev/null +++ b/Parser/Internal/DiagnosticsBag.cs @@ -0,0 +1,38 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Parser.Internal +{ + public class DiagnosticsBag : IEnumerable + { + internal DiagnosticsBag() + { + _diagnostics = new List(); + } + + private readonly List _diagnostics; + + public IReadOnlyCollection Diagnostics => _diagnostics.AsReadOnly(); + + private void Report(TextSpan span, string message) + { + var diagnostic = new Diagnostic(span, message); + _diagnostics.Add(diagnostic); + } + + internal void ReportUnexpectedEndOfFile(TextSpan span) + { + Report(span, "Unexpected end of file."); + } + + public IEnumerator GetEnumerator() + { + return _diagnostics.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Parser/Internal/MLexerGreen.cs b/Parser/Internal/MLexerGreen.cs index ddbf0ff..eabd4b1 100644 --- a/Parser/Internal/MLexerGreen.cs +++ b/Parser/Internal/MLexerGreen.cs @@ -19,6 +19,8 @@ namespace Parser.Internal private int TokensSinceNewLine { get; set; } private Stack TokenStack { get; } + public DiagnosticsBag Diagnostics { get; } = new DiagnosticsBag(); + public MLexerGreen(ITextWindow window) { Window = window; @@ -50,10 +52,11 @@ namespace Parser.Internal var c = Window.PeekChar(n); if (c == '\0') { - throw new ParsingException("Unexpected end of file while parsing multi-line comment."); + Diagnostics.ReportUnexpectedEndOfFile(new TextSpan(Window.Position.Offset, 0)); + return TokenFactory.CreateTrivia(TokenKind.Comment, Window.GetAndConsumeChars(n)); } - if (c == '\n') + if (c == '\n' || (c == '\r' && Window.PeekChar(n + 1) == '\n')) { atFirstLine = false; } diff --git a/Parser/Internal/TextSpan.cs b/Parser/Internal/TextSpan.cs new file mode 100644 index 0000000..80c054e --- /dev/null +++ b/Parser/Internal/TextSpan.cs @@ -0,0 +1,15 @@ +namespace Parser.Internal +{ + public class TextSpan + { + public TextSpan(int start, int length) + { + Start = start; + Length = length; + } + + public int Start { get; } + public int Length { get; } + public int End => Start + Length; + } +} \ No newline at end of file diff --git a/Parser/MParser.cs b/Parser/MParser.cs index 67d4ece..9a3ffd4 100644 --- a/Parser/MParser.cs +++ b/Parser/MParser.cs @@ -1,21 +1,39 @@ -namespace Parser +using System; +using Parser.Internal; + +namespace Parser { public class MParser { private readonly ITextWindow _window; - + public MParser(ITextWindow window) { _window = window; } - public FileSyntaxNode Parse() + public SyntaxTree Parse() { var lexer = new Internal.MLexerGreen(_window); + var diagnostics = lexer.Diagnostics; var tokens = lexer.ParseAll(); var parser = new Internal.MParserGreen(tokens, new Internal.SyntaxFactory()); var green = parser.ParseFile(); - return new FileSyntaxNode(null, green); + var root = new FileSyntaxNode(null, green); + return new SyntaxTree(root, diagnostics); } } + + public class SyntaxTree + { + public SyntaxTree(FileSyntaxNode root, DiagnosticsBag diagnostics) + { + Root = root ?? throw new ArgumentNullException(nameof(root)); + Diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics)); + } + + public FileSyntaxNode Root { get; } + public DiagnosticsBag Diagnostics { get; } + } + } \ No newline at end of file diff --git a/Parser/Position.cs b/Parser/Position.cs index 95ff528..56a71a5 100644 --- a/Parser/Position.cs +++ b/Parser/Position.cs @@ -5,6 +5,7 @@ public string FileName { get; set; } public int Line { get; set; } public int Column { get; set; } + public int Offset { get; set; } public override string ToString() { diff --git a/Parser/TextWindow.cs b/Parser/TextWindow.cs index befc40f..41d4dc5 100644 --- a/Parser/TextWindow.cs +++ b/Parser/TextWindow.cs @@ -3,42 +3,39 @@ public class TextWindow : ITextWindow { protected readonly string Text; - protected int Offset { get; set; } private Position _position; public Position Position => _position; public TextWindow(string text, string fileName = null) { Text = text; - Offset = 0; _position = new Position { FileName = fileName, Line = 0, - Column = 0 + Column = 0, + Offset = 0 }; } public bool IsEof() { - return Offset >= Text.Length; + return _position.Offset >= Text.Length; } public virtual char PeekChar() { - return Text[Offset]; + return Text[_position.Offset]; } - public virtual char PeekChar(int n) { - return Text[Offset + n]; + return Text[_position.Offset + n]; } - public void ConsumeChar() { - if (Text[Offset] == '\n' || Text[Offset] == '\r') + if (Text[_position.Offset] == '\n' || Text[_position.Offset] == '\r') { _position.Line++; _position.Column = 0; @@ -47,7 +44,7 @@ { _position.Column++; } - Offset++; + _position.Offset++; } public void ConsumeChars(int n) @@ -64,26 +61,26 @@ _position.Column++; } } - Offset += n; + _position.Offset += n; } public char GetAndConsumeChar() { - var c = Text[Offset]; + var c = Text[_position.Offset]; ConsumeChar(); return c; } public string GetAndConsumeChars(int n) { - var s = Text.Substring(Offset, n); + var s = Text.Substring(_position.Offset, n); ConsumeChars(n); return s; } public int CharactersLeft() { - return Text.Length - Offset; + return Text.Length - _position.Offset; } } } diff --git a/Parser/TextWindowWithNull.cs b/Parser/TextWindowWithNull.cs index 47ce283..70d1aee 100644 --- a/Parser/TextWindowWithNull.cs +++ b/Parser/TextWindowWithNull.cs @@ -13,7 +13,7 @@ public override char PeekChar(int n) { - return Offset + n >= Text.Length ? '\0' : base.PeekChar(n); + return Position.Offset + n >= Text.Length ? '\0' : base.PeekChar(n); } } }