Compiler construction Bison移位/减少冲突-tiger编译器

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

我已经根据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>
//#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