编译原理一点就通first follow LL()
编译原理中first集合的定义

编译原理中的first集合是指在文法中,一个非终结符号的所有可能的开始终结符号的集合。
在编译过程中,求解文法的first集合有着重要的意义。
下面将从定义、性质、求解方法和应用四个方面来详细介绍first集合。
一、定义在上下文无关文法中,对于一个非终结符号X,它的first集合定义为:1. 如果X可以直接推导出终结符号a,则将a加入X的first集合中;2. 如果X可以通过若干步推导出ε(空串),则将ε加入X的first集合中;3. 如果X可以通过若干步推导出Y1Y2...Yk,其中Y1,Y2,...,Yk是终结符号或者非终结符号,并且Y1不可为空,则将Y1的first集合中的所有元素(除去ε)加入X的first集合中。
二、性质1. first集合是关于文法的。
即不同的文法可能得到不同的first集合。
2. first集合可以为空。
即某些非终结符号的first集合可能为空集。
3. first集合的求解不一定唯一。
即针对同一个文法,可能有多种不同的求解方式。
三、求解方法在对文法的first集合进行求解时,常用的方法有两种:直接法和间接法。
1. 直接法:直接根据first集合的定义,对每个非终结符号进行推导,找出所有可能的开始终结符号。
2. 间接法:先求解终结符号的first集合,然后根据非终结符号的产生式和已经求解出的非终结符号和终结符号的first集合,来逐步求解非终结符号的first集合。
四、应用1. 在LL(1)文法的构造中,first集合的求解是至关重要的步骤。
通过求解文法符号的first集合,可以帮助我们构造出LL(1)文法,从而用于自顶向下的语法分析。
2. 在语法制导翻译中,通过利用first集合,可以帮助我们优化翻译过程,提高翻译的效率和准确性。
3. 在编译器的错误处理中,通过利用文法符号的first集合,可以帮助我们更好地定位并处理语法错误,提高编译器的鲁棒性和容错性。
first集合在编译原理中具有重要的地位和作用,它的求解对于文法分析、语法制导翻译和编译器的错误处理都具有重要意义。
【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集

