Worked examples — Context-free grammars (CFG) — productions, derivations, parse trees
The scenario matrix
Every CFG exercise you meet is one of these case classes. We will hit each one.
| Cell | Case class | What makes it tricky | Example |
|---|---|---|---|
| C1 | Balanced / nested structure | needs a stack — DFAs fail | Ex 1: (()()) |
| C2 | Equal-count language | counting two things in lockstep | Ex 2 |
| C3 | The degenerate case | empty string, zero-length yield | Ex 3 |
| C4 | Two-sided count / palindrome-like | symmetry around a centre | Ex 4: |
| C5 | Ambiguity detection | ≥2 parse trees for one string | Ex 5 |
| C6 | Ambiguity removal (precedence) | rewrite so each string has one tree | Ex 6 |
| C7 | Leftmost = rightmost = tree | ordering vs structure | Ex 7 |
| C8 | Word problem (real syntax) | model something real as a CFG | Ex 8: if/else |
| C9 | Exam twist — union of counts | one grammar, several sub-cases | Ex 9: or |
Terminals we use are the actual letters like , , , . Variables are capitals like . A production means "wherever you see , you may swap in ". Recall is one swap and is "zero or more swaps".
C1 — Nested & balanced structure
C2 — Equal counts
C3 — The degenerate case: the empty string
for ?
Forecast: Zero a's and zero b's — can the grammar produce a string of length 0?
- — Why? Apply immediately, with no wrapping first.
This is the member of : the string .
Verify: , count of a count of b. The derivation uses one step, and (the empty string is always in ), so it is a legal member of the language. ✓
is not a real string, so skip it." Why it feels right: it looks like nothing. The fix: is a genuine element of of length 0. Many languages (, balanced parens) include it for . Forgetting the / degenerate case is a classic exam error.
C4 — Two-sided / non-1:1 counts
aabbbb from
Forecast: For every one a, this grammar drops two b's. How many a's and b's should aabbbb have?
Here , so expect a's and b's.
- — Why? adds one
aand twob's at once, enforcing the ratio structurally. - — Why? Wrap again → two
a's, fourb's framed. - — Why? Stop with .
Verify: a count , b count , and so holds with . All a's precede all b's. ✓ This shows a grammar can encode any linear relation between counts, not just equality.
C5 — Detecting ambiguity
is ambiguous using a+a+a
Forecast: With three a's joined by +, can you group them as and ? If both derivations exist, the grammar is ambiguous.
Tree L (left group):
- — split into a left sum and a right term.
- — Why? Expand the left into , making the grouping .
- — finish all .
Tree R (right group):
- .
- — Why? This time expand the right , making .
- .
These two are distinct parse trees (different parent–child structure at the root), shown side by side:

Verify: Both trees yield the same string a+a+a (5 symbols). They differ in structure (which + is at the root), so by definition the grammar has ≥2 parse trees for one string ambiguous. ✓
The fix: you need two leftmost (or two tree-distinct) derivations. Two orderings of the same tree do not count — see Ex 7.
C6 — Removing ambiguity with precedence layers
a+a*a a unique tree using
Forecast: Which operator will end up nearest the root (evaluated last)? Guess before deriving.
- — Why?
+is the lowest-precedence layer, so it must sit at the top; only the -level rule produces it. - — Why? The left operand is just a term (no
+inside), so . - then — Why? That term is a bare factor
a. - — Why? The right operand
a*ais a*-term, produced only at the layer, which forces it below the+. - — finish with , .

Verify: There is exactly one parse tree — * is structurally forced under +, giving the grouping . Any attempt to make + bind tighter would need + inside a , but no -rule produces +. Unique tree ⇒ unambiguous for this string. ✓ (This is why Compilers — parsing uses layered grammars.)
C7 — Ordering vs structure (leftmost = rightmost = same tree)
ab in — same tree?
Forecast: One derivation expands first, another expands first. Different trees or not?
Leftmost: (always rewrite the leftmost variable). Rightmost: (always rewrite the rightmost variable).
- Both start — Why? Only one -rule exists.
- They differ only in which of you replace first — Why does that not matter? and live in separate subtrees; expanding one never affects the other.
Verify: Draw the tree: root , children and , with leaves and . Both derivations produce this identical tree. So there are 2 derivations but 1 parse tree ⇒ this grammar is not ambiguous by these strings. Distinct-tree count . ✓
Recall Derivations vs trees, in one line
Leftmost derivation ↔ parse tree is a bijection. Count trees, never raw derivations, when testing ambiguity.
C8 — Word problem: nested if/else (real syntax)
show the "dangling else" is ambiguous
Grammar (S = statement):
where is a plain statement. Forecast: In if if x else x, does the else attach to the inner if or the outer if? If both are legal, we have a real bug.
Attach to inner if:
- — Why? Outer
ifwith no else (first rule). - — Why? Inner statement is an if-else (second rule); the
elsebelongs to the innerif. - .
Attach to outer if:
- — Why? Outer
ifhas the else (second rule). - — inner is a bare
if x, so theelsebinds to the outer.
Verify: Both derivations yield the identical token string if if x else x, yet the else sits in different subtrees ⇒ two parse trees ⇒ ambiguous. This is the famous dangling-else problem; real languages fix it with a rule "else binds to nearest unmatched if" — exactly a precedence-layer trick like Ex 6. ✓
C9 — Exam twist: a union of two count-conditions
Forecast: Two separate conditions joined by "or". Guess: one start rule branching to two sub-grammars?
The or means union, so chooses one branch:
Branch forces (via ) and lets be free (via ). Branch forces (via ) and lets be free.
Test string aabbc (; here holds).
- — Why? Since , take the branch that enforces it.
- .
- — Why? Two wraps give , matching .
- — Why? One
cfor , free of any count constraint. - Assemble:
aabbc.
Verify: For aabbc: . Condition : ✓ (so the string is in regardless of ). Counts check: two a, two b, one c, in order . ✓
and simultaneously." Why it feels right: it's a stronger-looking grammar. The fix: (both at once) is not context-free — the Pumping lemma for CFLs rules it out. A single CFG can match one pair at a time; the union (or) is fine, the intersection (and, all three equal) is not.
Active Recall
Recall (C3) Why must you always check the
/ case? Because is a real length-0 string; languages like include it at . Skipping it loses a valid member.
Recall (C5 vs C7) Two derivations exist — is the grammar ambiguous?
Only if they give two distinct parse trees (two distinct leftmost derivations). Two orderings of the same tree (Ex 7) prove nothing.
Recall (C9) Can one CFG force
AND at once for ? No — is not context-free (pumping lemma). You can do the union (or), not the intersection (and).
Recall (C6) In a precedence-layered grammar, which operator ends up nearest the root?
The lowest-precedence one (e.g. +), because only the top layer produces it, forcing higher-precedence ops into deeper subtrees.
"Trees, not Trails." Count parse Trees; ignore the Trail (ordering) of rewrites.
Connections
- Parent: 4.6.08 Context-free grammars (CFG) — productions, derivations, parse trees (Hinglish)
- Regular expressions and DFAs · Pushdown automata · Chomsky hierarchy
- Chomsky normal form · Pumping lemma for CFLs · Compilers — parsing