自顶向下分析三个问题:
1、无左递归
2、无二义性
3、提取左公因子
为什么要定义LL(1)文法?
因为我们要自顶向下分析,适用于产生式的推导规则。
但是问题来了,我们如何确定使用哪个产生式去推导呢?
我们可以使用回溯,挨个尝试产生式。这就是递归下降的语法分析。
但是,效率太低了,怎么办?
我们在输入串中peek一个输入字符,如果我们能根据这个符号确定要使用哪个产生式去推导,就万事大吉了。
这时,如果我们使用递归下降的技术是尾递归的,可以接受。
如果我们能根据下一个字符确定使用哪个产生式,这个BNF就是LL(1)的。
现在的问题是,我们如何定量的描述我们的文法是不是LL(1)的呢?
需要引入First集。
我们要根据我们的产生式算出每个产生式的first集。这样,我们就可以根据产生式的first集确定使用哪个产生式。
如果我们的每一个非终结符号的所有产生式的first集都不相交,我们就能轻松的选择出下一步要使用的产生式。这时,我们的这个文法就是LL(1)的。
但是如果我们的产生式的fisrt集中有空怎么办?
这时,我们要通过这个产生式的fisrt集判断使用那个产生式就很困难了。 因为,我们可以使用任意遍产生出空的产生式。
那我们怎么选择呢?
显然,让我们做出选择的信息是不够的,怎么办?
我们需要引入Follow集。
因为,如果某个产生式能产生出空,那么我们peek的下一个字符,就可能是这个空产生式的下一个字符。所以,我们还要计算Follow集。
至此,我们已经可以描述我们的文法是否是LL(1)的了。
对于文法中所有具有相同左部的产生式A-> a | b;
(1) First(a) 交 First(b) = 空。
(2) 如果a=>空,First(b) 交 Follow(A) = 空。
(3) 如果b=>空,First(a) 交 Follow(A) = 空。
那么就到实现环节了!
我们如果高效的实现一个自顶向下分析程序?
预测分析法
现在要模拟我们手写推导的方式了。
思考我们手写推导时,当我们推导到中间某步时,我们的格局是一个最右句型。
首先,我们要记录当前的最右句型,我们采用什么数据结构呢?
思考我们推导时,总是选择最左边的非终结符号选择产生式进行推导,推导后依然推导最左非终结符号,思考我们拓展的方向,发现总是在左边删除一个非终结符,加入一个产生式,这不就是一个栈的结构吗!!!
所以,我们可以利用栈来保存我们当前的格局(最右句型) 栈中保存这个最右句型。
其实,这就是一个PDA呀(学过形式语言与自动机的同学会发现他们之间的相似性)
我们还要解决选择产生式的问题:即根据First和Follow集确定预测分析表
最后:
我们只需要根据读入输入符号,和栈顶的符号,查预测分析表,得到要用的产生式,压栈。