Theory of Computation & Computer Graphics β Introduction to Context Free Language, NEC licence examination syllabus (Nepal Engineering Council).
Ambiguous Grammar
When one string has two parse trees, it has two meanings β and a compiler cannot choose between them.
π Where this lives: The "dangling else" problem is the most famous ambiguity in programming language design, and it is still present in C, C++, Java and C# today. The languages resolve it by a rule stated in the specification β an else binds to the nearest unmatched if β rather than by fixing the grammar, which is why every one of those languages has a well-known source of beginner bugs that a different grammar design would have eliminated. Search "dangling else ambiguity C Java language specification".
Definition and the classic examples
A CONTEXT-FREE GRAMMAR IS AMBIGUOUS IF SOME STRING IN ITS
LANGUAGE HAS MORE THAN ONE DERIVATION TREE.
EQUIVALENT FORMULATIONS, all standard:
Β· some string has two or more distinct parse trees
Β· some string has two or more distinct LEFTMOST derivations
Β· some string has two or more distinct RIGHTMOST derivations
NOTE WHAT AMBIGUITY IS **NOT**: having two different
derivations is harmless if they produce the SAME tree, since
that reflects only the order in which variables were expanded.
AMBIGUITY IS ABOUT STRUCTURE, NOT ABOUT DERIVATION ORDER.
WHY IT MATTERS:
THE TREE DETERMINES THE MEANING. Two trees for one string
means two possible meanings, and a compiler has no principled
basis for choosing. A grammar that permits this cannot serve
as a language definition.
ββ EXAMPLE 1: THE EXPRESSION GRAMMAR βββββββββββββββββββββββ
E β E + E | E * E | id
The string id + id * id has TWO parse trees:
TREE A TREE B
E E
/ | \ / | \
E + E E * E
| /|\ /|\ |
id E * E E + E id
| | | |
id id id id
= id + (id * id) = (id + id) * id
WITH id = 2, 3, 4: TREE A gives 2 + 12 = 14, TREE B gives
5 Γ 4 = 20. THE SAME STRING, TWO ANSWERS β which is why this
is not a theoretical concern.
THE SAME GRAMMAR IS ALSO AMBIGUOUS FOR ASSOCIATIVITY: the
string id + id + id has two trees, grouping left or right.
For + it does not matter numerically; FOR β OR / IT MATTERS
ENORMOUSLY, since (8 β 3) β 2 = 3 while 8 β (3 β 2) = 7.
THE NUMBER OF TREES GROWS ALARMINGLY. For n operands with this
grammar the count is the (nβ1)α΅Κ° CATALAN NUMBER:
3 operands β 2 trees
4 operands β 5 trees
5 operands β 14 trees
6 operands β 42 trees
AMBIGUITY DOES NOT MERELY PERMIT A SECOND READING; IT
EXPLODES COMBINATORIALLY WITH EXPRESSION LENGTH.
THE FIX β the LAYERED GRAMMAR from the CFG topic:
E β E + T | T
T β T * F | F
F β ( E ) | id
Precedence is enforced by the LEVELS, associativity by the
DIRECTION of the recursion. THE LANGUAGE IS UNCHANGED; ONLY
THE GRAMMAR IS DIFFERENT β and it now has exactly one tree
per string.
ββ EXAMPLE 2: THE DANGLING ELSE ββββββββββββββββββββββββββββ
S β if E then S
| if E then S else S
| a
The string if E then if E then a else a has TWO trees:
TREE 1 β the else belongs to the INNER if:
if E then ( if E then a else a )
TREE 2 β the else belongs to the OUTER if:
if E then ( if E then a ) else a
THESE ARE GENUINELY DIFFERENT PROGRAMS. In tree 1 the else
branch runs when the inner condition fails; in tree 2 it
runs when the outer condition fails.
THE UNAMBIGUOUS GRAMMAR, which enforces "else binds to the
nearest unmatched if":
S β M | U
M β if E then M else M | a (MATCHED)
U β if E then S
| if E then M else U (UNMATCHED)
THE IDEA: between a then and its else, only a FULLY MATCHED
statement may appear, so an inner if without an else cannot
swallow an outer else.
IN PRACTICE most languages keep the ambiguous grammar and
resolve it with a disambiguating rule, because the
unambiguous version is markedly harder to read. YACC RESOLVES
SHIFT-REDUCE CONFLICTS BY PREFERRING SHIFT, WHICH HAPPENS TO
IMPLEMENT "NEAREST IF" AUTOMATICALLY β a pragmatic accident
that became the convention.
ββ OTHER STANDARD AMBIGUOUS GRAMMARS βββββββββββββββββββββββ
S β SS | a | Ξ΅
The string aa has many trees, and the Ξ΅ production makes
the count infinite.
S β aS | Sa | a
The string aa has two trees.
S β a | Sb | bS
Multiple derivations for strings such as bab.
Inherent ambiguity, and how to detect it
ββ THE CRITICAL DISTINCTION ββββββββββββββββββββββββββββββββ
AMBIGUITY IS A PROPERTY OF THE **GRAMMAR**, NOT OF THE
LANGUAGE.
A language may have an ambiguous grammar AND an unambiguous
one β as the expression language does. Rewriting the grammar
fixes the problem.
BUT SOME LANGUAGES ARE **INHERENTLY AMBIGUOUS**: EVERY grammar
generating them is ambiguous, and no rewriting can help.
THE STANDARD EXAMPLE:
L = { aβΏbβΏcα΅dα΅ : n,m β₯ 1 } βͺ { aβΏbα΅cα΅dβΏ : n,m β₯ 1 }
The first set matches a's with b's and c's with d's; the
second matches a's with d's and b's with c's. A STRING OF
THE FORM aβΏbβΏcβΏdβΏ BELONGS TO BOTH SETS, and any grammar
must derive it in both ways β through the structure that
generates the first set and through the structure that
generates the second. THE TWO DERIVATIONS REFLECT TWO
GENUINELY DIFFERENT WAYS THE STRING SATISFIES THE
DEFINITION, and no grammar can suppress one.
INHERENTLY AMBIGUOUS LANGUAGES ARE NEVER DETERMINISTIC
CONTEXT-FREE, so they cannot be parsed by any deterministic
pushdown automaton β which connects this topic to the PDA
material later in the section.
ββ DETECTING AMBIGUITY βββββββββββββββββββββββββββββββββββββ
THE FUNDAMENTAL AND UNCOMFORTABLE FACT:
DECIDING WHETHER AN ARBITRARY CONTEXT-FREE GRAMMAR IS
AMBIGUOUS IS **UNDECIDABLE**.
There is no algorithm that takes any CFG and correctly answers
"ambiguous or not" in all cases. This is proved by reduction
from Post's Correspondence Problem, which appears in the
Turing machine section.
SO IS DECIDING WHETHER A LANGUAGE IS INHERENTLY AMBIGUOUS.
WHAT CAN BE DONE IN PRACTICE:
Β· FIND A WITNESS. To prove a specific grammar ambiguous,
exhibit ONE string with two trees. THIS IS ALWAYS THE EXAM
TASK, and it is easy β the difficulty lies only in the
general decision problem.
Β· USE A PARSER GENERATOR. If yacc reports SHIFT-REDUCE or
REDUCE-REDUCE CONFLICTS, the grammar is not LALR(1), which
is often (though not always) because it is ambiguous.
CONFLICTS ARE A SYMPTOM, NOT A PROOF.
Β· RESTRICT TO A KNOWN-UNAMBIGUOUS CLASS. Every LL(k) and
every LR(k) grammar is unambiguous BY CONSTRUCTION,
because the parsing algorithm is deterministic and can
therefore produce only one tree. THIS IS THE PRACTICAL
ROUTE: design the grammar to be LR(1) and ambiguity cannot
arise.
ββ REMOVING AMBIGUITY β THE TECHNIQUES βββββββββββββββββββββ
1. IMPOSE PRECEDENCE BY LAYERING. One variable per precedence
level, tighter-binding operators lower down. This is the
expression grammar fix.
2. IMPOSE ASSOCIATIVITY BY THE DIRECTION OF RECURSION:
LEFT-recursive A β A op B gives LEFT associativity
RIGHT-recursive A β B op A gives RIGHT associativity
Exponentiation is normally right-associative, which is why
its rule is written the other way round from + and *.
3. MATCH CONSTRUCTS EXPLICITLY, as in the matched/unmatched
statement grammar for the dangling else.
4. ADD EXPLICIT DELIMITERS to the language itself. Requiring
endif, or braces around every branch, eliminates the
dangling else by making the grammar unable to express the
ambiguous form β WHICH IS WHY LANGUAGES LIKE ADA AND MODERN
STYLE GUIDES REQUIRE THEM.
5. DECLARE PRECEDENCE IN THE PARSER GENERATOR: yacc's %left
and %right directives resolve conflicts without changing
the grammar.
ββ THE SUMMARY WORTH CARRYING AWAY βββββββββββββββββββββββββ
AMBIGUITY IS A DESIGN DEFECT IN A GRAMMAR, USUALLY
REPAIRABLE BY RESTRUCTURING; INHERENT AMBIGUITY IS A
PROPERTY OF THE LANGUAGE ITSELF AND IS NOT REPAIRABLE AT
ALL. Programming languages are designed to avoid the second
case entirely, which is a deliberate act of language design
rather than a happy accident.
Ambiguity is a property of the grammar, and usually repairable by restructuring β the expression language has both an ambiguous grammar and an unambiguous one for the same set of strings. Inherent ambiguity is a property of the language, and no rewriting helps. Programming languages are deliberately designed to avoid the second case.
π Go further: The C++ grammar contains an ambiguity that no amount of restructuring fixes, because resolving it requires information a parser does not have. The statement T * p; is a multiplication if T is a variable and a pointer declaration if T is a type β and which it is depends on declarations elsewhere in the file. This is the "most vexing parse" family of problems, and it is why C++ parsers must consult the symbol table during parsing rather than after it, breaking the clean separation of phases that the Chomsky hierarchy suggests. The ambiguity is not in the grammar; it is in the language's decision to reuse the same syntax for two constructs. Search "most vexing parse C++ ambiguity type name lexer hack".
π‘ Exam angle: define ambiguity in terms of two parse trees (equivalently, two leftmost derivations) and stress that two derivations giving the same tree are not ambiguity. The guaranteed question is to prove a given grammar ambiguous by exhibiting one string and drawing both trees β id + id * id with E β E+E | E*E | id is the standard case, and show the numerical difference. Know the dangling else and its matched/unmatched fix. Give the removal techniques: layering for precedence, direction of recursion for associativity. State that ambiguity of a CFG is undecidable in general, and that inherently ambiguous languages exist, with the aβΏbβΏcα΅dα΅ βͺ aβΏbα΅cα΅dβΏ example.
Syllabus points
Definition of ambiguity
Showing a grammar is ambiguous
Removing ambiguity
Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Related topics in Introduction to Context Free Language