static SyntaxTokenList PostprocessTokens(List<SyntaxToken> tokens, NavCommonTokenStream cts, SyntaxNode syntax, CancellationToken cancellationToken) { var result = new List<SyntaxToken>(cts.AllTokens.Count); tokens.Sort((x, y) => x.Start - y.Start); // Wir haben bereits die signifikanten Token (T) im GrammarVisitor erstellt. // Wir können nicht alle Tokens hier ermitteln, da der Visitor viel mehr Kontextinformationen // hat. Somit wird aus einem "Identifier" z.B. je nach Kontext ein Keyword, oder Symbol (=> Classification). // Was uns hier jedoch noch fehlt sind vor allem die Whitespaces (w) und "unbekannten" (u), // die der Parser nie zu Gesicht bekommt. Der TokenStream liefert uns alle Token // (candidates = c): // -T---TTT---T----T-- <= bereits im Visitor erfasste Token // ccccccccccccccccccc <= alle Tokens (candidates) // wTwwwTTTwwwTwwwwTuu <= die Tokens, wie wir sie hier haben wollen var index = 0; foreach (var candidate in cts.AllTokens) { cancellationToken.ThrowIfCancellationRequested(); if (index < tokens.Count) { var existing = tokens[index]; // Das Token wurde bereits im Visitor erfasst (T) if (existing.Start == candidate.StartIndex) { result.Add(existing); index++; continue; } } // Das Token existiert noch nicht, da es der Parser/Visitor offensichtlich nicht "erwischt hat" (t, u) SyntaxTokenClassification tokenClassification; switch (candidate.Channel) { case NavGrammarLexer.TriviaChannel: switch (candidate.Type) { case NavGrammarLexer.NewLine: tokenClassification = SyntaxTokenClassification.Whitespace; break; case NavGrammarLexer.Whitespace: tokenClassification = SyntaxTokenClassification.Whitespace; break; case NavGrammarLexer.SingleLineComment: tokenClassification = SyntaxTokenClassification.Comment; break; case NavGrammarLexer.MultiLineComment: tokenClassification = SyntaxTokenClassification.Comment; break; case NavGrammarLexer.Unknown: tokenClassification = SyntaxTokenClassification.Skiped; break; default: // Wir haben sonst eigentlich nix im Trivia Channel throw new ArgumentException(); } break; case Lexer.DefaultTokenChannel: tokenClassification = SyntaxTokenClassification.Skiped; break; default: throw new ArgumentException(); } //TODO: hier evtl. den "echten" Parent herausfinden... SyntaxNode parent = syntax; result.Add(SyntaxTokenFactory.CreateToken(candidate, tokenClassification, parent)); } return new SyntaxTokenList(result); }
internal static SyntaxTree ParseTextCore(string sourceText, Func<NavGrammarParser, IParseTree> treeCreator, string filePath, Encoding encoding = null, CancellationToken cancellationToken = default(CancellationToken)) { var fileInfo = String.IsNullOrEmpty(filePath) ? null : new FileInfo(filePath); sourceText = sourceText ?? String.Empty; var stream = new AntlrInputStream(sourceText); var lexer = new NavGrammarLexer(stream); var cts = new NavCommonTokenStream(lexer); var parser = new NavGrammarParser(cts); var errorListener = new NavErrorListener(filePath); parser.RemoveErrorListeners(); parser.AddErrorListener(errorListener); var tree = treeCreator(parser); var visitor = new NavGrammarVisitor(expectedTokenCount: cts.AllTokens.Count); var syntax = visitor.Visit(tree); var tokens = PostprocessTokens(visitor.Tokens, cts, syntax, cancellationToken); var textLines = GetTextLines(sourceText); var diagnostics = errorListener.Diagnostics; var syntaxTree = new SyntaxTree(sourceText : sourceText, root : syntax, tokens : tokens, textLines : textLines, diagnostics: diagnostics, fileInfo : fileInfo, encoding : encoding); syntax.FinalConstruct(syntaxTree, null); return syntaxTree; }