✏️ Chapter 12

Practice & Self-Test

Apply what you learned — work through each problem yourself before revealing the step-by-step solution.

📖

How to Use This Page

1. Read the problem — understand the grammar and what's being asked.

2. Try it yourself — pause and work through it on paper before looking at the answer.

3. Click "Reveal Solution" — the step-by-step answer will slide open.

4. Take the quiz — 5 interactive multiple-choice questions at the end.

Work-Through Problems

Five complete problems covering FIRST sets, FOLLOW sets, LL(1) tables, LR(0) items, and SLR parsing.

1

Computing FIRST Sets

Topic: FIRST set algorithm from scratch

FIRST

Given the following grammar G:

S → aAB | bC
A → aA | ε
B → bB | d
C → cC | ε
Task: Compute FIRST(S), FIRST(A), FIRST(B), and FIRST(C).

Hint: Start with terminal productions, then work through each nonterminal. Remember: ε propagates only if ALL symbols before the current position can derive ε.
✅ Step-by-Step Solution — FIRST Sets
1

FIRST of Terminals

By definition, FIRST of any terminal is itself: FIRST(a)={a}, FIRST(b)={b}, FIRST(c)={c}, FIRST(d)={d}

2

Compute FIRST(A)

A → aA: production starts with terminal 'a', so add a to FIRST(A).

A → ε: production is ε, so add ε to FIRST(A).

FIRST(A) = {a, ε}
3

Compute FIRST(B)

B → bB: starts with terminal 'b', add b.

B → d: terminal 'd', add d. Note: B cannot derive ε.

FIRST(B) = {b, d}
4

Compute FIRST(C)

C → cC: starts with terminal 'c', add c.

C → ε: add ε. So C can derive ε.

FIRST(C) = {c, ε}
5

Compute FIRST(S)