【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集 近来复习编译原理,语法分析中的⾃上⽽下LL(1)分析法,需要构造求出⼀个⽂法的FIRST和FOLLOW集,然后构造分析表,利⽤分析表+⼀个栈来做⾃上⽽下的语法分析(递归下降/预测分析),可是这个FIRST集合FOLLOW集看得我头⼤。
教课书上的规则如下,⽤我理解的语⾔描述的:任意符号α的FIRST集求法:1. α为终结符,则把它⾃⾝加⼊FIRSRT(α)2. α为⾮终结符,则:(1)若存在产⽣式α->a...,则把a加⼊FIRST(α),其中a可以为ε(2)若存在⼀串⾮终结符Y1,Y2, ..., Yk-1,且它们的FIRST集都含空串,且有产⽣式α->Y1Y2...Yk...,那么把FIRST(Yk)-{ε}加⼊FIRST(α)。
如果k-1抵达产⽣式末尾,那么把ε加⼊FIRST(α) 注意(2)要连续进⾏,通俗地描述就是:沿途的Yi都能推出空串,则把这⼀路遇到的Yi的FIRST集都加进来,直到遇到第⼀个不能推出空串的Yk为⽌。
重复1,2步骤直⾄每个FIRST集都不再增⼤为⽌。
任意⾮终结符A的FOLLOW集求法:1. A为开始符号,则把#加⼊FOLLOW(A)2. 对于产⽣式A-->αBβ: (1)把FIRST(β)-{ε}加到FOLLOW(B) (2)若β为ε或者ε属于FIRST(β),则把FOLLOW(A)加到FOLLOW(B)重复1,2步骤直⾄每个FOLLOW集都不再增⼤为⽌。
⽼师和同学能很敏锐地求出来,⽽我只能按照规则,像程序⼀样⼀条条执⾏。
于是我把这个过程写成了程序,如下:数据元素的定义:1const int MAX_N = 20;//产⽣式体的最⼤长度2const char nullStr = '$';//空串的字⾯值3 typedef int Type;//符号类型45const Type NON = -1;//⾮法类型6const Type T = 0;//终结符7const Type N = 1;//⾮终结符8const Type NUL = 2;//空串910struct Production//产⽣式11 {12char head;13char* body;14 Production(){}15 Production(char h, char b[]){16 head = h;17 body = (char*)malloc(strlen(b)*sizeof(char));18 strcpy(body, b);19 }20bool operator<(const Production& p)const{//内部const则外部也为const21if(head == p.head) return body[0] < p.body[0];//注意此处只适⽤于LL(1)⽂法,即同⼀VN各候选的⾸符不能有相同的,否则这⾥的⼩于符号还要向前多看⼏个字符,就不是LL(1)⽂法了22return head < p.head;23 }24void print() const{//要加const25 printf("%c -- > %s\n", head, body);26 }27 };2829//以下⼏个集合可以再封装为⼀个⼤结构体--⽂法30set<Production> P;//产⽣式集31set<char> VN, VT;//⾮终结符号集,终结符号集32char S;//开始符号33 map<char, set<char> > FIRST;//FIRST集34 map<char, set<char> > FOLLOW;//FOLLOW集3536set<char>::iterator first;//全局共享的迭代器,其实觉得应该⽤局部变量37set<char>::iterator follow;38set<char>::iterator vn;39set<char>::iterator vt;40set<Production>::iterator p;4142 Type get_type(char alpha){//判读符号类型43if(alpha == '$') return NUL;//空串44else if(VT.find(alpha) != VT.end()) return T;//终结符45else if(VN.find(alpha) != VN.end()) return N;//⾮终结符46else return NON;//⾮法字符47 }主函数的流程很简单,从⽂件读⼊指定格式的⽂法,然后依次求⽂法的FIRST集、FOLLOW集1int main()2 {3 FREAD("grammar2.txt");//从⽂件读取⽂法4int numN = 0;5int numT = 0;6char c = '';7 S = getchar();//开始符号8 printf("%c", S);9 VN.insert(S);10 numN++;11while((c=getchar()) != '\n'){//读⼊⾮终结符12 printf("%c", c);13 VN.insert(c);14 numN++;15 }16 pn();17while((c=getchar()) != '\n'){//读⼊终结符18 printf("%c", c);19 VT.insert(c);20 numT++;21 }22 pn();23 REP(numN){//读⼊产⽣式24 c = getchar();25int n; RINT(n);26while(n--){27char body[MAX_N];28 scanf("%s", body);29 printf("%c --> %s\n", c, body);30 P.insert(Production(c, body));31 }32 getchar();33 }3435 get_first();//⽣成FIRST集36for(vn = VN.begin(); vn != VN.end(); vn++){//打印⾮终结符的FIRST集37 printf("FIRST(%c) = { ", *vn);38for(first = FIRST[*vn].begin(); first != FIRST[*vn].end(); first++){39 printf("%c, ", *first);40 }41 printf("}\n");42 }4344 get_follow();//⽣成⾮终结符的FOLLOW集45for(vn = VN.begin(); vn != VN.end(); vn++){//打印⾮终结符的FOLLOW集46 printf("FOLLOW(%c) = { ", *vn);47for(follow = FOLLOW[*vn].begin(); follow != FOLLOW[*vn].end(); follow++){48 printf("%c, ", *follow);49 }50 printf("}\n");51 }52return0;53 }主函数其中⽂法⽂件的数据格式为(按照平时做题的输⼊格式设计的):第⼀⾏:所有⾮终结符,⽆空格,第⼀个为开始符号;第⼆⾏:所有终结符,⽆空格;剩余⾏:每⾏描述了⼀个⾮终结符的所有产⽣式,第⼀个字符为产⽣式头(⾮终结符),后跟⼀个整数位候选式的个数n,之后是n个以空格分隔的字符串为产⽣式体。
计算first集合和follow集合--编译原理教案资料

