Compiler construction bison语法中reduce/reduce和shift/reduce冲突的求解

Compiler construction bison语法中reduce/reduce和shift/reduce冲突的求解,compiler-construction,bison,Compiler Construction,Bison,我试图在下面的bison语法中解决r/r和s/r冲突 %right TOK_IF TOK_ELSE %right '=' %left TOK_EQ TOK_NE '<' TOK_LE '>' TOK_GE %left '+' '-' %left '*' '/' '%' %right TOK_POS TOK_NEG '!' TOK_NEW %left TOK_BLK '.' TOK_CALL %precedence TOK_PARENT %start start

我试图在下面的bison语法中解决r/r和s/r冲突

%right TOK_IF TOK_ELSE
%right '='
%left  TOK_EQ TOK_NE '<' TOK_LE '>' TOK_GE
%left  '+' '-'
%left  '*' '/' '%'
%right TOK_POS TOK_NEG '!' TOK_NEW
%left  TOK_BLK '.' TOK_CALL
%precedence  TOK_PARENT

%start start

        expr       : expr BINOP expr   {$$=$2->adopt($1,$2);}
                   | UNOP              {$$ = $1;}
                   | allocator         {$$ = $1;}
                   | call              {$$ = $1;}
                   | expr '[' expr ']' %prec TOK_BLK  {
                     destroy($4);
                     $$ = $2->adopt($1,$3);}
                   | '(' expr ')' %prec TOK_PARENT {$$ = $2;}
                   | expr '.' expr {$$ = $2->adopt($1,$3);}
                   | variable {$$= $1;}
                   | constant {$$ = $1;}
                   ;
        BINOP      : TOK_IF   {$$ = $1;}
                   | TOK_ELSE {$$ = $1;}
                   | '='      {$$ = $1;}
                   | TOK_EQ   {$$ = $1;}
                   | TOK_NE   {$$ = $1;}
                   | '<'      {$$ = $1;}
                   | TOK_LE   {$$ = $1;}
                   | '>'      {$$ = $1;}
                   | TOK_GE   {$$ = $1;}
                   | '+'      {$$ = $1;}
                   | '-'      {$$ = $1;}
                   | '*'      {$$ = $1;}
                   | '/'      {$$ = $1;}
                   | '%'      {$$ = $1;}
                   ;

        UNOP       : '+' expr %prec TOK_POS {
                   $1->swap_token_code(TOK_POS);
                   $$ = $1->adopt($2);
                   }
                   | '-' expr %prec TOK_NEG{
                   $1->swap_token_code(TOK_NEG);
                   $$ = $1->adopt($2);
                   }
                   | '!' expr {$$ = $1->adopt($2);}
                   ;
        allocator  : TOK_NEW TOK_IDENT '('')' {
                   destroy($3);
                   $2->swap_token_code(TOK_TYPEID);
                   $$ = $1->adopt($2);
                   }
                   | TOK_NEW TOK_STRING '(' expr ')'{

                   }
                   | TOK_NEW basetype '[' expr ']'{
                   destroy($3);destroy($5);
                   $1->swap_token_code(TOK_NEWARRAY);
                   $$ = $1->adopt($2,$4);
                   }
                   ;
        call       : TOK_IDENT '(' exprs ')' %prec TOK_CALL{
                    destroy($4);
                    $2->swap_token_code(TOK_CALL);
                    $$ = ($2->adopt($1))->cannibalize($3);
                   }
                   ;
        exprs      : exprs expr  {$$ = $1->adopt($2);}
                   | {$$ = new astree ('{',{0,0,0}, "}");}
                   ;

        variable   : TOK_IDENT  {$$ = $1;}
               | expr '[' expr ']'{
               destroy($4);
               $$ = $2->adopt($1,$3);
               }
               | expr '.' TOK_IDENT {$$ = $2->adopt($1,$3);}
               ;
        constant   :TOK_INTCON    {$$ = $1;}
                |TOK_CHARCON   {$$ = $1;}
               |TOK_STRINGCON {$$ = $1;}
               |TOK_NULL      {$$ = $1;}
               ;
    %%
