3.2 Grammar Ambiguity, Elimination of Left Recursion & Left Factoring Algorithms
π‘ Core Intuition: The 3-Second Mental Modelβ
π³ The Everyday Analogy: The Confusing Fork in the Roadβ
Imagine driving down an expressway toward an intersection:
- The Ambiguous Road Sign: A sign reads: "Take Exit Left OR Take Exit Right to reach Airport", but one route charges a heavy toll while the other is free. Drivers make conflicting, unpredictable choices. An Ambiguous Grammar permits multiple distinct parse trees for the same string, creating undefined runtime behaviors.
- The Left Recursion Roundabout (Infinite Loop): A roundabout instruction reads: "To exit the roundabout, first drive around the roundabout." A Left-Recursive Grammar () forces a top-down parser to call itself recursively before consuming a single token, triggering a stack overflow.
- The Left Factoring Dilemma (Delayed Decision): Two highway destinations share the exact same access highway for 10 miles before branching into Downtown and Suburbs. Left Factoring defers the parsing decision until the common prefix is consumed and the diverging token appears.
π» Bridging to Computer Science: Ambiguity, Left Recursion, and Left Factoringβ
Before a top-down predictive parser (such as an LL(1) parser) can parse a programming language, the grammar must be made compiler-friendly:
- Eliminate Ambiguity: Ensure every valid sentence produces exactly one unique parse tree.
- Eliminate Left Recursion: Convert left-recursive rules into right-recursive forms so top-down parsers do not enter infinite recursion.
- Apply Left Factoring: Extract common prefixes to eliminate backtracking.
πTable of Contents
- ποΈ Architectural Foundations & Mechanics: Grammar Ambiguity
- π Algorithmic Elimination of Left Recursion
- π The General (Indirect) Left Recursion Elimination Algorithm
- βοΈ The Left Factoring Transformation Algorithm
- π Real-World Production Context: How Industry Builds This
- π Numerical Applications & Worked Examples
- π― Exam & Interview Pitfall Check
ποΈ Architectural Foundations & Mechanics: Grammar Ambiguityβ
Ambiguous vs Unambiguous Grammar Architecture
Contrasting non-deterministic ambiguity against structured precedence hierarchies
Ambiguous Grammar
- β’A single string admits two or more distinct parse trees (or LMDs/RMDs).
- β’Generates conflicting operator associativity and precedence.
- β’Expression 2 + 3 * 4 could evaluate to 14 or 20 depending on the tree.
- β’Undecidable: No general algorithm can determine if an arbitrary CFG is ambiguous.
Unambiguous Structured Grammar
- β’Every valid string produces exactly one unique parse tree.
- β’Precedence is encoded structurally into the depth of the grammar rules.
- β’Deeper subtrees execute prior to higher-level operators in post-order traversal.
- β’Enforces deterministic Left-to-Right or Right-to-Left associativity.
π Formal Definition of Ambiguityβ
A Context-Free Grammar is Ambiguous if and only if there exists at least one terminal string that admits:
- Two or more distinct Parse Trees, OR
- Two or more distinct Leftmost Derivations (LMD), OR
- Two or more distinct Rightmost Derivations (RMD).
Concrete Ambiguity Proof for Grammar: S β aS | Sa | aβ
Consider the grammar and the input string :
- Derivation 1: (using ).
- Derivation 2: (using ).
Because string possesses two distinct parse trees, the grammar is Ambiguous.
Fundamental Ambiguity Theorem: A grammar that is both left and right recursive on the same non-terminal (e.g., ) is always ambiguous! However, a grammar that is ambiguous need not be both left and right recursive.
π Algorithmic Elimination of Left Recursionβ
A grammar is Left-Recursive if it contains a non-terminal such that:
If a CFG contains left recursion, a top-down parser will enter an infinite recursive loop without consuming any input token. To avoid this, we transform left-recursive grammars into equivalent right-recursive productions.
β‘ Eliminating Immediate Left Recursionβ
Consider the immediate left-recursive production:
This generates strings of the regular expression pattern :
We replace left recursion with right recursion by introducing an auxiliary non-terminal :
Universal Formula for Multiple Productions:β
For productions of the form:
The equivalent transformed grammar is:
π The General (Indirect) Left Recursion Elimination Algorithmβ
A grammar may contain indirect left recursion where recursion spans multiple non-terminals:
Here, , forming a cycle . Immediate elimination cannot be applied directly. We apply the systematic ordering algorithm:
Systematic Indirect Left Recursion Elimination Algorithm
Systematic algorithm eliminating cycles and indirect left-recursive dependencies
Impose Global Ordering on Non-Terminals
OrderingArrange all non-terminals in some arbitrary fixed linear order: Aβ, Aβ, ..., Aβ.
Nested Forward-Substitution Loop
SubstitutionFor i = 1 to n: For j = 1 to i - 1: Replace every production Aα΅’ -> Aβ±Ό Ξ³ with Aα΅’ -> Ξ΄β Ξ³ | Ξ΄β Ξ³ | ... where Aβ±Ό -> Ξ΄β | Ξ΄β | ... are current productions of Aβ±Ό.
Eliminate Immediate Left Recursion on Aα΅’
Immediate FixApply the standard immediate left recursion transformation formula to the productions of Aα΅’.
Eliminate Unreachable Non-Terminals
CleanupRemove any non-terminals that became dead or unreachable during substitution passes.
βοΈ The Left Factoring Transformation Algorithmβ
A grammar is Non-Deterministic if two or more alternatives for a non-terminal share a common prefix:
A predictive top-down parser cannot determine whether to expand or by inspecting the lookahead token because both alternatives begin with . Without left factoring, the parser must rely on expensive backtracking.
π― Left Factoring Transformation Ruleβ
To remove the common prefix and make the grammar deterministic:
This defers the parsing decision until the common prefix has been consumed and the divergent token appears.
π Real-World Production Context: How Industry Builds Thisβ
In industrial compiler engineering:
- Top-Down Parsers (LL, Recursive Descent, ANTLR 3):
- Require strict elimination of all left recursion and full left factoring.
- Converting into flips the parse tree associativity from left-associative to right-associative. The compiler's AST construction actions must explicitly reconstruct left associativity.
- Bottom-Up Parsers (LR, LALR, GNU Bison, Yacc):
- Left recursion is actively preferred! In an LR parser, left-recursive rules reduce tokens immediately, keeping the parser state stack bounded at memory. Right-recursive rules force the parser to shift the entire input onto the stack, consuming stack space!
Modern parser generators like ANTLR 4 accept direct left-recursive expressions natively. At runtime, ANTLR's algorithm dynamically rewires left-recursive rules into precedence-climbing state graphs, sparing engineers from manual grammar rewrites.
π Numerical Applications & Worked Examplesβ
Example 1: Eliminating Indirect Left Recursionβ
Apply the general left recursion elimination algorithm to the following grammar:
Step-by-Step Analytical Solution:
Step 1: Order the Non-Terminals
Let and .
Step 2: Process ()
Productions for :
There is no immediate left recursion on . remains unchanged.
Step 3: Process ()
Substitute productions of into :
Since , replacing in gives:
Group -productions into left-recursive and non-left-recursive sets:
- Left-recursive terms: (so ).
- Non-left-recursive terms: (so ).
Apply immediate left recursion elimination on :
Final Transformed Grammar:
The grammar is now completely free of both direct and indirect left recursion!
Example 2: Step-by-Step Left Factoring of Nested Prefixesβ
Left-factor the following non-deterministic grammar:
Step-by-Step Analytical Solution:
Pass 1: Extract Common Prefix
All three alternatives share the common prefix :
Pass 2: Factor Remaining Alternatives in
Observe that in , the alternatives and share a common prefix :
The grammar is now completely left-factored and ready for deterministic predictive parsing without backtracking!
π― Exam & Interview Pitfall Checkβ
Question 1: "Why do Top-Down predictive parsers fail on Left-Recursive grammars while Bottom-Up parsers thrive on them?"
Key Focus Points:
- Top-Down Failure: A top-down parser predicts productions from the start symbol down to terminals. For , the parser calls function
A(), which immediately callsA()again before consuming any input token, resulting in infinite recursion. - Bottom-Up Advantage: A bottom-up parser shifts terminals onto its stack and reduces them when a handle appears. For , the parser shifts and reduces iteratively, maintaining stack depth.
Question 2: "Does Left Factoring eliminate ambiguity from an ambiguous grammar?"
Key Focus Points:
- No: Left factoring eliminates local non-determinism (shared prefixes), allowing a parser with 1-token lookahead to pick a branch.
- Preservation of Ambiguity: If the underlying language construct is ambiguous (e.g. the Dangling-Else), left factoring merely packages the conflict into a subsequent non-terminal (). Resolving ambiguity requires explicit disambiguation rules.
Trap 1: Does eliminating left recursion alter the language generated by the grammar?
Answer: No, but it alters the parse tree structure. The transformed right-recursive grammar generates the exact same language , but the parse trees become right-leaning rather than left-leaning. This reverses the apparent operator associativity from left-to-right to right-to-left.
Trap 2: Is every grammar that is both left and right recursive ambiguous?
Answer: Yes, if the recursion is on the same non-terminal symbol. For example, is both left and right recursive on , and is guaranteed to be ambiguous. However, having left recursion on one symbol () and right recursion on another symbol () does not imply ambiguity.