diff --git a/Parser.Tests/MParserShould.cs b/Parser.Tests/MParserShould.cs index a586436..7aa648d 100644 --- a/Parser.Tests/MParserShould.cs +++ b/Parser.Tests/MParserShould.cs @@ -129,5 +129,26 @@ namespace Parser.Tests rhs.FullSpan.Start.Should().Be(16); rhs.FullSpan.End.Should().Be(17); } + + [Fact] + public void ProvideSpan() + { + var text = "% Comment\n 2 + 3"; + var sut = GetSut(text); + var actual = sut.Parse(); + var statement = actual.Root.Body.Statements[0].AsNode() as ExpressionStatementSyntaxNode; + var expression = statement!.Expression as BinaryOperationExpressionSyntaxNode; + var lhs = expression!.Lhs; + var operation = expression.Operation; + var rhs = expression.Rhs; + expression.Span.Start.Should().Be(12); + expression.Span.End.Should().Be(17); + lhs.Span.Start.Should().Be(12); + lhs.Span.End.Should().Be(13); + operation.Span.Start.Should().Be(14); + operation.Span.End.Should().Be(15); + rhs.Span.Start.Should().Be(16); + rhs.Span.End.Should().Be(17); + } } } \ No newline at end of file diff --git a/Parser/Internal/GreenNode.cs b/Parser/Internal/GreenNode.cs index 1ee5b19..9a07bc0 100644 --- a/Parser/Internal/GreenNode.cs +++ b/Parser/Internal/GreenNode.cs @@ -191,11 +191,11 @@ namespace Parser.Internal } } - public virtual IReadOnlyList LeadingTrivia => GetFirstTerminal()?.LeadingTriviaCore ?? new List(); - public virtual IReadOnlyList TrailingTrivia => GetLastTerminal()?.TrailingTriviaCore ?? new List(); + public virtual GreenNode? LeadingTrivia => GetFirstTerminal()?.LeadingTriviaCore; + public virtual GreenNode? TrailingTrivia => GetLastTerminal()?.TrailingTriviaCore; - public abstract IReadOnlyList LeadingTriviaCore { get; } - public abstract IReadOnlyList TrailingTriviaCore { get; } + public virtual GreenNode? LeadingTriviaCore => null; + public virtual GreenNode? TrailingTriviaCore => null; public virtual string FullText { diff --git a/Parser/Internal/MLexerGreen.cs b/Parser/Internal/MLexerGreen.cs index 5c8a170..a1f592c 100644 --- a/Parser/Internal/MLexerGreen.cs +++ b/Parser/Internal/MLexerGreen.cs @@ -493,7 +493,7 @@ namespace Parser.Internal if (TokensSinceNewLine == 1 && !TokenStack.Any() && LastToken.Kind == TokenKind.IdentifierToken - && LastToken.TrailingTrivia.Any() + && LastToken.TrailingTrivia is not null && character != '=' && character != '(' && !SyntaxFacts.Keywords.Contains(LastToken.Text)) @@ -773,7 +773,7 @@ namespace Parser.Internal || LastToken.Kind == TokenKind.CloseSquareBracketToken || LastToken.Kind == TokenKind.IdentifierToken)) { - if (LastToken.TrailingTrivia.Count == 0 && leadingTrivia.Count == 0) + if (LastToken.TrailingTrivia is null && leadingTrivia.Count == 0) { Window.ConsumeChar(); tokenInfo.Kind = TokenKind.ApostropheToken; diff --git a/Parser/Internal/MParserGreen.cs b/Parser/Internal/MParserGreen.cs index a58011a..79df93a 100644 --- a/Parser/Internal/MParserGreen.cs +++ b/Parser/Internal/MParserGreen.cs @@ -348,7 +348,7 @@ namespace Parser.Internal switch (token.Kind) { case TokenKind.OpenBraceToken: // cell array element access - if (options.ParsingArrayElements && expression.TrailingTrivia.Any()) + if (options.ParsingArrayElements && expression.TrailingTrivia is not null) { return expression; } @@ -363,7 +363,7 @@ namespace Parser.Internal ); break; case TokenKind.OpenParenthesisToken: // function call - if (options.ParsingArrayElements && expression.TrailingTrivia.Any()) + if (options.ParsingArrayElements && expression.TrailingTrivia is not null) { return expression; } @@ -401,7 +401,7 @@ namespace Parser.Internal case TokenKind.UnquotedStringLiteralToken: return ParseCommandExpression(expression); case TokenKind.AtToken: - if (expression.TrailingTrivia.Any()) + if (expression.TrailingTrivia is not null) { return expression; } @@ -435,7 +435,7 @@ namespace Parser.Internal private ClassInvokationExpressionSyntaxNode ParseBaseClassInvokation(ExpressionSyntaxNode expression) { if (expression is IdentifierNameExpressionSyntaxNode methodName - && !expression.TrailingTrivia.Any()) + && expression.TrailingTrivia is null) { var atToken = EatToken(); var baseClassNameWithArguments = ParseExpression(); @@ -446,7 +446,7 @@ namespace Parser.Internal return Factory.ClassInvokationExpressionSyntax(methodName, atToken, baseClassNameWithArguments); } if (expression is MemberAccessExpressionSyntaxNode memberAccess - && !expression.TrailingTrivia.Any()) + && expression.TrailingTrivia is null) { var atToken = EatToken(); var baseClassNameWithArguments = ParseExpression(); @@ -530,7 +530,7 @@ namespace Parser.Internal var builder = new SyntaxListBuilder(); builder.Add(firstName); while (CurrentToken.Kind == TokenKind.DotToken - && !lastToken.TrailingTrivia.Any()) + && lastToken.TrailingTrivia is null) { var dot = EatToken(); builder.Add(dot); @@ -856,11 +856,18 @@ namespace Parser.Internal return Factory.ExpressionStatementSyntax(expression, semicolon: null); } - private bool TriviaContainsNewLine(IReadOnlyList trivia) + private bool TriviaContainsNewLine(GreenNode? trivia) { - foreach(var t in trivia) + var triviaList = trivia as SyntaxList; + if (triviaList is null) { - if (t.Text.Contains('\n')) + return false; + } + + for (var i = 0; i < triviaList.Length; i++) + { + var text = triviaList[i].Text; + if (text.Contains('\n')) { return true; } diff --git a/Parser/Internal/SyntaxList.cs b/Parser/Internal/SyntaxList.cs index 5a5fbd7..3966071 100644 --- a/Parser/Internal/SyntaxList.cs +++ b/Parser/Internal/SyntaxList.cs @@ -58,5 +58,7 @@ namespace Parser.Internal { return new SyntaxList(_elements, diagnostics); } + + public int Length => _elements.Length; } } \ No newline at end of file diff --git a/Parser/Internal/SyntaxList`1.cs b/Parser/Internal/SyntaxList`1.cs index 21ce891..b3b4dad 100644 --- a/Parser/Internal/SyntaxList`1.cs +++ b/Parser/Internal/SyntaxList`1.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; namespace Parser.Internal { @@ -31,11 +32,18 @@ namespace Parser.Internal return (T)_list.GetListSlot(i); } + public T this[int i] => (T)_list.GetListSlot(i); + public static SyntaxList List(T[] elements) { return new SyntaxList(elements); } + public static SyntaxList List(IReadOnlyList elements) + { + return new SyntaxList(elements.ToArray()); + } + public static SyntaxList Empty => new SyntaxList(new T[] { }); public override bool IsList => true; @@ -49,5 +57,7 @@ namespace Parser.Internal { return new SyntaxList(_list._elements.Select(x => (T)x).ToArray(), diagnostics); } + + public int Length => _list.Length; } } \ No newline at end of file diff --git a/Parser/Internal/SyntaxNode.cs b/Parser/Internal/SyntaxNode.cs index 2b0a450..999f06d 100644 --- a/Parser/Internal/SyntaxNode.cs +++ b/Parser/Internal/SyntaxNode.cs @@ -53,8 +53,8 @@ namespace Parser.Internal public override string FullText => CollectFullText(); - public override IReadOnlyList LeadingTriviaCore => throw new NotImplementedException(); - public override IReadOnlyList TrailingTriviaCore => throw new NotImplementedException(); + public override GreenNode? LeadingTriviaCore => throw new NotImplementedException(); + public override GreenNode? TrailingTriviaCore => throw new NotImplementedException(); } internal abstract class StatementSyntaxNode : SyntaxNode diff --git a/Parser/Internal/SyntaxToken.cs b/Parser/Internal/SyntaxToken.cs index 22ff7ac..6bc4ce2 100644 --- a/Parser/Internal/SyntaxToken.cs +++ b/Parser/Internal/SyntaxToken.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; namespace Parser.Internal { @@ -16,66 +15,66 @@ namespace Parser.Internal public SyntaxTokenWithTrivia( TokenKind kind, string text, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia) : base(kind) + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) : base(kind) { _text = text; LeadingTriviaCore = leadingTrivia; TrailingTriviaCore = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public SyntaxTokenWithTrivia( TokenKind kind, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia) : base(kind) + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) : base(kind) { _text = base.Text; LeadingTriviaCore = leadingTrivia; TrailingTriviaCore = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public SyntaxTokenWithTrivia( TokenKind kind, string text, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia, TokenDiagnostic[] diagnostics) : base(kind, diagnostics) { _text = text; LeadingTriviaCore = leadingTrivia; TrailingTriviaCore = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public SyntaxTokenWithTrivia( TokenKind kind, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia, TokenDiagnostic[] diagnostics) : base(kind, diagnostics) { _text = base.Text; LeadingTriviaCore = leadingTrivia; TrailingTriviaCore = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (_text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) { - if (leading) + if (leading && LeadingTrivia is SyntaxList leadingTrivia) { - foreach (var trivia in LeadingTrivia) + for (var i = 0; i < leadingTrivia.Length; i++) { - writer.Write(trivia.Text); + leadingTrivia[i].WriteTriviaTo(writer); } } base.WriteTokenTo(writer, leading, trailing); - if (trailing) + if (trailing && TrailingTrivia is SyntaxList trailingTrivia) { - foreach (var trivia in TrailingTrivia) + for (var i = 0; i < trailingTrivia.Length; i++) { - writer.Write(trivia.Text); + trailingTrivia[i].WriteTriviaTo(writer); } } } @@ -85,9 +84,9 @@ namespace Parser.Internal return new SyntaxTokenWithTrivia(Kind, _text, LeadingTrivia, TrailingTrivia, diagnostics); } - public override IReadOnlyList LeadingTriviaCore { get; } + public override GreenNode? LeadingTriviaCore { get; } - public override IReadOnlyList TrailingTriviaCore { get; } + public override GreenNode? TrailingTriviaCore { get; } } internal class SyntaxTokenWithValue : SyntaxToken @@ -138,46 +137,47 @@ namespace Parser.Internal TokenKind kind, string text, T value, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia) : base(kind, text, value) + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) + : base(kind, text, value) { LeadingTriviaCore = leadingTrivia; TrailingTriviaCore = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public SyntaxTokenWithValueAndTrivia( TokenKind kind, string text, T value, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia, TokenDiagnostic[] diagnostics) : base(kind, text, value, diagnostics) { LeadingTriviaCore = leadingTrivia; TrailingTriviaCore = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } - public override IReadOnlyList LeadingTriviaCore { get; } + public override GreenNode? LeadingTriviaCore { get; } - public override IReadOnlyList TrailingTriviaCore { get; } + public override GreenNode? TrailingTriviaCore { get; } public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) { - if (leading) + if (leading && LeadingTrivia is SyntaxList leadingTrivia) { - foreach (var trivia in LeadingTrivia) + for (var i = 0; i < leadingTrivia.Length; i++) { - writer.Write(trivia.Text); + leadingTrivia[i].WriteTriviaTo(writer); } } base.WriteTokenTo(writer, leading, trailing); - if (trailing) + if (trailing && TrailingTrivia is SyntaxList trailingTrivia) { - foreach (var trivia in TrailingTrivia) + for (var i = 0; i < trailingTrivia.Length; i++) { - writer.Write(trivia.Text); + trailingTrivia[i].WriteTriviaTo(writer); } } } @@ -220,49 +220,49 @@ namespace Parser.Internal internal class SyntaxIdentifierWithTrivia : SyntaxIdentifier { - private readonly IReadOnlyList _leadingTrivia; - private readonly IReadOnlyList _trailingTrivia; + private readonly GreenNode? _leadingTrivia; + private readonly GreenNode? _trailingTrivia; - public override IReadOnlyList LeadingTriviaCore => _leadingTrivia; - public override IReadOnlyList TrailingTriviaCore => _trailingTrivia; + public override GreenNode? LeadingTriviaCore => _leadingTrivia; + public override GreenNode? TrailingTriviaCore => _trailingTrivia; public SyntaxIdentifierWithTrivia( string text, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia - ) : base(text) + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) + : base(text) { _leadingTrivia = leadingTrivia; _trailingTrivia = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public SyntaxIdentifierWithTrivia( string text, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia, TokenDiagnostic[] diagnostics) : base(text, diagnostics) { _leadingTrivia = leadingTrivia; _trailingTrivia = trailingTrivia; - _fullWidth = (leadingTrivia?.Sum(t => t.FullWidth) ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.Sum(t => t.FullWidth) ?? 0); + _fullWidth = (leadingTrivia?.FullWidth ?? 0) + (text?.Length ?? 0) + (trailingTrivia?.FullWidth ?? 0); } public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) { - if (leading) + if (leading && LeadingTrivia is SyntaxList leadingTrivia) { - foreach (var trivia in LeadingTrivia) + for (var i = 0; i < leadingTrivia.Length; i++) { - writer.Write(trivia.Text); + leadingTrivia[i].WriteTriviaTo(writer); } } base.WriteTokenTo(writer, leading, trailing); - if (trailing) + if (trailing && TrailingTrivia is SyntaxList trailingTrivia) { - foreach (var trivia in TrailingTrivia) + for (var i = 0; i < trailingTrivia.Length; i++) { - writer.Write(trivia.Text); + trailingTrivia[i].WriteTriviaTo(writer); } } } @@ -280,8 +280,8 @@ namespace Parser.Internal { public MissingTokenWithTrivia( TokenKind kind, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia + GreenNode? leadingTrivia, + GreenNode? trailingTrivia ) : base(kind, leadingTrivia, trailingTrivia) { _isMissing = true; @@ -289,8 +289,8 @@ namespace Parser.Internal public MissingTokenWithTrivia( TokenKind kind, - IReadOnlyList leadingTrivia, - IReadOnlyList trailingTrivia, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia, TokenDiagnostic[] diagnostics) : base(kind, leadingTrivia, trailingTrivia, diagnostics) { _isMissing = true; @@ -312,7 +312,7 @@ namespace Parser.Internal { } - internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, s_EmptySyntaxTriviaList, s_EmptySyntaxTriviaList); + internal static SyntaxToken NoneToken => new MissingTokenWithTrivia(TokenKind.None, null, null); public virtual object? Value => null; @@ -320,8 +320,8 @@ namespace Parser.Internal public virtual int Width => Text.Length; - public override IReadOnlyList LeadingTriviaCore => s_EmptySyntaxTriviaList; - public override IReadOnlyList TrailingTriviaCore => s_EmptySyntaxTriviaList; + public override GreenNode? LeadingTriviaCore => null; + public override GreenNode? TrailingTriviaCore => null; public override GreenNode? GetSlot(int i) { diff --git a/Parser/Internal/SyntaxTrivia.cs b/Parser/Internal/SyntaxTrivia.cs index 277bc5a..e577bb3 100644 --- a/Parser/Internal/SyntaxTrivia.cs +++ b/Parser/Internal/SyntaxTrivia.cs @@ -40,12 +40,17 @@ namespace Parser.Internal writer.Write(_text); } + public override void WriteTokenTo(TextWriter writer, bool leading, bool trailing) + { + writer.Write(_text); + } + public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics) { return new SyntaxTrivia(Kind, _text, diagnostics); } - public override IReadOnlyList LeadingTriviaCore => new List(); - public override IReadOnlyList TrailingTriviaCore => new List(); + public override GreenNode? LeadingTriviaCore => null; + public override GreenNode? TrailingTriviaCore => null; } } \ No newline at end of file diff --git a/Parser/Internal/TokenFactory.cs b/Parser/Internal/TokenFactory.cs index 1281663..d3d28ee 100644 --- a/Parser/Internal/TokenFactory.cs +++ b/Parser/Internal/TokenFactory.cs @@ -13,6 +13,16 @@ namespace Parser.Internal TokenKind kind, IReadOnlyList leadingTrivia, IReadOnlyList trailingTrivia) + { + var leading = leadingTrivia.Count > 0 ? SyntaxList.List(leadingTrivia) : null; + var trailing = trailingTrivia.Count > 0 ? SyntaxList.List(trailingTrivia) : null; + return new SyntaxToken.SyntaxTokenWithTrivia(kind, leading, trailing); + } + + public static SyntaxToken CreateTokenWithTrivia( + TokenKind kind, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) { return new SyntaxToken.SyntaxTokenWithTrivia(kind, leadingTrivia, trailingTrivia); } @@ -21,6 +31,16 @@ namespace Parser.Internal string text, IReadOnlyList leadingTrivia, IReadOnlyList trailingTrivia) + { + var leading = leadingTrivia.Count > 0 ? SyntaxList.List(leadingTrivia) : null; + var trailing = trailingTrivia.Count > 0 ? SyntaxList.List(trailingTrivia) : null; + return new SyntaxToken.SyntaxIdentifierWithTrivia(text, leading, trailing); + } + + public static SyntaxToken CreateIdentifier( + string text, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) { return new SyntaxToken.SyntaxIdentifierWithTrivia(text, leadingTrivia, trailingTrivia); } @@ -31,6 +51,18 @@ namespace Parser.Internal T value, IReadOnlyList leadingTrivia, IReadOnlyList trailingTrivia) + { + var leading = leadingTrivia.Count > 0 ? SyntaxList.List(leadingTrivia) : null; + var trailing = trailingTrivia.Count > 0 ? SyntaxList.List(trailingTrivia) : null; + return new SyntaxToken.SyntaxTokenWithValueAndTrivia(kind, text, value, leading, trailing); + } + + public static SyntaxToken CreateTokenWithValueAndTrivia( + TokenKind kind, + string text, + T value, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) { return new SyntaxToken.SyntaxTokenWithValueAndTrivia(kind, text, value, leadingTrivia, trailingTrivia); } @@ -40,6 +72,22 @@ namespace Parser.Internal string value, IReadOnlyList leadingTrivia, IReadOnlyList trailingTrivia) + { + var leading = leadingTrivia.Count > 0 ? SyntaxList.List(leadingTrivia) : null; + var trailing = trailingTrivia.Count > 0 ? SyntaxList.List(trailingTrivia) : null; + return new SyntaxToken.SyntaxTokenWithValueAndTrivia( + TokenKind.UnquotedStringLiteralToken, + text, + value, + leading, + trailing); + } + + public static SyntaxToken CreateUnquotedStringLiteral( + string text, + string value, + GreenNode? leadingTrivia, + GreenNode? trailingTrivia) { return new SyntaxToken.SyntaxTokenWithValueAndTrivia( TokenKind.UnquotedStringLiteralToken, @@ -54,7 +102,9 @@ namespace Parser.Internal IReadOnlyList? leadingTrivia, IReadOnlyList? trailingTrivia) { - return new SyntaxToken.MissingTokenWithTrivia(kind, leadingTrivia ?? SyntaxToken.s_EmptySyntaxTriviaList, trailingTrivia ?? SyntaxToken.s_EmptySyntaxTriviaList); + var leading = (leadingTrivia is { } l && l.Count > 0) ? SyntaxList.List(leadingTrivia) : null; + var trailing = (trailingTrivia is { } c && c.Count > 0) ? SyntaxList.List(trailingTrivia) : null; + return new SyntaxToken.MissingTokenWithTrivia(kind, leading, trailing); } } } \ No newline at end of file diff --git a/Parser/SyntaxNavigator.cs b/Parser/SyntaxNavigator.cs new file mode 100644 index 0000000..5b7c7ad --- /dev/null +++ b/Parser/SyntaxNavigator.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace Parser +{ + public class SyntaxNavigator + { + public static SyntaxNavigator Singleton = new SyntaxNavigator(); + + public IEnumerable EnumerateTokens(SyntaxNode node) + { + foreach (var child in node.GetChildNodesAndTokens()) + { + if (child.IsNode) + { + foreach (var token in EnumerateTokens(child.AsNode()!)) + { + yield return token; + } + } + if (child.IsToken) + { + yield return child.AsToken(); + } + } + } + } +} \ No newline at end of file diff --git a/Parser/SyntaxNode.cs b/Parser/SyntaxNode.cs index 009acfd..baa4f7a 100644 --- a/Parser/SyntaxNode.cs +++ b/Parser/SyntaxNode.cs @@ -1,6 +1,5 @@ using Parser.Internal; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; namespace Parser @@ -40,6 +39,15 @@ namespace Parser public TextSpan FullSpan { get; } + public TextSpan Span => CalculateSpan(); + + private TextSpan CalculateSpan() + { + var leadingTriviaWidth = LeadingTrivia?.Width ?? 0; + var trailingTriviaWidth = TrailingTrivia?.Width ?? 0; + return new TextSpan(Position + leadingTriviaWidth, _green.FullWidth - leadingTriviaWidth - trailingTriviaWidth); + } + public int FullWidth => _green.FullWidth; internal int GetChildPosition(int slot) @@ -82,24 +90,32 @@ namespace Parser public virtual string FullText => _green.FullText; - public virtual IReadOnlyList LeadingTrivia + public virtual SyntaxTriviaList? LeadingTrivia { get { - var p = Parent; - return _green.LeadingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList(); + return GetFirstToken()?.LeadingTrivia; } } - public virtual IReadOnlyList TrailingTrivia + public virtual SyntaxTriviaList? TrailingTrivia { get { - var p = Parent; - return _green.TrailingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList(); + return GetLastToken()?.TrailingTrivia; } } + public SyntaxToken? GetFirstToken() + { + return SyntaxNavigator.Singleton.EnumerateTokens(this).Select(t => (SyntaxToken?)t).FirstOrDefault(); + } + + public SyntaxToken? GetLastToken() + { + return SyntaxNavigator.Singleton.EnumerateTokens(this).Select(t => (SyntaxToken?)t).LastOrDefault(); + } + public abstract void Accept(SyntaxVisitor visitor); public SyntaxDiagnostic[] GetDiagnostics() @@ -170,12 +186,16 @@ namespace Parser internal override SyntaxNode? GetNode(int index) { - throw new System.NotImplementedException(); + return index switch + { + 0 => GetRed(ref _file!, 0), + _ => null, + }; } public override void Accept(SyntaxVisitor visitor) { - throw new System.NotImplementedException(); + visitor.VisitRoot(this); } diff --git a/Parser/SyntaxToken.cs b/Parser/SyntaxToken.cs index 7b1cc67..33844d7 100644 --- a/Parser/SyntaxToken.cs +++ b/Parser/SyntaxToken.cs @@ -33,6 +33,15 @@ namespace Parser public TextSpan FullSpan { get; } + public TextSpan Span => CalculateSpan(); + + public TextSpan CalculateSpan() + { + var leadingTriviaWidth = LeadingTrivia.Width; + var trailingTriviaWidth = TrailingTrivia.Width; + return new TextSpan(Position + leadingTriviaWidth, FullWidth - leadingTriviaWidth - trailingTriviaWidth); + } + public object? Value => _token.GetValue(); public bool Equals(SyntaxToken other) @@ -69,21 +78,21 @@ namespace Parser public int FullWidth => _token.FullWidth; public bool IsMissing => _token.IsMissing; - public IReadOnlyList LeadingTrivia + public SyntaxTriviaList LeadingTrivia { get { - var p = _parent; - return _token.LeadingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList(); + return new SyntaxTriviaList(this, Token.LeadingTriviaCore, this.Position); } } - public IReadOnlyList TrailingTrivia + public SyntaxTriviaList TrailingTrivia { get { - var p = _parent; - return _token.TrailingTrivia.Select(trivia => new SyntaxTrivia(p, trivia)).ToImmutableList(); + var trailingGreen = Token.TrailingTriviaCore; + var trailingTriviaWidth = trailingGreen?.FullWidth ?? 0; + return new SyntaxTriviaList(this, trailingGreen, this.Position + this.FullWidth - trailingTriviaWidth); } } } diff --git a/Parser/SyntaxTriviaList.cs b/Parser/SyntaxTriviaList.cs new file mode 100644 index 0000000..61eadd7 --- /dev/null +++ b/Parser/SyntaxTriviaList.cs @@ -0,0 +1,52 @@ +using Parser.Internal; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Parser +{ + public struct SyntaxTriviaList : IReadOnlyList + { + internal GreenNode? Node { get; } + + internal SyntaxTriviaList(SyntaxToken token, GreenNode? node, int position) + { + Node = node; + Token = token; + Position = position; + } + + public SyntaxToken Token { get; } + + public int Position { get; } + + public int Count => (Node as SyntaxList)?.Length ?? 0; + + public int Width => Node?.FullWidth ?? 0; + + public SyntaxTrivia this[int index] + { + get + { + return Node switch + { + SyntaxList triviaList => new SyntaxTrivia(Token.Parent, triviaList[index]), + _ => throw new IndexOutOfRangeException(), + }; + } + } + + public IEnumerator GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return this[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Parser/SyntaxVisitor.cs b/Parser/SyntaxVisitor.cs index b03fd73..2f89563 100644 --- a/Parser/SyntaxVisitor.cs +++ b/Parser/SyntaxVisitor.cs @@ -15,5 +15,10 @@ { DefaultVisit(list); } + + public virtual void VisitRoot(RootSyntaxNode node) + { + DefaultVisit(node); + } } } \ No newline at end of file diff --git a/Semantics/GetClass.cs b/Semantics/GetClass.cs index 49a38cd..4d74ddf 100644 --- a/Semantics/GetClass.cs +++ b/Semantics/GetClass.cs @@ -11,14 +11,14 @@ namespace Semantics { var name = methodDefinition.Name.Text; var description = ""; - description += string.Join("", methodDefinition.LeadingTrivia.Select(x => x.FullText)); + description += string.Join("", methodDefinition.LeadingTrivia?.Select(x => x.FullText)); if (methodDefinition.Body == null) { - description += string.Join("", methodDefinition.EndKeyword.LeadingTrivia.Select(x => x.FullText)); + description += string.Join("", methodDefinition.EndKeyword.LeadingTrivia?.Select(x => x.FullText)); } else { - description += string.Join("", methodDefinition.Body.LeadingTrivia.Select(x => x.FullText)); + description += string.Join("", methodDefinition.Body.LeadingTrivia?.Select(x => x.FullText)); } return new MMethod(name, description); @@ -28,7 +28,7 @@ namespace Semantics { var name = methodDeclaration.Name.Text; var description = ""; - description += string.Join("", methodDeclaration.LeadingTrivia.Select(x => x.FullText)); + description += string.Join("", methodDeclaration.LeadingTrivia?.Select(x => x.FullText)); return new MMethod(name, description); }