开发者社区> XOSG> 正文

编译原理FIRST、FOLLOW、SELECT集の通俗解释

简介:
+关注继续查看

1.为什么要引入FIRST集的概念?

  • 因为有公共左因子的问题,公共左公因子是指在文法的产生式集合中,某个非终结符的多个候选式具有相同的前缀。
  • 一般来说,公共左公因子的产生式为 
    Aαβ1αβ2A→αβ1│αβ2
  • 如果有公共左因子的问题,那么只能采取试探的方法来分析每一个候选式,分析的过程很可能产生回溯,回溯分析法是一种不确定的方法。
  • 若所有候选式都没有公共左因子就可以选择惟一匹配的候选式,不会产生(公共左公因子引起的)回溯。
  • 为了消除回溯,对任何一个非终结符和当前的待匹配符号,期望 
    • 要么只有一个候选式可用
    • 要么没有候选式可用

因此引入以下FIRST集合的概念:

  • α(VTVN)α∈(VT⋃VN)∗,有 
    FIRST(α){a|αa,aVT}FIRST(α)={a|α⟹∗a⋅⋅⋅,a∈VT}

    特别地,若αεα⟹∗ε,则εFIRST(α)ε∈FIRST(α)

因此对于每一文法符号 XVTVNX∈VT⋃VN,构造FIRST(X)FIRST(X)的方法为: 
使用下列规则,直至每个FIRST集不再增大为止:

  • XVTX∈VT,则FIRST(X)={X}FIRST(X)={X}(意思是如果XX是终结符,则其FIRSTFIRST集合为其自身);
  • 若 XVNX∈VN,那么XX的产生式分以下三种情况:

    • XεX→ε
    • εFIRST(X)ε∈FIRST(X)
    • XaX→a⋅⋅⋅
    • aFIRST(X)a∈FIRST(X)
    • XYX→Y⋅⋅⋅,且YVNY∈VN
    • FIRST(Y){ε}FIRST(X)FIRST(Y)–{ε}⊆FIRST(X)

    • 特例: XY1Y2Yi1YiYkX→Y1Y2⋅⋅⋅Yi−1Yi⋅⋅⋅Yk 
      且 Y1,Y2,Yi1VNY1,Y2,⋅⋅⋅Yi−1∈VN 
      Y1,Y2,Yi1εY1,Y2,⋅⋅⋅Yi−1⟹∗ε

      • FIRST(Yj){ε}FIRST(X)(1ji1)FIRST(Yi)FIRST(X)FIRST(Yj)–{ε}⊆FIRST(X)(1≤j≤i−1),FIRST(Yi)⊆FIRST(X)
    • 特别地,当Y1Y2Yi1YiYkεY1Y2⋅⋅⋅Yi−1Yi⋅⋅⋅Yk⟹∗ε

      • 则 εFIRST(X)ε∈FIRST(X)

结论:针对无空产生式的文法G,同一非终结符的任两个产生式的右部符号串的FIRST集无交集,即可进行确定的自顶向下分析。

2.为什么要引入FOLLOW集的概念?

考虑文法G[S]: 
SaAS→aA 
SdS→d 
AbASA→bAS 
AεA→ε 
求得各终结符和符号串的FIRST集合如下: 
FIRST(S)={a,d}FIRST(S)={a,d} 
FIRST(A)={b,ε}FIRST(A)={b,ε} 
FIRST(aA)={a}FIRST(aA)={a} 
FIRST(d)={d}FIRST(d)={d} 
FIRST(bAS)={b}FIRST(bAS)={b} 
FIRST(ε)={ε}FIRST(ε)={ε} 
若输入串W=abdW=abd,则试图推导出abd串的推导过程为SaAabASabSabdS⇒aA⇒abAS⇒abS⇒abd 
从以上推导过程中可以看到,在第2步到第3步的推导中,即abASabSabAS⇒abS时,因为当前面临的输入符号为dd,但是最左非终结符AA的产生式右部的开始符号集都不包含dd,但有εε,因此对于dd的匹配自然认为只能依赖于在可能的推导过程中AA的后面的符号,所以这时候选用产生式AεA→ε向下推导。而当前AA后面的符号为SSSS产生式右部的开始符号集包含了dd,所以例子中可用SdS→d推导得到匹配。 
语法树如下所示: 
这里写图片描述 
很显然,我们从以上叙述中可以得出: 
当某一非终结符的产生式中含有空产生式时,它的非空产生式右部的开始符号集两两不相交,并与在推导过程中紧跟该非终结符右部可能出现的终结符集也不相交,则仍可构造确定的自顶向下分析。因此,引入了一个文法符号的后跟符号集合。 
引入以下FOLLOW集的概念:

  • AVNA∈VN,有 
    FOLLOW(A)={a|SAaaVT}FOLLOW(A)={a|S⟹∗⋅⋅⋅Aa⋅⋅⋅,a∈VT} 
    SAS⟹∗⋅⋅⋅A,则规定#FOLLOW(A)#∈FOLLOW(A) 
    这里用#作为输入串的结束符,也称为输入串括号。

