LR(0) and SLR(1) construction
LR(0) items, the augmented grammar, closure and goto, the canonical collection and its automaton of viable prefixes, the LR(0) table and its conflicts, and the SLR(1) table that reduces only on FOLLOW; with state counting and the standard grammar that is not SLR(1).
Why it exists
An LR parser needs a table. The table is built from a finite automaton that recognises exactly the viable prefixes of the grammar, the strings that can appear on the stack. The automaton's states are sets of items that record how much of each production has been seen. From the automaton you read off shifts (transitions on terminals), GOTO entries (transitions on nonterminals) and, where an item is complete, the reductions. LR(0) and SLR(1) are the two simplest ways to choose when to reduce.
Prerequisites
You need the LR driver and the shape of the ACTION and GOTO tables (LR parsing) and FOLLOW sets (FIRST and FOLLOW).
Precise definitions
LR(0) item. A production with a dot somewhere in its right side: . The dot marks how much of the right side the parser has already seen on the stack. The production gives four items: , , , . The production has a single item, . A production with symbols on the right gives items.
Augmented grammar. Add a new start symbol and the production . When the parser is about to reduce by this production (item with the input at $) it accepts. The augmentation guarantees a single accepting point.
Closure. For a set of items , is built by: put every item of in it; repeat: if is in the set and is a production, add ; until nothing is added. Meaning: if the parser expects to see a next, it might be at the start of any production of .
Kernel and non-kernel items. The kernel of a state consists of its starting item (in state 0) and every item whose dot is not at the left end; the other items (dot at the left end) come from the closure.
Goto. is the closure of the set of all items such that is in : move the dot over and close.
Canonical collection of LR(0) item sets. Start with and repeatedly add for every state and every symbol that follows a dot in , until nothing new appears. The sets are the states; the gotos are the transitions. This is a DFA that recognises the viable prefixes. (State numbers depend on the order of construction; questions give or ask for the number of states, which does not.)
LR(0) parsing table. For each state :
- if is in ( a terminal) and : ACTION[, ] = shift ;
- if is in and : ACTION[, ] = reduce for every terminal (and
$); - if is in : ACTION[,
$] = accept; - if for a nonterminal : GOTO[, ] = .
If a cell receives two entries the grammar is not LR(0): a shift-reduce conflict (a state with a complete item and a shift item) or a reduce-reduce conflict (two complete items). A grammar is LR(0) if the construction has no conflict. A state that contains a complete item () must contain no other complete item and no shift item, so LR(0) grammars are rare; is one.
SLR(1) table. The same item sets, but a complete item gives a reduce only for the terminals in . Rows and shifts are as for LR(0). A grammar is SLR(1) if the resulting table has no conflict. Equivalently, for every state: for a complete item and any shift item , ; and two complete items , have disjoint FOLLOW sets.
The weakness of SLR: FOLLOW() takes the follow set of over the whole grammar, but in a particular state fewer tokens can really follow. The next page fixes this with lookaheads attached to items.
Counting. The number of LR(0) items of a grammar (augmented) is the sum over all productions of (length of the right side + 1). The number of states of the automaton is what is asked more often; it needs the construction.
How it works
The first animation builds the LR(0) collection for the expression grammar, one goto at a time. Each row is a state with its items (complete closure) and its outgoing transitions. Step through it: whenever a goto creates an existing set, no new state appears (for example state 4 on ( leads back to state 4).
LR(0) item sets of the expression grammar: start
Computed by a script, not typed- 1E → E + T
- 2E → T
- 3T → T * F
- 4T → F
- 5F → ( E )
- 6F → id
| state | items | transitions |
|---|---|---|
| I0 | E' → · E E → · E + T E → · T T → · T * F T → · F F → · ( E ) F → · id | |
| I1 | ||
| I2 | ||
| I3 | ||
| I4 | ||
| I5 | ||
| I6 | ||
| I7 | ||
| I8 | ||
| I9 | ||
| I10 | ||
| I11 |
state 0 = closure
Step 0
Augment the grammar with E' → E so that there is a single accepting item. State 0 is the closure of E' → · E: the dot is at the start, and wherever the dot is before a nonterminal, add that nonterminal's productions with the dot at the beginning (repeat). A transition goto(I, X) moves the dot over X in every item of I that has X after the dot, then takes the closure.
Now the SLR(1) table for this grammar, filled state by state. In state 2 the items are and : the shift on * and the reduce for would collide in LR(0) (a reduce on every terminal), but in SLR(1) the reduce is only on FOLLOW() = {+, ), $}, which does not contain *: no conflict.
Building the SLR(1) table of the expression grammar: start
Computed by a script, not typed- 1E → E + T
- 2E → T
- 3T → T * F
- 4T → F
- 5F → ( E )
- 6F → id
| state | + | * | ( | ) | id | $ | E | T | F |
|---|---|---|---|---|---|---|---|---|---|
| 0 | |||||||||
| 1 | |||||||||
| 2 | |||||||||
| 3 | |||||||||
| 4 | |||||||||
| 5 | |||||||||
| 6 | |||||||||
| 7 | |||||||||
| 8 | |||||||||
| 9 | |||||||||
| 10 | |||||||||
| 11 |
Step 0
Fill the table state by state. For an item A → α · a β (a terminal) enter shift to goto(I, a); for an item A → α · B β enter goto(I, B) in the GOTO part; for a complete item A → α · reduce on every terminal in FOLLOW of the left side; for E' → E · enter accept on $. A cell that gets two entries is a conflict.
The standard grammar that is not SLR(1): , , (assignments with dereference). State 2 contains and . The shift on = conflicts with the reduction , because = is in FOLLOW() (since can be followed by whatever follows , and is followed by = in ). In the real context, though, an at the start of a statement is followed only by $, so the reduction would never be right on =: the conflict is spurious, caused by the coarse FOLLOW set.
The grammar S → L = R | R is not SLR(1): start
Computed by a script, not typed- 1S → L = R
- 2S → R
- 3L → * R
- 4L → id
- 5R → L
| state | = | * | id | $ | S | L | R |
|---|---|---|---|---|---|---|---|
| 0 | |||||||
| 1 | |||||||
| 2 | |||||||
| 3 | |||||||
| 4 | |||||||
| 5 | |||||||
| 6 | |||||||
| 7 | |||||||
| 8 | |||||||
| 9 |
Step 0
Fill the table state by state. For an item A → α · a β (a terminal) enter shift to goto(I, a); for an item A → α · B β enter goto(I, B) in the GOTO part; for a complete item A → α · reduce on every terminal in FOLLOW of the left side; for S' → S · enter accept on $. A cell that gets two entries is a conflict.
Worked examples
EX 1Closure of the start item
EasyExpression grammar augmented with . : start with ; the dot is before , so add and ; the dot is before (in the second), so add and ; before : add and . Seven items (matching the first row of the animation). Adding needed nothing new: its dot is again before , already expanded.
EX 2Goto
Easy: items of with the dot before are and ; move the dot: and . The closure adds nothing (the dot is before , a terminal, or at the end). This is state 1. : from get ; the closure adds , , , : state 6. State 1 contains the complete item , so ACTION[1, $] = accept, and the item gives ACTION[1, +] = shift 6.
EX 3Counting items and states
MediumThe augmented expression grammar has productions with right sides of length 1 (), 3 (), 1, 3, 1, 3, 1: items . The LR(0) automaton has 12 states. For , : 10 items and 7 states. In SLR(1) and in LALR(1) the number of states is the same as in LR(0), since the same item sets are used.
EX 4LR(0) fails, SLR(1) works
MediumIn state 2 of the expression grammar the items are and . LR(0): ACTION[2, *] would hold both shift 7 (from the second item) and reduce (the first, reduce on every terminal): conflict (and the same in state 9 with ). SLR(1): the reduce applies only to FOLLOW() = {+, ), $}, which does not contain *: ACTION[2, *] = shift 7 alone. So the grammar is SLR(1) but not LR(0). A quick LR(0) test: any state that contains a complete item together with another item is a conflict.
EX 5Is S → L = R | R, L → * R | id, R → L SLR(1)?
GATE levelFOLLOW sets: FOLLOW() = {$}; FOLLOW() = {=, $} (from and from with FOLLOW()); FOLLOW() = {=, $} (from it gets FOLLOW()). State 2 = goto(, ) = {S → L · = R, R → L ·}. Shift on = (to the state with ) and reduce on FOLLOW() which contains =: both on =. Not SLR(1). The grammar is nevertheless unambiguous, and an LR(1) parser (and also an LALR(1) parser) accepts it: the real lookahead of in this state is only $. The tables built by script show exactly one conflicting cell for SLR(1) and none for LALR(1).
Cheat box
Cheat box
- Item = production with a dot; -symbol right side gives items; gives one.
- Augment with . Closure adds when the dot is before . Goto moves the dot over a symbol and closes.
- States = canonical collection of LR(0) item sets (DFA of viable prefixes).
- LR(0): reduce on every terminal; conflict if a state has a complete item and anything else.
- SLR(1): reduce only on FOLLOW of the left side. Same states as LR(0).
- Not SLR(1): , , .
- Shift entries come from terminal transitions; GOTO from nonterminal transitions; accept from .
Traps and GATE patterns
Connections
Across subjects
No cross-subject links yet.
The driver is the one of LR parsing; the sets come from FIRST and FOLLOW. The weakness of SLR is repaired by the lookahead items of CLR(1) and LALR(1). All four methods are compared with LL(1) in the parser hierarchy. The viable-prefix automaton is a DFA in the sense of DFA, built here from items instead of from regular expressions.
PYQs
6 previous year questions from your book. I re-solved each one myself (5 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider the canonical LR(0) parsing of the
grammar below using terminals {a, b, c} and non-
terminals {A, B, C, S} with S as the start symbol.
S → ACB
A → aA | ε
C → cC | ε
B → bB | b
Which one of the following options gives the
number of shift-reduce conflicts that will occur in
the LR(0) ACTION table?- (a)2
- (b)3
- (c)4
- (d)5
Consider the augmented grammar with { +, *, (, ), id
} as the set of terminals.
S' →S
S→S+ R|R
R→R*P|P
P→(S) |id
If
I0
is
the
set
of
two
LR(0)
items
{[S'→S.],[S→S.+R]} then goto(closure (I0 ),+)
contains exactly _____ items.Consider the following statements. S1: Every SLR(1) grammar is unambiguous but there are certain unambiguous grammars that are not SLR(1). S2 : For any context-free grammar, there is a parser that takes at most O(n3 ) time to parse a string of length n. Which one of the following options is correct?
- (a)S1 is true and S2 is false
- (b)S1 is false and S2 is false
- (c)S1 is false and S2 is true
- (d)S1 is true and S2 is true
Consider the augmented grammar given below:
S' → S
S → 〈L〉 | id
L → L, S | S
Let I0 = CLOSURE ({[S' → ·S]}). The number of
items in the set GOTO (I0 , 〈 ) is: _____.A canonical set of items is given below: S → L. > R Q → R. On input symbol < the set has:-
- (a)a shift-reduce conflict and a reduce-reduce conflict.
- (b)a shift-reduce conflict but not a reduce-reduce conflict.
- (c)a reduce-reduce conflict but not a shift-reduce conflict.
- (d)neither a shift-reduce nor a reduce-reduce conflict. COMPILER DESIGN
Consider the grammar
S → (S) | a
Let the number of states in SLR(1), LR(1) and
LALR(1) parsers for the grammar be n1, n2 and n3
respectively. The following relationship holds good- (a)n1 < n2 < n3
- (b)n1 = n3 < n2
- (c)n1 = n2 = n3
- (d)n1 ≥ n3 ≥ n2
Self-check
Question 1
How many LR(0) items does have after augmentation?
Show answerHide
: 2; : 4; : 1. Total 7.
Question 2
State contains and . When is the conflict absent in SLR(1)?
Show answerHide
When .
Question 3
How many states does the LR(0) automaton of , have, and how many does the LALR(1) automaton have?
Show answerHide
Seven in both cases.
Question 4
Why is , , not SLR(1) though it is unambiguous?
Show answerHide
In the state with and the symbol = is both a shift and, because = is in FOLLOW(), a reduce. FOLLOW is too coarse; the true lookahead of this reduction is $.
Revision card
One-screen revision
- Item: production with a dot; symbols give items. Augment with .
- Closure adds productions of the nonterminal after the dot; goto moves the dot and closes; states = canonical collection.
- LR(0): reduce on all terminals; SLR(1): reduce on FOLLOW(left side). Same states.
- Conflicts: shift-reduce (complete item + shift item), reduce-reduce (two complete items).
- grammar: not SLR(1) (conflict on
=) but LALR(1).
References
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (2nd edition), Chapter 4 Syntax Analysis (4.6.2 items and the LR(0) automaton, 4.6.3 the LR-parsing algorithm, 4.6.4 constructing SLR-parsing tables)
- ·Cooper and Torczon, Engineering a Compiler, Chapter 3 Parsers (building the LR(1) tables; LR(0) and SLR as special cases)
Verification
The item sets, transitions and tables are built by script (scripts/cd_lib.py, class LR) and compared with a second, differently written implementation of the LR(0) collection on eight grammars (scripts/enum-checks/cd_lr.py); the state numbering of the expression grammar equals that of the Dragon book's figure. Numbers quoted (12 states, 20 items, the single SLR conflict of S -> L = R | R, 7 states for S -> C C) are produced by the same code. The statement that the conflict of S -> L = R | R disappears in LALR(1) was computed, not taken from a book. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (token counts, FIRST and FOLLOW sets, LL(1) tables and moves, LR item sets, state counts and parses, operator-precedence values, attribute evaluation and printing schemes, activation-record counts and access links, parameter-passing results, three-address code, jumping and backpatched code, leaders and blocks, value numbering and DAGs, gen/kill and data flow sets, Sethi-Ullman labels and live ranges); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)