计算f i r s t集合和f o l l o w集合--编译原理计算first 集合和follow 集合姓名:彦清 学号:E10914127一、实验目的输入:任意的上下文无关文法。
输出:所输入的上下文无关文法一切非终结符的first 集合和follow 集合。
二、实验原理设文法G[S]=(V N ,V T ,P ,S ),则首字符集为:FIRST (α)={a | α⇒*a β,a ∈V T ,α,β∈V *}。
若α⇒*ε,ε∈FIRST (α)。
由定义可以看出,FIRST (α)是指符号串α能够推导出的所有符号串中处于串首的终结符号组成的集合。
所以FIRST 集也称为首符号集。
设α=x 1x 2…x n ,FIRST (α)可按下列方法求得:令FIRST (α)=Φ,i =1;(1)若x i ∈V T ,则x i ∈FIRST (α); (2) 若x i ∈V N ;① 若ε∉FIRST (x i ),则FIRST (x i )∈FIRST (α);② 若ε∈FIRST (x i ),则FIRST (x i )-{ε}∈FIRST (α);(3) i =i+1,重复(1)、(2),直到x i ∈V T ,(i =2,3,…,n )或x i ∈V N 且若ε∉FIRST (x i )或i>n 为止。
当一个文法中存在ε产生式时,例如,存在A →ε,只有知道哪些符号可以合法地出现在非终结符A 之后,才能知道是否选择A →ε产生式。
这些合法地出现在非终结符A 之后的符号组成的集合被称为FOLLOW 集合。
下面我们给出文法的FOLLOW 集的定义。
设文法G[S]=(V N ,V T ,P ,S ),则FOLLOW (A )={a | S ⇒… Aa …,a ∈V T }。
若S ⇒*…A ,#∈FOLLOW (A )。
由定义可以看出,FOLLOW (A )是指在文法G[S]的所有句型中,紧跟在非终结符A 后的终结符号的集合。
编译原理实验+求first集和follow集+代码

