为了语法突出显示,是否建议使用令牌?

时间:2017-06-11 14:17:54

标签: c# .net parsing antlr antlr4

我正在尝试使用Xamarin在Android上的C#中实现语法突出显示。我正在使用ANTLR v4 library来实现此目的。我的代码(当前使用this grammar突出显示Java的语法)不会尝试构建解析树并使用访问者模式。相反,我只是将输入转换为令牌列表:

Unit Delay

然后我遍历荧光笔中的所有标记,并根据它们的种类为它们指定颜色。

private static IList<IToken> Tokenize(string text)
{
    var inputStream = new AntlrInputStream(text);
    var lexer = new JavaLexer(inputStream);
    var tokenStream = new CommonTokenStream(lexer);
    tokenStream.Fill();
    return tokenStream.GetTokens();
}

最初,我认为这是明智的,因为语法突出显示主要与上下文无关。但是,我已经发现自己需要public void HighlightAll(IList<IToken> tokens) { int tokenCount = tokens.Count; for (int i = 0; i < tokenCount; i++) { var token = tokens[i]; var kind = GetSyntaxKind(token); HighlightNext(token, kind); if (kind == SyntaxKind.Annotation) { var nextToken = tokens[++i]; Debug.Assert(token.Text == "@" && nextToken.Type == Identifier); HighlightNext(nextToken, SyntaxKind.Annotation); } } } public void HighlightNext(IToken token, SyntaxKind tokenKind) { int count = token.Text.Length; if (token.Type != -1) { _text.SetSpan(_styler.GetSpan(tokenKind), _index, _index + count, SpanTypes.InclusiveExclusive); _index += count; } } 前面的特殊情况标识符,因为我希望这些标识符作为注释突出显示,就像在GitHub(example)上一样。 GitHub还有一些在某些上下文中着色标识符的示例:here@List是彩色的,而ArrayList则不是。mItems。我可能需要添加更多代码来突出显示那些场景中的标识符。

我的问题是,在这里检查令牌而不是解析树是一个好主意吗?一方面,我担心当令牌的邻居改变应该如何突出显示时,我可能不得不做很多特殊的套管。另一方面,解析将为内存受限的移动设备增加额外开销,并且当用户在代码编辑器中编辑文本时,使得实现有效语法突出显示(例如,不重新标记/解析所有内容)变得更加复杂。我还发现处理所有令牌类型而不是解析器规则类型要复杂得多,因为你switch上的token.Type而不是覆盖一堆Visit*方法。

作为参考,语法高亮显示的完整代码可用here.

3 个答案:

答案 0 :(得分:2)

这取决于你的语法高亮。

如果您使用天真的解析器,那么文本中的任何语法错误都会导致突出显示失败。这使得它成为一个非常脆弱的解决方案,因为您可能希望语法突出显示的许多文本不能保证是正确的(特别是用户输入,在完全键入之前最多不会是正确的)。由于语法突出显示可以帮助使语法错误可见并且通常用于此目的,因此完全失败的语法错误会适得其反。

有错误的文本不适合语法树。但它确实具有比令牌流更多的结构。可能最准确的表示形式是一个子树片段的森林,但这是一个比树更难处理的数据结构。

无论您选择哪种解决方案,最终都会在相互冲突的目标之间进行协商:复杂性与准确性与速度与可用性之间的关系。解析器可以是解决方案的一部分,但也可以是临时模式匹配。

答案 1 :(得分:1)

你的方法完全没问题,几乎是每个人都在使用的方法。通过环顾四周来调整类型匹配是完全正常的(因为令牌类型被缓存,所以它很便宜)。因此,如果您需要调整实际使用的SyntaxKind,您可以随时在令牌流中向后或向前看。不要开始解析您的输入。它对你没有帮助。

答案 2 :(得分:1)

我最终选择使用解析器,因为有太多的临时规则。例如,虽然我想将常规标识符着色为白色,但我希望类型声明中的类型(例如C中的class C)为绿色。最终总共约有20条特殊规则。此外,与我的应用程序中的其他瓶颈相比,解析的额外开销变得微不足道。

对于有兴趣的人,您可以在此处查看我的代码:https://github.com/jamesqo/Repository/blob/e5d5653093861bc35f4c0ac71ad6e27265e656f3/Repository.EditorServices/Internal/Java/Highlighting/JavaSyntaxHighlighter.VisitMethods.cs#L19-L76。我已经强调了我必须做出的所有约20项特殊规则。