0

当我尝试使用 $ bison --output=calcy.c -d calc.y 我得到这个消息: calc.y: 冲突:2 shift/reduce

这是我的代码

program:DEBUT
     corps   
      FIN 
  ;
corps:liste_declaration
      liste_instruction
  ;
liste_declaration:
| declaration";"liste_declaration
;
declaration: ENTIER IDENTIFICATEUR
;
liste_instruction:
|instruction";"liste_instruction
;
instruction: IDENTIFICATEUR"="expression
| DEBUT liste_instruction FIN
| SI expression 
ALORS instruction
SINON instruction
| SI expression ALORS instruction
| TANTQUE expression FAIRE instruction
| ECRIRE liste_argument
| LIRE IDENTIFICATEUR
;
liste_argument:
| expression 
| expression ";"liste_argument
;
expression:expression"<"expression_simple
|expression">"expression_simple
|expression"=="expression_simple
|expression"!="expression_simple
|expression"<="expression_simple
|expression">="expression_simple
|expression_simple
;
expression_simple:expression_simple"+"terme
|expression_simple"-"terme
|"("expression_simple")"
|terme
|"-"terme
;
terme:terme"*"facteur
|terme"/"facteur
|terme"^"facteur
|facteur
;
facteur:IDENTIFICATEUR|NOMBRE
;

我搜索了很多,我认为这是模棱两可的原因,但我找不到它在哪里等待你们的答案!

Edit1:我使用选项 -v 正如 Chris Dodd 先生提到的那样,我在我的 .output 文件中得到了这个

Grammar

    0 $accept: program $end

    1 program: DEBUT corps FIN

    2 corps: liste_declaration liste_instruction

    3 liste_declaration: /* empty */
    4                  | declaration ";" liste_declaration

    5 declaration: ENTIER IDENTIFICATEUR

    6 liste_instruction: /* empty */
    7                  | instruction ";" liste_instruction

    8 instruction: IDENTIFICATEUR "=" expression
    9            | DEBUT liste_instruction FIN
   10            | SI expression ALORS instruction SINON instruction
   11            | SI expression ALORS instruction
   12            | TANTQUE expression FAIRE instruction
   13            | ECRIRE liste_argument
   14            | LIRE IDENTIFICATEUR

   15 liste_argument: /* empty */
   16               | expression
   17               | expression ";" liste_argument

   18 expression: expression "<" expression_simple
   19           | expression ">" expression_simple
   20           | expression "==" expression_simple
   21           | expression "!=" expression_simple
   22           | expression "<=" expression_simple
   23           | expression ">=" expression_simple
   24           | expression_simple

   25 expression_simple: expression_simple "+" terme
   26                  | expression_simple "-" terme
   27                  | terme

   28 terme: terme "*" facteur
   29      | terme "/" facteur
   30      | facteur

   31 facteur: X "^" facteur
   32        | X

   33 X: "(" expression_simple ")"
   34  | "-" X
   35  | IDENTIFICATEUR
   36  | NOMBRE

这就是冲突的状态

state 33

   16 liste_argument: expression .
   17               | expression . ";" liste_argument
   18 expression: expression . "<" expression_simple
   19           | expression . ">" expression_simple
   20           | expression . "==" expression_simple
   21           | expression . "!=" expression_simple
   22           | expression . "<=" expression_simple
   23           | expression . ">=" expression_simple

    ";"   shift, and go to state 53
    "<"   shift, and go to state 40
    ">"   shift, and go to state 41
    "=="  shift, and go to state 42
    "!="  shift, and go to state 43
    "<="  shift, and go to state 44
    ">="  shift, and go to state 45

    ";"       [reduce using rule 16 (liste_argument)]
    $default  reduce using rule 16 (liste_argument)

state 56

   10 instruction: SI expression ALORS instruction . SINON instruction
   11            | SI expression ALORS instruction .

    SINON  shift, and go to state 70

    SINON     [reduce using rule 11 (instruction)]
    $default  reduce using rule 11 (instruction)