编译原理实验+求first集和follow集+代码/*说明:输入格式:每行输入一个产生式,左部右部中间的→用空格代替。
非终结符等价于大写字母^ 表示空输入到文件结束,或用 0 0 结尾。
Sample Input:(习题5·3):S MHS aH LSoH ^K dMLK ^L eHfM KM bLM0 0*/#include#include#include#include#includeusing namespace std;char l;string r;multimap sentence; //存储产生式multimap senRever; //产生式逆转set ter; //非终结符集合map toEmpty; //非终结符能否推出空bool flag;set fir; // 保存单个元素的first集set follow; //保存单个元素的follow集vector rightSide; //右部char Begin;bool capL(char c) //字母是否大写{if(c<='Z' && c>='A')return true;return false;}/*bool lowerL(char c) //小写字母{if(c<='z' && c>='a')return true;return false;}*/bool CapLString(string s) // 大写字符串{for(int i=0; iif(!capL(s[i])) {return false;}}return true;}bool isToEmpty(char ch) // 判断终结符能否推出空{bool flag;// for(set::iterator sIter = ter.begin(); sIter!=ter.end(); ++sIter) {flag = false;multimap::iterator mIter = sentence.find(ch);int cnt = sentence.count(ch);for(int i=0; iif(mIter->second=="^") {return true;// toEmpty[ch] = true;}else if(CapLString(mIter->second)){string s(mIter->second);bool flag2 = true;for(int j=0; jif(!isToEmpty(s[j]) || s[j]==ch) {flag2 = false;break;}}if(flag2) { // 右部全为终结符,全能推出空return true;}}}// }return false;}void getFirst(char ch, set &First) //求单个元素的 FIRST集{// if(flag)// return;multimap::iterator imul = sentence.find(ch);if(imul==sentence.end())return;int sum = sentence.count(imul->first);// cout<first<<endl;for(int i=0; i// cout<second<<endl;string s(imul->second);for(int j=0; jif(!capL(s[j])) {// cout<<" "<<s[j]<<endl;First.insert(s[j]);// flag = true;break;}else if(capL(s[j])) {if(s[j]==ch) { //有左递归,跳出循环break;;}getFirst(s[j], First);if(toEmpty[s[j] ]==false) {break;}}}}flag = true;}bool isLast(string s, char ch) //ch 是否是 s 的直接或间接的最后一个非终结符{if(!capL(ch))return false;for(int i=s.size()-1; i>=0; i--) {if(ch==s[i])return true;if(!capL(s[i]) || toEmpty[s[i] ]==false) {return false;}}return false;}void getFollow(char ch, set<cha</cha</s[j]<<endl;</endl;</endl;r> &follow) //求单个元素的 FOLLOW集{if(!capL(ch))return;for(vector::iterator iter=rightSide.begin(); iter!=rightSide.end(); ++iter) {for(int i=0; i<(*iter).size(); i++) {if(ch==(*iter)[i] && i!=(*iter).size()-1) {if(!capL((*iter)[i+1])) {follow.insert((*iter)[i+1]);}else {getFirst((*iter)[i+1], follow);}}if(ch==(*iter)[i] && i==(*iter).size()-1) { //判断是否是右部的最后一个非终结符 follow +#follow.insert('#');}else if(ch==(*iter)[i] && i<(*iter).size()-1){ //不是最后一个但之后全是非终结符且都能推出空 follow +#bool flag1=true;for(int j=i+1;j<(*iter).size(); j++) {if(!capL((*iter)[j]) || toEmpty[(*iter)[j]]==false) {flag1 = false;if(!capL((*iter)[j])) {follow.insert((*iter)[j]);}break;}}if(flag1 == true) {follow.insert('#');}}}if(isLast(*iter, ch)) { //ch是*iter的最后一个符号(直接或间接)int n = senRever.count(*iter);multimap::iterator mIter = senRever.find(*iter);for(int i=0 ;iif(mIter->second!=ch )getFollow(mIter->second, follow);}}}}int main(){int cnt=0;while(cin>>l>>r) {if(cnt==0) {Begin = l;cnt++;}if(l=='0')break;sentence.insert(make_pair(l, r)); //产生式senRever.insert(make_pair(r,l));ter.insert(l); //非终结符集合(左部)rightSide.push_back(r); //右部的集合/* if(r=="^") { // 判断是否有非终结符->^toEmpty[l] = true;}else {if(toEmpty.find(l)==toEmpty.end()) {toEmpty[l] = false;}} */}for(set::iterator sIter = ter.begin(); sIter!=ter.end(); ++sIter) { // 判断是否有非终结符->^if(isToEmpty(*sIter) ) {toEmpty[*sIter] = true;}else {toEmpty[*sIter] = false;}}for(set::iterator iter=ter.begin(); iter!=ter.end(); iter++) {flag = false;cout<<*iter<<" FIRST集 :";fir.clear();getFirst(*iter, fir);for(set::iterator iterF=fir.begin(); iterF!=fir.end(); iterF++) {cout<<" "<<*iterF;}cout<<endl;follow.clear();getFollow(*iter, follow);cout<<" FOLLOW集:";if(*iter==Begin) {cout<<" #";}for(set::iterator iterF=follow.begin(); iterF!=follow.end(); ++iterF) {if(*iterF!='^')cout<<" "<<*iterF;}cout<<endl<<endl;}system("pause");return 0;}</endl<<endl; </endl;。
编译原理:FIRST(x)FOLLOW(x)SELECT(x)的计算

