Parsing 为什么ANTLR4语法不明确?

Parsing 为什么ANTLR4语法不明确?,parsing,antlr,antlr4,ambiguity,Parsing,Antlr,Antlr4,Ambiguity,我正在努力理解ANTLR4算法以及它如何处理左递归。希望有人能教育我一点 以左下角的递归语法为例: grammar Dummy; TOK1 : 'foo'; TOKE_OPT : 'bar'; TOK2 : 'baz'; TOKDERP : 'derp'; SPACES : [ \u000B\t\r\n] -> channel(HIDDEN) ; rr : rr TOK1 rr TOKE_OPT? | '(' TOK2 ')' | TOKDERP

我正在努力理解ANTLR4算法以及它如何处理左递归。希望有人能教育我一点

以左下角的递归语法为例:

grammar Dummy;

TOK1 : 'foo';
TOKE_OPT : 'bar';
TOK2 : 'baz';
TOKDERP : 'derp';

SPACES
 : [ \u000B\t\r\n] -> channel(HIDDEN)
 ;

rr
    : rr TOK1 rr TOKE_OPT?
    | '(' TOK2 ')'
    | TOKDERP
    ;
和以下输入字符串:

derp foo derp foo  derp
当运行
TestRig-diagnostics
ANTLR时,得出语法不明确的结论,我不明白为什么:

line 1:5 reportAttemptingFullContext d=2 (rr), input='foo'
line 1:9 reportContextSensitivity d=2 (rr), input='foo derp'
line 1:14 reportAttemptingFullContext d=2 (rr), input='foo'
line 2:0 reportAmbiguity d=2 (rr): ambigAlts={1, 2}, input='foo  derp
'
如果有人能解释为什么这个语法是歧义的,以及如何消除歧义,我们将不胜感激。也有可能我不明白为什么歧义的意思是:)

如果我删除
TOKE_OPT?
子句,警告就会消失


我使用的是ANTLR版本
4.7.2

,该语法实际上是不明确的,因为该语法允许对
derp foo derp foo derp
进行两种解释:

(rr (rr (rr derp) foo (rr derp)) foo (rr derp))
(rr (rr derp) foo (rr (rr derp) foo (rr derp)))
(就我个人而言,我认为,如果您只使用看似合理的运算符和操作数标记,而不是从表达式中抽象出来,那么整个问题将更容易理解。但我离题了。)

Antlr4是一种LL解析器,它不能真正处理左递归。它通过将左递归规则转换为简单的等价形式,有效地改变了:

rule: base
    | rule more
    ;
进入

但这并不足以处理左递归规则的典型情况,即代数表达式。这里,典型的语法可能是:

expr: expr '*' expr
    | expr '+' expr
    | atom
    ;
其意图是:

expr: atom ('*' atom)* ('+' ('*' atom)*)*
但这是一个复杂的转换,不能很好地推广,所以Antlr真正做的是在每个规则中引入谓词,以强制运算符优先顺序。使用这些谓词,语法变得明确,并且(通常)符合关于表达式语法应该如何解析的预期

但是,只有在没有“隐藏”的左递归或右递归的情况下,Antlr才能正确地获取优先谓词。(“Hidden right recursion”并不意味着递归是隐藏的。隐藏的是递归发生在规则末尾的事实。)特别是,在规则末尾放置一个可选标记隐藏了一个事实,即可选标记前面的非终端可能是右递归的,因此,Antlr4不会试图用优先谓词消除规则的歧义。这使得语法模棱两可

您可以通过避免隐藏右侧递归来解决此问题:

rr
    : rr  TOK1 rr TOKE_OPT
    | rr  TOK1 rr
    | '(' TOK2 ')'
    | TOKDERP
    ;
现在,正确的递归规则是明确的,而另一个规则(以
TOKE_OPT
结尾)并不含糊。(或者至少不会以同样的方式模棱两可。)


有关Antlr4用于重写规则的算法的更精确描述,请参见附录末尾。

我也不太明白。当我从您的示例中删除“TOKE_OPT”时,它将不再能够解析您的输入字符串,除非我将其设置为
rr(TOK1-rr)
@Daniel:您是指
在输入“”时没有可行的替代方法
错误吗?这是一个长期存在的问题的结果,可以通过添加开始符号来解决。感谢您详尽的回复。对抽象标记表示抱歉,尽管看起来像,但该语法并非来自泛型表达式/运算符用例。@dkfso:当然,还有其他用例。我得到了抽象的论点;只是所有的德普在黑暗中看起来都一样。
rr
    : rr  TOK1 rr TOKE_OPT
    | rr  TOK1 rr
    | '(' TOK2 ')'
    | TOKDERP
    ;