Yasir Explains/Compiler Design/Parsing/LALR Parse Tables
Parsing

LALR Parse Tables

On this page

What is LALR(1)?The Merging AlgorithmRunning Grammar (Same as SLR / CLR)Step 1 — Find Mergeable GroupsStep 2 — Union the LookaheadsStep 3 — Redirect TransitionsStep 4 — Fill the LALR(1) Parse TableReduce/Reduce Conflicts Unique to LALRWhy LALR(1) is the Industry Standard
Parsing

LALR Parse Tables

Build an LALR(1) parse table by merging compatible CLR(1) states — keeping nearly all of CLR's power while shrinking the table back to SLR's size. The standard used by yacc, bison, and most production compilers.

What is LALR(1)?

LALR(1) stands for Look-Ahead LR with 1 token of lookahead. It is the engineering compromise between SLR(1) and CLR(1) — and is the parser-construction technique used by virtually every production parser generator: yacc, bison, GNU Bison, Java CUP, Menhir.

Example
Power: LALR(1) ≈ CLR(1) ← nearly as strong as full LR(1)
Table size: LALR(1) ≈ SLR(1) ← about the same as the simplest variant

For a typical real-world grammar, LALR's parse table can be 5–10× smaller than CLR's, while still handling almost every grammar CLR handles. That's why bison's default is LALR and not CLR.


The Key Insight

Look back at the CLR(1) construction. Several states had identical LR(0) cores — the same items, ignoring lookaheads — and differed only in their lookahead sets.

Example
I₃ : [C → c · C , c/d] I₆ : [C → c · C , $]
[C → · c C , c/d] [C → · c C , $]
[C → · d , c/d] [C → · d , $]
LR(0) core (strip lookaheads): identical.

These pairs were split apart by CLR purely to keep their lookaheads separate. LALR merges them back together, uniting their lookahead sets:

Example
I₃₆ : [C → c · C , c/d/$ ]
[C → · c C , c/d/$ ]
[C → · d , c/d/$ ]

The result has the same number of states as LR(0)/SLR, but each reduce item carries its own lookahead — almost the same precision as CLR.

The Merging Algorithm

The simplest way to build an LALR(1) table is in two steps: build CLR(1) first, then merge.


Step-by-Step

Example
1. Build the full CLR(1) DFA.
2. Group the LR(1) states by their LR(0) core
- two states belong to the same group iff
stripping the lookaheads gives identical item sets.
3. For each group:
- merge all members into one new state
- the merged state has the same items as any one member
(same LR(0) core)
- each item's lookahead is the union of the corresponding
lookaheads across the group
4. Adjust GOTO transitions
- every transition that targeted a member of a group
now targets the merged state.
5. Fill ACTION and GOTO using the merged states
and the CLR(1) reduce rule.

Why the LR(0) Core Matters

If two CLR(1) states have the same LR(0) core, they react to inputs in exactly the same way:

Example
GOTO(I₃, c) = some state with LR(0) core { C → c · C, … }
GOTO(I₆, c) = some state with LR(0) core { C → c · C, … }

Both transitions go to states with the same core — merging the targets is consistent with merging the sources. The LR(0) DFA structure is preserved.


What Merging Costs

Most of the time: nothing. The merged lookahead set is just the union, and reductions fire correctly.

But occasionally, merging creates a problem:

Example
Before merge:
state Iₐ : [A → α · , x] reduces A→α on x
state I_b : [B → β · , x] reduces B→β on x
After merge (same LR(0) core):
merged : [A → α · , x]
[B → β · , x] ← two reductions on the same lookahead!

That is a reduce/reduce conflict introduced by the merge. The grammar is CLR(1) but not LALR(1). These cases are rare — yacc and bison print them as errors when they occur.


What Merging Does NOT Cause

Merging never introduces shift/reduce conflicts. A shift/reduce conflict requires a terminal transition out of the state on the same symbol that triggers the reduction — and the LR(0) transitions are identical between the merged states, so a shift/reduce conflict in the merged state must have existed in every original state too. If CLR was conflict-free, LALR cannot break that by merging.

Running Grammar (Same as SLR / CLR)

We continue with the same grammar so the three constructions can be compared.

Example
(0) S' → S
(1) S → C C
(2) C → c C
(3) C → d

From the CLR topic, the full LR(1) DFA had 10 states:

