Ambiguity in CFGs¶
Definition¶
A CFG G is ambiguous if there exists at least one string w ∈ L(G) that has two or more distinct parse trees.
Equivalently, G is ambiguous if some string has two or more distinct leftmost derivations (or two or more distinct rightmost derivations — these are equivalent conditions).
Distinct derivations ≠ distinct parse trees
A single parse tree can correspond to many different derivations (depending on the order you expand non-terminals). What matters for ambiguity is whether the tree structure — the hierarchical grouping — is unique.
- Unique parse tree does not imply unique derivation.
- Two or more distinct parse trees does imply ambiguity.
Why Ambiguity Matters¶
Ambiguous grammars are problematic in practice because the parse tree encodes the meaning of the string (e.g., the order of operations in an arithmetic expression). Multiple parse trees mean multiple interpretations — and computers need a single, deterministic answer.
A compiler for a programming language cannot tolerate an ambiguous grammar: the same source code would have multiple possible meanings.
Example 1: Arithmetic Expressions¶
The Ambiguous Grammar¶
E → E + E | E * E | id
This grammar is ambiguous. Consider deriving id + id * id:
Parse Tree 1 (interprets as (id + id) * id — wrong operator precedence):
E
/ \ \
E * E
/ \ \ \
E + E id
| |
id id
Wait — let's write it more precisely. Derivation 1 (+ first):
E ⟹ E * E ⟹ (E + E) * E ⟹ (id + E) * E ⟹ (id + id) * E ⟹ (id + id) * id
Parse Tree 1: * is the root, left child is E + E.
E
/|\
E * E
/|\ \
E + E id
| |
id id
Derivation 2 (* first):
E ⟹ E + E ⟹ id + E ⟹ id + E * E ⟹ id + id * E ⟹ id + id * id
Parse Tree 2: + is the root, right child is E * E.
E
/|\
E + E
| /|\
id E * E
| |
id id
These are two distinct parse trees for the same string id + id * id. The grammar is ambiguous.
Removing the Ambiguity¶
The ambiguity arises because the grammar does not encode operator precedence (* binds tighter than +) or associativity.
Unambiguous grammar:
E → E + T | T
T → T * F | F
F → (E) | id
This forces * to always bind tighter than + (because * lives at the T level, below + at the E level). And both operators are left-associative (the left-recursive form E → E + T ensures left-to-right evaluation).
Derivation of id + id * id in the unambiguous grammar:
E ⟹ E + T ⟹ T + T ⟹ F + T ⟹ id + T ⟹ id + T * F ⟹ id + F * F ⟹ id + id * F ⟹ id + id * id
This is the only leftmost derivation. There is only one parse tree, with + at the top and * inside the right subtree.
Example 2: The Dangling Else Problem¶
The Ambiguous Grammar¶
Consider a grammar for if-then-else statements:
S → if C then S
| if C then S else S
| A
C → p | q | r
A → a | b | c
The string if p then if q then a else b is ambiguous. Two interpretations:
Interpretation 1 — else b belongs to the inner if:
if p then (if q then a else b)
Interpretation 2 — else b belongs to the outer if:
(if p then if q then a) else b
Both are valid derivations under the grammar.
Removing the Ambiguity¶
The standard fix: every else matches the nearest unmatched if (Interpretation 1 above). We encode this by splitting S into:
Sm(matched): anif-then-elsewhere thethenbranch is also fully matched.Su(unmatched): anif-thenwith noelse, or anif-then-elsewhere thethenbranch is unmatched.
Unambiguous grammar:
S → Sm | Su
Sm → if C then Sm else Sm | A
Su → if C then S | if C then Sm else Su
C → p | q | r
A → a | b | c
Under this grammar, if p then if q then a else b has the unique derivation:
S ⟹ Su ⟹ if C then S ⟹ if p then S ⟹ if p then Sm
⟹ if p then if C then Sm else Sm
⟹ if p then if q then Sm else Sm
⟹ if p then if q then A else Sm
⟹ if p then if q then a else Sm
⟹ if p then if q then a else A
⟹ if p then if q then a else b
The else b is unambiguously attached to the inner if q.
General Strategies for Removing Ambiguity¶
| Strategy | When to Apply |
|---|---|
| Set precedence levels | When operators of different "strength" are at the same grammar level |
| Set associativity (left or right recursion) | When the same operator can group left or right |
| Split non-terminals | When a non-terminal is overloaded (e.g., matched vs. unmatched statements) |
| Factor out common prefixes | When two rules share a common prefix, they can be merged |
| Simplify/restructure rules | Remove redundant choices that create ambiguity |
Inherently Ambiguous Languages
Some context-free languages are inherently ambiguous — every CFG for them is ambiguous. No grammar transformation can remove the ambiguity. An example is:
L = {aⁱbʲcᵏ | i = j or j = k}
For strings where i = j = k, any parse tree must "decide" which equality is the reason for acceptance — and there is no way to make that decision uniquely.