S → aAB: starts with terminal 'a', add a. (We don't look further because 'a' is a terminal.)

S → bC: starts with terminal 'b', add b. (C can derive ε, but S→bC starts with 'b' not C.)

Does S derive ε? No — both alternatives start with a terminal (a or b), so S→ε is impossible.

FIRST(S) = {a, b}
FINAL ANSWERS
FIRST(S)
{ a, b }
FIRST(A)
{ a, ε }
FIRST(B)
{ b, d }
FIRST(C)
{ c, ε }
2

Computing FOLLOW Sets

Topic: FOLLOW set algorithm using three rules

FOLLOW

Using the same grammar as Problem 1:

S → aAB | bC
A → aA | ε
B → bB | d
C → cC | ε
Task: Compute FOLLOW(S), FOLLOW(A), FOLLOW(B), and FOLLOW(C).

Three FOLLOW rules:
R1: $ ∈ FOLLOW(S) — where S is the start symbol.
R2: A → αBβ: FIRST(β)\{ε} ⊆ FOLLOW(B).
R3: A → αB or A → αBβ with ε ∈ FIRST(β): FOLLOW(A) ⊆ FOLLOW(B).
✅ Step-by-Step Solution — FOLLOW Sets
1

Rule 1: Start Symbol

S is the start symbol → $ ∈ FOLLOW(S)

FOLLOW(S) = { $ }
2

Compute FOLLOW(A)

From S → aAB: A is followed by B. Apply Rule 2: FIRST(B)\{ε} ⊆ FOLLOW(A).

FIRST(B) = {b, d}, no ε, so add {b, d} to FOLLOW(A).

From S → aAB: Can B derive ε? No (FIRST(B) has no ε). So Rule 3 doesn't apply here.

From A → aA: A is at the end of the production — Rule 3 applies: FOLLOW(A) ⊆ FOLLOW(A). (Circular, no new info.)

FOLLOW(A) = { b, d }
3

Compute FOLLOW(B)

From S → aAB: B is at the end of the production → Rule 3: FOLLOW(S) ⊆ FOLLOW(B). Add {$}.

From B → bB: B is at the end → Rule 3: FOLLOW(B) ⊆ FOLLOW(B). (Circular, no new info.)

FOLLOW(B) = { $ }
4

Compute FOLLOW(C)

From S → bC: C is at the end of the production → Rule 3: FOLLOW(S) ⊆ FOLLOW(C). Add {$}.

From C → cC: C is at the end → Rule 3: FOLLOW(C) ⊆ FOLLOW(C). (Circular, no new info.)

FOLLOW(C) = { $ }
FINAL ANSWERS
FOLLOW(S)
$
FOLLOW(A)
{ b, d }
FOLLOW(B)
$
FOLLOW(C)
$
3

LL(1) Parsing Table Construction + Simulation

Topic: Build LL(1) table and trace a parse

LL(1)
S → aS | b
Tasks:
(1) Compute FIRST and FOLLOW for S.
(2) Build the complete LL(1) parsing table M[S, ?].
(3) Simulate parsing the string "aab$" step by step using your table.
✅ Step-by-Step Solution — LL(1) Table & Parse
1

FIRST and FOLLOW

FIRST(aS) = {a} — starts with terminal 'a'

FIRST(b) = {b} — just the terminal

FIRST(S) = {a, b} — union of both productions

FOLLOW(S) = {$} — S is the start symbol, and nothing follows it in aS except end-of-input; from S→aS, S follows S, circular, same set

FIRST(S)
{ a, b }
FOLLOW(S)
$
2

LL(1) Table Construction

Rule: M[A,a] = A→α when a ∈ FIRST(α)

S → aS: FIRST(aS) = {a} → M[S, a] = S → aS

S → b: FIRST(b) = {b} → M[S, b] = S → b

No ε in any FIRST, so no FOLLOW rule entries needed.

Non-terminal a b $
S S → aS S → b —

No cell has two entries → grammar IS LL(1) ✓

3

Parse Simulation: "aab$"

Stack grows rightward; top-of-stack is leftmost symbol.

StackRemaining InputAction
$Saab$M[S,a] = S→aS; replace S with aS → stack: $Sa
$Saaab$Top is 'a', input is 'a' → match, pop 'a'
$Sab$M[S,a] = S→aS; replace S with aS → stack: $Sa
$Saab$Top is 'a', input is 'a' → match, pop 'a'
$Sb$M[S,b] = S→b; replace S with b → stack: $b
$bb$Top is 'b', input is 'b' → match, pop 'b'
$$Stack = $, Input = $ → ACCEPT ✓
4

LR(0) Items, Closure & DFA

Topic: Building the LR(0) automaton from scratch

LR(0)
S → AA
A → aA | b
Tasks:
(1) Write the augmented grammar (add S' → S).
(2) List all LR(0) items (dot in every position of every production).
(3) Compute closure(I0) where I0 = {[S'→•S]}.
(4) Draw the complete LR(0) DFA (all states and transitions).
✅ Step-by-Step Solution — LR(0) DFA
1

Augmented Grammar

S' → S(augmented)
S → AA
A → aA
A → b
2

All LR(0) Items

S' → •S
S' → S•
S → •AA
S → A•A
S → AA•
A → •aA
A → a•A
A → aA•
A → •b
A → b•

Total: 10 items (one per position in each production, including augmented)

3

Compute I0 = closure({S'→•S})

Start kernel: { S' → •S }

→ S has dot before S (non-terminal) → add all S-productions with dot at left:

Add: S → •AA

→ S → •AA has dot before A → add all A-productions:

Add: A → •aA, A → •b

→ A → •aA has dot before 'a' (terminal). A → •b has dot before 'b' (terminal). No more expansions.

I0 closure({S'→•S})
S' → •S
S → •AA
A → •aA
A → •b
4

Complete LR(0) DFA — All 8 States

I0 Initial
S' → •S
S → •AA
A → •aA
A → •b
I1 GOTO(I0,S)
S' → S•
⚡ Accept on $
I2 GOTO(I0,A)
S → A•A
A → •aA
A → •b
I3 GOTO(I0,a) = GOTO(I2,a) = GOTO(I3,a)
A → a•A
A → •aA
A → •b
I4 GOTO(I0,b) = GOTO(I2,b) = GOTO(I3,b)
A → b•
Reduce A→b on all
I5 GOTO(I2,A)
S → AA•
Reduce S→AA on all
I6 GOTO(I3,A)
A → aA•
Reduce A→aA on all
GOTO TABLE
GOTO(I0, S) = I1   GOTO(I0, A) = I2
GOTO(I0, a) = I3   GOTO(I0, b) = I4
GOTO(I2, A) = I5
GOTO(I2, a) = I3   GOTO(I2, b) = I4
GOTO(I3, A) = I6
GOTO(I3, a) = I3 (self-loop)   GOTO(I3, b) = I4
5

Full SLR Parsing Table & Parse Trace

Topic: Complete SLR construction and simulation

SLR
E → E + T | T
T → T * id | id
Tasks:
(1) Write the augmented grammar.
(2) Identify FOLLOW sets for E and T.
(3) List all LR(0) states (I0–I5).
(4) Build the full SLR Action + Goto table.
(5) Simulate parsing: "id + id * id $"
✅ Step-by-Step Solution — SLR Table for E → E+T | T
1

Augmented Grammar

(0) E' → E
(1) E → E + T
(2) E → T
(3) T → T * id
(4) T → id
2

FOLLOW Sets

FOLLOW(E'): {$} (start symbol)

FOLLOW(E): E' → E: add FOLLOW(E') = {$}; E → E+T: E followed by '+', add {+}; So FOLLOW(E) = {+, $}

FOLLOW(T): E → E+T: T at end, add FOLLOW(E) = {+, $}; T → T*id: T followed by '*', add {*}; So FOLLOW(T) = {+, *, $}

FOLLOW(E)
{ +, $ }
FOLLOW(T)
{ +, *, $ }
3

LR(0) States

I0
E' → •E
E → •E+T
E → •T
T → •T*id
T → •id
I1 GOTO(I0,E)
E' → E•
E → E•+T
I2 GOTO(I0,T)
E → T•
T → T•*id
I3 GOTO(I0,id)
T → id•
I4 GOTO(I1,+)
E → E+•T
T → •T*id
T → •id
I5 GOTO(I2,*)
T → T*•id
I6 GOTO(I4,T)
E → E+T•
T → T•*id
I7 GOTO(I4,id), GOTO(I5,id)
T → id•
I8 GOTO(I5,id)
T → T*id•
4

SLR Action + Goto Table

State ACTION GOTO
id + * $ E T
0s312
1s4acc
2r2s5r2
3r4r4r4
4s76
5s8
6r1s5r1
7r4r4r4
8r3r3r3

r1=E→E+T, r2=E→T, r3=T→T*id, r4=T→id. No conflicts → grammar is SLR(1) ✓

5

Parse Trace: "id + id * id $"

StackInputAction
0id+id*id$s3 — shift 'id'
0 id 3+id*id$r4 — reduce T→id (FOLLOW(T)∋+)
0 T 2+id*id$r2 — reduce E→T (FOLLOW(E)∋+)
0 E 1+id*id$s4 — shift '+'
0 E 1 + 4id*id$s7 — shift 'id'
0 E 1 + 4 id 7*id$r4 — reduce T→id (FOLLOW(T)∋*)
0 E 1 + 4 T 6*id$s5 — shift '*'
0 E 1 + 4 T 6 * 5id$s8 — shift 'id'
0 E 1 + 4 T 6 * 5 id 8$r3 — reduce T→T*id
0 E 1 + 4 T 6$r1 — reduce E→E+T
0 E 1$acc — ACCEPT ✓

Exam Tips & Common Mistakes

Key strategies for tackling parsing problems on exams — based on the most common student errors.

🔢 #1

FIRST Set Algorithm

Start from terminals (trivial), then work outward to nonterminals. ε only propagates if ALL symbols before the current position can derive ε. A common mistake: forgetting to check the full prefix.

🌐 #2

FOLLOW Circular Dependencies

Always add $ to FOLLOW(S) first. FOLLOW sets can be circular (A appears in B's rule, B in A's). Resolve by taking the union iteratively until the fixed point. Don't panic — just keep iterating.

⚔️ #3

LL(1) Table Conflicts

If M[A,a] would need two entries, the grammar is NOT LL(1). The two root causes: (1) left recursion (E→E+T), (2) common prefixes in alternatives (S→if...|if...else...). Fix with left-factoring or left-recursion elimination.

🔄 #4

LR State Closure

Always compute the full closure before computing GOTO. A kernel item [A→α•Bβ] requires adding ALL B-productions with dot at the start. Forgetting closure items is the #1 DFA-building mistake.

🏆 #5

The Canonical SLR vs CLR Example

Grammar: S→L=R|R, L→*R|id, R→L. SLR conflicts in I2 because = ∈ FOLLOW(R). CLR resolves it: [R→L•, {$}] only reduces on $, not '='. This is the exam example — know it cold.

🧩 #6

LALR State Merging

LALR merges CLR states with the same LR(0) core. The only time this introduces conflicts is reduce/reduce: if two merged states have different lookaheads that collide. This is rare in practice but examinable.

📐 #7

Reading the SLR/CLR Table

sN means "shift and push state N". rK means "reduce by production K". Pop 2×|α| entries, push the LHS nonterminal, then do GOTO. Remember: the state at the top after popping is used for GOTO.

⚠️ #8

Grammar Class Ordering

LR(0) ⊂ SLR ⊂ LALR ⊂ LR(1). LL(1) ⊂ LALR but is not the same family as LR. For any exam question "which parser handles this grammar", remember: more powerful parsers can always handle simpler grammars.

Quick Quiz — 5 Questions

Test your understanding. Click an answer — you'll see immediately if you're right and why.

Question 1 of 5
Which set is used by SLR but NOT by LR(0) to restrict when to apply a reduction?
✅ Correct Answer: B) FOLLOW sets

LR(0) reduces on every terminal when a complete item is present — it uses zero lookahead. SLR improves this by only reducing when the current token is in FOLLOW(A) for the production A→α. This eliminates many false conflicts but FOLLOW sets can still be too coarse (they can contain tokens that shouldn't actually trigger a reduction in that state).
Question 2 of 5
In an LR item [A → α • β], what does the dot (•) represent?
✅ Correct Answer: C)

The dot in an LR item tracks the current recognition position. Everything to the left of the dot has already been matched (pushed on the stack). Everything to the right is what the parser still expects to see. When the dot reaches the end [A→α•], the item is "complete" and a reduction may be performed.
Question 3 of 5
LALR merges CLR states that share the same ___
✅ Correct Answer: B) LR(0) core

Two CLR states have the same LR(0) core when they contain the same set of items if you strip the lookahead annotations. LALR merges all such state pairs, combining their lookahead sets via union. This gives LALR the same number of states as LR(0)/SLR, while retaining much of CLR's precision. The risk: unioning lookaheads can introduce reduce/reduce conflicts that CLR doesn't have.
Question 4 of 5
If FIRST(A) contains ε, what additional computation must you perform when filling M[A, a] in the LL(1) table?
✅ Correct Answer: C)

The LL(1) table fill rule is: M[A, a] = A→α when a ∈ FIRST(α). But also: if ε ∈ FIRST(α), then M[A, a] = A→α for every a ∈ FOLLOW(A). This handles the case where A can silently vanish (derive ε) — in that situation, we should still use A→α (or A→ε specifically) when we see a token that could come after A. If both cases apply to the same cell, the grammar has a conflict and is not LL(1).
Question 5 of 5
Which parsing strategy is most commonly used in real-world parser generators like yacc and bison?
✅ Correct Answer: D) LALR(1)

LALR(1) is the workhorse of real-world parser generators. yacc, GNU bison, Berkeley YACC, Happy (Haskell), and many other tools all generate LALR(1) parsers. Why? LALR has the same state count as LR(0) (compact tables) while handling almost all well-designed programming language grammars without conflicts. CLR is more powerful but the state table can be exponentially larger. LR(0) has too many conflicts for practical grammars.
0 / 5

Quiz complete!