编译原理之first集,follow集,select集解析

编译原理 专栏收录该内容
5 篇文章 4 订阅

为了方便自顶向下语法分析,需要求文法对应的first集,follow集,以及select集。

本文主要分为两部分,一个是求法解析,还有一个例子详解:

第一部分是求法解析

将对first集,follow集,select集分为三种讲解方法

①定义介绍及推导

②简单推导演示

③规范式推导


讲解顺序为,先用最简单的推导介绍三种不同的集,然后介绍概念,最后介绍正规的推导方法

三种方法,不同人喜欢不同的理解方法,大家都可以参考,推导方法越往后,越规范!


默认规则:

起始符S,其余大写字母为非终结符;

小写字母为终结符,#为ε; 

产生式: A->BC, 其中这个是关于A的产生式,A为左部(左边部分),BC为右部 (右边部分)

Vt是终结符

Vn是非终结符



首先文法G[S]

S-> AB|bC

A->#|b

B->aD|#

C->AD|b

D->aS|c


first集:

①简单推导

first,顾名思义就是该关于该符号的所有产生式右部第一个遇到终结符。


例如:

first(S)

用上面那句话:关于S的产生式有两个:S->AB,S->bC

先看简单的情况:S->bC,明显右部第一个终结符是b,  那关于这个产生式的终结符就是b 了,解决一个!

这个时候first(S)={b}

然后是S->AB,这时右部的第一个是A,非终结符,所以不成立。这时你就要再把A的产生式引进来(因为A有关于他的产生式)。

关于A的产生式为:A->#,A->b,分别代入S->AB的产生式得:S->B(应该是S->#B,但是#可以省略) 和S->bB.

看第二个S->bB ,马上就可以知道遇到的第一个终结符是b,

这个时候first(S)={b}
然后看第一个S->B,这个时候B不是终结符,所以不成立,这时就要把B的产生式导进来。

变成S->aD ,S->#,

然后上面两个式子,分别第一个终结符为a,和 #

