我正在研究一个简单的LL(1)解析器生成器,并且考虑到某些输入语法,我遇到了PREDICT / PREDICT冲突的问题。例如,给定一个输入语法,如:
E → E + E
| P
P → 1
我可以从E
中删除左递归,用大致相当的右递归规则替换它,从而得出语法:
E → P E'
E' → + E E'
| ε
P → 1
接下来,我可以为语法计算相关的FIRST和FOLLOW集,最后得到以下结果:
FIRST(E) = { 1 }
FIRST(E') = { +, ε }
FIRST(P) = { 1 }
FOLLOW(E) = { +, EOF }
FOLLOW(E') = { +, EOF }
FOLLOW(P) = { +, EOF }
最后,使用PREDICT(A → α) = { FIRST(α) - ε } ∪ (FOLLOW(A) if ε ∈ FIRST(α) else ∅)
构造语法的PREDICT集,结果集如下。
PREDICT(1. E → P E') = { 1 }
PREDICT(2. E' → + E E') = { +, EOF }
PREDICT(3. E' → ε) = { +, EOF }
PREDICT(4. P → 1) = { 1 }
所以这就是我遇到PREDICT(2) = PREDICT(3)
冲突的地方,因此,我不能生成一个解析表,因为语法不是LL(1),因为解析器将无法选择哪个规则应该应用。
我真正想知道的是,是否有可能解决冲突或使语法因素可以避免冲突,并产生合法的LL(1)语法,而不必直接修改原始输入语法。 / p>
答案 0 :(得分:1)
这里的问题是你原来的语法含糊不清。
E → E + E
E → P
表示P + P + P
可以解析为(P + P) + P
或P + (P + P)
。消除左递归并不能解决歧义问题,因此修改后的语法也不明确。模棱两可的语法不能是LL(k)(或者就此而言,LR(k))。
所以你需要使语法明确:
E → E + P
E → P
(这是常见的左关联版本。)一旦消除了左递归,你最终得到:
E → P E'
E' → + P E'
| ε
现在+
不在FOLLOW(E')。
(这个例子直接来自龙书,但是已经简化了;它的例子是我在相当破旧的旧版本中的例子4.8。)
值得注意的是,此处使用的转换会保留由语法派生的字符串集,但不会保留派生。由修改后的语法产生的解析树实际上是右关联的,因此需要对其进行重新处理以恢复所需的解析。龙书作者简单地提到了这个事实:
尽管左递归消除和左分解很容易,但它们使得结果语法难以阅读且难以用于翻译目的。 (我的重点)
他们继续建议运算符优先级解析可用于表达式,然后提及如果LR解析器生成器可用,则将语法划分为预测部分并且不再需要运算符优先级部分。