Search code examples
c++bisonbisonc++

Bison shift/reduce conflict in "else"


Consider the following grammar:

%start stmt;
%right "else";

stmt: "foo"
   | "if" "(" exp ")" stmt
   | "if" "(" exp ")" stmt "else" stmt 

exp: "foo2"

On running bison (with producing counter examples) I get:

parser.yy: warning: 1 shift/reduce conflict [-Wconflicts-sr]
parser.yy: warning: shift/reduce conflict on token "else" [-Wcounterexamples]
  Example: "if" "(" exp ")" "if" "(" exp ")" stmt • "else" stmt
  Shift derivation
    stmt
    ↳ 2: "if" "(" exp ")" stmt
                          ↳ 3: "if" "(" exp ")" stmt • "else" stmt
  Reduce derivation
    stmt
    ↳ 3: "if" "(" exp ")" stmt                         "else" stmt
                          ↳ 2: "if" "(" exp ")" stmt •

This is somehow the shift/reduce represented in bison's manual here. But I don't have "then" after this grammar. The manual says that this problems can be solved defining left and right associativity and precedence. At above I've defined right associativity for "else" but it has no effect on this shift reduce problem.
I don't want to suppress the warning by shift/reduce counts since it is too dangerous because my grammar is too big.
What can I do ?


Solution

  • In your counterexample, you can see that bison does not know if it should shift or reduce the "if" "(" exp ")" that is the stmt after your first ")". In other words, if it should group your first if with the else:

    if(exp) (if(exp) stmt) else stmt
    

    or the second if with the else:

    if(exp) (if(exp) stmt else stmt)
    

    To solve this you could for example also give a precedence for the last terminal symbol of your if, e.g. %right "else" ")".
    Alternatively, you could also modify your grammar to have a statement-terminator symbol, e.g.:

    stmt: "foo"
       | "if" "(" exp ")" stmt ";"
       | "if" "(" exp ")" stmt "else" stmt ";"