Property of a string or a grammar.
Ambiguous String
A string is ambiguous iff it has more than one derivation tree.
Unambiguous Grammar
A grammar is unambiguous iff every string has exactly one derivation tree.
Example:
Ambiguous Grammar
A grammar is ambiguous iff its not unambiguous (i.e. there exists a string in with multiple derivation trees). Ambiguity often arises due to operator precedence and associativity.
Some ambiguous grammars can be rewritten as an equivalent unambiguous grammar.
Associativity
Defines how operators of the same precedence are grouped.
Left Associative
Grouping from left to right.
Right Associative
Grouping from right to left.
Precedence
Defines order of evaluation. For example, multiplication has higher precedence than addition.
When precedence is equal, associativity resolves ambiguity.
Examples:
-
Left associative
-
Right associative
4^3^2 =
Proving Ambiguity
To prove a grammar is ambiguous, exhibit 1 string with 2 distinct leftmost derivations.
Approach
- Identify productions with 2 or more non-terminals on the RHS. Split-point ambiguity can only arise there.
- Determine what strings each non-terminal generates.
- Find a string derivable from such a production where the split between adjacent non-terminals is not unique.
- Construct 2 explicit leftmost derivations.
Example
Grammar:
From and , generates all strings of 1 or more s.
The production derives followed by 2 adjacent strings from . For the suffix , 2 valid splits exist:
So has 2 distinct leftmost derivations.
Derivation 1:
S ⟹ bSS ⟹ bSaS ⟹ baaS ⟹ baaa
Derivation 2:
S ⟹ bSS ⟹ baS ⟹ baSa ⟹ baaa
Parse tree 1:
S
/ | \
b S S
/ \ |
S a a
|
a
Parse tree 2:
S
/ | \
b S S
| / \
a S a
|
a
Examples
Grammar of arithmetic
Ambiguous because has 2 possible leftmost derivations.
The above grammar can be made unambiguous, using precedence + associativity:
Each non-terminal corresponds to 1 precedence level. Operators at lower precedence sit closer to in the hierarchy, so they appear higher in any parse tree and bind less tightly. Left-recursion on each rule (, ) enforces left associativity.
Dangling Else
Consider the grammar:
<stmt> ->
if (<expr>) <stmt>
| if (<expr>) <stmt> else <stmt>
| <other_stmt>
Example statement:
if (expr1) if (expr2) f(); else g();
Two interpretations:
if (expr1) {
if (expr2) f();
}
else g();
if (expr1) {
if (expr2) f();
else g();
}
Programming languages usually adopt rule “else associates with the closest if.”