Example
I₀ [S'→·S,$] [S→·CC,$] [C→·cC,c/d] [C→·d,c/d]
I₁ [S'→S·,$]
I₂ [S→C·C,$] [C→·cC,$] [C→·d,$]
I₃ [C→c·C,c/d] [C→·cC,c/d] [C→·d,c/d]
I₄ [C→d·,c/d]
I₅ [S→CC·,$]
I₆ [C→c·C,$] [C→·cC,$] [C→·d,$]
I₇ [C→d·,$]
I₈ [C→cC·,c/d]
I₉ [C→cC·,$]

We will now collapse it to 7 states — the same count as SLR.

Step 1 — Find Mergeable Groups

Strip each state's lookaheads and look at its LR(0) core. Group states whose cores are identical.

Example
LR(0) core States with this core
──────────────────────────────────────────────────────────────────
{ S'→·S, S→·CC, C→·cC, C→·d } I₀ only
{ S'→S· } I₁ only
{ S→C·C, C→·cC, C→·d } I₂ only
{ C→c·C, C→·cC, C→·d } I₃ , I₆ ← merge!
{ C→d· } I₄ , I₇ ← merge!
{ S→CC· } I₅ only
{ C→cC· } I₈ , I₉ ← merge!

Three pairs merge, four states stay as they are. The LALR(1) DFA will have 7 states.


Naming the Merged States

A common naming convention is to combine the original numbers:

Example
I₃ + I₆ → I₃₆
I₄ + I₇ → I₄₇
I₈ + I₉ → I₈₉

We'll use these names throughout.

Step 2 — Union the Lookaheads

For each group, the merged state's items are the LR(0) core items, with lookaheads taken as the union of the corresponding lookaheads in the originals.


Merging I₃ and I₆ → I₃₆

Example
I₃ items: I₆ items:
[C → c · C , c/d] [C → c · C , $]
[C → · c C , c/d] [C → · c C , $]
[C → · d , c/d] [C → · d , $]
Merged I₃₆:
[C → c · C , c/d/$]
[C → · c C , c/d/$]
[C → · d , c/d/$]

Merging I₄ and I₇ → I₄₇

Example
I₄ : [C → d · , c/d]
I₇ : [C → d · , $]
Merged I₄₇:
[C → d · , c/d/$]

Merging I₈ and I₉ → I₈₉

Example
I₈ : [C → c C · , c/d]
I₉ : [C → c C · , $]
Merged I₈₉:
[C → c C · , c/d/$]

Result — All 7 LALR(1) States

Example
I₀ [S' → · S , $] [S → · CC , $] [C → · cC , c/d] [C → · d , c/d]
I₁ [S' → S · , $]
I₂ [S → C · C , $] [C → · cC , $] [C → · d , $]
I₃₆ [C → c · C , c/d/$] [C → · cC , c/d/$] [C → · d , c/d/$]
I₄₇ [C → d · , c/d/$]
I₅ [S → CC · , $]
I₈₉ [C → cC · , c/d/$]

Step 3 — Redirect Transitions

When we merge states, the GOTO transitions targeting the original states must be redirected to the merged ones.


Updated Transition Table

Example
From → On → To (LALR)
─────────────────────────
I₀ S I₁
I₀ C I₂
I₀ c I₃₆ (was I₃)
I₀ d I₄₇ (was I₄)
I₂ C I₅
I₂ c I₃₆ (was I₆ — merged with I₃)
I₂ d I₄₇ (was I₇ — merged with I₄)
I₃₆ C I₈₉ (was I₈ from I₃, or I₉ from I₆)
I₃₆ c I₃₆ (self-loop)
I₃₆ d I₄₇
I₁, I₄₇, I₅, I₈₉ — no outgoing transitions

Both I₂ — c → ? and I₃ — c → ? now point to the same merged state I₃₆. Same for the other merged pairs.


Visual Diagram of the Merged DFA

The merged LALR(1) DFA has 7 states — the SLR count — but each item keeps a unioned lookahead set. Compare this with the 10-state CLR diagram: the three split pairs have collapsed into I₃₆, I₄₇, and I₈₉.

How to read it:

  • Blue — the start state (I₀).
  • Cyan — the accept state (I₁, on lookahead $).
  • Amber — reduce states (I₄₇, I₅, I₈₉), each now reducing on its merged lookahead set.
  • Arrow labels — the grammar symbol that triggers the transition.

Both I₀ and I₂ now transition to the single merged state I₃₆ on c (and to I₄₇ on d) — the merge preserved the LR(0) transition structure exactly, so the diagram has the same shape as the SLR DFA, just with richer lookaheads inside each node.

Step 4 — Fill the LALR(1) Parse Table

With the merged DFA in hand, the LALR(1) table is built using the same rule as CLR(1) — each item's lookahead set drives the reduce entries.


Reduce Entries

Example
I₄₇ : [C → d · , c/d/$]
⇒ ACTION[4-7][c] = r3
ACTION[4-7][d] = r3
ACTION[4-7][$] = r3
I₅ : [S → CC · , $]
⇒ ACTION[5][$] = r1
I₈₉ : [C → c C · , c/d/$]
⇒ ACTION[8-9][c] = r2
ACTION[8-9][d] = r2
ACTION[8-9][$] = r2

Accept Entry

Example
I₁ contains [S' → S · , $] ⇒ ACTION[1][$] = accept

Shift and GOTO Entries

Straight from the redirected transitions.


The Complete LALR(1) Parse Table

Example
ACTION GOTO
State | c d $ | S C
────────┼─────────────────────────────────────────────
0 | s36 s47 | 1 2
1 | acc |
2 | s36 s47 | 5
3-6 | s36 s47 | 8-9
4-7 | r3 r3 r3 |
5 | r1 |
8-9 | r2 r2 r2 |

Every cell has at most one entry — the grammar is LALR(1).


Compare Side by Side

AspectSLR(1)LALR(1)CLR(1)
States7710
Reduce in "C → d" statesr3 on c, d, $ (all of FOLLOW(C))r3 on c, d, $ (merged lookaheads)r3 split: c/d in I₄, $ in I₇
PowerSome grammars failAlmost all CFGsAll LR(1) grammars
Used byTeachingyacc, bison, production toolsTheory, rarely production

For this particular grammar, the LALR(1) and SLR(1) tables happen to coincide — both grammars accept on the same lookaheads. The difference shows up on harder grammars (like the L = R example) where SLR fails and LALR succeeds.

Reduce/Reduce Conflicts Unique to LALR

Almost every grammar that is CLR(1) is also LALR(1) — but almost is not "always." When merging combines states whose distinct lookaheads happen to clash, a new reduce/reduce conflict can appear.


A Minimal Example

Example
S' → S
S → a A d | b B d | a B e | b A e
A → c
B → c

After processing a c we are in a state containing [A → c · , d] and [B → c · , e] — no conflict, two different lookaheads.

After processing b c we are in a (different) state containing [B → c · , d] and [A → c · , e] — also no conflict.

But both states share the same LR(0) core {A → c ·, B → c ·}, so LALR merges them:

Example
Merged state:
[A → c · , d/e]
[B → c · , d/e]
ACTION on lookahead d : reduce A → c OR reduce B → c ← CONFLICT!
ACTION on lookahead e : reduce A → c OR reduce B → c ← CONFLICT!

The grammar is CLR(1) (no conflict in 10 separate states), but not LALR(1) (conflicts after merging).


How Bison Handles It

When bison detects this pattern it reports:

Example
warning: reduce/reduce conflict on tokens ...

The default resolution is to prefer the production listed first in the grammar file. The fix is usually to rewrite the grammar so the two non-terminals are merged or distinguished earlier:

Example
S → a X d | b X e ← X stands for "either A or B"
X → c (semantic action picks A or B later)

Frequency

In practice, LALR-only conflicts are rare. The grammars of C, Java, Pascal, SQL, JavaScript, PHP, Ruby, and Python are all expressible in LALR(1) (sometimes with a small number of bison %prec directives to nudge the resolver). The LALR(1) sweet spot is no accident — it covers nearly everything you would want to parse.

Why LALR(1) is the Industry Standard

LALR(1) wins by dominating the trade-off space.


The Three-Way Comparison

Example
┌────────────┬───────────────┬─────────────┐
│ SLR(1) │ LALR(1) │ CLR(1) │
─────────────┼────────────┼───────────────┼─────────────┤
power │ weak │ nearly full │ full LR(1) │
table size │ small │ small │ large │
used by │ teaching │ yacc/bison │ theory │
└────────────┴───────────────┴─────────────┘
  • SLR(1) → small but not powerful enough for many real grammars.
  • CLR(1) → maximum power but tables grow huge.
  • LALR(1) → nearly the power of CLR with the size of SLR.

Real Tools That Use LALR(1)

ToolUsed by
yacc (Yet Another Compiler-Compiler)Original Unix; the prototype LALR generator
GNU BisonGCC, Bison itself, GAWK, Bash, PHP, Ruby, PostgreSQL, MySQL, …
Java CUPJava compiler tools
MenhirOCaml — modern LR(1) generator that uses LALR(1) optimizations
PLY (Python Lex-Yacc)Python LR(1) generator with LALR option

This is not a coincidence — the LALR(1) algorithm was designed in 1969 by Frank DeRemer explicitly to make LR parsing practical on the memory-limited machines of that era. The trade-off held up so well that fifty years later, it is still the default.


Practical Workflow

Example
1. Write the grammar in a .y file (bison syntax).
2. Run bison — it builds the LALR(1) tables for you.
3. Inspect any reported conflicts:
- shift/reduce → usually safe, handled by precedence directives.
- reduce/reduce → must be fixed by rewriting the grammar.
4. Link the generated parser against your lexer.

The whole point of LALR(1) is that you almost never have to think about it. The tool handles construction, conflict reporting, and table generation — you focus on grammar design and semantic actions.


Summary

Example
SLR builds simple tables via FOLLOW.
CLR builds precise tables via per-item lookaheads.
LALR builds CLR, then merges away the size — keeping (almost) all the precision.

If you ever build a programming language with bison or a similar tool, you are using LALR(1). The next topic — Shift-Reduce Parsing — shows the runtime engine that interprets these tables.