先决条件:上下文无关语法,二义性语法,二义性和非二义性语法之间的区别,运算符的优先级和结合性,递归语法
在这篇文章中,我们将看到使用合适的例子从语法中消除歧义。
歧义与明确语法:
具有不止一个派生树或解析树的文法 是有歧义的语法。这些语法不被任何解析器解析。
例子-
1.考虑下图所示的生产——
S->aSbS | bSaS | ∈
比如说,我们想从上面的语法中生成字符串“abab” 。我们可以观察到给定的字符串可以使用两个解析树导出。所以,上面的语法是有歧义的。
只有一棵派生树或解析树的文法称为无歧义文法。
2.考虑下图所示的制作——
S -> AB
A -> Aa | a
B -> b
对于字符串“aab”,我们只有一个用于上述语法的解析树,如下所示。
需要注意的是,没有直接的算法来确定语法是否有歧义。我们需要为给定的输入字符串构建解析树,该输入字符串属于语法生成的语言,然后根据如上所述获得的解析树的数量来决定语法是模糊的还是无歧义的。
注 –必须仔细选择字符串,因为在只有一个解析树的无歧义文法生成的语言中可能有一些可用的字符串。
消除歧义:
我们可以仅基于以下两个属性来消除歧义——
1.优先级——
如果使用不同的运算符,我们将考虑运算符的优先级。三个重要的特征是:
- 生产出现的级别表示所用运算符的优先级。
- 更高级别的生产将具有较低优先级的运算符。在解析树中,位于顶层或靠近根节点的节点将包含较低优先级的运算符。
- 较低级别的生产将有更高优先级的运算符。在解析树中,处于较低级别或靠近叶节点的节点将包含较高优先级的运算符。
2.关联性——
如果在生产中使用相同的优先级运算符,那么我们将不得不考虑结合性。
- 如果结合性是从左到右,那么我们必须在产生式中提示左递归。解析树也将保持递归并在左侧增长。
+、-、*、/ 是左结合运算符。 - 如果结合性是从右到左,那么我们必须在产生式中提示正确的递归。解析树也将向右递归并在右侧增长。
^ 是一个右结合运算符。
示例 1 –考虑二义性语法
E -> E-E | id
The language in the grammar will contain { id, id-id, id-id-id, ….}
比如说,我们想要导出字符串id-id-id。让我们考虑 id=3 的单个值以获得更多见解。结果应该是:
3-3-3 =-3
Since the same priority operators, we need to consider associativity which is left to right.
解析树 –生长在根左侧的解析树将是正确的解析树,以使语法明确。
因此,为了使上述语法无歧义,只需将产生式右侧最左边的非终结符 E 替换为另一个随机变量P来使语法左递归。语法变为:
E -> E – P | P
P -> id
上面的语法现在是明确的,并且将只包含上面表达式的一个解析树,如下所示 –
类似地,表达式 : 2^3^2的明确语法将是 –
E -> P ^ E | P // Right Recursive as ^ is right associative.
P -> id
示例 2 –考虑下面显示的语法,它有两个不同的运算符:
E -> E + E | E * E | id
显然,上面的语法是有歧义的,因为我们可以为字符串“id+id*id”绘制两个解析树,如下所示。考虑表达式:
3 + 2 * 5 // “*” has more priority than “+”
The correct answer is : (3+(2*5))=13
优先级最低的“+”必须在上层,并且必须等待下层“*”运算符产生的结果。因此,第一个解析树是正确的,并给出与预期相同的结果。
无歧义文法将包含在较低级别具有最高优先级运算符(示例中的“*”)的产生式,反之亦然。两个运算符是Left to Right 。因此,无歧义文法必须保持递归。语法将是:
E -> E + P // + is at higher level and left associative
E -> P
P -> P * Q // * is at lower level and left associative
P -> Q
Q -> id
(or)
E -> E + P | P
P -> P * Q | Q
Q -> id
E用于执行加法运算, P用于执行乘法运算。它们是独立的,将保持解析树中的优先顺序。
字符串“id+id*id+id”的解析树将是——
注意:需要注意的是,在将歧义语法转换为无歧义语法时,我们不应该更改歧义语法提供的原始语言。因此,歧义文法中的非终结符必须用其他变量替换,这样我们才能得到与之前派生的语言相同的语言,并且同时保持优先级和结合性规则。
这就是我们在上面的示例中替换它们后编写产生式E -> P and P -> Q and Q -> id 的原因,因为该语言也包含字符串{ id, id+id } 。
类似地,具有运算符-,*,^的表达式的明确语法是:
E -> E – P | P // Minus operator is at higher level due to least priority and left associative.
P -> P * Q | Q // Multiplication operator has more priority than – and lesser than ^ and left associative.
Q -> R ^ Q | R // Exponent operator is at lower level due to highest priority and right associative.
R -> id
此外,还有一些歧义文法无法转换为无歧义文法。