编译原理:FIRST(x)FOLLOW(x)SELECT(x)的计算⽬录已知⽂法G[S]:S→MH|aH→LSo|εK→dML|εL→eHfM→K|bLM判断G是否是LL(1)⽂法。
First计算First集合的定义就是某个⾮终结符表达式可以推导出来的第⼀个字符可以是什么⽐如表达式S --> abb,它的First(S)={a}First(S)={a}+First(M)+First(H)+{ε}={a,b,d,e,ε}# S表达式存在单个的终结符a,添加{a}# S表达式以M开头,添加First(M)# 根据后⾯的表达式判断,M可以推出K,K可以推出空,所以M可以为空,此时S以H开头,添加First(H)# 由于H也可以推出空,所以S最终也会指向空,添加空集First(M)={b}+First(K)+{ε}={b,d,ε}# M表达式以终结符b开头,添加{b}# M表达式可以推导出单个的K表达式,所以添加First(K)# K有可能推导出空集,即M可以推导出空,所以添加空集First(K)={d}+{ε}={d,ε}# K可以以终结符d开头,添加{d}# K可以推导出空,添加空集First(H)=First(L)+{ε}={e,ε}# H可以推导出以L开头的表达式,所以添加First(L)# H可以推导出空,所以添加空集First(L)={e}# L只能推导出⼀个表达式,并且开头是终结符,所以添加{e}## 最后将已知的表达式代⼊到未知的表达式当中去,即可求出全部First集合Follow计算Follow表⽰某个⾮终结符后⾯可以跟着什么样的字符Follow集不存在空集为表达式编号1: S→MH|a2: H→LSo|ε3: K→dML|ε4: L→eHf5: M→K|bLMFollow(S)={$}+{o}={o,$}# 在表达式1中,S是⼀个⾮终结符,S是孤⽴的⼀个表达式,其Follow可以添加$,即添加{$}# 在表达式2中,S后⾯跟上了o,所以添加{o}Follow(H)=Follow(S)+{f}={f,o,$}# 在表达式1中,S后⾯跟了什么,MH后⾯就跟了什么,所以添加Follow(S)# 在表达式4中,H后⾯跟了f,所以添加{f}Follow(M)=First(H)+Follow(S)+First(L)={e,o,$}# 在表达式1中,M后⾯跟了H,所以添加First(H)# 在表达式2中可知,H可以推导出空,所以回到表达式1,S后⾯跟了什么,M后⾯就跟了什么,所以添加Follow(S)# 在表达式3中,M后⾯跟了⾮终结符L,所以添加First(L)# 在表达式5中,M后⾯跟了什么,bLM后⾯就跟什么,都是Follow(M),表达式不变Follow(L)=First(S)+Follow(K)+{o}+{$}+First(M)+Follow(M)={a,b,d,e,o,$}# 在表达式2中,L后⾯跟了⾮终结符S,所以添加First(S)# 在表达式2中,First(S)可以推出空,所以此时L后⾯跟着o,添加{o}# 在表达式3中,K后⾯跟了什么,dML后⾯就跟了什么,所以添加Follow(K)# 在表达式4中,L属于单⼀元素,所以添加$# 在表达式5中,L后⾯跟上了⾮终结符M,所以添加First(M)# 在表达式5中,从上得知,First(M)可以推导出空,所以此时M后⾯跟着什么,L后⾯就要跟着什么,所以添加Follow(M) Follow(K)={$}+Follow(M)={e,o,$}# 在表达式3中,K是单⼀字符,添加{$}# 在表达式5中,M后⾯跟着什么,K后⾯就跟着什么,所以添加Follow(M)注意:在书写Follow集中要时刻检查First集是否可以为空.Select计算分割表达式,如果⾮空则是First集,是空则为Follow集Select(S→MH)=First(M)+First(H)+Follow(S)={b,d,e,o,$}# S以M开头,加⼊First(M)# First(M)可以为空,加⼊First(H)# M和H都可以为空,加⼊Follow(S)Select(S→a)={a}# S只能推导出a,加⼊{a}Select(H→LSo)=First(L)={e}# H以L开头,并且First(L)不可以为空,即加⼊First(L)Select(H→ε)=Follow(H)={f,o,$}# H推导出空,加⼊Follow(H)Select(K→dML)={d}# K以终结符d开头,加⼊{d}Select(K→ε)=Follow(K)={e,o,$}# K可以为空,加⼊Follow(K)Select(L→eHf)={e}# L以终结符e开头,加⼊{e}Select(M→K)=First(K)+Follow(M)={d,e,o,$}# M可以推出K,加⼊First(K)# First(K)可以为空,即M可以加⼊Follow(M)Select(M→bLM)={b}# M可以推出以终结符b开头,加⼊{b}判断是否是LL(1)⽂法LL(1)⽂法就是同⼀⾮终结符推出来的Select集合相交为空,现在开始逐⼀判断Select(S→MH)∩Select(S→a)Select(H→LSo)∩Select(H→ε)Select(K→dML)∩Select(K→ε)# L只有⼀个表达式,省略LSelect(M→K)∩Select(M→bLM)易知,上述表达式都为空,所以这个表达式是LL(1)⽂法预测分析表的书写先列出First集和Follow集的表格First FollowS{a,d,b,e, ε}{$,o}H{e,ε}{f,o,$}K{d,ε}{e,o,$}L{e}{a,b,d,e,o,$}M{b,d,ε}{e,o,$}然后将⾮终结符、终结符作为横纵⾏,填⼊表达式。
编译原理 FIRST集和FOLLOW集的求法

