YACC:在语法中找到转移/减少冲突

时间:2014-11-26 10:46:28

标签: c yacc computation-theory

我正在阅读书籍theory of computation,第2章中有一种语言PL在YACC中实施。该计划非常基础。指定了语法规则,在运行程序后,它会检查给定文件是否具有指定语法的语法。书中给出了所有规则,我想实现它。

但是当我实现它时,我得到了shift / reduce冲突代码。我在网上搜索了错误,发现错误是指语法模糊。我试过找到它但是不能。 in here有一个类似的问题,一个用户指出它是一个警告,可以忽略,因为有些语言含糊不清。

问题:

  • 有人可以指出歧义可能在哪里吗?
  • 当我尝试运行如下的代码时,程序并不理解它。它给出了语法错误。虽然这应该根据我应用的语法规则被接受。我用错误的语法传递语法吗?

    while X = 10;
    X = Y + 10;
    end;
    

我的代码:

    %start program
    %%
    LETTER : 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I'
          | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R'
          | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' 
          ;

    DIGIT : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
         ;

    name : LETTER
         | name DIGIT
         | name LETTER
         ;


    numeral :   DIGIT 
        |   numeral DIGIT
        ;


    operation   :   '+'
                |   '-'
                |   '*'
                |   '/'
                |   '='
                |   '<'
                |   '>'
                |   '>' '='
                |   '<' '='
                ;

    expression  :   name 
            |   numeral
            |   '(' '!' expression ')'
            |   '(' expression operation expression ')'
            ;

    assignment : name '<' '-' expression
               ;

    instruction : assignment
                | 'g' 'o' 't' 'o' name
                | 's' 't' 'o' 'p'
                ;


    labelinstr : name ':' instruction ';'
               | instruction ';'
               ;

    loop : 'l' 'o' 'o' 'p' expression ';'
         |  name ':' 'l' 'o' 'o' 'p' expression ';'
         ;

    ifthen  :   'i' 'f' expression 't' 'h' 'e' 'n' ';'
            |   name ':' 'i' 'f' expression 't' 'h' 'e' 'n' ';'
            ;
    while   :   'w' 'h' 'i' 'l' 'e' ';'
            |   name ':' 'w' 'h' 'i' 'l' 'e' expression ';'
            ;


    end : 'e' 'n' 'd' ';'
        | name ':' 'e' 'n' 'd' ';'
        ;

    program : labelinstr
            | loop program end
            | while program end
            | ifthen program end
            | ifthen program end 'e' 'l' 's' 'e' ';' program end
            | program program
            ;





    %%
    #include <stdio.h>

    yylex() {
      int c;
      while ( (c=getchar()) == ' ' || c == '\n' || c == '\t') {
         printf("%c",c);}
      printf("%c",c);
      return(c);
     }

2 个答案:

答案 0 :(得分:3)

识别转换/减少冲突的第一步是使用-v标志bison并检查生成的文件中的状态机,该文件具有足够的.output。这将告诉您哪些状态显示错误以及哪些规则导致该状态。例如,对于您的程序,我们看到两个状态有shift / reduce冲突,状态65和状态84。

州84相对简单:

State 84

   72 program: ifthen program end .
   73        | ifthen program end . 'e' 'l' 's' 'e' ';' program end

    'e'  shift, and go to state 101

    'e'       [reduce using rule 72 (program)]
    $default  reduce using rule 72 (program)

这类似于经典的“悬空”问题。通常使用像end;这样的语句终止符可以解决这个问题,但是你提出的语法很奇怪,即使在end;子句的情况下,它仍然存在else。所以

if (a > 3) then a <- 3; else a <- 2; end,

无效。相反,语法坚持

if (a > 3) then a <- 3; end; else a <- 2; end;

这无助于解决悬空的其他问题,因为end不区分带有和不带else子句的语句,因此以下内容仍然含糊不清:

if (a > 3) then if (a < 7) then a <- 3; end; else a <- 7; end;

语法似乎不太可能正确。我怀疑if制作应该是:

        | ifthen program end
        | ifthen program 'e' 'l' 's' 'e' ';' program end

另一个问题是状态65 :(这里,我省略了转换)

State 65

   74 program: program . program
   74        | program program .

这显然是模棱两可的。假设你有:

statement statement statement

这可以解析为从左到右或从右到左的结合:

[program: [program: statement] [program: [program: statement] [program: statement]]] 
[program: [program: [program: statement] [program: statement]] [program: statement]] 

粗略地说,解决方案通常类似于:

statement: if_statement
         | loop_statement
         | ...

program: statement
       | program statement

虽然就个人而言,我可能会考虑标签。

答案 1 :(得分:1)

歧义问题与语法错误无关。考虑一下:

 while   :   'w' 'h' 'i' 'l' 'e' ';'
         |   name ':' 'w' 'h' 'i' 'l' 'e' expression ';'
         ;

其中一个替代方案缺少某些东西。你想在贴上标签的时候循环,而在没有标签时什么都没有?

 while   :   'w' 'h' 'i' 'l' 'e' expression ';'
         |   name ':' 'w' 'h' 'i' 'l' 'e' expression ';'
         ;

除此之外:你应该把标签分解出去。您已经有了“标签声明”制作。使用它。

 while X = 10;

表达式可以是简单的名称或数字,也可以是括号,因此X = 10本身无效。

 while (X = 10) ;

这不是作业:

 X = Y + 10;

这是:

 X <- (Y + 10) ;

通过这些修复,不再存在语法错误(仍存在冲突,但它们无关)。