Antlr4“原始”递归

时间:2019-10-09 16:01:33

标签: antlr antlr4

http://blog.ptsecurity.com/2016/06/theory-and-practice-of-source-code.html#java--and-java8-grammars之后,我尝试在相当复杂的语法中减少左递归。据我了解,非原始形式的递归会导致内存和处理时间方面的性能问题。

因此,我试图在语法中重构这些规则以仅使用“原始”递归。当然,该博客文章是我唯一一次看到关于Antlr的“原始”递归短语。因此,我只是猜测其含义/意图。在我看来,这意味着一条规则最多仅将一个规则分支称为lhs。是吗?

此刻我有一个表达式规则,

expression
    : expression DOUBLE_PIPE expression         # ConcatenationExpression
    | expression PLUS expression                # AdditionExpression
    | expression MINUS expression               # SubtractionExpression
    | expression ASTERISK expression            # MultiplicationExpression
    | expression SLASH expression               # DivisionExpression
    | expression PERCENT expression             # ModuloExpression
    ...
    ;

...包含许多子规则,这些子规则也回头引用expression。但这是唯一具有直接递归的。

如果我理解正确,将它们重构为“原始”递归将类似于:

expression
    : binaryOpExpression                        # BinaryOpExpression
    ...
    ;

binaryOpExpression
    : expression DOUBLE_PIPE expression         # ConcatenationExpression
    | expression PLUS expression                # AdditionExpression
    | expression MINUS expression               # SubtractionExpression
    | expression ASTERISK expression            # MultiplicationExpression
    | expression SLASH expression               # DivisionExpression
    | expression PERCENT expression             # ModuloExpression
    ;

首先,这是正确的重构吗?

其次,这真的有助于提高性能吗?归根结底,这仍然是相同的决定,所以我不太了解这如何提高性能(除了也许产生更少的ATNConfig对象)。

谢谢

1 个答案:

答案 0 :(得分:0)

在这种情况下,我之前从未听说过“原始递归”,而作者可能只打算在ANTLR4中命名特定形式的递归。

事实是ANTLR4中有3种相关的递归形式:

  • 直接左递归:从规则中的第一个规则引用(到同一规则)进行递归。例如:a: ab | c;
  • 间接左递归:不是直接来自同一规则的左递归。例如:a: b | c; b: c | d; c: a | e;(在ANTLR4中不允许)
  • 右递归:规则中的任何其他递归。例如:a: ba | c;。但是,“右递归”这个名称仅在二进制表达式的情况下才是正确的,但通常通常用来与左递归区分开。

已经说过,很明显您的重写是错误的,因为它将创建ANLTR4不支持的间接左递归。从内存或性能的角度来看,直接左递归通常不是问题,因为ANTLR4会将它们转换为非递归的ATN规则图。

正确的递归可能会成为问题,因为它们是通过代码递归(运行时中的递归函数调用)实现的,这可能会很快耗尽CPU堆栈。我见过大表达式无法在单独线程中解析的情况,因为我无法将线程堆栈大小设置为更大的值(主线程堆栈大小通常可以通过链接器设置进行调整)。

后一种情况的唯一解决方案(我发现非常有用)是减少语法中相互调用的解析器规则的数量。当然,将某些表达式元素放在不同的规则中(例如andExpressionorExpressionbitExpression等,是结构,可读性等问题,但这可能会导致很深的调用堆栈,这可能会耗尽CPU堆栈和/或需要大量时间来处理它们。