Ambiguity, left recursion and left factoring
Preparing a grammar for parsing: recognising ambiguous grammars, removing ambiguity by precedence and associativity layers and the matched-unmatched rule for dangling else, eliminating immediate and indirect left recursion, and left factoring, with the effect of each on the parse tree.
Why it exists
A parser must be able to decide, for every sentence, which tree it has, and top-down parsers must also be able to decide, from one token, which production to use. Real grammars as people first write them often fail both tests: the natural grammar for expressions is ambiguous, the natural way of writing a list is left-recursive, and two alternatives often begin with the same symbols. This page shows how to rewrite such grammars without changing the language so that parsers can use them.
Prerequisites
You need context-free grammars, derivations and parse trees (context-free grammars). Quick recap: a derivation replaces a nonterminal by the right side of one of its productions at each step; in a leftmost derivation the leftmost nonterminal is replaced each time, in a rightmost one the rightmost. Each parse tree corresponds to exactly one leftmost and exactly one rightmost derivation.
Precise definitions
Ambiguous grammar. A grammar is ambiguous if some string of its language has more than one parse tree. Equivalently: more than one leftmost derivation, or more than one rightmost derivation. (Two different derivations with the same tree, such as a leftmost and a rightmost one, do not make a grammar ambiguous.)
Ambiguity is a property of the grammar, not of the language: many languages have both ambiguous and unambiguous grammars. A language is inherently ambiguous if every grammar for it is ambiguous; for example . It is undecidable whether an arbitrary context-free grammar is ambiguous, so there is no algorithm that settles it for all grammars; for a given small grammar you look for a string with two trees.
Why it matters: the meaning of a program is attached to its parse tree, so two trees mean two meanings. Every LL(1) and every LR(k) grammar is unambiguous, so an ambiguous grammar is never LL(1) or LR(k); parser generators such as Yacc nevertheless accept some ambiguous grammars and resolve the conflicts with precedence and associativity declarations.
Removing ambiguity from expressions. The grammar gives no precedence and no associativity. Fix it by layering, one nonterminal per precedence level, lowest precedence at the top:
A production of the form is left recursive, which makes left associative: is . A right-associative operator (such as exponentiation or assignment) uses right recursion: . Binding tighter means being deeper in the layering: is generated below , so must group as .
Dangling else. In the string if E then if E then S else S has two trees (the else belongs to the inner or the outer if). The rule of nearly all languages is: each else matches the nearest unmatched then. The unambiguous grammar separates the two kinds of statement:
(M is a matched statement, U an unmatched one.) The then branch before an else must be matched, so no else can slip past an inner unmatched if.
Left recursion. A grammar is left recursive if some nonterminal derives for some (). It is immediate if there is a production and indirect if the cycle goes through other nonterminals. A top-down parser (recursive descent or LL(1)) loops forever on it: to expand it expands again without consuming a token. Bottom-up (LR) parsers have no problem with it; in fact left recursion makes them use less stack.
Immediate. If where no begins with , replace by
The new grammar generates the same strings (a followed by any number of 's) but is right recursive.
Indirect (the general algorithm). Number the nonterminals . For to : for to , replace every production by where are the current productions of ; then eliminate the immediate left recursion of . (The algorithm assumes the grammar has no cycles and no -productions.)
Left factoring. If two alternatives of begin with the same string, , a predictive parser cannot choose between them by looking at the next token. Defer the decision:
Repeat until no two alternatives share a prefix.
Effects on the tree. Removing left recursion or factoring changes the shape of the parse tree and the grammar but not the language. In particular after removing left recursion from the tree of leans to the right even though is still meant to be left associative; the semantic actions (see translation schemes) must compensate. Left factoring does not remove ambiguity: the dangling else stays ambiguous after factoring.
How it works
First the ambiguity. The grammar is run on the string and the leftmost derivations found by exhaustive search are shown as trees growing. There are exactly two. In the first the root production is (so the product is computed first, which is the usual meaning); in the second the root is (the sum is computed first).
Derivation of id + id * id with + at the root: start
Parser run by a script, not typedLeftmost derivation (the sentential form is on the left; the string to derive is on the right).
- 1E → E + E
- 2E → E * E
- 3E → ( E )
- 4E → id
Step 0
Start from E. A leftmost derivation always replaces the leftmost nonterminal; each step corresponds to one node of the parse tree being expanded.
Derivation of id + id * id with * at the root: start
Parser run by a script, not typedLeftmost derivation (the sentential form is on the left; the string to derive is on the right).
- 1E → E + E
- 2E → E * E
- 3E → ( E )
- 4E → id
Step 0
Start from E. A leftmost derivation always replaces the leftmost nonterminal; each step corresponds to one node of the parse tree being expanded.
The layered grammar , , derives the same string in exactly one way (found by the same exhaustive search):
The only derivation of id + id * id in the layered grammar: start
Parser run by a script, not typedLeftmost derivation (the sentential form is on the left; the string to derive is on the right).
- 1E → E + T
- 2E → T
- 3T → T * F
- 4T → F
- 5F → ( E )
- 6F → id
Step 0
Start from E. A leftmost derivation always replaces the leftmost nonterminal; each step corresponds to one node of the parse tree being expanded.
The number of parse trees of the flat grammar for binary operators in a row is the Catalan number : 1, 2, 5, 14 for (counted by two independent methods in the checks). So id+id+id+id has 5 trees.
Worked examples
EX 1Show a grammar is ambiguous
EasyShow is ambiguous. Take . Tree 1 has at the root with the right operand : derivation . Tree 2 has at the root with the left operand : . Two trees for one string, so the grammar is ambiguous (the animations above show both).
EX 2Layering for precedence and associativity
EasyMake bind tighter than , both left associative, and let (exponent) bind tightest and associate to the right. Layers from lowest to highest binding: , , , . Check: groups as because the recursion in is on the right; groups as because the recursion in is on the left; and groups as .
EX 3Immediate left recursion
MediumRemove the left recursion from , , . For : and , so and . For : and . is unchanged. The result (checked by the script to be exactly this) is
This is the grammar used for LL(1) parsing. A second case: becomes and .
EX 4Left factoring the if statement
Medium, . The first two alternatives share the prefix . Factor: and . Now the choice at the start of is made on one token ( or ). But the grammar is still ambiguous: on the input i b t i b t a e a the e can be taken by either S', so factoring did not fix the dangling else. (The original grammar has two trees for this input; the checks confirm it.)
EX 5Indirect left recursion
GATE level, . Here , so is left recursive through . Order the nonterminals . For : no earlier nonterminal, and no immediate left recursion. For : substitute in : . Now remove the immediate left recursion of : , , giving
The final grammar is plus these (compared with the script's output and with the original language up to strings of length 7).
Cheat box
Cheat box
- Ambiguous = some string has two parse trees (two leftmost derivations). Deciding it for any CFG is undecidable.
- Precedence: higher precedence = deeper layer. Associativity: left recursion for left-associative, right recursion for right-associative.
- Dangling else: match each
elsewith the nearest unmatchedthen(matched and unmatched statements). - becomes , .
- becomes , .
- Left recursion hurts top-down parsers only. Left factoring does not remove ambiguity.
- binary operators of one flat grammar: Catalan number of trees.
Traps and GATE patterns
Connections
Unlocks
Ambiguity is also studied as a property of grammars in ambiguity in grammars. The transformed expression grammar is the one used to build the FIRST and FOLLOW sets (FIRST and FOLLOW) and the table of the LL(1) parser. Bottom-up parsers (LR parsing) accept the left-recursive layered grammar directly. The tree shape after removing left recursion is repaired by semantic actions, see translation schemes.
PYQs
8 previous year questions from your book. I re-solved each one myself (6 by running or simulating, 2 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider two grammars G1 and G2 with the
production rules given below:
G1: S → if E then S | if E then S else S | a
E → b
G2: S → if E then S | M
M → if E then M else S | c
E → b
where if, then, else, a, b, c are the terminals.
Which of the following option(s) is/are CORRECT?- (a)G1 is not LL(1) and G2 is LL(1).
- (b)G1 is LL(1) and G2 is not LL(1).
- (c)G1 and G2 are not LL(1).
- (d)G1 and G2 are ambiguous.
Consider the following parse tree for the expression a#b$c$d#e#f, involving two binary operators $ and #. Which one of the following is correct for the given parse tree?
- (a)$ has higher precedence and is left associative; # is right associative
- (b)# has higher precedence and is left associative; $ is right associative
- (c)$ has higher precedence and is left associative; # is left associative
- (d)# has higher precedence and is right associative; $ is left associative
Consider the following expression grammar G: E → E - T | T T → T + F | F F → (E) | id Which of the following grammars is not left recursive, but is equivalent to G?
- (a)E → E – T | T T → T + F | F F → (E) | id
- (b)E →T E' E'→ – TE' | ε T → T + F | F F → (E) | id
- (c)E → TX X →–TX | ε T → FY Y → +FY | ε F → (E) | id
- (d)E → TX | (TX) X → –TX | +TX | ε T → id
Which one of the following grammars is free from left recursion?
- (a)S → AB A → Aa | b B → c
- (b)S → A b | B b | C A → Bd | ε B →e COMPILER DESIGN
- (c)S→Aa | B A→B b | Sc | ε B→ d
- (d)S→Aa | Bb | c A→B d | ε B→ d | ε
Consider the grammar defined by the following production rules, with two operators * and + S → T * P T → U | T * U P → Q + P | Q Q → Id U → Id Which one of the following is TRUE?
- (a)+ is left associative, while * is right associative
- (b)+ is right associative, while * is left associative
- (c)Both + and * are right associative
- (d)Both + and * are left associative
Consider two binary operators '↑' and '↓' with the precedence of operator ↓ being lower than that of the operator ↑. Operator ↑ is right associative while operator ↓ is left associative. Which one of the following represents the parse tree for expression (7↓3↑4↑3↓2) ?
- (a)
- (b)
- (c)
- (d)COMPILER DESIGN
The grammar A → AA | (A) | ε is not suitable for predictive-parsing because the grammar is
- (a)ambiguous
- (b)left-recursive
- (c)right-recursive
- (d)an operator-grammar
Consider the context-free grammar
E → E + E
E → (E * E)
E → id
where E is the starting symbol, the set of terminals is
{id, (+,),*}, and the set of non-terminals is {E}.
For the terminal string id + id + id + id, how many
parse trees are possible?
COMPILER DESIGN- (a)5
- (b)4
- (c)3
- (d)2
Self-check
Question 1
Is ambiguous? Give the evidence.
Show answerHide
Yes. The string has two trees: with the left expanded to (grouping ) and with the right expanded to (grouping ).
Question 2
Remove the left recursion: .
Show answerHide
and .
Question 3
How many parse trees does give for ?
Show answerHide
Three operators, so the Catalan number .
Question 4
Does left factoring make an ambiguous grammar unambiguous?
Show answerHide
No: it changes the grammar but the language and the ambiguity of strings remain; the dangling else is still ambiguous after factoring.
Revision card
One-screen revision
- Ambiguous: two parse trees (two leftmost derivations) for some string; undecidable in general.
- Precedence by layers (tighter operator deeper), associativity by recursion side; dangling else by matched and unmatched statements.
- gives , ; use the ordering algorithm for indirect recursion.
- Left factor common prefixes: .
- Left recursion breaks top-down parsers, not LR. Factoring does not remove ambiguity.
References
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (2nd edition), Chapter 4 Syntax Analysis (4.2 context-free grammars, ambiguity, 4.3 writing a grammar: eliminating ambiguity, left recursion, left factoring)
- ·Hopcroft, Motwani, Ullman, Introduction to Automata Theory, Languages, and Computation, Chapter 5 Context-Free Grammars and Languages (ambiguity)
Verification
The two derivations and the number of parse trees are computed by exhaustive enumeration of leftmost derivations and re-counted with an independent span-counting method (scripts/enum-checks/cd_grammar.py): the flat expression grammar has a Catalan number of trees for n operators, the layered grammar never more than one. Left recursion removal and left factoring are implemented in scripts/cd_lib.py and their results on the grammars quoted below were compared with the page text; each transformed grammar was checked to generate the same strings (up to length 7) as the original. The statement that ambiguity of a general CFG is undecidable is a standard theorem quoted from textbooks, not machine-checked. 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)