考虑下面的Bison语法(这是从我正在研究的更大的语法中删除的):
%token ident
%left '+'
%left CALLPREC
%%
start: add ';' ;
expr: ident | call | add ;
call: expr '(' ')' %prec CALLPREC ;
add: expr '+' expr ;
在解析像foo + bar()
这样的表达式时,显然没有优先权就会出现s / r冲突。我试图理解为什么%prec
声明无法解决冲突。我使用的是Bison 3.0.2,它似乎认为该指令毫无用处:
$ bison -r state,solved -Wall ambigram.y
ambigram.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
ambigram.y:5.1-5: warning: useless precedence and associativity for CALLPREC [-Wprecedence]
奇怪的是,取消%prec CALLPREC
并声明%left '('
可以解决冲突,但声明%left ')'
却没有。这与我对Bison文档的期望相反,后者说[by] default, the precedence of a rule is that of its last token。
答案 0 :(得分:6)
移位/减少冲突的Bison优先解决方案通过在两个令牌和规则上具有优先级来工作。当它发现移位/减少冲突时,bison会比较要减少的规则的优先级和要移位的令牌的优先级,并选择更高的优先级。 %prec
指令只设置规则的优先级;它对令牌的优先顺序没有影响。
语法中的冲突(含糊不清)来自
之类的输入ident '+' ident '(' ')'
可以解析为第二个操作数是调用的add,也可以解析为被调用的expr是add的调用。它表示移位/缩小解析器作为移位/减少冲突
在看到add
输入后减少'('
规则和转移expr + expr
令牌之间的关系。因此,重要的是add
规则和'('
令牌的优先级 - call
规则的优先级无关紧要,因为呼叫尚未被识别。
在您的情况下,您会收到第二个警告,因为调用规则的显式设置优先级永远不会用于解决任何冲突。
我已经想到了编写yacc变体的想法,该变体能够以更符合大多数人直觉的方式处理优先级。它不是优先于令牌,而是在规则上只有 。当发生转移/减少冲突时,它会将要减少的规则的优先级与转移令牌后可以减少的规则的优先级进行比较。这可能无法解决冲突(如果转变导致多个规则可以减少,一些更高的优先级和一些更低的规则),但通常会更灵活,并且不太可能通过以意想不到的方式解决冲突来使人们陷入困境。