编译原理之first集 & follow集 & select集 详解


叮嘟!这里是小啊呜的学习课程资料整理。好记性不如烂笔头,今天也是努力进步的一天。一起加油进阶吧!
在这里插入图片描述
为了方便自顶向下语法分析,需要求文法对应的first集,follow集,以及select集。本文主要分为两部分,一个是求法解析,还有一个例子详解。

一、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

1、关于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);

2、关于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开始往下找(这样也可以找到所求符号的上级),这样虽然繁杂,但是不会陷入死循环。

3、关于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)={}

在这里插入图片描述

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(左部)即可。

在这里插入图片描述

Ending!
更多课程知识学习记录随后再来吧!

就酱,嘎啦!

在这里插入图片描述

注:
1、人生在勤,不索何获。
2、编译原理之first集,follow集,select集解析文章参见:
https://blog.csdn.net/hxfghgh/article/details/80150011

发布了72 篇原创文章 · 获赞 106 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43543789/article/details/105463641