三:Ambiguity:
上一次结尾时候提到ambiguity,在left most推导过程中出现了两种parse tree。
E E
-> E + E / \
-> E * E + E E + E
-> id * E + E / \ |
-> id * id + E E * E id
-> id * id + id | |
id id
同时我们又可以这么构建:
E E-> E * E / \
-> E * E + E E * E
-> id * E + E | / \
-> id * id + E id E + E
-> id * id + id | |
id id
这篇的主要目的是介绍一种解决ambiguity的方法,之所以有两种推到方式的原因是我们在替换non-terminal的时候很多选择,我们可以选择乘法,可以选择加法,当然我们可以说必须先选择排在左边的替换规则,如果该规则不适用,再往右边选择。但是这种规则会带来其它问题,这里主要不是介绍这种方法。
一个更常用的方法是重写规则,体现出优先级:
我们将 E -> E + E | E * E | (E) | id的规则改写为
E -> E' + E | E' (1)
E' -> id * E' | id | (E) * E' | (E) (2)
这里分离了+和*的规则, 第一个规则是我们可以可以最终得到 E' + E' + E' + ... + E', 第二个规则可以使我们得到(单看前两个production) id' * id * id * id * ... * id;
这样使得加法规则必然是在乘法规则的外围,或者说multiply binds more tight than plus rule. 从上面的例子可以看出,优先级高的运算符在树的更低层。
除了优先级之外还有结合性来解决解决ambiguity。
所以这里我们是通过重写规则来解决ambiguity问题,这同样也是一种非常non-trivial的工作,规则更难懂,而且并不是所有事情都可以用优先级解决的。所以大多数Parser都采用一些disambiguity mechanisms。