First集合的求法:First集合最终是对产生式右部的字符串而言的,但其关键是求出非终结符的First集合,由于终结符的First集合就是它自己,所以求出非终结符的First集合后,就可很直观地得到每个字符串的First集合。
1. 直接收取:对形如U-a…的产生式(其中a是终结符),把a收入到First(U)中2. 反复传送:对形入U-P…的产生式(其中P是非终结符),应把First(P)中的全部内容传送到First(U)中。
Follow集合的求法:Follow集合是针对非终结符而言的,Follow(U)所表达的是句型中非终结符U所有可能的后随终结符号的集合,特别地,“#”是识别符号的后随符。
1. 直接收取:注意产生式右部的每一个形如“…Ua…”的组合,把a直接收入到Follow(U)中。
2.直接收取:对形如“…UP…”(P是非终结符)的组合,把First(P)除ε直接收入到Follow(U)中。
3.反复传送:对形如P-…U的产生式(其中U是非终结符),应把Follow(P)中的全部内容传送到Follow(U)中。
(或 P-…UB且First(B)包含ε,则把First(B)除ε直接收入到Follow(U)中,并把Follow(P)中的全部内容传送到Follow(U)中)例1:判断该文法是不是LL(1)文法,说明理由 S→ABc A→a|ε B→b|ε?First集合求法就是:能由非终结符号推出的所有的开头符号或可能的ε,但要求这个开头符号是终结符号。
如此题A可以推导出a和ε,所以FIRST(A)={a,ε};同理FIRST (B)={b,ε};S可以推导出aBc,还可以推导出bc,还可以推导出c,所以FIRST(S)={a,b,c}。
Follow集合的求法是:紧跟随其后面的终结符号或#。
但文法的识别符号包含#,在求的时候还要考虑到ε。
具体做法是把所有包含你要求的符号的产生式都找出来,再看哪个有用。
编译原理实验LL(1)文法的判断及转换

2016.11.30LL(1)文法的判断及转换目录一、实验名称 (2)二、实验目的 (2)三、实验原理 (2)1、First集定义 (2)2、Follow集定义 (2)3、Select集定义 (3)4、含左递归文法 (3)四、实验思路 (3)1、求非终结符是否能导出空 (3)2、求First集算法 (3)3、求Follow集算法 (3)4、求Select集算法 (4)五、实验小结 (4)六、附件 (4)1、源代码 (4)2、运行结果截图 (10)一、实验名称LL(1)文法的判断及转换二、实验目的输入:任意一个文法输出:(1)是否为LL(1)文法(2)若是,给出每条产生式的select集(3)若不是,看看是否含有左公共因子或者含有左递归,并用相应的方法将非LL(1)文法变成LL(1)文法,并输出新文法中每条产生式的select集。
三、实验原理1、First集定义令X为一个文法符号(终止符或非终止符)或ε,则集合First(X)有终止符组成,此外可能还有ε,它的定义如下:1.若X是终止符或ε,则First(X)={X}。
2.若X是非终结符,则对于每个产生式X—>X1X2…Xn,First(X)包含了First (X1)-{ε}。
若对于某个i<n,所有的集合First(X1),...,First(Xi)都包含了ε,则First(X)也包括了First(Xi+1)- {ε}。
若所有集合First(X1),...,First (Xn)都包括了ε,则First(X)也包括了ε。
2、Follow集定义给出一个非终结符A,那么集合Follow(A)则是由终结符组成,此外可能还含有#(#是题目约定的字符串结束符)。
集合Follow(A)的定义如下:1. 若A是开始符号,则#在Follow(A)中。
2. 若存在产生式B—>αAγ,则First(γ)- {ε}在Follow(A)中。
3. 若存在产生式B—>αAγ,且ε在First(γ)中,则Follow(A)包括Follow (B)。
编译原理语法分析_自上而下__LL(1)文法的判别

