我发现了大量与ANTLR4性能相关的问题(特别是this one,它与许多其他问题相关联)并且答案经常指出需要两阶段解析。我甚至读过tech report虽然有很多我还不太了解,但这个很清楚:"但是,如果SLL模式找到了语法错误,它可能发现了SLL弱点或真正的语法错误,因此我们必须使用优化的LL模式重试整个输入,这是第二阶段。"
问题是"整个输入"可能不存在(例如,在处理来自远程硬件的状态事件日志的实时流时),或者它可能太大而无法保留在内存中并且重新创建缓慢(例如脚本翻译复杂数据库查询的结果)。有时候(可能)无效输入的解析器性能也是相关的(编辑器使用智能感知或只是回归测试)。通常,当发现语法或弱点时运行较慢解析模式的想法是奇怪的 - 如果语法中需要ALL(*)模式进行解析的任何规则那么需要它的可能性随着输入的大小而增加。因此,不仅长时间的输入需要更长的时间来解析,他们更有可能更长时间 。
似乎准备语法SLL是最好的方法。然而,这并不总是可能的和/或容易的,它是耗时的并且很可能不是中性的。 F.E.如果某个规则在多个地方使用,原始的非SLL版本将为此规则生成单个上下文类,这意味着您可以轻松地单个访问例程处理此规则的结果。一旦将规则内联到各种使用上下文中并减少使其成为SLL,处理它的代码必须分散在不同的访问中。
替代方法是始终以ALL(*)模式运行解析 - 它非常方便,并且它似乎不会影响性能太多,除非有些情况(当它确实是 BIG )。 F.E.它在经典的if-else冲突中特别糟糕,特别是下面的表格会很快杀死它:
stmt
:
some_stmt1
|
some_stmt2
|
...
|
'if' '(' expr ')' stmt
( 'else' 'if' '(' expr ')' stmt )*
( 'else' stmt )?
;
值得庆幸的是,使用谓词后,您可以在'if'
之后的stmt
调用中阻止'else'
替代stmt
,这有助于提高性能,不会过多地破坏规则并且可以应用即使if (a)
if (b) c; else d;
else
if (e) f; else g;
规则没有直接调用自己。
出现两个问题:
有没有办法限制什么"整个输入"是什么意思? F.e。我想运行"捕捉并切换到ALL(*)"只有一个声明。一旦在SLL中没有正确解析的语句在ALL(*)中解析,我想要将模式切换回SLL并从那里继续。那可能吗?我的怀疑来自观察,即ANTLR有时会毫无理由地做出可笑的长期预测,因此它可以“跳跃”#34;在预测期间的声明中,即使有一个不能出现在声明中的保护分号。
if-else上的行为以及谓词如何帮助我将引发以下问题。 有没有办法让ANTLR停止考虑其他问题 一旦达到某一点就会有其他选择吗?假设它已经运行了"从最顶层的替代方案开始预测期间并行的所有替代方案,如果我可以告诉它选择首先检查的道路,它将有助于if-else和更多案例。
在测试if-else案例期间,我发现了ANTLR的一个特殊行为。
'else'
遇到第一个'if'
时,它可能适合第一个或第二个'else'
。一旦遇到第二个'else'
,唯一的替代活动应该是首先匹配'if'
与第二个'else'
并且刚刚遇到第二个'if'
且第一个'else'
。 我希望ANTLR停止在规则中间预测,如果在第二个'else'
之后有一个语法错误,则稍后运行此路由可能会遇到(实际)语法错误。问题是"最深的" lookahead(由IntelliJ IDEA的ANTLR插件报告)在f
之后从第一个stmt
跨越到分号。这表明ANTLR继续预测唯一可行的替代方案,直到它能够成功完成它。它为什么这样做?我的猜测是它与错误恢复有某种关系。 有没有办法让它像我预期的那样工作?
更新:我正在做hacky" trapdoor"谓词(仅在第一次询问时返回true,然后在重置之前返回false)。它没有用。但与此同时,我注意到if-elseif-else问题的谓词解决方案在开始时提出也不起作用。在更改打开之前,我仍然拥有控制台的结果,它清楚地表明解决方案确实有效。我试图将我的测试语法恢复到原来的状态,但我无法让这个解决方案再次运作 - 不知道发生了什么,我之前犯了什么样的错误。它现在表现得好像在预测期间没有执行谓词。我认为最好在这里告诉它以防万一其他人尝试并期望它能够正常工作。
至少将if_elseif_else
规则划分为单独的stmt_no_if
和else
部分的解决方案,并且仅在最后{{1}}之后调用后者仍然可以正常工作。如果缺少rule inlining该解决方案正在添加多余的上下文,那就太糟糕了。