Ambiguous Grammar
Ambiguous grammar is a formal grammar that allows more than one parse tree for the same string. This ambiguity leads to multiple interpretations, making it unsuitable for parsing in compilers. Such grammars often cause issues in syntax analysis, affecting compiler construction and program execution.
1. Definition of Ambiguous Grammar
A grammar is ambiguous if there exists at least one string in the language it generates that has more than one distinct parse tree. This means there are multiple ways to derive the same string using different rule applications.
2. Common Causes of Ambiguity
- Lack of Operator Precedence: Ambiguous grammars often fail to specify precedence levels for operators, leading to multiple parse trees.
- Associativity Issues: Some grammars do not enforce left or right associativity, leading to different grouping of operators.
- Dangling-Else Problem: This arises when an
else
statement can be associated with multipleif
statements. - Recursive Definitions Without Clear Priority: If recursion is used without explicitly resolving conflicts, multiple derivations become possible.
3. Violations Caused by Ambiguous Grammars
Ambiguous grammars lead to multiple issues in compilers and language interpretation:
- Shift-Reduce and Reduce-Reduce Conflicts: Ambiguity in grammar creates conflicts in LR parsing tables, making it impossible to decide between shifting or reducing at a given point.
- Unclear Code Semantics: If a program can be parsed in multiple ways, it can lead to unintended behavior, particularly in expression evaluation.
- Parsing Errors: Ambiguous grammars cannot be handled by deterministic parsers like LL and LR without additional conflict resolution.
- Compiler Inefficiencies: Extra processing is required to resolve ambiguities, increasing compilation time.
4. Examples of Ambiguous Grammar
Consider the following expression grammar:
E → E + E | E * E | ( E ) | id
This grammar is ambiguous because it does not specify precedence or associativity of +
and *
. The string id + id * id
can have two parse trees:
4.1 Parse Tree 1 (Multiplication has Higher Precedence)
. E
/|\
E + E
/|\
E * E
id id id
4.2 Parse Tree 2 (Addition Evaluated First)
. E
/|\
E * E
/|\
E + E
id id id
The two parse trees indicate different evaluation orders, leading to ambiguity in interpretation.
5. Resolving Ambiguity
Ambiguity can be resolved using several methods:
- Operator Precedence and Associativity Rules: Define precedence levels to enforce a single parse tree. Example:
E → E + T | T
T → T * F | F
F → (E) | id
6. The Dangling-Else Ambiguity
Another classic case of ambiguity is the dangling-else problem:
stmt → if expr then stmt | if expr then stmt else stmt | other
Given the input:
if E1 then if E2 then S1 else S2
The ambiguity arises because else S2
could be associated with either the first or second if
.
Solution: Use separate rules for matched and unmatched statements:
stmt → matched_stmt | unmatched_stmt
matched_stmt → if expr then matched_stmt else matched_stmt | other
unmatched_stmt → if expr then stmt | if expr then matched_stmt else unmatched_stmt
This ensures each else
is correctly matched with the nearest unmatched if
.