ANTLR:当用户修改文本时,如何避免重新解析整个文件

时间:2017-07-17 20:38:26

标签: c# parsing compiler-construction antlr antlr4

编辑:对于有兴趣/想要查看我正在做什么的人,可以找到我的应用的源代码here

我正在使用C#构建一个代码编辑器应用程序,它提供语法高亮显示。我目前正在使用ANTLR for C#来解析代码以突出显示它。到目前为止,我的应用程序可以在用户最初打开文件时非常快地突出显示代码。但是,当用户开始编辑时,我没有编写任何代码来重新突出显示文本。

我希望编辑器能够很好地处理大型文件,所以每次用户输入一个字符时我都不想重新解析整个文件。我做了一些研究,看起来我正在寻找的是增量解析器。不幸的是,它似乎是ANTLR v4 can't do incremental parsing,所以我不确定该怎么做。

我的问题是:是否有另一种方法,我可以使用ANTLR,在用户输入时不冻结应用程序?我真的很犹豫是否放弃了ANTLR,因为它有a bunch of free grammars可用,因此添加对新语言的支持并不多。我查看了TextMate语法,VSCode使用了很多语法,但我不理解它们,并且没有可用于操作它们的C#库。

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

每次击键后我都不会解析,但我会解析整个文件。这适用于我创建的特定于域的语言中的中等大小的文件。我没有尝试仅解析文件的某些部分,而是使用混合方法,解析当三个条件中的第一个存在时:

  1. 用户类型 n 字符
  2. 计时器说 m 毫秒没有变化。
  3. 对于某些语法,用户键入行终止符/分隔符;
  4. 最重要的是,你可能会惊讶于人们花了多少时间暂停和思考,因为他们正在键入任何强加语法的东西。可以利用这些暂停来在用户思考时进行有用的工作,即使是400毫秒。我在我为工作创建的DSL中使用了#1和#2,因为它们的语法。

    "没有变化"每次按键事件后,时钟都会被重置,当 n 字符后解析发生时, n 字符计数器当然会被设置。我发现像这样的组合方法在IDE类型环境中运行良好。

    要记住的一件事是,如果你这样做,在发现语法错误时不要弄乱文本控件的插入点,因为错误在键入时是不可避免的。我只是在标签中显示一条消息:

        public override void Recover(Parser recognizer, RecognitionException e)
        {
            IToken token = recognizer.CurrentToken;
            string message = string.Format("parse error at line {0}, position {1} right before {2} ", token.Line, token.Column, GetTokenErrorDisplay(token));
            BasicEnvironment.SyntaxError = message;
    

    在我的使用环境中,计时器通常会控制它何时熄灭;值得800毫秒和10个字符我得到了很好的结果,计时器通常在解析开始时起作用。

答案 1 :(得分:1)

@JLH基本上说明了我使用的方法,但我想添加一些你可能需要注意的事项:
首先,我将在与编辑器的UI线程不同的线程中进行解析,以防止解析器在用户决定继续编码的同时启动的情况。如果您使用的是其他线程,则用户可以继续编码,甚至不会注意到解析器在后台运行。当然,您需要使用某种机制,在这种情况下取​​消解析,或者至少不要在编辑器中使用生成的解析树执行任何操作,因为它已经是错误的。

接下来我发现ANTLR在中等大小的文件(~100行)上变得非常慢。而且非常慢,我的意思是解析可能需要20秒!这可以通过switchung ANTLR的默认解析算法到SLL并使用BailoutStrategy来防止,以便它不会尝试从语法错误中恢复。如果发生这种情况,您必须使用正常的LL模式再次解析整个事情,以检查它是否真的是语法错误,或只是SLL无法处理的事情。 /> 通过这种方法,我将一些文件中的解析时间从~20s缩短到了~1s。

你可以在我的source code中看到一个有效的(肯定不完美的)实现(虽然它在java中但是原理应该是相同的)。