Y
N
计算FOLLOW集
(a) 设S为文法中开始符号, 把 # 加入FOLLOW(S)中 (b) 若 A→αBβ 是一个产生式, 则把 FIRST(β)\{ε} 加入 FOLLOW(B) 中 (c) 若 A→αB 是一个产生式, 或 A→αBβ 是一个产生式,且β可推出ε, 即ε∈FIRST(β) , 则把 FOLLOW(A) 也加入 FOLLOW(B) 中 (d) 反复使用(b)直到每个非终结符的FOLLOW集 不再增大为止。
推出ε FIRST集 FOLLOW集 E N (,i ),# E' Y + ,ε ),# T N (,i +,),# T' Y * ,ε +,),# F N (,i *,+,),#
计算SELECT集
给定上下文无关文法的产生式A→α, A∈VN, α∈V*, * ε, • 若α SELECT(A→α) = FIRST(α) * ε, • 若α SELECT(A→α) = (FIRST(α)-{ε})∪ FOLLOW(A)
G': E → TE' E'→ +TE' E'→ ε T → FT' T'→ *FT' T'→ ε F → (E) F→i 推出ε N Y N Y N
E E' T T' F
求出能推出ε的非终结符 - 算法描述
建立一个一维数组X[ ],用以记录非终结符能否 推出ε。 ① 将数组X[ ]中对应每一非终结符的标记置初值 为“未定”。
♨ LL(1)文法的判别
左递归和左公因子 * ε的非终结符 求出能 求First集 求Follow集 LL(1)判别
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1
编译原理
2013年11月28日
LL 的含义
-自左向右扫描分析输入符号串
-从识别符号开始生成句子的最左推导
LL(1):向前看一个输入符号,便能唯一确定当前应选择的规则LL(k):向前看k 个输入符号,才能唯一确定当前应选择的规则
4.2.3 LL(1)文法的判别
要构造确定的自顶向下分析程序要求描述文法必须是LL(1)文法
2
编译原理
2013年11月28日
同一非终结符有多个候选式时
引起回溯的原因
【例4.1】α=acb G[S]:S →aAb A →cd|c
(1)候选式的终结首符号相同
(2)候选式的终结首符号相同
【例4.8】S →Aa A →a|
3
编译原理
2013年11月28日
1. FIRST 集
FIRST(α):从α可能推导出的所有开头终结符号或ε对于文法G 的所有非终结符的每个候选式α,其终结首符号集称为FIRST 集,定义如下:
ε,则规定ε∈FIRST(α)
若α
【例】S →aAb A →cd|c
a …,a ∈V T
FIRST(α)={a|α
FIRST(aAb )={a}FIRST(cd )={c}FIRST(c )={c}
【例】S →Aa A →a|ε
FIRST(a )={a}FIRST(ε)= {ε}FIRST(Aa)={a}
FIRST(S )={a}FIRST(A )={c}
FIRST(S )={a}FIRST(A )={a, ε}
4
编译原理
2013年11月28日
(1)若α=a α′,且a ∈V T ,则a ∈FIRST(α);
例:FIRST(i)={i}
FIRST(+TE')={+}
E →TE'E'→+TE'|ε
T →FT'T'→*FT'|ε
F →(E)|i 构造FIRST 集的算法
(2)若α=X α′,X ∈V N ,且有产生式X →b …,则把b 加入到FIRST(α)中;例:FIRST(FT')={(,i} ??
5
编译原理
2013年11月28日
①将FIRST(X 1)中的一切非ε的终结符加进FIRST(α);②若ε∈FIRST(X 1),则将FIRST(X 2)中的一切非ε的终结符加进FIRST(α);
③若ε∈FIRST(X 1)且ε∈FIRST(X 2),则将FIRST(X 3)中的一切非ε的终结符加进FIRST(α);
④依此类推,若对于一切1≤i ≤n,ε∈FIRST(X i ),则将ε加进FIRST(α)。
(3)若α=X 1X 2…X n α′,其中X i ∈V N , 1≤i ≤n ;
E →TE'E'→+TE'|ε
T →FT'T'→*FT'|εF →(E)|i
例:FIRST(FT')=注意:要顺序往下做,一旦不满足条件,过程就要中断进行
FIRST(F)-{ε}={(,i}
6
编译原理
2013年11月28日
FIRST(F)={(,i}FIRST(T ’)={*,ε}
FIRST(T)=FIRST(F)-{ε}={(,i}FIRST(E ’)={+,ε}
FIRST(E)= FIRST(T)-{ε}={(,i}FIRST(TE ’)=FIRST(T)-{ε}={(,i}
FIRST(+TE ’)={+} FIRST(ε)={ε}FIRST(FT ’)= FIRST(F)-{ε}={(,i}FIRST(*FT ’)={*}FIRST((E ))={(}FIRST(i)={i}
【例4.9】G[E]
E →TE'
E'→+TE'|εT →FT'T'→*FT'|εF →(E)|i
7
编译原理
2013年11月28日
2. FOLLOW 集
FOLLOW(A):是所有句型中紧接A 之后的终结符号或#
对于文法G 的非终结符的后继符号集称为FOLLOW 集,定义如下:
…A ,则规定#∈FOLLOW(A)
若S
…Aa …,a ∈V T }
FOLLOW(A)={a|S E →TE'E'→+TE'|εT →FT'T'→*FT'|εF →(E)|i
T+TE ',则+∈FOLLOW(T)
E
8
编译原理
2013年11月28日
构造集合FOLLOW 的算法
(1)若为开始符号,则把“#”加入FOLLOW(A)中;(2)若B →αA β(β≠ε),则把FIRST(β)-{ε}加入FOLLOW(A)中;
注:FOLLOW 集合中不能有ε
(3)若B →αA 或B →αA β,且β
则把FOLLOW(B)加入FOLLOW(A) 中。
ε,
E →TE'E'→+TE'|ε
T →FT'
T'→*FT'|εF →(E)|i
#∈FOLLOW(E)
由F →(E)可知,)∈FOLLOW(E)
由E →TE',应把FOLLOW(E)加入∈FOLLOW(E')
由E '→+ TE '且E '⇒ε,应把FOLLOW(E ')加入FOLLOW(T)
9
编译原理
2013年11月28日
【例4.10】G[E]
E →TE'E'→+TE'|εT →FT'T'→*FT'|ε
F →(E)|i
求FOLLOW
FOLLOW(E)={#,)} ∵E 是开始符号∴#∈FOLLOW(E)
又F →(E) ∴)∈FOLLOW(E)
FOLLOW(E ’)={#,)} ∵E →TE ’∴FOLLOW(E)加入FOLLOW(E ’)FOLLOW(T)={+,),#} ∵E ’→+TE ’∴FIRST(E ’)-{ε}加入FOLLOW(T)
又E ’⇒ε,∴FOLLOW(E ’)加入FOLLOW(T)
FOLLOW(T ’)= FOLLOW(T)= {+,),#}
∵T →FT ’∴FOLLOW(T)加入FOLLOW(T ’)
FOLLOW(F)={*,+,),#}
∵T →FT ’∴FOLLOW(F)=FIRST (T ’)-{ε}又T ’⇒ε∴FOLLOW(T)加入FOLLOW(F)
FIRST(F)={(,i}FIRST(T ’)={*,ε}FIRST(T) ={(,i}FIRST(E ’)={+,ε}FIRST(E)={(,i}
10
编译原理
2013年11月28日
若一个文法满足以下条件,则称该文法G 为LL(1)文法:
(1)文法不含左递归;
(2)对于每个非终结符A 的各个候选式的终结首符号集两两不相交。
即,如果A →α1|α2|…|αn ,则
FIRST(αi )∩FIRST(αj )= Φ,其中1≤i ,j ≤n ,且i ≠j 。
(3)对于文法中每个非终结符A ,若它某个候选式的终结首符号集包含ε,则FIRST(A)∩FOLLOW(A)=Φ
3.LL(1)文法的判别条件
【例4.11】G[E]:
E→TE'
E'→+TE'|ε
T→FT'
T'→*FT'|ε
F→(E)|i
判别该文法是否为LL(1)文法。
FIRST(F)={(,i} FIRST(T’)={*,ε} FIRST(T) ={(,i} FIRST(E’)={+,ε} FIRST(E)={(,i}
11
编译原理
2013年11月28日。