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 ?
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 ";"