Note [shift/reduce conflicts]
The 'happy' tool turns this grammar into an efficient parser that follows the
shift-reduce parsing model. There's a parse stack that contains items parsed so
far (both terminals and non-terminals). Every next token produced by the lexer
results in one of two actions:
SHIFT: push the token onto the parse stack
REDUCE: pop a few items off the parse stack and combine them
with a function (reduction rule)
However, sometimes it's unclear which of the two actions to take.
Consider this code example:
if x then y else f z
There are two ways to parse it:
(if x then y else f) z
if x then y else (f z)
How is this determined? At some point, the parser gets to the following state:
parse stack: 'if' exp 'then' exp 'else' "f"
next token: "z"
Scenario A (simplified):
1. REDUCE, parse stack: 'if' exp 'then' exp 'else' exp
next token: "z"
(Note that "f" reduced to exp here)
2. REDUCE, parse stack: exp
next token: "z"
3. SHIFT, parse stack: exp "z"
next token: ...
4. REDUCE, parse stack: exp
next token: ...
This way we get: (if x then y else f) z
Scenario B (simplified):
1. SHIFT, parse stack: 'if' exp 'then' exp 'else' "f" "z"
next token: ...
2. REDUCE, parse stack: 'if' exp 'then' exp 'else' exp
next token: ...
3. REDUCE, parse stack: exp
next token: ...
This way we get: if x then y else (f z)
The end result is determined by the chosen action. When Happy detects this, it
reports a shift/reduce conflict. At the top of the file, we have the following
directive:
%expect 0
It means that we expect no unresolved shift/reduce conflicts in this grammar.
If you modify the grammar and get shift/reduce conflicts, follow the steps
below to resolve them.
STEP ONE
is to figure out what causes the conflict.
That's where the -i flag comes in handy:
happy -agc --strict compiler/GHC/Parser.y -idetailed-info
By analysing the output of this command, in a new file `detailed-info`, you
can figure out which reduction rule causes the issue. At the top of the
generated report, you will see a line like this:
state 147 contains 67 shift/reduce conflicts.
Scroll down to section State 147 (in your case it could be a different
state). The start of the section lists the reduction rules that can fire
and shows their context:
exp10 -> fexp . (rule 492)
fexp -> fexp . aexp (rule 498)
fexp -> fexp . PREFIX_AT atype (rule 499)
And then, for every token, it tells you the parsing action:
']' reduce using rule 492
'::' reduce using rule 492
'(' shift, and enter state 178
QVARID shift, and enter state 44
DO shift, and enter state 182
...
But if you look closer, some of these tokens also have another parsing action
in parentheses:
QVARID shift, and enter state 44
(reduce using rule 492)
That's how you know rule 492 is causing trouble.
Scroll back to the top to see what this rule is:
Grammar
...
...
exp10 -> fexp (492)
optSemi -> ';' (493)
...
...
Hence the shift/reduce conflict is caused by this parser production:
exp10 :: { ECP }
: '-' fexp { ... }
| fexp { ... } -- problematic rule
STEP TWO
is to mark the problematic rule with the %shift pragma. This signals to
'happy' that any shift/reduce conflicts involving this rule must be resolved
in favor of a shift. There's currently no dedicated pragma to resolve in
favor of the reduce.
STEP THREE
is to add a dedicated Note for this specific conflict, as is done for all
other conflicts below. References 0
This Note does not link to any other.
Referenced by 0
Nothing in the tree points here.