更新:我发现我的理解有问题

虽然我得到了正确的结果,但我的编译器一直告诉我,在下面的语法中存在移位/减少冲突。我认为不应该有冲突,因为我正确地指定了优先级和关联性

%left '+'
%left '*'

%%
expr : expr BINOP expr
     | TOK_INTCON 

BINOP :  '+'  
      |  '*'
%%

这是对您的更新部分的回应,因为这是问题的核心

TL;DR的答案是,优先级和关联性声明
%left'+'
%left'*'
在解析
BINOP
时适用,但在解析
expr
时不适用,因此它们太早了(此时它们什么也不做),并且在需要时就消失了


我修改了您的示例,以便Bison能够处理它:

$ cat expr.y
%token TOK_INTCON
%left '+'
%left '*'

%%
expr : expr BINOP expr
     | TOK_INTCON 

BINOP :  '+'  
      |  '*'
%%
$ bison -v expr.y
expr.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
这里的
-v
的要点是编写
expr.output
,显示Bison是如何解释你的语法的。您可以仔细阅读此内容以查看移位/减少冲突发生的确切位置,但简而言之,它发生在输入包含以下内容时,例如

1 op 2 op 3
语法允许将其解析为:

  op
 /  \
1    op
    /  \
   2    3
(如果每次选择“shift”而不是“reduce”,则会得到解析树),或者:

%left
%right
%nonassoc
所做的是为特定标记分配优先级。现在,如中所述,优先级确实会传递到规则,但至关重要的是,不会传递到非终结符本身:它们只适用于非终结符中的单个规则。它们的功能是决定是转移到新的状态,还是按规则减少,一旦发生转移或减少,就已经做出了决定

通过将所有的二进制运算符减少为
binop
非终结符,就剥夺了解析器区分每个不同二进制运算符的方法。在Bison将生成的LALR语法中,如果每个操作符都有一个单独的规则,那么每个操作符都有一个单独的状态,这允许单独的移位或减少决策

$ cat expr-repaired.y
%token TOK_INTCON
%left '+'
%left '*'

%%
expr : expr '+' expr
     | expr '*' expr
     | TOK_INTCON 
%%
$ bison -v expr-repaired.y
$ 
有趣的是,状态的总数保持不变(无论是
expr.output
还是
expr.output
都显示了七种状态)。然而,国家的含义是不同的。处理旧的
BINOP
非终结符的几个状态消失了;取而代之的是多个状态,用于正确处理左关联的不同优先级运算符,同时根据我们决定如何减少第一个
expr
来决定是否减少
expr
。仔细观察新的状态6,例如:

State 6

    1 expr: expr . '+' expr
    1     | expr '+' expr .
    2     | expr . '*' expr

    '*'  shift, and go to state 5

    $default  reduce using rule 1 (expr)

如果我们处于这种状态,并且下一个标记是
*
,我们将进行移位,这样我们将处理
expr*expr
,并在我们减少
expr+(expr from reduce)

之前减少它,这不是答案,但您的一些代码操作是不正确的。没有理由销毁
]
,也没有理由将二进制运算符作为
采用()
的参数。感谢您的反馈。关于我的后一个问题的答案就足够了。好吧,我所理解的是野牛可以识别'*'和'+'的优先级,但不能识别BINOP的优先级。这不太正确:问题是,
BINOP
没有优先级。实际上,通过在规则中使用
%prec
,您可以给它一个。。。但这只是一个,而不是两个不同的,这取决于它匹配的运算符。我知道了,谢谢。我发布了另一个问题
$ cat expr-repaired.y
%token TOK_INTCON
%left '+'
%left '*'

%%
expr : expr '+' expr
     | expr '*' expr
     | TOK_INTCON 
%%
$ bison -v expr-repaired.y
$ 
State 6

    1 expr: expr . '+' expr
    1     | expr '+' expr .
    2     | expr . '*' expr

    '*'  shift, and go to state 5

    $default  reduce using rule 1 (expr)