编译原理实验报告FIRST集与FOLLOW集
- 格式:docx
- 大小:15.47 KB
- 文档页数:11
【编译原理】语法分析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个以空格分隔的字符串为产⽣式体。
华东交通大学课程设计(论文)任务书软件学院专业项目管理班级2005-4一、课程设计(论文)题目正规文法的First集合Follow集求解过程动态模拟二、课程设计(论文)工作:自2008年6月23 日起至2008年 6 月27 日止。
三、课程设计(论文)的内容要求:1、基本要求:进一步培养学生编译器设计的思想,加深对编译原理和应用程序的理解,针对编译过程的重点和难点内容进行编程,独立完成有一定工作量的程序设计任务,同时强调好的程序设计风格,并综合使用程序设计语言、数据结构和编译原理的知识,熟悉使用开发工具VC 6.0 或其它软件编程工具。
为了使学生从课程设计中尽可能取得比较大的收获,对课程设计题目可根据自己的兴趣选题(须经老师审核),或从老师给定题目中选择完成(具体见编译原理课程设计题目要求)。
通过程序实现、总结报告和学习态度综合考评,并结合学生的动手能力,独立分析解决问题的能力和创新精神。
成绩分优、良、中、及格和不及格五等。
2、具体要求设计一个由正规文法生成Fisrt集Follow集的动态过程模拟动态模拟算法的基本功能是:●输入一个正规文法;●输出由文法构造的First集的算法;●输出First集;●输出由文法构造的Follow集的算法;●输出Follow集;学生签名:2008 年 6 月 27 日课程设计(论文)评阅意见评阅人职称副教授2008 年 6 月 27 日目录一、需求分析 (3)二、总体设计 (4)三、详细设计 (9)四、课设小结 (12)五、谢辞 (13)六、参考文献 (14)一、 需求分析问题描述设计一个由正规文法生成First 集和Follow 集并进行简化的算法动态模拟。
(算法参见教材) 【基本要求】动态模拟算法的基本功能是: (1) 输入一个文法G ;(2) 输出由文法G 构造FIRST 集的算法; (3) 输出First 集;(4) 输出由文法G 构造FOLLOW 集的算法; (5) 输出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集合的求法是:紧跟随其后面的终结符号或#。
但文法的识别符号包含#,在求的时候还要考虑到ε。
具体做法是把所有包含你要求的符号的产生式都找出来,再看哪个有用。
摘要:编译原理是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。
由于该课程教、学难度都非常大,往往费了大量时间而达不到预期教学效果俗语说:学习的最好方法是实践。
本次课程设计的目的正是基于此,力求为学生提供一个理论联系实际的机会,通过布置一定难度的课题,要求学生独立完成。
我们这次课程设计的主要任务是编程实现对给定文法的FIRST 集和FOLLOW集的求解。
通过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索前言知识的习惯,树立团队协作精神。
同时,课程设计可以充分弥补课堂教学及普通实验中知识深度与广度有限的缺陷,更好地帮助学生从全局角度把握课程体系。
关键词:编译原理;FIRST集;FOLLOW集目录1 课题综述 (1)1.1 课题来源 (1)1.2 课题意义 (1)1.3 预期目标 (1)1.4 面对的问题 (1)1.5 需解决的关键技术 (1)2 系统分析 (2)2.1 基础知识 (2)2.1.1 FIRST集定义 (2)2.1.2FIRST集求解算法.................................................................... 错误!未定义书签。
2.1.3FOLLOW集的定义 (4)2.1.4 FOLLOW集算法 (4)2.2 解决问题的基本思路 (4)2.3 总体方案 (4)3 系统设计 (5)3.1 算法实现 (5)3.2 流程图 (6)4 代码编写 (10)5 程序调试 (15)6 运行与测试 (15)1 课题综述1.1 课题来源文法:包含若干终结符,非终结符,由终结符与非终结符组成的产生式。
本次课程设计就是对产生式进行左递归分析,待无左递归现象后进行FIRST集与FOLLOW集的求解。
1.2 课题意义由文法产生的若干个句子有可能是合法的或者不合法的,也有可能产生歧义,所以要消除歧义先消除文法左递归,然后根据求得的FIRST集与FOLLOW 集构造分析表,分析给定句子的合法性。
《编译原理》实验报告
专业班级:计101班
学号:109074002
姓名:卞恩会
指导老师:王森玉
实验内容:
1.求出每个非终结符的FIRST集合
2.求出每个产生式右部的FIRST集合
3.求出每个非终结符的Follow集合实验环境:
Visual Studio2010
实验目的:
掌握FIRST集合和FOLLOW集合的求法
测试数据1:
S->aH
H->aMd|d
M->Ab|@
A->aM|e
输出结果:
测试数据2:S->AB
S->bC
A->@
A->b
B->@
B->aD
C->AD
C->b
D->aS
D->c
测试数据3:E->TX
X->+TX|@ T->FY
Y->*FY|@ F->i|(E)
输出结果:
感受:通过上机调试代码让我对书上的单纯的理论的知识有了一个更深的理解同时让我明白了对待一个问题我们应该全面的去理解它,这样才能学到的更多。
程序清单:。
编译原理实验报告实验名称计算first集合和follow集合实验时间院系计算机科学与技术班级软件工程1班学号姓名1.实验目的输入:任意的上下文无关文法。
输出:所输入的上下文无关文法一切非终结符的first集合和follow集合。
2.实验原理设文法G[S] =(V N , V T, P, S),则首字符集为:*FIRST (a)= {a | a= a B, a € V T , a , p € V }。
*若 a = £,氏 FIRST ( a )o由定义可以看出,FIRST (a)是指符号串a能够推导出的所有符号串中设a = X1X2…X n, FIRST ( a)可按下列方法求得:令 FIRST (a)=①,i = 1;(1)若 X i€ V T,贝U X i€ FIRST (a);(2)若 X i€ V N;①若“ FIRST (X i),贝U FIRST (FIRST (a);②若疋 FIRST (X i),贝U FIRST (X i)- { } € FIRST (a);(3)i = i+1,重复(1)、( 2),直到 X i € V T,( i= 2, 3,…,n) 或X i€ V N且若“ FIRST (xj或i>n为止。
当一个文法中存在&产生式时,例如,存在 A - E,只有知道哪些符号可以合法地出现在非终结符 A之后,才能知道是否选择 A- E产生式。
这些合法地出现在非终结符A之后的符号组成的集合被称为 FOLLOW集合。
下面我们给出文法的FOLLOW集的定义。
设文法 G[S] =(V N,V T,P,S),贝UFOLLOW (A)= {a | S => -Aa …,a € V T}。
若 S=…A,#€ FOLLOW (A)。
由定义可以看出,FOLLOW (A)是指在文法 G[S]的所有句型中,紧跟在非终结符A后的终结符号的集合。
FOLLOW集可按下列方法求得:(1)对于文法G[S]的开始符号S,有#€ FOLLOW (S); *(2)若文法 G[S]中有形如 B-xAy的规则,其中 x,y € V *,则 FIRST (y)-{ E € FOLLOW (A);(3)若文法G[S]中有形如B-xA的规则,或形如B-xAy的规则且E € FIRST( y),其中 x,y € V *,贝U FOLLOW ( B) € FOLLOW(A );3.实验内容计算first集合和follow集合4.实验心得通过上机实验我对文法符号的FIRST集和FOLLOW集有了更深刻的理解,已经熟练的掌握了求解的思想和方法,同时也锻炼了自己的动手解决问题的能力,对编程能力也有所提高。
5.实验代码与结果#in clude<iostream>#in clude<stri ng>#in clude<algorithm> using n amespace std。
#defi ne MAXS 50int NONE[MAXS]={0}。
string strings 。
//产生式string Vn 。
//非终结符string Vt 。
//终结符string first[MAXS] 。
// 用于存放每个终结符的first 集string First[MAXS] 。
// 用于存放每个非终结符的first 集string Follow[MAXS] 。
// 用于存放每个非终结符的follow 集int N 。
//产生式个数struct STR{string left 。
string right 。
}。
//求VN 和VT void VNVT(STR *p){int i,j 。
for(i=0 。
i<N 。
i++){for(j=0 。
j<(int)p[i].left.length() 。
j++){ if((p[i].left[j]>='A'&&p[i].left[j]<='Z')){if(Vn.find(p[i].left[j])>100)Vn+=p[i].left[j] 。
}else{if(Vt.find(p[i].left[j])>100)Vt +=p[i].left[j] 。
}}for(j=0 。
j<(int)p[i].right.length() 。
j++) {if(!(p[i].right[j]>='A'&&p[i].right[j]<='Z')) {if(Vt.find(p[i].right[j])>100)Vt +=p[i].right[j] 。
} elseif(Vn.find(p[i].right[j])>100)Vn+=p[i].right[j] 。
}}}}void getlr(STR *p,int i){ int j 。
for(j=0 。
j<strings.length() 。
j++){ if(strings[j]=='-'&&strings[j+1]=='>'){ p[i].left=strings.substr(0,j) 。
p[i].right=strings.substr(j+2,strings.length()-j) 。
}}}//对每个文法符号求first 集string Letter_First(STR *p,char ch){int t 。
if(!(Vt.find(ch)>100)){ first[Vt.find(ch)]="ch" 。
return first[Vt.find(ch)-1] 。
} if(!(Vn.find(ch)>100)){for(int i=0。
i<N。
i++){if(p[i].left[0]==ch){ if(!(Vt.find(p[i].right[0])>100)){ if(First[Vn.find(ch)].find(p[i].right[0])>100){ First[Vn.find(ch)]+=p[i].right[0] 。
} } if(p[i].right[0]=='*'){if(First[Vn.find(ch)].find('*')>100){First[Vn.find(ch)]+='*' 。
}}if(!(Vn.find(p[i].right[0])>100)){if(p[i].right.length()==1){string ff 。
ff=Letter_First(p,p[i].right[0]) 。
for(int i_i=0 。
i_i<ff.length() 。
i_i++){if( First[Vn.find(ch)].find(ff[i_i])>100){First[Vn.find(ch)]+=ff[i_i] 。
}}}else{for(int j=0 。
j<p[i].right.length() 。
j++){ string TT 。
TT=Letter_First(p,p[i].right[j]) 。
if(!(TT.find('*')>100)&&(j+1)<p[i].right.length()){sort(TT.begin(),TT.end()) 。
string tt 。
for(int t=1 。
t<TT.length() 。
t++){tt+=TT[t] 。
}TT=tt 。
tt="" 。
for(t=0 。
t<TT.length() 。
t++){if( First[Vn.find(ch)].find(TT[t])>100){First[Vn.find(ch)]+=TT[t] 。
}else{for(t=0 。
t<TT.length() 。
t++){if( First[Vn.find(ch)].find(TT[t])>100){First[Vn.find(ch)]+=TT[t] 。
}}break 。
}}}}return First[Vn.find(ch)] 。
}}// 求每个非终结符的Follow 集string Letter_Follow(STR *p,char ch){int t,k 。
NONE[Vn.find(ch)]++ 。
if(NONE[Vn.find(ch)]==2){NONE[Vn.find(ch)]=0 。
return Follow[Vn.find(ch)] 。
}for(int i=0。
i<N。
i++){for(int j=0 。
j<p[i].right.length() 。
j++){ if(p[i].right[j]==ch) {if(j+1==p[i].right.length()) {string gg。
gg=Letter_Follow(p,p[i].left[0])NONE[Vn.find(p[i].left[0])]=0for(int k=0 。
k<gg.length() 。
k++){ if(Follow[Vn.find(ch)].find(gg[k])>100) {Follow[Vn.find(ch)]+=gg[k] 。
}}}else{string FF 。
for(int jj=j+1 。
jj<p[i].right.length() 。
jj++){string TT 。
TT=Letter_First(p,p[i].right[jj]) 。
if(!(TT.find('*')>100)&&(jj+1)<p[i].right.length()){sort(TT.begin(),TT.end()) 。
string tt。
for(int t=1 。
t<TT.length() 。
t++){tt+=TT[t] 。
}TT=tt 。
tt="" 。
for(t=0 。
t<TT.length() 。
t++){if( FF.find(TT[t])>100&&TT[t]!='*'){FF+=TT[t] 。
}}else{for(t=0 。
t<TT.length() 。
t++){if( FF.find(TT[t])>100) {FF+=TT[t] 。
break。