Make parsing conflicts visible instead of theoretical. Understand why LR parsing tables fail and how parser generators resolve them.
Consider this ambiguous grammar:
For the input:
At some state, the parser sees E + E and the next token is +. It must decide:
+ onto the stack?E + E to E?Because the grammar does not define precedence.
id + id be grouped immediately? (Reduce)Consider this grammar where two non-terminals derive the same string:
For the input:
The parser reaches a state with id • and must reduce. But which rule produced it?
Because the grammar allows structural ambiguity.
The parser cannot decide whether to reduce using A → id or B → id because both perfectly match the stack.
Step through how conflicts arise in a state and how different parsers (LR0, SLR, CLR) handle them.
Has no lookahead. It blindly allows shifts and reduces simultaneously, causing many conflicts.
Adds FOLLOW sets to restrict reductions. Only reduce A → id if lookahead ∈ FOLLOW(A). Fixes many conflicts.
Uses full LR(1) lookahead per item (e.g., [A → id •, '+']). Each reduction is context-aware. Almost no conflicts.
Merges CLR states with the same LR(0) core. Keeps combined lookaheads. A practical compromise with very few conflicts.
A conflict is not randomness. It is two mathematically valid derivations competing for control.
Parsing is not guessing. It is rule enforcement under constraints.
The entire LR family exists to reduce that uncertainty step by step.