4

1 回答 1

0

您有两个班次减少冲突,它们的原因完全不同。只有一个严格来说是模棱两可的;具有讽刺意味的是,它更容易修复。

1. 歧义:悬空 else

一个是经典的“dangling-else”歧义(维基百科的法语页面建议可能被翻译为sinon pendant。这种歧义很容易修复或解决:

  • 添加%expect 1到您的野牛文件中。(Bison 允许您将此声明放在文件中的任何位置,因此将其放在定义之前或之后是合理的instruction。但在其他 yacc 方言中,您需要将其放在%%文件中的第一个之前。)

    所有这一切都是告诉解析器生成器在恰好存在一个移位减少冲突时抑制警告消息。冲突将使用默认解决方案来解决,即优先转移SINON令牌。碰巧,这正是您在这种情况下想要的,这就是为什么它是默认分辨率。

  • 使用优先声明使转移SINON比减少ALORS生产更可取。(作品以作品中的最后一个终端命名,而不是第一个终端。)

    这与第一个具有完全相同的效果,但以稍微更稳健的方式,因为它不依赖于移位减少冲突的总数。

  • 重写语法以SI ... ALORS ... SINON明确。

    这可以说是最好的解决方案,但它需要做更多的工作,因为它迫使您考虑所有可以以语句结尾的复合语句。在您的情况下,只有两个 - SI 和 TANTQUE - 但您可能希望稍后添加其他循环结构。您可以在 Wikipedia 页面中找到示例代码(目前相当混乱,因此它可能不是目前查看的最佳位置)或通过搜索“dangling else”并谨慎行事。

2. 几乎没有歧义:双重含义;

另一个 shift-reduce 冲突是由于您;同时使用来分隔语句和分隔语句中的参数ECRIRE。这意味着当解析器到达;while 分析ECRIRE语句时,它无法知道语句是否;终止。如果是语句的结尾,则解析器必须ECRIRE liste_argument归约为instruction; 否则,它需要移动;以将另一个添加expressionliste_argument.

严格来说,这并不是模棱两可的,因为您的语言要求所有语句都以关键字或 with 开头IDENTIFICATEUR "=",而这些都不能是表达式的开头。因此,解析器最多只需要查看三个符号(包括;),就可以确定接下来是表达式还是语句。

这个事实对你没有多大帮助,因为野牛解析器通常只会查看下一个符号,即;,并且必须根据它做出减少决定。因此,即使语法没有歧义——每个有效程序只能以一种方式解析——野牛解析器在它需要知道的那一刻也无法知道使用哪个解析。

可以解决这个问题,因为可以为任何可以用有界前瞻解析的语言编写单符号前瞻语法。但这很乏味,而且生成的语法又大又笨重,而且难以阅读。

此外,将来对您的语言的某些添加可能会使ECRIRE陈述实际上模棱两可。例如,您(当前)不允许函数调用。但是在某些时候,您可能希望在您的语言中添加函数,并且当您这样做时,您可能希望在不将函数返回值分配给任何变量的情况下调用函数。这通常只需添加expression到可能instruction的 s 列表中即可完成,但是一旦您这样做,分号 inECRIRE就会变得模棱两可,因为它后面可以跟要打印的表达式,或者要执行的表达式下一个声明。

如果您确定这永远不会成为问题,并且语法确实是明确的,就像现在一样,您可以通过告诉 bison 生成一个%glr-parser. 尽管这会使 bison 的工作复杂化,但它对您的影响并不大:您可以继续以相同的方式使用语法,只要它不模棱两可。

但是如果你认为你可能想在某个时候引入表达式语句,你最好改变你的语言的语法。如果您习惯于,将参数分开ECRIRE并保留;到单独的语句(这是一种非常常见的方法),那么两个定界符将不会相互干扰,并且语法将自动明确。

于 2021-02-02T22:36:08.300 回答