解析器如何解决转移/减少冲突?

时间:2017-02-14 19:55:49

标签: parsing yacc shift-reduce-conflict

我有一个算术表达式的语法,它解决了文本文件中的表达式数量(每行一个)。在编译YACC时,我收到消息2 shift减少冲突。但我的计算是正确的。如果解析器提供正确的输出,它如何解决shift / reduce冲突。在我的情况下,有任何方法可以在YACC语法中解决它。

YACC GRAMMAR

Calc  : Expr               {printf(" = %d\n",$1);} 
  | Calc Expr          {printf(" = %d\n",$2);}
  | error              {yyerror("\nBad Expression\n ");}
  ;
Expr  : Term               { $$ = $1;         }
  | Expr '+' Term      { $$ = $1 + $3;    }
  | Expr '-' Term      { $$ = $1 - $3;    }
  ;
Term  : Fact               { $$ = $1;         }
  | Term '*' Fact      { $$ = $1 * $3;    }
  | Term '/' Fact      { if($3==0){ 
                yyerror("Divide by Zero Encountered.");
            break;}
               else
                $$ = $1 / $3;    
                   }
  ;
Fact  : Prim               { $$ = $1;        }
  | '-' Prim           { $$ = -$2;       }
  ;      
Prim  : '(' Expr ')'       { $$ = $2;        }
  | Id                 { $$ = $1;        }
  ;
Id    :NUM                 { $$ = yylval;    }
  ;

我应该采取哪些措施来消除语法中的冲突?

1 个答案:

答案 0 :(得分:2)

Bison / yacc通过选择转移来解决转移减少冲突。有关Shift-Reduce冲突的部分bison manual中对此进行了解释。

您的问题是您的输入只是一系列Expr,一起运行,它们之间没有任何分隔符。这意味着:

4 - 2

可以是一个表达式(4-2),也可以是两个表达式(4-2)。由于bison生成的解析器总是喜欢移位,解析器会选择将其解析为一个表达式,即使它是在两行上键入的:

4
-2

如果你想允许用户在没有任何分隔符的情况下输入他们的表达式,那么你可以忍受冲突(因为它是相对温和的),或者你可以把它编成你的语法,但是那个相当多的工作。要将它放入语法中,你需要定义两种不同类型的Expr:一个(你在顶层使用的那个)不能以一元减号开头,另一个(你可以使用)允许以一元减号开始。

我怀疑你真正想做的是使用换行符或其他类型的表达式分隔符。这很简单,只需将换行符传递给解析器并将Calc更改为Calc: | Calc '\n' | Calc Expr '\n'

我确定这出现在SO的其他地方,但我无法找到它。所以这里是你如何禁止在表达式的开头使用一元减号,这样你就可以在没有分隔符的情况下一起运行表达式。从n_开始的非终端不能以一元减号开头:

input:  %empty | input n_expr { /* print $2 */ }

expr:   term | expr '+' term | expr '-' term
n_expr: n_term | n_expr '+' term | n_expr '-' term

term:   factor | term '*' factor | term '/' factor
n_term: value | n_term '+' factor | n_term '/' factor

factor: value | '-' factor

value:  NUM | '(' expr ')'

解析与语法相同的语言,但不会产生shift-reduce冲突。由于它解析相同的语言,输入

4
-2

仍将被解析为单个表达式;要获得预期的结果,您需要输入

4
(-2)