因此对于每一文法符号 AVNA∈VN,实际上求FOLLOW(A)FOLLOW(A) 
就是考察A在产生式右端的出现情况,哪些终结符号可以跟随在A后面?

使用下列规则,直至每个FOLLOW集不再增大为止:

  • 首先,设S为文法的开始符号,把{#}{#}加入FOLLOW(S)FOLLOW(S)中(这里#为句子括号)
  • BαAβB→αAβ是一个产生式,则 FIRST(β){ε}FOLLOW(A)FIRST(β)−{ε}⊆FOLLOW(A)
  • 如果BαAB→αA或者BαAβB→αAββεβ⟹∗ε,则 FOLLOW(B)FOLLOW(A)FOLLOW(B)⊆FOLLOW(A) 
    • 解释: 因为在推导过程中可能出现如下的句型序列:
    • Sα1Bβ1α1αAββ1α1αAβ1S⇒∗⋅⋅⋅α1Bβ1⋅⋅⋅⇒⋅⋅⋅α1αAββ1⋅⋅⋅⇒⋅⋅⋅α1αAβ1⋅⋅⋅

例题: 
ABCc | gDBA→BCc | gDB 
BbCDE | εB→bCDE | ε 
CDaB | caC→DaB | ca 
DdD | εD→dD | ε 
EgAf | cE→gAf | c

非终结符 FIRST FOLLOW
A {a, b, c, d, g} {f, #}
B {b, εε} {a, c, d, g, f, #}
C {a, c, d} {c, d, g}
D {d, εε} {a, b, c, g, f, #}
E {c, g} {a, c, d, g, f, #}

对于FIRST集合,解释其中的FIRST(A)的求解。

  • ABCcA→BCc属于上述产生式的特例情况,很显然BεCεB⇒∗ε,C⇏∗ε,因此 
    (FIRST(B){ε})FIRST(C)FIRST(A)(FIRST(B)−{ε})⋃FIRST(C)⊆FIRST(A)
  • AgDBA→gDB属于上述产生式的第二种情况,因此gFIRST(A)g∈FIRST(A)
  • 最后得出: 
    FIRST(A)=(FIRST(B){ε})FIRST(C){g}FIRST(A)=(FIRST(B)−{ε})⋃FIRST(C)⋃{g}
  • FIRST(B)={b,ε}FIRST(C)={a,c,d}FIRST(B)={b,ε},FIRST(C)={a,c,d}
  • FIRST(A)={a,b,c,d,g}FIRST(A)={a,b,c,d,g}

对于FOLLOW集合,有如下的计算情况:

FOLLOW(A)=(FIRST(f){ε}){#}={f,#}FOLLOW(A)=(FIRST(f)−{ε})⋃{#}={f,#}

FOLLOW(B)   =(FIRST(Cc){ε})FOLLOW(A)FOLLOW(C)={a,c,d}{f,#}FOLLOW(C)={a,c,d}{f,#}{c,d,g}={a,c,d,g,f,#}FOLLOW(B)=(FIRST(Cc)−{ε})⋃FOLLOW(A)⋃FOLLOW(C) ={a,c,d}⋃{f,#}⋃FOLLOW(C) ={a,c,d}⋃{f,#}⋃{c,d,g} ={a,c,d,g,f,#}

FOLLOW(C)  =(FIRST(c){ε})(FIRST(DE){ε})={c}(FIRST(D){ε})FIRST(E)={c,d,g}FOLLOW(C)=(FIRST(c)−{ε})⋃(FIRST(DE)−{ε}) ={c}⋃(FIRST(D)−{ε})⋃FIRST(E) ={c,d,g}

FOLLOW(D)  =(FIRST(B){ε})FOLLOW(A)(FIRST(E){ε})(FIRST(aB){ε})={b}{f,#}{c,g}{a}={a,b,c,g,f,#}FOLLOW(D)=(FIRST(B)−{ε})⋃FOLLOW(A)⋃(FIRST(E)−{ε})⋃(FIRST(aB)−{ε}) ={b}⋃{f,#}⋃{c,g}⋃{a} ={a,b,c,g,f,#}

FOLLOW(E) =FOLLOW(B)={a,c,d,g,f,#}FOLLOW(E)=FOLLOW(B) ={a,c,d,g,f,#}

3.为什么要引入SELECT集的概念?

由于从2中我们得出结论: 
当某一非终结符的产生式中含有空产生式时,它的非空产生式右部的开始符号集两两不相交,并与在推导过程中紧跟该非终结符右部可能出现的终结符集也不相交,则仍可构造确定的自顶向下分析。 
因此当文法中含有形如: 

AαAβA→αA→β

的产生式时,其中AVN,αβVA∈VN,α、β∈V∗,若ααββ不能同时推导出空,假定αεβεα⇏∗ε,β⇒∗ε,则当 
FIRST(α)FOLLOW(A)= FIRST(β)FOLLOW(A)=FIRST(α)⋂FOLLOW(A)=∅ FIRST(β)⋂FOLLOW(A)=∅

也即是FIRST(α)(FIRST(β)FOLLOW(A))=FIRST(α)⋂(FIRST(β)⋃FOLLOW(A))=∅时,对于非终结符A的替换仍可唯一地确定候选。为了表示和分析方便,因此引入了SELECT集合。 
SELECT集合定义如下:

  • 一个产生式的选择符号集SELECT。给定上下文无关文法的产生式Aα,AVN,αVA→α,A∈VN,α∈V∗,若αεα⇏∗ε,则SELECT(Aα)=FIRST(α)SELECT(A→α)=FIRST(α)

  • 如果αεα⇒∗ε,则SELECT(Aα)=(FIRST(α){ε})FOLLOW(A)SELECT(A→α)=(FIRST(α)−{ε})⋃FOLLOW(A)

因此一个上下文无关文法是LL(1)文法的充分必要条件是,对每个非终结符A的两个不同产生式,AαAβA→α,A→β,满足 

SELECT(Aα)SELECT(Aβ)=SELECT(A→α)⋂SELECT(A→β)=∅

其中αβα、β不同时能ε⇒∗ε

再次回到上述例题

ABCc | gDBA→BCc | gDB 
BbCDE | εB→bCDE | ε 
CDaB | caC→DaB | ca 
DdD | εD→dD | ε 
EgAf | cE→gAf | c


非终结符 FIRST FOLLOW
A {a, b, c, d, g} {f, #}
B {b, εε} {a, c, d, g, f, #}
C {a, c, d} {c, d, g}
D {d, εε} {a, b, c, g, f, #}
E {c, g} {a, c, d, g, f, #}


右部产生式 FIRST
BCc {a, b, c, d}
gDB { g }
bCDE { b }
εε {εε}
DaB {a, d}
ca { c }
dD { d }
gAf { g }
c { c }

  因此根据以上所求得的FIRST集和FOLLOW集,可求得各产生式的SELECT集合如下: 

         SELECT(ABCc)={a,b,c,d}SELECT(AgDB)={g}SELECT(BbCDE)={b}SELECT(Bε)={a,c,d,g,f,#}SELECT(CDaB)={a,d}SELECT(Cca)={c}SELECT(DdD)={d}SELECT(Dε)={a,b,c,g,f,#}SELECT(EgAf)={g}SELECT(Ec)={c}SELECT(A→BCc)={a,b,c,d} SELECT(A→gDB)={g} SELECT(B→bCDE)={b} SELECT(B→ε)={a,c,d,g,f,#} SELECT(C→DaB)={a,d} SELECT(C→ca)={c} SELECT(D→dD)={d} SELECT(D→ε)={a,b,c,g,f,#} SELECT(E→gAf)={g} SELECT(E→c)={c}

由上可知,有相同左部产生式的SELECT集合的交集为空,所以文法是LL(1)文法。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
吃午饭前,按书上的代码写会儿--Hunt the Wumpus第一个版本
有空就要慢慢练起~~~~脑袋动起来是很快乐的事儿。。。。:) 《易学PYTHON》演练一遍。 from random import choice cave_numbers = range(1,21) wumpus_location = choice(cave_numbers) pl...
957 0
Thinking In C++笔记和例子
发现很多编程的地方都离不开C++,想要深入android底层更需要学习C/C++,之前虽然学过但是没有深入,花了20多天的时间完整的把上卷撸了一遍。所以这次的笔记全部写在项目里:github地址:Thinking In C++。
1131 0
Haskell趣學指南--这个有意思
正在慢慢了解不同于命令式的函数式语言。 希望能获得新的视野。。 ~~~~~~~~~~~ http://learnyouahaskell-zh-tw.csie.org/zh-cn/ready-begin.html ~~~~~~~~~~~~~~~~~~~~~~~~ 中文版,从HASKELL的列表中,居然找到了些PYTHON的感觉。
786 0
零元学Expression Design 4 - Chapter 7 使用内建功能「Clone」来达成Path的影分身之术
原文:零元学Expression Design 4 - Chapter 7 使用内建功能「Clone」来达成Path的影分身之术 本章所介绍的是便利且快速的内建工具Clone ? 本章所介绍的是便利且快速的内建工具Clone ? ? 为什麽会说像是影分身之术呢? ? 请参照火影忍者(NARUTO): 《分身术》会分身术者,能以一身分出几身,几十身,乃至千百身。
1100 0
《C++ Primer》经典语句(三)
CH4 1.       表达式由一个或多个操作数(operand)构成,最简单的表达式由一个文字常量或一个对象构成。在更一般的情况下,表达式由一个或多个操作数、以及应用在这些操作数上的操作构成。
945 0
+关注
XOSG
半路出家的MEAN全栈工程师; 从运维转型开发的佛系程序员
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载