现代编译原理——第二章:语法分析之LL(K)

  转自: http://www.cnblogs.com/BlackWalnut/p/4472122.html

  LL(K)语法分析技术是建立在预测分析的技术之上的。我们先来了解预测分析技术。考虑以下文法:

  

     当使用该文法对(1*2-3)+4和(1*2-3)进行分析,前者因该调用E->E+T,而后者应该调用E->T,怎么确定到底使用哪个产生式呢?这就要使用预测分析技术来构建预测分析语法分析器,LL(k)是其一种。预测分析技术的关键是构建一个无冲突的预测分析表。所谓预测分析表就是程序可以根据当前的状态来查询该表,然后确定下一步使用哪个产生式。

  构建预测分析表要要用到两个集合,分别是first集合和follow集合。γ是终结符和非终结符组成的字符串,first(γ)是从γ中可以推到出的任意字符串中所包含的开头终结符所组成的集合。A是一个非终结符,follow(A)的意思可以直接跟在A后面的所有终结符的集合。这两个集合的求法可以描述为如下:

    First集合的求法:

    First集合最终是对产生式右部的字符串而言的,但其关键是求出非终结符的First集合,由于终结符的First集合就是它自己,所以求出非终结符的First集合后,就可很直观地得到每个字符串的First集合。

1.  直接收取:对形如U-a…的产生式(其中a是终结符),把a收入到First(U)中
2.  反复传送:对形入U-P…的产生式(其中P是非终结符),应把First(P)中的全部内容传送到First(U)中。  
    Follow集合的求法:
    Follow集合是针对非终结符而言的,Follow(U)所表达的是句型中非终结符U所有可能的后随终结符号的集合,特别地,“#”是识别符号的后随符。
1.反复传送:对形如U-…P的产生式(其中P是非终结符),应把Follow(U)中的全部内容传送到Follow(P)中2.  直接收取:注意产生式右部的每一个形如“…Ua…”的组合,把a直接收入到Follow(U)中。
3.直接收取:对形如“…UPA…”(P是非终结符)的组合,把First(P)直接收入到Follow(U)中。如果P的first集合包含由空(ε),则first(A)也要放入Follow(U)中。
  需要注意的是,空是只能在First集合中不能在follow集合中。
  从上面的求法中可以知道,其实first集合就是一个非终结符的等价终结符的可选集合,也就是说A可以直接推到出这些终结符,如果first集合可以为空,则说明A可以直接忽略,这个时候,为了预测A为空后的情形,我们构建了follow集合。可见,者两个完全是为了预测分析而产生的集合。
  预测分析表是一个二维表,用非终结符符标注每行,用终结符好标注每一列,根据这first和follow两个集合,我们使用如下规则构建一个预测分析表:从产生式集合中取出一个产生式A->γ,如果终结符a在first(γ)中,则讲A->γ放入(A,a)确定的位置中,如果γ可以为空且a在follow(A)中,也则讲A->γ放入(A,a)确定的位置中。这样我们根据文法以下文法得到其相应的预测分析表:
                           
                         
  如何使用这个分析表呢?要知道,我们所分析的数据是由词法分析其产生的,对于语法分析器而言,词法分析器产生的数据都是终结符。我们利用堆栈(用到堆栈的地方一般也可以用递归来解决,可以参考虎书英文版的第47页代码)来记录当前正在分析的非终结符,从词法分析器得到一个终结符a,以及栈顶的数据来查表,然后确定使用的产生式,如果产生式的右面有非终结符,将其从右到左压栈(保证每次从对产生式的最左面的非终结符开始推到,称为左推到),然后继续使用使用a对栈顶元素分析。直到找到一个产生式的右只包含终结符a,然后将a抛弃,重新读取新的终结符,继续分析。如果在过程中无法得到只包含终结符a的产生式,那么说明有语法错误。
  上面的算法过程就是一个预测分析过程我们称为LL(1)算法,第一个L是left to right parse,第二个L是leftmost derivation ,1是1-symbol lookahead.意思就是从左到右的分析词法分析器产生的数据,采用最左推到原则,每次只看超前查看一个终结符来确定后续动作。
  但是就对上面这张表而言,(Z,d)的位置有两个产生式,那么说明该文法是二义的,说明它是不是LL(K)文法。就不能使用预测分析技术来解析该语言,要使用功能更强大的LR(k)技术。
   这里介绍两种可能产生二义性的例子,第一个为左递归。查看下左图,因为first(T)正好是first(E+T)的子集合,所以,一定会产生冲突。我们使用右图来替换原来的产生式,称为消除左递归。
                                                          
  还有一种情况,我们解决的办法称为提取右因子。
                                                          
  以上就是LL(K)文法的全部,相比LR(k)文法,它的功能不够强大(我们在LR(K)中进行比较),但是想要构造一个预测分析表却十分的简单。对于某些LR(k)处理不了的情况,我们可以快速的建立相应的LL(K)分析表来解决。
原文地址:https://www.cnblogs.com/jacksplwxy/p/10052758.html