bisonflex-lexeroperator-precedenceshift-reduce-conflict

Parsing a function call (e.g. `exp '(' exp ')'`) in Bison: results in shift/reduce errors (precedence issue)


I'm trying to parse a function call (currently just one argument, but I'll allow for several when I get it working).

Suppose exp is defined as

%left '+'
%precedence CALL
exp:
    exp '+' exp { ... }
|   exp '(' exp ')' %prec CALL { ... }
|   LITERAL { ... }
;

This creates an ambiguity. If I use -Wcounterexamples then it says that exp '+' exp · '(' exp ')' could be parsed in 2 ways, either shifting or reducing at the '('.

calc.y: warning: shift/reduce conflict on token '(' [-Wcounterexamples]
  Example: exp '+' exp • '(' exp ')'
  Shift derivation
    exp
    ↳ exp '+' exp
              ↳ exp • '(' exp ')'
  Example: exp '+' exp • '(' exp ')'
  Reduce derivation
    exp
    ↳ exp             '(' exp ')'
      ↳ exp '+' exp •

It appears that it doesn't know that a call should have a precedence that is higher or lower than +. What can I do to make sure that it knows that x+y(z) should be equivalent to x+(y(z)) and not (x+y)(z)?

Bison: strange shift-reduce conflict seems related.


Solution

  • You told bison the relative precedence of the production exp: exp '(' exp ')' and the lookahead symbol +. But you haven't told bison the relative precedence of the production exp: exp '+' exp and the lookahead symbol (. And that's why you have a conflict on the lookahead symbol '('

    Remember that precedence comparisons are always between a production -- which might be reduced -- and a lookahead symbol -- which might be shifted. I know that yacc/bison makes it look like the comparison is between two symbols, but that's an illusion. Each production is represented by a symbol -- by default the rightmost terminal -- but that's as far as it goes. This convention is intuitive for simple cases but otherwise confusing, IMHO.

    The simplest solution is to identify the precedence of the call production with the symbol ( instead of inventing a pseudotoken. That is, change the %prec declaration to %prec '(' and replace CALL with ( in your precedence rule.

    (In fact, you don't really need the %prec declaration because no conflict is possible with the + lookahead token.)