我制作了一个 yacc 文件,试图将 miniC 语言(功能有限)转换为三地址代码。这是我遇到冲突的代码片段
/*initialization and headers*/
body: body line
|
;
line:
branch
/* other features like declarations etc */
;
branch: if_else
;
if_else:
IF '(' boolean ')' {// finalize evaluation of the boolean} block {//finalize block}
| IF '(' boolean ')' {// finalize evaluation of the boolean} block {//finalize block} ELSE {//initialization for else block} block {//finalize block}
;
block: '{' body '}'
;
Currently I just trying to implement single
if
and single if-else
.
这让我轮班减少冲突,并说
if_else
的第二个生产机构中的行动是无用的。
为什么会这样?我应该如何重组这个语法?任何帮助将不胜感激。
在那种情况下,转移/减少冲突是正常的,根本不是问题。 Yacc(或野牛)在 shift/reduce 冲突时选择 shift 而不是 reduce,这是 C 语言(以及所有编程语言)中挂起的
else
的预期行为,因为总是期望 else
附加到最里面的 if 语句。这在here.中有更详细的解释
真正的问题将来自规则中的中间规则操作引入的减少/减少冲突
if_else
(通过“完成布尔值的评估”这对操作)。当您引入中间规则操作时,yacc 将其替换为生成的 epsilon 规则(产生式为空的规则)。所以,您有这 2 个中间规则操作,yacc 必须选择它应该执行哪一个。默认情况下,yacc 将选择第一个。问题在于,通过做出该选择,它肯定会丢弃其他产品(带有else
的产品)。因此,它告诉你生产是无用的。
但解决方案很简单:删除中间规则操作。您不需要它们,并且应该能够管理语义操作中所需的所有操作(在制作结束时)。
正如 P.PICARD 所指出的,真正的问题是来自重复的中间规则操作的减少/减少冲突。您可以通过将它们组合成一个(明确的)epsilon 规则来解决这个问题。
if_else:
IF '(' boolean ')' _finalize_bool block {//finalize block}
| IF '(' boolean ')' _finalize_bool block {//finalize block} ELSE {//initialization for else block} block {//finalize block}
;
_finalize_bool: {// finalize evaluation of the boolean}
如果有问题的块需要从
boolean
访问值,这会很复杂——它现在需要使用 $-1 而不是 $2。您可以通过拆分整个条件来清理它:
if_else:
IF condition block {//finalize block}
| IF condition block {//finalize block} ELSE {//initialization for else block} block {//finalize block}
;
condition: '(' boolean ')' {// finalize evaluation of the boolean}
您可能还会发现将大部分
//finalize block
移动到 block
的动作中很有用,这样您就可以摆脱 IF-ELSE 案例中的一个中间动作规则