Every parser in one place — understand the trade-offs, grammar classes, and when to use which strategy.
Each parser was invented to solve the limitations of its predecessor. Hover each node to see what problem it solved and what limitation remains.
The top path (Recursive Descent → LL(1)) is Top-Down parsing. The bottom path (LR(0) → SLR → CLR → LALR) is Bottom-Up. Both share the same underlying goal: recognizing valid sentences of a context-free grammar.
Every attribute of every parsing strategy, side by side. Scroll horizontally if needed.
| Parser | Direction | Strategy | Lookahead | States | Grammar Class | Conflicts | Real-World Use |
|---|---|---|---|---|---|---|---|
| Recursive Descent | Top-Down | Recursive functions | 1+ token (manual) | N/A | LL(k) | Manual backtracking | GCC, Java, Rust, Clang (hand-written) |
| LL(1) | Top-Down | Table-driven, predict | 1 token (FIRST/FOLLOW) | N/A | LL(1) | Table cell conflicts | ANTLR (LL mode), educational tools |
| LR(0) | Bottom-Up | Shift / Reduce | 0 (none) | n states | LR(0) | Many S/R & R/R | Academic only, very limited use |
| SLR | Bottom-Up | Shift / Reduce + FOLLOW | FOLLOW(A) | n states | SLR(1) | Fewer conflicts | Some simple tools, academic |
| CLR (LR(1)) | Bottom-Up | Shift / Reduce + exact LA | Exact per item | n × k states | LR(1) | Almost none | Rarely used alone (too many states) |
| LALR | Bottom-Up | Merged CLR states | Merged CLR lookaheads | n states | LALR(1) | Rare R/R | yacc, bison, GCC (older), Ruby, Perl |
SLR and LALR have the same number of states as LR(0) — they share the same DFA core. CLR can have exponentially more states. LALR gives LR(1) parsing power with LR(0) state count, which is why it dominates real-world tools.
Every grammar class is a subset of the next. A grammar handled by a simpler parser is also handled by all more powerful parsers above it.
The outermost ring. Context-free grammars include ambiguous grammars, which no deterministic parser can handle without disambiguation rules.
Any unambiguous deterministic CFL can be recognized. CLR handles the most grammars but has the most states.
Slightly smaller than LR(1). Almost all programming language grammars fit here. yacc/bison target this class.
SLR is strictly a subset of LALR. The canonical example S→L=R|R, L→*R|id fails SLR but works in LALR.
LL(1) ⊂ LALR(1). Every LL(1) grammar is also LALR(1). But LALR(1) can handle left-recursive grammars (like E→E+T) that LL(1) cannot. They overlap but are distinct families.
Understanding when and why conflicts arise — and how each parser generation reduces them.
State I2 contains [S→L•=R] (shift on =) and [R→L•] (reduce). Since = ∈ FOLLOW(R) = {=, $}, SLR says reduce R→L on '='. But we also need to shift '='. Shift/reduce conflict!
CLR tracks exact lookaheads. [R→L•, {$}] only reduces on $, not on '='. [S→L•=R, {$}] shifts on '='. The lookaheads don't overlap — no conflict!
Which parsing strategy do major compilers and tools actually use in production?
Despite LALR being theoretically powerful, hand-written recursive descent parsers offer better error messages, easier IDE integration, simpler debugging, and the ability to handle context-sensitive constructs (like C++ template ambiguity) that are impossible to express in a pure CFG.
Follow this decision tree to choose the right parser for your grammar.
For a course project or tool: LALR (use bison/yacc) handles nearly all well-designed languages. For a hand-written parser: Recursive Descent is easiest to write, debug, and extend.
The essential formulas and rules for every parsing technique — your exam cheat-sheet.
Parsing table size: |NT| × |T| entries. One table, linear parsing O(n). Table construction O(|G|).
Same DFA: n states. Action table: n × |T|. Goto table: n × |NT|. Parsing: O(n).
Up to n×k states where k = number of terminals. In practice: can be 3–10× more states than LR(0).
Same n states as LR(0). Merges CLR states by core, retaining LR(1) lookahead information. Best of both worlds.