在 dragon book 中,LL语法定义如下:
当且仅当对于任何作品A -> a|b
时,语法为LL,以下两个条件适用。
FIRST(a)
和FIRST(b)
不相交。这意味着它们不能同时派生EMPTY
如果b
可以派生EMPTY
,则a
无法派生任何以FOLLOW(A)
开头的字符串,即FIRST(a)
和{{1}必须是不相交的。
我知道LL语法不能递归,但正式的原因是什么?我猜左递归语法会违反规则2,对吧?例如,我写了以下语法:
FOLLOW(A)
因为S->SA|empty
A->a
和FIRST(SA) = {a, empty}
,FOLLOW(S) ={$, a}
和FIRST(SA)
不是不相交的,所以这个语法不是LL。但是我不知道左递归是否使FOLLOW(S)
和FIRST(SA)
不相交,还是有其他原因?换句话说,每个左递归语法都会产生违反LL语法条件2的产品吗?
答案 0 :(得分:14)
好吧,我弄清楚,如果语法包含左递归生成,例如:
S->SA
然后它必须包含另一个产品来“完成”递归,比如说:
S->B
由于FIRST(B)是FIRST(SA)的子集,因此它们是联合的,这违反了条件1,在FIRST(B)和FIRST(SA)中填写与终端相对应的解析表条目时必定存在冲突)。总而言之,左递归语法可能导致两个或多个产品的FIRST集合具有公共终端,从而违反条件1.
答案 1 :(得分:11)
考虑你的语法:
S->SA|empty
A->a
这是三条规则的简写:
S -> SA
S -> empty
A -> a
现在考虑字符串aaa
。它是如何产生的?如果你没有预测,你一次只能读一个字符,所以你就这样开始(你有S
作为开始符号):
S -> SA
S -> empty
A -> a
很好,你已经制作了第一个a
。但现在你不能再申请任何规则,因为没有更多的非终端。你被困住了!
你应该做的是:
S -> SA
S -> SA
S -> SA
S -> empty
A -> a
A -> a
A -> a
但是如果不阅读整个字符串,你就不知道这一点。你需要无限量的前瞻。
在一般意义上,是,每个左递归语法都可以有不明确的字符串而没有无限的前瞻。再看一下这个例子:S
有两种不同的规则。我们应该使用哪一个?
答案 2 :(得分:9)
LL(k)
语法是允许构造仅具有前瞻k
符号的确定性下降解析器的语法。左递归的问题在于,在检查完整的输入字符串之前,无法确定应用哪个规则,这使得所需的k
可能无限。
使用您的示例,选择k
,并为解析器提供长度为n >= k
的输入序列:
aaaaaaa...
解析器无法通过查看前面的S->SA
符号来决定它是应该应用S->empty
还是k
,因为决定将取决于已选择S->SA
次的次数之前,这是解析器没有的信息。
解析器必须选择S->SA
完全n
次和S->empty
一次,并且通过查看第一个k
符号无法确定哪个是正确的输入流。
要知道,解析器必须同时检查完整的输入序列,并记录已选择S->SA
的次数,但这样的解析器将超出LL(k)
的定义
请注意,无限前瞻不是解决方案,因为解析器在有限的资源上运行,因此总会有一个有限的输入序列,其长度足以使解析器在生成任何输出之前崩溃。