则这个时候first(S)={b, a , #}


这里强调一下 。为什么S->AB ,会变成后面的S->B 而S->aD就没有S->D, 因为A的产生式是有一个A->#,就是指向空,这个时候代入S->AB不就等于 S->B了吗。而S-aD。因为已经找到第一个终结符了,所以这个产生式就结束推导了





②定义讲解:

定义:设G=(Vt,Vn,P,S)是上下文无关文法。

first(A)={a| A=*>ab, a∈Vt,b∈V*}

若a=*>ε ,则规定ε ∈first(a).成first(a)为a的开始符号集或首符号集


上面是大白话,下面是系统总结一下:

first,顾名思义就是该关于该符号的所有产生式右部第一个遇到终结符。

举例:

一、单个非终结符的first集

(1)求终结符的first集,是他本身

first(a)={a}

(2)X->a....

first(X)= a....,取右部的终结符,则a∈first(x)

(3)X->YZ...

first(X)=YZ...,  当右部第一个符号(Y)是非终结符的时候,你就要把这个符号(Y)的产生式导进来了,然后再判断右边第一个是否是终结符。若Y的产生式存在#,则继续导入Z的产生式寻找右边第一个终结符,重复以上动作。即

first(X)=first(Y),若存在Y->#,则继续first(X)=first(Z),直到非终结符不存在#结束。



二、多个非终结符

X->M...

Y->N...

first(XY)=M...N...  ,同样找右边第一个是终结符的符号,

③正规推导:

(1)对每一文法X∈V,计算first(X)

(2)

①X∈Vt,则first(X)={X}

②X∈Vn(非终结符),且有X->a , 则a∈first(X)

③X∈Vn(非终结符),且有X-># , 若#∈first(X)

④若有X,Y1,Y2,Y3...∈Vn,且有产生式X->Y1,Y2,Y3=*>#,则first(Y1)-{#},first(Y2)-{#}...first(Yi)都属于first(X)找中
重复②-④

⑤当所有Yi=*>#   则first(X)=first(Y1)-{#}∪first(Y2)-{#}.....∪first(Yi)
当有X-># ,才能说#∈first(X)


follow集

follow,顾名思义,就是该符号后面跟着的第一个终结符

PS:求follow集,都是从开始符号S开始推导


①定义介绍

设G=(Vt,Vn,P,S)是上下文无关算法,A属于Vn,S是开始符号

follow(A)={a|S=*>uAb 且a∈Vt,a∈first(b),u∈Vt,b属于V*}

也可定义为

follow(A)={a | S=*>.....Aa....,a∈Vt}.....(1)

若

S=*>...AB....,则follow(A)=first(B)....(2)

若

S=*>....A   ,因为这个时候A属于S,因此这个时候应该看得是S后面的东西也就是follow(S)∈follow(A)....(3)


若

S=*>...AB,这个时候first(B)存在空的情况,而这个时候first的空集不能出现在follow集中,因此还应该看S后面的东西,因此这个等于follow(A)=(first(B)-{#})∪follow(S)....(4)

通过上面(1)(2)(3)(4)不难得出,follow(A)就是从开始符号通过各种产生式推导,直到出现A,然后取A后面的第一个终结符


ps first产生的空集不能给follow,只有follow产生的才可以给,参考上面的(4)


②简单推导:

(1)终结符的follow没有定义,只有非终结符的follow才有定义



(2)

假设:

S->xAy

A->aBb

先求上面

follow(A):

因为要找A后面的跟着的第一个终结符,所以应该直接看谁能产生A,得

S->xAy,

此时A后面跟着的终结符是y,因此follow(A)={y} ,解决!



求FOLLOW(B)



则先找谁能产生B:

B的产生式:

A->aBb,此时,要把first(b)(因为是B后面遇到第一个终结符,也就是first(b),也就是b的first集合)





但是如果这个时候b等于#,那么B的follow集应该取得是b(假设b可能为空的情况)后面的东西,那么问题来了,b后面的东西怎么来呢?

S->xAy,先看下A的产生式,(因为永远是从S出发,因此当产生式出现B的时候一定是这样的流程)

然后代入A的产生

S->xaBby

可以看到出现B的时候是这样的,所以这个时候就应该是把b忽视,直接看y,那么这个时候是不是很熟悉?没错就是跟上面求FOLLOW(A)的一样做法,也就是follow(A)∈follow(B)

这个时候可以总结一个规律

当b为#的时候,follow(B) 就应该看谁产生生它的那个非终结符的follow(),在这里就是因为A->xBy,因此看follow(A)

③正规推导

计算follow集

①设S为起始,{#}加入follow(S)

②要求follow(B),若A->aBb是一个产生式,则把first(B)的非空元素加入follow(B)中

③若B->#,则把follow(A)加入follow(B)中

解释:因为  若D->xAy,A->aBb, 则 D->xaBby,且b=#,则first(y)或者说是 follow(A),  ∈follow(B)



就是所求符号的右边如果等于# 则不停找上一级


前排提示:

做first集的时候,应该第一个找的是左部跟所求符合相同的产生式,然后再分别找这些产生式中右部第一个终结符,然后他们组成一个集合,就是结果


做follow集的时候,应该第一个找的是右部存在所求符号的产生式(区别:first找左,follow找右,first找的符号要跟所求符号完全一样,follow只要找左部存在所有符号的即可),然后再找该符号后面跟着的第一个终结符,如果后面没有符号(即为#),则继续follow()这个产生式的左部,直到找到终结符为止。


做follow集的时候往往要不停往上面找上一级,如果有循环语句的话,不妨顺着从S开始往下找(这样也可以找到所求符号的上级),这样虽然繁杂,但是不会陷入死循环



select集

在求出first集和follow集后

select就方便多了

例:

select(X->Y),先求first(Y),如果first(Y)存在#∈first(Y)的情况,则再求follow(X),最后求两者的并集即可即可



例子:

默认规则:大写字母为非终结符,小写字母和字符(包括 '^'  '( '   ',' 等三个符号 )都是终结符


S->a

S->^

S->(T)

T->SN

N->,SN

N->#

 

 first(S)=first(a)∪first(^)∪first((T)) ={a,^,T}

first(T)=first(S)={a,^,T}

first(N)=first(,SN)∪first(#)={ ‘,’ #}



只有开始符号才有默认{#} ,其余符号的follow集不能通过first产生#,只能通过 ‘∪follow(S)‘ 产生

follow(S)={#}∪follow(N)={#}

follow(T)=first()) ={  ) }

follow(N)=follow(T)={)}


 

是否=>#

First

Follow

S

{a,^,(}

{#,  ’,’ ,  ) }

T

{a,^,(}

{ ) }

N

{‘,’ , #}

{ ) }


Select(S->a) =first(a)= {a}

Select(S->^) =first(^)={^}

Select(S->(T)) =first( (T)  )={ ( }

Select(T->SN) = first(S)={a,^,(}

Select(N->,SN)=first( , ) ={ , }

Select(N->#) =follow(N) = { ) }
select技巧。如select(N->,SN)  即求N->,SN  ,看下图,判断右部 有否必否,有终必否,  如果确定是否的,则只计算first(左部)即可。
在这里左部(,SN) 其中 ‘,‘ 是终结符,所以直接select(N->,SN)=first(,SN). 你也可以判断说:因为S有下图可知是‘否‘, 因此也是只计算first(,SN)



©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

GroovRain

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值