[英]Seemingly equivalent Menhir rules change the shift/reduce conflicts found in grammar
我正在使用Menhir创建一个解析器,并且有一种行为总是让我绊倒,但我不明白。 我创建了以下最小示例来演示它; 这以Go语言( http://golang.org/ref/spec#Method_declarations )的方法声明显示了接收方参数的声明:
%{
%}
%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
来在解析器中进行处理,但是我一直在that恼我不明白为什么会发生这种情况。
(如果您不知道menhir,它是一个LR(1)解析器生成器,因此可能会应用其他类似工具的工作知识。)
我想Menhir通过一些标准转换将EBNF还原为BNF。 这很普遍。 不幸的是,这些转换会破坏LR(1)的可分析性。
考虑您的规则,采用另一种类似于EBNF的语法:
demo → IDENTIFIER? STAR? IDENTIFIER
将其转换为BNF的一种方法将是您在第二组规则中所做的:定义四个不同的规则,每种可能性一个。 该转换永远不会改变LR(1)的可解析性,并且对于带有“可选”运算符的规则来说始终是可能的,但是它有两个缺点:
如果规则中有多个可选元素,则最终结果是大量生产。
它对重复运算符无效。
似乎更通用的另一种方法是为每个扩展的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仍可以将一个很好的LR(1)语法转换为非LR(1)语法。
尽管对于Menhir而言无关紧要,但可能有趣的是,上面的EBNF转换,如果仔细进行,不会引起歧义,否则就不会出现歧义。 因此,即使生成的语法不再是LR(1),它仍然是明确的,可以使用GLR解析器进行解析。 但是,据我所知,Menhir不会生成GLR解析器,因此这一事实可能不是很有用。
在第二条规则中,您明确指定了解决歧义的顺序。 确实,您可以通过对子句重新排序而以几种不同的方式重写第二条规则。 这就是为什么门希尔抱怨,他不知道你喜欢什么顺序。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.