您的位置:首页 > 编程语言 > PHP开发

详解NULLABLE、FIRSTPOS、LASTPOS和FOLLOWPOS的计算规则

2014-03-19 15:37 645 查看


转载请注明出处:http://www.cnblogs.com/dzodzo/archive/2009/12/15/1624225.html 

PDF版:http://www.fsderno.com/pdf/complier1.pdf 


引入

  正在上编译原理的课程,为了对抗遗忘,写下这篇文章加强自己的记忆,同时也希望能给大家带来帮助。

  在编译原理中,要把正则表达式转化为DFA,其中有一步就是要计算语法分析树上各结点的nullable、firstpos、lastpos和followpos。如果不理解其中的原理去背计算规则,这是一件非常痛苦的事情,本文的目的就是希望能说清楚两个问题,为什么要计算,如何计算。


前置知识:

  句子:给定文法G[Z],若有Z +=> x, 且x∈VT*;则称x是文法G[Z] 的句子 。 

  or结点:标号为并运算符|的内部结点。

  cat结点:标号为连接运算符·的内部结点。

  star结点:标号为星号运算符*的内部结点。

  位置:每个终结符对应一个位置。如图1。

 

 



 

 

 


计算NULLABLE(N)

  说完了前置知识,我们先说一下比较容易理解的nullable(n),为什么要计算nullable(n)呢?那是因为我们要根据结点n的nullable(n)函数的取值而采取不同的方法去计算firstpos和lastpost。说到这里可能有人要骂我了,“你这说了等于没说嘛”。希望大家稍安勿躁,因为问题是环环相扣的,我只是把解答放到后面了呵呵。

  nullable(n)表示以n为根结点推导出的句子集合是否包括空串,当推导出的句子集合包含空串,那么nullable(n)=true;如果不包含空串,那么nullable(n)=false。现在我们针对结点n的五种情况进行分析:

  1、当结点n是叶子结点并且取值为空,此时以n为根结点推导出的句子肯定为空,所以此时nullable(n)为true。如图2所示。

 



  2、当结点n是叶子结点并且取值为id,此时以n推导出的句子有非空值id,所以此时nullable(n)为false。

 



  3、当结点n是or结点,此时n结点肯定为内部结点。由于是或运算,当结点n的左子树(c1)或者右子树(c2)能推导出空串时,结点n也能推导出空串。即nullable(n)=nullable(c1) or nullable(c2)。以下三种情况都会使nullable(n)=true。(c1->ε表示c1能推导出空串ε,下同)



  4、当结点n是cat结点,此时n结点肯定为内部结点。由于是连接运算,当结点n的左子树(c1)和右子树(c2)能同时推导出空串,则结点n能推导出空串,即nullable(n)=nullable(c1) and nullable(c2)。以下一种情况nullable(n)=true。

 



  5、当结点n是star结点,此时n结点肯定为内部结点,由Kleene闭包运算的定义可知结点n推导出的句子集合包括空串,因此nullable(n)=true。


计算FIRSTPOS(N)

  说完了nullable的计算规则,我们来说下firstpos(n)。为什么要算firstpos?那是因为我们在为计算followpos做准备:)不要郁闷,继续往下看,你一定会知道终极的原因!firstpos(n)函数定义了以结点n为根的子树中的位置集合。这些位置对应于以结点n为根推导出的某个句子的第一个符号(“某个”代表可能有多个,因此firstpos的计算结果是位置的集合)。我们还是按照nullable的分析方法,以五种结点类型说明firstpos(n)的计算规则。

  1、当结点n为叶子结点并且为空串,因为是空串,所以此时没有第一个符号,即firstpos(n)={Ø}。



  2、当结点n为位置i的叶子结点。此时结点n只能推导出位置i的终结符,因此firstpos(n)={i}



  3、当结点n为or结点(内部结点),此时进行or运算,左子树(c1)推导出的第一个位置的集合firstpos(c1)包含于firstpos(n),同样右子树(c1)推导出的第一个位置的集合firstpos(c2)包含于firstpos(n)。因此firstpos(n)等于左右子树firstpos的并集。即firstpos(n)=firstpos(c1) U firstpos(c2)。



  4、当结点n为cat结点(内部结点),此时进行连接运算,若左子树不能推导出空串,那么结点n推导出的某个句子的第一个符号一定在firstpos(n)中。若左子树能推导出空串,那么第一个符号就有可能出现在右子树推导出的句子中。

  因此:if(nullable(c1))

      firstpos(n)=firstpos(c1) U firstpos (c2) //图9

      else

         firstpos(n)=firstpos(c1)  //图10



  5、当结点n为star结点(内部结点),结点n只有一个棵子树(c1),无论结点n的能不能推导出空串,firstpos(n)=firstpos(c1)。




计算LASTPOS(N)

  计算lastpos的规则在本质上和计算firstpos的规则相同,但是在针对cat结点的规则中,子树c1和c2的角色要对调。为了避免文章冗长,本文就不详述lastpos的计算规则了。


计算FOLLOWPOS(I)

  看完上文,相信你已经跨越了大部分障碍了,为了乘胜追击,我们继续介绍followpos的计算规则。在说明计算方法规则,先介绍一下为什么要计算followpos() 。回到本文的第二段,我们计算的目的是要把正则表达式转化为DFA,DFA有多个状态(对应本文的位置),而在一个状态n上,followpos(n)能告诉我们在当前状态下,能到达的下一个状态的集合。由此,只要知道起始的状态集合,我们就可以通过计算followpos构造出正则表达式的DFA了(具体的构造方法参看中文紫龙P113)。

  下面我们开始分析followpos(n)的计算规则,值得庆幸的是,这次讨论的情况只有两种。因为只有结点n为cat结点或者star结点会使得一个正则表达式的某个位置会跟在另外一个位置之后。or结点最终只会选择其中一棵子树来推导句子,所以两子树位置之间不存在依赖关系。

  1、当n是一个cat结点,此时进行连接运算,由此,左子树c1的lastpos集合中的每个位置的后面肯定跟着右子树c2的firstpos。如下图,lastpos(c1)={1,3}那么对于每个位置i,followpos(i)=first(c2),即follow(1)=follow(3)=first(c2)。



  2、当n是star结点,由闭包运算的定义我们知道lastpos(n)中的位置i的followpos集合等于firstpos(n)。这就像一条饥饿的蛇,它饿晕了,以为自己的尾巴是别的食物,它一口要上去,此时,它的头部接着他的尾部,这就跟star结点讨论的情况相似。为了说清楚我们还是再举一个简单的例子吧,如图,firstpos(n)={2},lastpos(n)={1,3},那么1后面可能会跟着2,即follow(1)={2}。follow(3)同理。



 


总结

  nullable(n)、firstpos(n)、lastpos(n)的计算规则总结:

  


  followpos(i)计算规则总结:

    ① 当n是一个cat结点,且其左右子树分别为c1、c2,那么对于lastpos(c1)中的所有位置i,firstpos(c2)中的所有位置都在followpos(i)中。

    ② 当n是一个star结点,且i是lastpos(n)中的一个位置,那么firstpos(n)中的所有位置都在followpos(i)中。

 


写在最后

  由于时间仓促,本文肯定会有不少疏漏之处,希望得到大家的提点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: