深入理解
在哈工大的陈老师讲这个的时候(也就是21集),我觉得她已经将
- 为什么要有预测分析
- FIRST集合和FOLLOW集合的实际意义
- LL(1)文法的定义形成过程
都讲的十分清楚了。所以大家可以先去看看老师的视频,然后再来看我的文章。
FIRST集的定义
可从α推导得到的串的首符号的集合,其中α是任意的文法符号串。
FIRST集的实际意义
因为我们采用自顶向下的分析方法的时候,可能会遇到回溯。这个时候可能程序就会因为做出了一个错误的选择导致分析器的效率变慢。如果我们在每一步我们都能够预测出正确的选择的话,我们就不需要回溯。我们称这样的分析为预测分析。当然也不是所有的文法都可以采用预测分析的技术,LL(1)是可以采用预测分析技术的。
那么如何可以每次都能正确预测出下一步所要用到产生式呢?只要每一个产生式直接或者间接开头的非终结符各不相同就可以了。举个例子:
你自己可以尝试推导一遍,发现可以顺利进行。那是因为S->aBc,此时B是第一个非终结符,而B的产生式中也仅仅只有一个产生式是有d所开头。所以就可以迅速的采用B->dB这个产生式。这个也就是FIRST集合的实际意义。想像这个一个场景:
从文法开始符号开始推导,输入指针到d字符的时候,就是aBC。此时第一个非终结符就是B,然后我们查找B的产生式却有两个产生式都是都可以退出d,所以可以替换为adcC(采用B->dC推导出来)或者adB(采用B->dB推导)。所以,分析器就无法100%做出一个正确的判断。所以就引出了FIRST集合,所以能够使用预测分析的程序就得受到一些限制(这里就是要求文法中每一个非终结符A的各个产生式的FIRST集合两两不相交)
FIRST集的计算方法
方法就是教材上的方法,不过我在这里说几点要注意的:
- X->ABC的时候,如果FIRST(A)里面有ε的话,FIRST(X)也要包含FIRST(B)的所有非ε符号。
- ε会不会出现在FIRST(X)中,取决于X->ABC这个产生式可不可以推导出ε,而不是依赖别人的FIRST集中的ε。
FOLLOW集的定义
FOLLOW(A)集合是所有紧跟A之后的终结符或#所组成的集合(#是句尾的标志)
FOLLOW集的实际意义
是不是只要有FIRST集合就可以完成预测分析的任务了呢?在视频中,老师举了一个例子(具体见视频,后面我也讲到了这个例子),在一个非终结符的FIRST集合中包含ε的时候,就仅仅使用FIRST就不行了。这点在《编译原理》蒋宗礼老师这本书中P128中也举例了。所以我们引入了FOLLOW集合
当产生式中有ε的时候,可以如果当前的最左非终结符的所有非ε产生式都与当前的输入符号不匹配,那么就可以选择ε产生式来处理。这样是当前的最左非终结符就会往后移动,可以寻找别的来匹配了。
为什么FOLLOW集合会有这样的效果捏?还是举例说明
我们这里测试ada这个输入串。在推导出adBC的时候,此时输入指针指向a,第一个非终结符B的FIRST集合中没有与对应的,所以我们采用B->ε来让第一个非终结符移到C上。你可以想像为这里的B->ε是斗地主中的癞子,它可以和任何东西匹配。然后现在第一个非终结符C的FIRST集合中包含a,所以最后推导成功。但是输入串ade就不能在你使用B->ε之后,匹配成功。具体我就不分析了,大家可以自行分析。
在分析ada的时候,我们使用空产生式可以奏效,但是分析ade的时候却不能。就是因为在S->aBC的时候,C在B的后面。所以,如果一旦使用了S->aBC这个产生式的时候,就隐含着C的FIRST集合的终结符一定是在B后面出现的。也就是说,如果采用B->ε之后,就相当于采用C的FIRST集合来匹配现在的输入串。其实本质上就是然后多看的了一个字符,只不过这里不是在输入串的这里多看一个字符,而是在匹配串这里多看一个字符。当然,不引入FOLLOW集合,在输入符号串多看一个字符也是可以解决这个问题,至于为什么没有这么做,我觉得应该也有原因。但是也有LL(2)这样的文法。
FOLLOW集的计算方法
我平时在写这样的题目的时候,就是
- 从后往前扫描每一个表达式
- 找到每一个表达式中非终结符
- 然后根据规则来做
就是可能这样的规则不太好理解:
如果存在一个产生式A→αB,或存在产生式A→αBβ且first(β)包含ε,那么follow(A)中的所有符号都在follow(B)中。
就是为什么FOLLOW(A)中的元素赋值給FOLLOW(B)。因为A出现的地方都可转化为aB。例如,C->Aa;此时A的FOLLOW(A)中有a。此时将A->cB转化,就是C->cBa,那么不就是说,FOLLOW(B)中有a吗。这一点和算符优先中LASTVT集合有点区别。
预测分析表的实质
在哈工大老师的讲课视频中,他提出了一个龙书上面没有的概念,也就是SELECT集。其实预测分析表就是一个SELECT。我们为什么需要预测分析表,希望根据当前的最左非终结符A和当前的输入符号a,来选择一个正确的A-产生式。因为如果A-的产生式有多个的话,我们就不得不去“试探的”选择一个A-产生式,然后来判断这个选择是否正确,如果正确的,那自然就好。但是如果不是正确的,就必须到之前的分叉点来重新做出选择。其实可以理解为一个dfs的过程。但是dfs的效率不高,这个也就是为什么人们在dfs种有一个重大的优化就是“剪枝”。我们要做的就是“剪枝”就绝了,直接将每一个分叉路口的选择减少为一个,那么我们每一次就不用选择了,因为仅仅只有一条路径可以选择了。这样做的前提就是文法属于LL文法,我们采用提前读取一个字符的操作来达到预测的效果。这个又依赖与每一个具有相同左部的产生式的右部的FIRST的集合交集为空集。因为如果有了交集之后,你提前预读的一个字符可能还是无法确定唯一的一个产生式。
LL(1)文法的判断
暂时没有写,之后会补充!