我正在使用Menhir创建一个解析器,并且有一种行为总会让我感到沮丧,而我却不理解它。我创建了以下最小例子来演示它;这显示了Go语言(http://golang.org/ref/spec#Method_declarations)中方法声明中的receiver参数声明:
%{
%}
%token <string> T_identifier
%token T_star
%start <unit> demo
%%
(* This rule has a shift/reduce conflict
demo:
| option(T_identifier) option(T_star) T_identifier { () }
*)
(* This rule is okay. *)
demo:
| T_identifier T_star T_identifier { () }
| T_identifier T_identifier { () }
| T_star T_identifier { () }
| T_identifier { () }
据我所知,这两个规则在语义上是等价的:我们正在寻找一个可选的标识符(接收者的名字),一个可选的星号(指针或非指针)和一个强制类型名称(接收器的类型) )。但是,第一条规则(注释掉的那条规则)会产生转移/减少冲突,而第二条规则可以正常工作。
我已经能够在我的解析器中取得进展,只要发生这种情况就用多个规则替换option
,但是我一直在唠叨我不明白为什么会发生这种情况。
(如果您不了解menhir,那么它是一个LR(1)解析器生成器,因此可能适用其他类似工具的工作原理。)
答案 0 :(得分:3)
我认为Menhir通过一些标准转换将EBNF减少为BNF。这很常见。不幸的是,这些转换会破坏LR(1)的可解析性。
考虑你的规则,用另一种类似EBNF的语法:
demo → IDENTIFIER? STAR? IDENTIFIER
将其翻译成BNF的一种方法就像你在第二套规则中所做的那样:定义四个不同的规则,每个规则对应一种可能性。这种转换永远不会改变LR(1)的可解析性,并且带有“可选”运算符的规则总是可能的,但它有两个缺点:
如果规则中有多个可选元素,则最终结果是 lot 的制作。
它对重复操作符不起作用。
另一种似乎更通用的方法是为每个扩展BNF运算符创建一个新的非终端。所以我们可以这样做:
optional_identifier → IDENTIFIER | ε
optional_star → STAR | ε
demo → optional_identifier optional_star IDENTIFIER
类似的转换适用于x*
:
repeated_x → ε | repeated_x x
这肯定会产生一种等效语言,但现在语法可能不是LR(1)。
特别是,demo
不再是LR(1)。它在开始时就失败了。假设第一个输入标记是IDENTIFIER
。这可能是
IDENTIFIER IDENTIFIER
或
IDENTIFIER
(或其他一些可能性,但这足以说明问题。)
在第二种情况下(只是一种类型),我们需要减少optional_identifier
和optional_star
才能转移IDENTIFIER
。在第一种情况下(变量和类型),我们需要立即移动IDENTIFIER
。我们可用于区分的唯一信息是前瞻标记IDENTIFIER
,这显然是不够的。
如果我们使用四向扩展生产,则没有问题:
demo → IDENTIFIER
| STAR IDENTIFIER
| IDENTIFIER IDENTIFIER
| IDENTIFIER STAR IDENTIFIER
在这里,当我们看到IDENTIFIER
时,我们不知道它是第一次制作,第三次制作还是第四次制作的一部分。但并不重要,因为在所有情况下,我们只需转移IDENTIFIER
并等待更多信息。
yacc/bison
和其他允许中规则操作(MRA)的解析器生成器也会出现类似的现象。 MRA变成了一个新的非终端,其唯一的生产是ε生产;新的非终端的目的是在MRA减少时运行MRA。这真的很酷,除了有时新的非终端是在我们无法知道是否适合减少它的地方引入的。因此,MRA可以将非常好的LR(1)语法转换为非LR(1)语法,即使语言没有改变。
虽然与Menhir的情况不相关,但可能有趣的是,如果仔细地进行上述EBNF转换,则不会引入歧义,而这种歧义并非存在。因此,即使得到的语法不再是LR(1),它仍然是明确的,并且可以用GLR解析器解析。但是,据我所知,由于Menhir没有生成GLR解析器,因此这个事实可能不太有用。
答案 1 :(得分:1)
在第二条规则中,您明确指出应该以何种顺序解决歧义。实际上,您可以通过重新排序子句以几种不同的方式重写第二条规则。这就是为什么menhir抱怨,他不知道你喜欢什么样的顺序。