Compiler construction Bison移位/减少冲突-tiger编译器
我已经根据Tiger手册(附录a,Tiger手册)编写了一个yacc文件 但仍然存在一些转移/减少冲突。我不知道如何解决这些冲突Compiler construction Bison移位/减少冲突-tiger编译器,compiler-construction,yacc,shift-reduce-conflict,Compiler Construction,Yacc,Shift Reduce Conflict,我已经根据Tiger手册(附录a,Tiger手册)编写了一个yacc文件 但仍然存在一些转移/减少冲突。我不知道如何解决这些冲突 % yacc --version bison (GNU Bison) 3.0.2 您可以使用此cmd重现问题: % yacc -dvt tiger.y tiger.y: warning: 37 shift/reduce conflicts [-Wconflicts-sr] %cat tiger.y: %{ #include <stdio.h> //#i
% yacc --version
bison (GNU Bison) 3.0.2
您可以使用此cmd重现问题:
% yacc -dvt tiger.y
tiger.y: warning: 37 shift/reduce conflicts [-Wconflicts-sr]
%cat tiger.y
:
%{
#include <stdio.h>
//#include "util.h"
//#include "errormsg.h"
int yylex(void); /* function prototype */
void yyerror(char *s)
{
EM_error(EM_tokPos, "%s", s);
}
%}
%union {
int pos;
int ival;
string sval;
}
%token <sval> ID STRING
%token <ival> INT
%token
COMMA COLON SEMICOLON LPAREN RPAREN LBRACK RBRACK
LBRACE RBRACE DOT
PLUS MINUS TIMES DIVIDE EQ NEQ LT LE GT GE
AND OR ASSIGN
ARRAY IF THEN ELSE WHILE FOR TO DO LET IN END OF
BREAK NIL
FUNCTION VAR TYPE
%right ASSIGN
%left OR
%left AND
%nonassoc EQ NEQ LT LE GT GE
%left PLUS MINUS
%left TIMES DIVIDE
%left UNARYMINUS
%precedence THEN
%precedence ELSE
%start program
%%
program: exp { }
;
exp:lvalue { }
|NIL { }
|LPAREN explist RPAREN { }
|LPAREN RPAREN {}
|INT {}
|STRING {}
|MINUS exp %prec UNARYMINUS {}
|ID LPAREN RPAREN {}
|ID LPAREN arglist RPAREN {}
|exp PLUS exp {}
|exp MINUS exp {}
|exp TIMES exp {}
|exp DIVIDE exp {}
|exp EQ exp {}
|exp NEQ exp {}
|exp LT exp {}
|exp LE exp {}
|exp GT exp {}
|exp GE exp {}
|exp AND exp {}
|exp OR exp {}
|ID LBRACE RBRACE {}
|ID LBRACE idlist RBRACE {}
|ID LBRACK exp RBRACK OF exp {}
|lvalue ASSIGN exp {}
|IF exp THEN exp ELSE exp {}
|IF exp THEN exp {}
|WHILE exp DO exp {}
|FOR ID ASSIGN exp TO exp DO exp {}
|BREAK {}
|LET decs IN END {}
|LET decs IN explist END {}
;
lvalue: ID {}
| lvalue DOT ID {}
| lvalue LBRACK exp RBRACK {}
;
explist: exp {}
| explist SEMICOLON exp {}
;
arglist:exp {}
|exp COMMA arglist {}
;
idlist:ID EQ exp {}
|ID EQ exp COMMA idlist {}
;
decs:dec {}
|decs dec {}
;
dec:tydec {}
|vardec {}
|fundec {}
;
tydec:TYPE ID EQ ty {}
;
ty:ID {}
|LBRACK tyfields RBRACK {}
|ARRAY OF ID {}
;
tyfields:/* NULL */
|notnulltyfields {}
;
notnulltyfields:ID COLON ID {}
|ID COLON ID COMMA notnulltyfields {}
;
vardec:VAR ID ASSIGN exp {}
|VAR ID COLON ID ASSIGN exp {}
;
fundec:FUNCTION ID LPAREN tyfields RPAREN EQ exp {}
|FUNCTION ID LPAREN tyfields RPAREN COLON ID EQ exp {}
;
%{
#包括
//#包括“util.h”
//#包括“errormsg.h”
int-yylex(void);/*函数原型*/
无效错误(字符*s)
{
EM_错误(EM_tokPos,“%s”,s);
}
%}
%联合{
int pos;
国际竞争力;
字符串sval;
}
%令牌ID字符串
%标记整数
%代币
逗号冒号分号LPAREN RPAREN LBRACK RBRACK
LBRACE RBRACE点
加减倍除以等式NEQ LT LE GT GE
分配
数组IF-THEN-ELSE WHILE FOR-DO-LET-IN结束
破零
函数变量类型
%权利转让
%左或右
%左和
%非ASSOC均衡器
%左加减
%左倍分割
%左一元负
%那么优先权呢
%其他优先权
%启动程序
%%
程序:exp{}
;
exp:lvalue{}
|零{}
|LPAREN解释程序RPAREN{}
|LPAREN RPAREN{}
|INT{}
|字符串{}
|负exp%prec一元负{}
|ID LPAREN RPAREN{}
|ID LPAREN arglist RPAREN{}
|exp加exp{}
|exp减去exp{}
|exp乘以exp{}
|exp DIVIDE exp{}
|exp EQ exp{}
|exp NEQ exp{}
|exp LT exp{}
|exp-LE exp{}
|exp GT exp{}
|exp GE exp{}
|exp和exp{}
|exp或exp{}
|ID LBRACE RBRACE{}
|ID LBRACE idlist RBRACE{}
|ID LBRACK exp RBRACK OF exp{}
|左值赋值exp{}
|IF exp然后exp ELSE exp{}
|如果exp那么exp{}
|而exp DO exp{}
|对于ID,将exp分配给exp DO exp{}
|中断{}
|让decs在结尾{}
|让decs在explist结尾{}
;
左值:ID{}
|左值点ID{}
|左值LBRACK exp RBRACK{}
;
解释列表:exp{}
|解释列表分号exp{}
;
arglist:exp{}
|exp逗号arglist{}
;
idlist:ID EQ exp{}
|ID EQ exp逗号idlist{}
;
decs:dec{}
|decs dec{}
;
dec:tydec{}
|vardec{}
|fundec{}
;
tydec:TYPE ID EQ ty{}
;
ty:ID{}
|LBRACK tyfields RBRACK{}
|ID{}的数组
;
tyfields:/*NULL*/
|notnulltyfields{}
;
notnulltyfields:ID冒号ID{}
|ID冒号ID逗号notnulltyfields{}
;
vardec:VAR ID ASSIGN exp{}
|变量ID冒号ID分配exp{}
;
fundec:FUNCTION ID LPAREN tyfields RPAREN EQ exp{}
|函数ID LPAREN tyfields RPAREN冒号ID EQ exp{}
;
通过查看使用-v
标志生成的tiger.output
文件,可以轻松发现移位减少冲突
下面是一个例子(我删去了重复部分):
我们可以看到,当一个表达式可以减少时,状态88就会出现(从状态描述中
的位置可以明显看出:
29 | WHILE exp DO exp .
如果此时的先行标记是一个二进制运算符,那么解析器不知道是移动运算符,使WHILE
表达式中的尾部exp
更长,还是立即减少WHILE
。显然(对我们来说,不是对bison
),解决方案是移位。bison
不知道这一点,因为产品exp:WHILE exp DO exp
没有优先级。该产品的优先级将是其最后一个终端的优先级,即DO
,因此简单的解决方案是为DO
定义优先级。毫不奇怪,它应该ld与ELSE
的优先级相同,这一事实表明IF exp-THEN exp-ELSE exp.
不会产生移位/减少冲突
类似的问题出现在状态112和129中
状态1中的移位/减少冲突也可以从输出文件中清除:
State 1
9 exp: ID . LPAREN RPAREN
10 | ID . LPAREN arglist RPAREN
23 | ID . LBRACE RBRACE
24 | ID . LBRACE idlist RBRACE
25 | ID . LBRACK exp RBRACK OF exp
34 lvalue: ID .
LPAREN shift, and go to state 15
LBRACK shift, and go to state 16
LBRACE shift, and go to state 17
LBRACK [reduce using rule 34 (lvalue)]
$default reduce using rule 34 (lvalue)
这里,解析器刚刚在exp
可能会减少的上下文中找到了ID
,它面临两种可能性:
shift。exp
是exp
的ID[exp],因此最终结果将是:
ID '[' exp ']' OF exp --> exp (rule 25)
reduce。exp
是左值ID[exp]
,使用以下结果:
ID --> lvalue (rule 34)
lvalue '[' exp ']' --> lvalue (rule 36)
lvalue --> exp (rule 2)
为了使用第二种选择,解析器必须立即将ID
减少到lvalue
,这就是问题所在:解析器在看到匹配后的的之前,无法知道这两种可能性中的哪一种是正确的,但这是很遥远的将来——事实上,它可能是一个任意数把代币拿走
这里的解决方案是避免强迫解析器在此时做出决定
由于表达式只能是
的ID[exp](而不是更复杂的内容),因此我们可以将ID
排除在冲突之外:
exp : ID
| lvalue_not_id
| ...
lvalue: ID
| lvalue_not_id
lvalue_not_ID
: lvalue DOT ID
| ID LBRACK exp RBRACK
| lvalue_not_ID LBRACK exp RBRACK
将当前状态机与此更改后的状态机进行比较,可以清楚地了解其工作原理(这是学习自底向上解析的一个有用练习)
如果你不想从事所有这些工作,你可以简单地添加一个“明显多余”的作品,正如阿佩尔在他的教科书中所建议的:
lvalue: ID
| lvalue DOT ID
| lvalue LBRACK exp RBRACK
| ID LBRACK exp RBRACK
向lvalue
添加的产品显然会产生移位-减少冲突;事实上,它与原始语法中的移位-减少冲突完全相同。但这一次,冲突发生在lvalue
的两个不同产品之间,默认移位操作肯定是您希望在ca中执行的操作se是一个空的ID
后跟一个[。在移位之后,lvalue
产生式和exp
产生式仍然可用,因此解析器不必做出决定,直到它在后面找到标记]
此解决方案的缺点是解析器生成器将继续报告移位减少冲突,因为存在clearl
lvalue: ID
| lvalue DOT ID
| lvalue LBRACK exp RBRACK
| ID LBRACK exp RBRACK