【问题标题】:How to fix YACC shift/reduce conflicts from post-increment operator?如何修复后增量运算符的 YACC 移位/减少冲突?
【发布时间】:2009-05-20 20:24:08
【问题描述】:

我正在用 YACC(实际上是 Bison)编写语法,但遇到了移位/减少问题。它是由于包含后缀递增和递减运算符而产生的。这是语法的精简版:

%token NUMBER ID INC DEC

%left      '+' '-'
%left      '*' '/'
%right     PREINC
%left      POSTINC

%%

expr: NUMBER
|     ID
|     expr '+' expr
|     expr '-' expr
|     expr '*' expr
|     expr '/' expr
|     INC expr %prec PREINC
|     DEC expr %prec PREINC
|     expr INC %prec POSTINC
|     expr DEC %prec POSTINC
|     '(' expr ')'
;

%%

Bison 告诉我有 12 个移位/减少冲突,但如果我注释掉后缀递增和递减的行,它就可以正常工作。有谁知道如何解决这个冲突?在这一点上,我正在考虑迁移到 LL(k) 解析器生成器,这使它更容易,但 LALR 语法似乎总是更自然地编写。我也在考虑 GLR,但我不知道有什么好的 C/C++ GLR 解析器生成器。

【问题讨论】:

  • 如果反对者至少给出一个理由,我将不胜感激......

标签: parsing yacc bison shift-reduce-conflict lalr


【解决方案1】:

如果在选项部分指定%glr-parser,Bison/Yacc 可以生成 GLR 解析器。

【讨论】:

  • 哇!我不知道...我只是尝试了一下,但是我仍然遇到与原始语法的 shift-reduce 冲突。我猜 Bison 的 GLR 算法不能很好地与优先级配合,但这可能与 David Dolson 的回答结合使用。
  • 好的,我刚刚用大卫多尔森的方法试了一下,效果很好!
  • -1:使用 GLR 模式不是一个好的解决方案,因为您仍然需要以某种方式解决歧义,更糟糕的是,如果您不修复歧义,bison 会默默接受语法没有抱怨,而是给你运行时错误,所以当你没有解决问题时很容易认为你已经解决了问题。
【解决方案2】:

试试这个:

%token NUMBER ID INC DEC

%left       '+' '-'
%left       '*' '/'
%nonassoc   '++' '--'
%left       '('
%%

expr: NUMBER
|     ID
|     expr '+' expr
|     expr '-' expr
|     expr '*' expr
|     expr '/' expr
|     '++' expr 
|     '--' expr 
|     expr '++'
|     expr '--'
|     '(' expr ')'
;

%%

关键是将后缀运算符声明为非关联。否则你将能够

++var++--

还需要优先考虑括号以最小化移位/减少警告

【讨论】:

  • 实际上,因为对于正常的 C 行为,您希望 ++var++ 解析为 ++(var++) 并且不会作为错误被拒绝(后缀的优先级高于前缀),您希望 %right 而不是 @987654326 @
  • 另外,'(' 的优先级没有意义,因为这里没有涉及它的冲突。如果存在冲突(例如,如果您添加了 C 风格的函数调用或强制转换语法),您将需要它,但在这种情况下,您可能需要 %right 而不是 %left
【解决方案3】:

我喜欢定义更多的项目。你不应该需要 %left, %right, %prec 的东西。

simple_expr: NUMBER
 | INC simple_expr
 | DEC simple_expr
 | '(' expr ')'
;

term: simple_expr
 | term '*' simple_expr
 | term '/' simple_expr
;

expr: term
 | expr '+' term
 | expr '-' term
;

试试这种方法。

【讨论】:

  • 我以前尝试过这种方法,但我不喜欢它。当你有更复杂的表达式语法(比如 C++)时,如果你想修改它,就很难准确理解你必须做什么。 IMO,使用优先级要干净得多。
【解决方案4】:

这个基本问题是INCDEC 标记没有优先权,因此它不知道如何解决涉及INCDEC 前瞻的歧义。如果你添加

%right INC DEC

在优先级列表的末尾(你希望一元的优先级更高,后缀高于前缀),它会修复它,你甚至可以摆脱所有 PREINC/POSTINC 的东西,因为它是无关紧要。

【讨论】:

    【解决方案5】:

    preincrement 和 postincrement 运算符具有非关联性,因此在优先级部分和规则中使用 %prec 定义这些运算符的优先级较高

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-24
      • 1970-01-01
      相关资源
      最近更新 更多