为了方便自顶向下语法分析,需要求文法对应的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)