如何修复yacc:“ 2班次/减少冲突”错误?

时间:2019-01-09 12:43:23

标签: yacc

几天前,我开始使用yacc / flex进行编程,但我不知道为什么我不能只在第31行做递归调用(s:SKIP {System.out.println(“ txt:” + $ 1 );} s)。递归调用使我无需使用括号即可保持数字自由文本。我在做什么错了?

%{
  import java.io.*;
%}

%token OPEN_PAREN;
%token CLOSE_PAREN;
%token <sval> SKIP;

%start s

%%

parens  : OPEN_PAREN s CLOSE_PAREN
        | OPEN_PAREN CLOSE_PAREN

exp     : parens

exps    : exp SKIP { System.out.println("S: "+$2); }
        | exp

s       : SKIP { System.out.println("txt: "+$1); } s
        | exps
        | s exps

%%

void yyerror(String s)
{
 System.out.println("err:"+s);
 System.out.println("   :"+yylval.sval);
}

static Yylex lexer;
int yylex()
{
 try {
  return lexer.yylex();
 }
 catch (IOException e) {
  System.err.println("IO error :"+e);
  return -1;
 }
}

public static void main(String args[])
{
 System.out.println("[Quit with CTRL-D]");
 Parser par = new Parser();
 lexer = new Yylex(new InputStreamReader(System.in), par);
 par.yyparse();
}

1 个答案:

答案 0 :(得分:0)

该摘录中只有一个移位/减少冲突,这是s中模棱两可的递归性的结果。由于它与中间规则操作无关,因此可以简化为以下内容:

s: BEFORE s
 | AFTER
 | s AFTER

这是BEFORE* AFTER+的语法。 (语法中的AFTER实际上是非终结符的事实也不会改变任何东西。)

这是模棱两可的,因为应用第一个和最后一个产生式的顺序不受语法的限制。即使在简单的输入中

BEFORE AFTER AFTER

有两个不同的解析:

      s                             s
   /     \                       /    \
  /       \                     /      \
BEFORE     s                   s      AFTER
  |      /   \               /   \      |
  |    AFTER   s         BEFORE   s     |
  |      |     |            |     |     |
BEFORE AFTER AFTER       BEFORE AFTER AFTER

对您而言,应用哪个解析可能都无关紧要(尽管实际上适用,因为它会影响操作的执行顺序)。但这对bison / yacc很重要,因为后者不知道命令的重要性。

要解决冲突,我们需要指定一个顺序。有两种可能性:

  1. 前缀前的后缀。这是编程语言中通常使用的顺序,其中后缀运算符(例如数组下标)比前缀运算符(例如一元减号)绑定更紧密,因此-a[1]-(a[1])相同,而不是{{ 1}}。在这种情况下,语法将如下所示:

    (-a)[1]
  2. 后缀之前的前缀。这样做的好处是,前缀部分中的操作先于后缀部分中的操作执行。 (但是请注意,s: t | BEFORE s t: AFTER | t AFTER 的右递归性意味着,如果其生产中有操作,则这些操作将从右到左执行。)

    t

    此替代方案的完全左递归实现:

    s: t
     | s AFTER
    t: AFTER
     | BEFORE t