first集follow集求解算法与构造预测分析表
编译原理-第4章 语法分析--习题答案

第4章语法分析习题答案1.判断(1)由于递归下降分析法比较简单,因此它要求文法不必是LL(1)文法。
(× )LL(1)文法。
(× )(3)任何LL(1)文法都是无二义性的。
(√)(4)存在一种算法,能判定任何上下文无关文法是否是LL(1) 文法。
(√)(× )(6)每一个SLR(1)文法都是LR(1)文法。
(√)(7)任何一个LR(1)文法,反之亦然。
(× )(8)由于LALR是在LR(1)基础上的改进方法,所以LALR(× )(9)所有LR分析器的总控程序都是一样的,只是分析表各有不同。
(√)(10)算符优先分析法很难完全避免将错误的句子得到正确的归约。
(√)2.文法G[E]:E→E+T|TT→T*F|FF→(E)|i试给出句型(E+F)*i的短语、简单短语、句柄和最左素短语。
答案:画出语法树,得到:短语: (E+F)*i ,(E+F) ,E+F ,F ,i简单短语: F ,i句柄: F最左素短语: E+F3.文法G[S]:S→SdT | TT→T<G | GG→(S) | a试给出句型(SdG)<a的短语、简单短语、句柄和最左素短语。
答案:画出语法树,得到:短语:(SdG)<a 、(SdG) 、SdG 、G 、a简单(直接)短语:G 、a句柄:G最左素短语:SdG4.对文法G[S]提取公共左因子进行改写,判断改写后的文法是否为LL(1)文法。
S→if E then S else SS→if E then SS→otherE→b答案:提取公共左因子;文法改写为:S→if E then S S'|otherS'→else S|E→bLL(1)文法判定:① 文法无左递归② First(S)={if,other}, First(S')={else, }First(E)={b}Follow(S)= Follow(S')={else,#}Follow(E)={then}First(if E then S S')∩First(other)=First(else S)∩First( )=③First(S')∩Follow(S')={else}不为空集故此文法不是LL(1)文法。
first集follow集求解算法及构造预测分析表

构造预测分析表源程序:#include<stdlib.h>#include<stdio.h>#include<string.h>/*******************************************/int count=0; /*分解的产生式的个数*/int number; /*所有终结符和非终结符的总数*/char start; /*开始符号*/char termin[50]; /*终结符号*/char non_ter[50]; /*非终结符号*/char v[50]; /*所有符号*/char left[50]; /*左部*/char right[50][50]; /*右部*/char first[50][50],follow[50][50]; /*各产生式右部的FIRST和左部的FOLLOW 集合*/char first1[50][50]; /*所有单个符号的FIRST集合*/char select[50][50]; /*各单个产生式的SELECT集合*/char f[50],F[50]; /*记录各符号的FIRST和FOLLOW是否已求过*/char empty[20]; /*记录可直接推出@的符号*/char TEMP[50]; /*求FOLLOW时存放某一符号串的FIRST集合*/int validity=1; /*表示输入文法是否有效*/int ll=1; /*表示输入文法是否为LL(1)文法*/int M[20][20]; /*分析表*/char choose; /*用户输入时使用*/char empt[20]; /*求_emp()时使用*/char fo[20]; /*求FOLLOW集合时使用*/ /*******************************************判断一个字符是否在指定字符串中********************************************/int in(char c,char *p){int i;if(strlen(p)==0)return(0);for(i=0;;i++){if(p[i]==c)return(1); /*若在,返回1*/if(i==strlen(p))return(0); /*若不在,返回0*/}}/*******************************************得到一个不是非终结符的符号********************************************/ char c(){char c='A';while(in(c,non_ter)==1)c++;return(c);}/*******************************************分解含有左递归的产生式********************************************/void recur(char *point){ /*完整的产生式在point[]中*/ int j,m=0,n=3,k;char temp[20],ch;ch=c(); /*得到一个非终结符*/k=strlen(non_ter);non_ter[k]=ch;non_ter[k+1]='\0';for(j=0;j<=strlen(point)-1;j++){if(point[n]==point[0]){ /*如果'|'后的首符号和左部相同*/ for(j=n+1;j<=strlen(point)-1;j++){while(point[j]!='|'&&point[j]!='\0')temp[m++]=point[j++];left[count]=ch;memcpy(right[count],temp,m);right[count][m]=ch;right[count][m+1]='\0';m=0;count++;if(point[j]=='|'){n=j+1;break;}}}else{ /*如果'|'后的首符号和左部不同*/ left[count]=ch;right[count][0]='@';right[count][1]='\0';count++;for(j=n;j<=strlen(point)-1;j++){if(point[j]!='|')temp[m++]=point[j];else{left[count]=point[0];memcpy(right[count],temp,m);right[count][m]=ch;right[count][m+1]='\0';printf(" count=%d ",count); m=0;count++;}}left[count]=point[0]; memcpy(right[count],temp,m); right[count][m]=ch;right[count][m+1]='\0';count++;m=0;}}}void non_re(char *point){int m=0,j;char temp[20];for(j=3;j<=strlen(point)-1;j++) {if(point[j]!='|')temp[m++]=point[j];else{left[count]=point[0];memcpy(right[count],temp,m); right[count][m]='\0';m=0;count++;}}left[count]=point[0];memcpy(right[count],temp,m); right[count][m]='\0';count++;m=0;}/*******************************************读入一个文法********************************************/char grammer(char *t,char *n,char *left,char right[50][50]) {char vn[50],vt[50];char s;char p[50][50];int i,j,k;printf("请输入文法的非终结符号串:");scanf("%s",vn);getchar();i=strlen(vn);memcpy(n,vn,i);n[i]='\0';printf("请输入文法的终结符号串:");scanf("%s",vt);getchar();i=strlen(vt);memcpy(t,vt,i);t[i]='\0';printf("请输入文法的开始符号:");scanf("%c",&s);getchar();printf("请输入文法产生式的条数:");scanf("%d",&i);getchar();for(j=1;j<=i;j++){printf("请输入文法的第%d条(共%d条)产生式:",j,i); scanf("%s",p[j-1]);getchar();}for(j=0;j<=i-1;j++)if(p[j][1]!='-'||p[j][2]!='>'){ printf("\ninput error!");validity=0;return('\0');} /*检测输入错误*/for(k=0;k<=i-1;k++){ /*分解输入的各产生式*/if(p[k][3]==p[k][0])recur(p[k]);elsenon_re(p[k]);}return(s);}/*******************************************将单个符号或符号串并入另一符号串********************************************/void merge(char *d,char *s,int type){ /*d是目标符号串,s是源串,type=1,源串中的' @ '一并并入目串; type=2,源串中的' @ '不并入目串*/int i,j;for(i=0;i<=strlen(s)-1;i++)if(type==2&&s[i]=='@');else{for(j=0;;j++){if(j<strlen(d)&&s[i]==d[j]) break;if(j==strlen(d)) {d[j]=s[i];d[j+1]='\0';break;}}}}}/*******************************************求所有能直接推出@的符号********************************************/ void emp(char c){ /*即求所有由' @ '推出的符号*/ char temp[10];int i;for(i=0;i<=count-1;i++){if(right[i][0]==c&&strlen(right[i])==1){temp[0]=left[i];temp[1]='\0';merge(empty,temp,1);emp(left[i]);}}}/*******************************************求某一符号能否推出' @ '********************************************/int _emp(char c){ /*若能推出,返回1;否则,返回0*/ int i,j,k,result=1,mark=0;char temp[20];temp[0]=c;temp[1]='\0';merge(empt,temp,1);if(in(c,empty)==1)return(1);for(i=0;;i++){if(i==count)return(0);if(left[i]==c) /*找一个左部为c的产生式*/ {j=strlen(right[i]); /*j为右部的长度*/if(j==1&&in(right[i][0],empty)==1)return(1);else if(j==1&&in(right[i][0],termin)==1)return(0);else{for(k=0;k<=j-1;k++)if(in(right[i][k],empt)==1) mark=1;if(mark==1)continue;else{for(k=0;k<=j-1;k++){result*=_emp(right[i][k]);temp[0]=right[i][k];temp[1]='\0';merge(empt,temp,1);}}}if(result==0&&i<count)continue;else if(result==1&&i<count)return(1);}}}/******************************************* 判断读入的文法是否正确********************************************/ int judge(){int i,j;for(i=0;i<=count-1;i++){if(in(left[i],non_ter)==0){ /*若左部不在非终结符中,报错*/printf("\nerror1!");validity=0;return(0);}for(j=0;j<=strlen(right[i])-1;j++){if(in(right[i][j],non_ter)==0&&in(right[i][j],termin)==0&&right[i][j]!='@') { /*若右部某一符号不在非终结符、终结符中且不为' @ ',报错*/ printf("\nerror2!");validity=0;return(0);}}}return(1);}/*******************************************求单个符号的FIRST********************************************/void first2(int i){ /*i为符号在所有输入符号中的序号*/ char c,temp[20];int j,k,m;c=v[i];char ch='@';emp(ch);if(in(c,termin)==1) /*若为终结符*/{first1[i][0]=c;first1[i][1]='\0';}else if(in(c,non_ter)==1) /*若为非终结符*/{for(j=0;j<=count-1;j++){if(left[j]==c){if(in(right[j][0],termin)==1||right[j][0]=='@') {temp[0]=right[j][0];temp[1]='\0';merge(first1[i],temp,1);}else if(in(right[j][0],non_ter)==1){if(right[j][0]==c)continue;for(k=0;;k++)if(v[k]==right[j][0])break;if(f[k]=='0'){first2(k);f[k]='1';}merge(first1[i],first1[k],2);for(k=0;k<=strlen(right[j])-1;k++){empt[0]='\0';if(_emp(right[j][k])==1&&k<strlen(right[j])-1){for(m=0;;m++)if(v[m]==right[j][k+1])break;if(f[m]=='0'){first2(m);f[m]='1';}merge(first1[i],first1[m],2);}else if(_emp(right[j][k])==1&&k==strlen(right[j])-1) {temp[0]='@';temp[1]='\0';merge(first1[i],temp,1);}elsebreak;}}}}}f[i]='1';}/******************************************* 求各产生式右部的FIRST********************************************/ void FIRST(int i,char *p){int length;int j,k,m;char temp[20];length=strlen(p);if(length==1) /*如果右部为单个符号*/ {if(p[0]=='@'){if(i>=0){first[i][0]='@';first[i][1]='\0';}else{TEMP[0]='@';TEMP[1]='\0';}}else{for(j=0;;j++)if(v[j]==p[0])break;if(i>=0){memcpy(first[i],first1[j],strlen(first1[j])); first[i][strlen(first1[j])]='\0';}else{memcpy(TEMP,first1[j],strlen(first1[j]));TEMP[strlen(first1[j])]='\0';}}}else /*如果右部为符号串*/{for(j=0;;j++)if(v[j]==p[0])break;if(i>=0)merge(first[i],first1[j],2);elsemerge(TEMP,first1[j],2);for(k=0;k<=length-1;k++){empt[0]='\0';if(_emp(p[k])==1&&k<length-1){for(m=0;;m++)if(v[m]==right[i][k+1])break;if(i>=0)merge(first[i],first1[m],2);elsemerge(TEMP,first1[m],2);}else if(_emp(p[k])==1&&k==length-1) {temp[0]='@';temp[1]='\0';if(i>=0)merge(first[i],temp,1);elsemerge(TEMP,temp,1);}else if(_emp(p[k])==0)break;}}}/******************************************* 求各产生式左部的FOLLOW********************************************/ void FOLLOW(int i){int j,k,m,n,result=1;char c,temp[20];c=non_ter[i]; /*c为待求的非终结符*/temp[0]=c;temp[1]='\0';merge(fo,temp,1);if(c==start){ /*若为开始符号*/temp[0]='#';temp[1]='\0';merge(follow[i],temp,1);}for(j=0;j<=count-1;j++){if(in(c,right[j])==1) /*找一个右部含有c的产生式*/ {for(k=0;;k++)if(right[j][k]==c)break; /*k为c在该产生式右部的序号*/for(m=0;;m++)if(v[m]==left[j])break; /*m为产生式左部非终结符在所有符号中的序号*/ if(k==strlen(right[j])-1){ /*如果c在产生式右部的最后*/if(in(v[m],fo)==1){merge(follow[i],follow[m],1);continue;}if(F[m]=='0'){FOLLOW(m);F[m]='1';}merge(follow[i],follow[m],1);}else{ /*如果c不在产生式右部的最后*/for(n=k+1;n<=strlen(right[j])-1;n++){empt[0]='\0';result*=_emp(right[j][n]);}if(result==1){ /*如果右部c后面的符号串能推出^*/ if(in(v[m],fo)==1){ /*避免循环递归*/merge(follow[i],follow[m],1);continue;}if(F[m]=='0'){FOLLOW(m);F[m]='1';}merge(follow[i],follow[m],1);}for(n=k+1;n<=strlen(right[j])-1;n++)temp[n-k-1]=right[j][n]; temp[strlen(right[j])-k-1]='\0';FIRST(-1,temp);merge(follow[i],TEMP,2);}}}F[i]='1';}/*******************************************判断读入文法是否为一个LL(1)文法********************************************/int ll1(){int i,j,length,result=1;char temp[50];for(j=0;j<=49;j++){ /*初始化*/first[j][0]='\0';follow[j][0]='\0';first1[j][0]='\0';select[j][0]='\0';TEMP[j]='\0';temp[j]='\0';f[j]='0';F[j]='0';}for(j=0;j<=strlen(v)-1;j++)first2(j); /*求单个符号的FIRST集合*/ printf("\n各非终结符导出的first集:");for(j=0;j<=strlen(v)-1;j++)printf("%c:%s ",v[j],first1[j]);printf("\n能导空的非终结符集合:%s",empty);// printf("\n_emp:");//for(j=0;j<=strlen(v)-1;j++)// printf("%d ",_emp(v[j]));for(i=0;i<=count-1;i++)FIRST(i,right[i]); /*求FIRST*/for(j=0;j<=strlen(non_ter)-1;j++){ /*求FOLLOW*/if(fo[j]==0){fo[0]='\0';FOLLOW(j);}}//printf("\nfirst:");//for(i=0;i<=count-1;i++)// printf("%s ",first[i]);printf("\nfollow集合:");for(i=0;i<=strlen(non_ter)-1;i++)printf("%s ",follow[i]);for(i=0;i<=count-1;i++){ /*求每一产生式的SELECT集合*/ memcpy(select[i],first[i],strlen(first[i]));select[i][strlen(first[i])]='\0';for(j=0;j<=strlen(right[i])-1;j++)result*=_emp(right[i][j]);if(strlen(right[i])==1&&right[i][0]=='@')result=1;if(result==1){for(j=0;;j++)if(v[j]==left[i])break;merge(select[i],follow[j],1);}}printf("\nselect集合顺序是:");for(i=0;i<=count-1;i++)printf("%s ",select[i]);memcpy(temp,select[0],strlen(select[0]));temp[strlen(select[0])]='\0';for(i=1;i<=count-1;i++){ /*判断输入文法是否为LL(1)文法*/ length=strlen(temp);if(left[i]==left[i-1]){merge(temp,select[i],1);if(strlen(temp)<length+strlen(select[i]))return(0);}else{temp[0]='\0';memcpy(temp,select[i],strlen(select[i])); temp[strlen(select[i])]='\0';}}return(1);}/*******************************************构造分析表M********************************************/ void MM(){int i,j,k,m;for(i=0;i<=19;i++)for(j=0;j<=19;j++)M[i][j]=-1;i=strlen(termin);termin[i]='#'; /*将#加入终结符数组*/ termin[i+1]='\0';for(i=0;i<=count-1;i++){for(m=0;;m++)if(non_ter[m]==left[i])break; /*m为产生式左部非终结符的序号*/ for(j=0;j<=strlen(select[i])-1;j++){if(in(select[i][j],termin)==1){for(k=0;;k++)if(termin[k]==select[i][j])break; /*k为产生式右部终结符的序号*/ M[m][k]=i;}}}}/*******************************************判断符号串是否是该文法的句型********************************************/void syntax(){int i,j,k,m,n,p,q;char ch;char S[50],str[50];printf("请输入该文法的句型:");scanf("%s",str);getchar();i=strlen(str);str[i]='#';str[i+1]='\0';S[0]='#';S[1]=start;S[2]='\0';j=0;ch=str[j];while(1){if(in(S[strlen(S)-1],termin)==1){if(S[strlen(S)-1]!=ch){printf("该符号串不是文法的句型!");return;}else if(S[strlen(S)-1]=='#'){printf("该符号串是文法的句型."); return;}else{S[strlen(S)-1]='\0'; j++;ch=str[j];}}else{for(i=0;;i++)if(non_ter[i]==S[strlen(S)-1]) break;for(k=0;;k++){if(termin[k]==ch)break;if(k==strlen(termin)){printf("词法错误!");return;}}if(M[i][k]==-1){printf("语法错误!");return;}else{m=M[i][k];if(right[m][0]=='@')S[strlen(S)-1]='\0';else{p=strlen(S)-1;q=p;for(n=strlen(right[m])-1;n>=0;n--)S[p++]=right[m][n]; S[q+strlen(right[m])]='\0';}}}printf("S:%s str:",S);for(p=j;p<=strlen(str)-1;p++)printf("%c",str[p]);printf(" \n");}}/******************************************* 一个用户调用函数********************************************/ void menu(){syntax();printf("\n是否继续?(y or n):");scanf("%c",&choose);getchar();while(choose=='y'){menu();}}/*******************************************主函数********************************************/void main(){int i,j;start=grammer(termin,non_ter,left,right); /*读入一个文法*/ printf("count=%d",count);printf("\n开始符号为:%c",start);strcpy(v,non_ter);strcat(v,termin);printf("\n所有符号集为:%s",v);printf("\n非终结符集合:{%s",non_ter);printf("}");printf("\n终结符集合:{%s",termin); printf("}");printf("\n文法所有右边表达式依次是:");for(i=0;i<=count-1;i++)printf("%s ",right[i]);printf("\n文法所有左边开始符依次是:"); for(i=0;i<=count-1;i++)printf("%c ",left[i]);if(validity==1)validity=judge();//printf("\nvalidity=%d",validity);if(validity==1){ll=ll1();// printf("\nll=%d",ll);if(ll==0)printf("\n该文法不是一个LL1文法!"); else{printf("\n该文法是一个LL(1)文法!");MM();// printf("\n");//for(i=0;i<=19;i++)// for(j=0;j<=19;j++)// if(M[i][j]>=0)//printf("M[%d][%d]=%d ",i,j,M[i][j]); menu();}}运行结果:。
求first集和follow集

{
cout<<s1[i]<<" "<<follow(s1[i])<<endl;
}
return 0;
}
四.实验截图
}
}
}
}
}
return s;
}
//******************************
int main()
{
int i;
string s1,s2;
char b;
cout<<"\t************求First集和Follow集**************"<<endl;
cout<<"请输入产生式的数目:";
}
else
{
rights=p[pos.front()].right;
for(i=0;i<rights.size();i++)
{
if(find(rights[i]))
{
s+=first(rights[i]);
index= s.find_last_of('@');
s=s.erase(index,1);
}
编译原理实验
实验名称:求first集和follow集
姓名:
学号:
教师签字:
成绩:
一.实验目的:
.掌握和了解first集和follow集的求解过程。
二.实验原理:
1.first集的求解:(1)若X∈Vt,则FIRST(X)={X};
(2)若X∈Vn,且有产生式X->a……,a∈Vt,则a∈FIRST(X);
【编译原理】语法分析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集的方法

1、构造 FIRST 集的算法 (1) 对于 G 中的每个文法符号 X,为求 FIRST(X),反复应用如下规则,直到集合不再增大: ① 若 X∈VT,则 FIRST(X)是{X} ② 若 X∈VN ,且 X→aα (a∈VT ),则{ a } FIRST(X) X→ε, 则{ε} FIRST(X) ③ 若 X->Y1Y2 … Yi-1 Yi … YK∈P,Y1∈VN ,则 FIRST(Y1)-{ε} FIRST(X)
则 FIRST(Xi) -{} FIRST() 特别是,若所有的 FIRST(Xj)均含有,1jn,则{} FIRST()。 显然,若=则 FIRST()={}。
2、构造 FOLLOW 集的算法 对于 G 中的每一 AFOLLOW 集不再增大为止: ① 对于文法的开始符号 S,令# ∈ FOLLOW(S)。 ② 对于每一 A→αBβ∈P, 令 FIRST(β) - {ε} FOLLOW(B) 。 ③ 对于每一 A→αB∈P, 或 A→αBβ∈P,且ε∈FIRST(β), 则令 FOLLOW(A) FOLLOW(B) 。
∗
而对所有的 j(1≤j ≤i-1), Yj ∈VN,且 Yj⇒ ε,则令 FIRST(Yj)-{ε} FIRST(X) (1≤j ≤i) 特别,当ε∈FIRST(Yj) (1≤j ≤k)时,令ε∈FIRST(X)
(2) 对文法 G 的任何符号串=X1X2…Xn 构造集合 FIRST() ① 置 FIRST(X1)-{} FIRST() ② 若对任何 1ji-1,FIRST(Xj),
第三四章习题课编译原理

0 A 0 C
27
1
1
习题15(1/3)
15、给定右线性文法G: S→0S|1S|1A|0B A→1C|1 B→0C|0 C→0C|1C|1|0 求出一个与G等价的左线性文法。
Y
4
正规式转化为NFA(2/2)
• 若 R 是复合正规式,则按下图的转换规则对 R 进行分裂和加 进新结,直至每个边上只留下一个符号(或 )为止。
A A
r1r2 r1|r2
B B
代换为 代换为
A A
r1
C r1 r2
r2
B B
A
r1*
B
代换为
A
C
r1
B
整个分裂过程中,所有新结点均采用不同的名字,保留 X,Y 为 全图唯一初态结点和终态结点
l
首先 M的状态分成两组:终态组{F},非终 态组{A,B,C,D,E} 考察{A,B,C,D,E},由于{A,B,C,D,E}1 属于 {B,C,F}, 它既不包含在{A,B,C,D,E}中也不包含在{F} 之中,因此,应把{A,B,C,D,E}一分为二。 因为状态 E 经 1 弧到达状态 F,而状态A、 B,C,D经 1 弧都到达 {B,C},因此,应把 E 分 出来,形成{A,B,C,D}、{E}、{F}。 {A,B,C,D}0 属于{D,E},它不含在任何划分 中,因为状态 C 经 0弧到达状态 E,而状态 {A,B,D}经 0 弧都到达 D,因此,应把 C 分 出来,形成{A,B,D}、{C}、{E}、{F}。 由于{A,B,D}1={B,C},它不包含在任何划 分之中,因此,应把{A,B,D}一分为二。因 为状态B、D经 1 弧都到达 {C},经 0弧都到 达 {D}因此,应把 A分出来,形成{A}、 {B,D}、{C}、{E}、{F}。{B,D}无法再分。 至此,整个分划含有四组: {A}、{B,D}、 {C}、{E}、{F} 。每个组都不可再分。
编译原理教程课后习题答案——第三章

第三章语法分析3.1 完成下列选择题:(1) 文法G:S→xSx|y所识别的语言是。
a. xyxb. (xyx)*c. xnyxn(n≥0)d. x*yx*(2) 如果文法G是无二义的,则它的任何句子α。
a. 最左推导和最右推导对应的语法树必定相同b. 最左推导和最右推导对应的语法树可能不同c. 最左推导和最右推导必定相同d. 可能存在两个不同的最左推导,但它们对应的语法树相同(3) 采用自上而下分析,必须。
a. 消除左递 a. 必有ac归b. 消除右递归c. 消除回溯d. 提取公共左因子(4) 设a、b、c是文法的终结符,且满足优先关系ab和bc,则。
b. 必有cac. 必有bad. a~c都不一定成立(5) 在规范归约中,用来刻画可归约串。
a. 直接短语b. 句柄c. 最左素短语d. 素短语(6) 若a为终结符,则A→α·aβ为项目。
a. 归约b. 移进c. 接受d. 待约(7) 若项目集Ik含有A→α· ,则在状态k时,仅当面临的输入符号a∈FOLLOW(A)时,才采取“A→α· ”动作的一定是。
a. LALR文法b. LR(0)文法c. LR(1)文法d. SLR(1)文法(8) 同心集合并有可能产生新的冲突。
a. 归约b. “移进”/“移进”c.“移进”/“归约”d. “归约”/“归约”【解答】(1) c (2) a (3) c (4) d (5) b (6) b (7) d (8) d3.2 令文法G[N]为G[N]: N→D|NDD→0|1|2|3|4|5|6|7|8|9(1) G[N]的语言L(G[N])是什么?(2) 给出句子0127、34和568的最左推导和最右推导。
【解答】(1) G[N]的语言L(G[N])是非负整数。
(2) 最左推导:NNDNDDNDDDDDDD0DDD01DD012D0127NNDDD3D34NNDNDDDDD5DD56D568最右推导:NNDN7ND7N27ND27N127D1270127NNDN4D434NNDN8ND8N68D685683.3 已知文法G[S]为S→aSb|Sb|b,试证明文法G[S]为二义文法。
编译原理大题

五、语法分析——自底向上分析法已知文法G:EE+TE TTT*FTFF(E)Fi(1)求文法G中每个非终结符的First集和Follow集。
(2)构造文法G的SLR(1)预测分析表。
(20分)首先构造增广文法:SEEE+TE TTT*FTFF(E)FiFirst(S)=First(E)=First(T)=First(F)={(,I)Follow(S)={#} Follow(E)={+,#,}}Follow(T)={+,},#,*} Follow(F)={+,},#,*}状态Action Gotoi + * ( ) # E T F0 S5 S4 1 2 31 S6 Acc2 r 2 S7 r 2 r 23 r4 r 4 r 4 r44 S5 S4 8 2 35 r6 r 66 S5 9 37 S5 108 S6 S119 r 1 S7 r 1 r 110 r 3 r 3 r 3 r 311 r 5 r 5 r 5 r 5注:识别可归前缀的DFA共12项。
词法分析——确定性有穷自动机为以下字符集编写正规表达式,并构造与之等价的最简DFA(写出详细的具体过程):在字母表{a,b}上的包含偶数个a且含有任意数目b的所有字符串。
(15分)(b*ab*ab*)*b a b1a状态Action GOTOa b d e f $ S R T0 S3 11 acc2 r2 S3 r2 r2 53 S6 S4 24 r4 r4 r4 r45 S10 96 77 S88 r3 r3 r3 r39 r1 r1 r110 r6 S6 S4 r6 r6 1111 S1212 r5 r5 r5五、语法分析——自底向上分析法已知文法G:S’SS bRSTS bRRdSaR eTfRaTf(1)求文法G中每个非终结符的First集和Follow集。
(2)构造文法G的SLR(1)预测分析表。
(20分)frist(s’)={b} follow(s’)={$}frist(s)={b} follow(s)={f,a, $}frist(R) ={d,e} follow( R )={a,b,f, $}frist(T)={t} follow (T)={a,f,#}五、对下面的文法(15分)S->UTa|TbT->S|Sc|dU->US|e判断是否为LR(0),SLR(1),说明理由,并构造相应的分析表。
编译原理 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集合的求法是:紧跟随其后面的终结符号或#。
但文法的识别符号包含#,在求的时候还要考虑到ε。
具体做法是把所有包含你要求的符号的产生式都找出来,再看哪个有用。
LL1语法分析,first集,follow集,分析表Java实现

import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.table.DefaultTableModel;import java.sql.*;import java.util.Vector;public class LL1 extends JFrame implements ActionListener { /****/private static final long serialVersionUID = 1L;JTextField tf1;JTextField tf2;JLabel l;JButton b0;JPanel p1, p2, p3;JTextArea t1, t2, t3;JButton b1, b2, b3;JLabel l0, l1, l2, l3, l4;JTable table;Statement sta;Connection conn;ResultSet rs;DefaultTableModel dtm;String Vn[] = null;Vector<String> P = null;int firstComplete[] = null;// 存储已判断过first的数据char first[][] = null;// 存储最后first结果int followComplete[] = null;// 存储已判断过follow的数据char follow[][] = null;// 存储最后follow结果char select[][] = null;// 存储最后select结果int LL = 0;// 标记是否为LL(1)String vt_tou[] = null;// 储存VtObject shuju[][] = null;// 存储表达式数据char yn_null[] = null;// 存储能否推出空LL1() {setLocation(100, 0);setSize(700, 780);tf1 = new JTextField(13);tf2 = new JTextField(13);l = new JLabel(">>");l0 = new JLabel("输入字符串:");l1 = new JLabel("输入的文法为:");l2 = new JLabel(" ");l3 = new JLabel("分析的结果:");l4 = new JLabel("预测分析表:");// p1=new JPanel();p2 = new JPanel();p3 = new JPanel();t1 = new JTextArea(24, 20);t2 = new JTextArea(1, 30);t3 = new JTextArea(24, 40);b0 = new JButton("确定(S为开始)");b1 = new JButton(" 判断文法 ");b2 = new JButton("输入");b3 = new JButton("清空");table = new JTable();JScrollPane jp1 = new JScrollPane(t1);JScrollPane jp2 = new JScrollPane(t2);JScrollPane jp3 = new JScrollPane(t3);p2.add(tf1);p2.add(l);p2.add(tf2);p2.add(b0);p2.add(b1);p2.add(l0);p2.add(l2);p2.add(jp2);p2.add(b2);p2.add(b3);p2.add(l1);p2.add(l3);p2.add(jp1);p2.add(jp3);p3.add(l4);p3.add(new JScrollPane(table));add(p2, "Center");add(p3, "South");b0.addActionListener(this);b1.addActionListener(this);b2.addActionListener(this);b3.addActionListener(this);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);table.setPreferredScrollableViewportSize(new Dimension(660, 200));setVisible(true);}////////////////////界面设计public void actionPerformed(ActionEvent e) {if (e.getSource() == b0) {String a = tf1.getText();String b = tf2.getText();t1.append(a + '→' + b + '\n');}if (e.getSource() == b1) {t3.setText("");int Vnnum = 0, k;Vn = new String[100];P = new Vector<String>();String s[] = t1.getText().split("\n");for (int i = 0; i < s.length; i++) {if (s.length < 2) {t3.setText("文法输入有误,请重新输入");// 判断长度是否符合return;}if (s[i].charAt(0) <= 'Z' && s[i].charAt(0) >= 'A'&& s[i].charAt(1) == '→') {for (k = 0; k < Vnnum; k++) {if (Vn[k].equals(s[i].substring(0, 1))) {break;}}if (Vnnum == 0 || k >= Vnnum) {Vn[Vnnum] = s[i].substring(0, 1);// 存入Vn数据Vnnum++;}P.add(s[i]);} else {t3.setText("文法输入有误,请重新输入");return;}}yn_null = new char[100];first = new char[Vnnum][100];int flag = 0;String firstVn[] = null;firstComplete = new int[Vnnum];for (int i = 0; Vn[i] != null; i++) // 依次求 FIRST**{flag = 0;firstVn = new String[20];if ((flag = add_First(first[i], Vn[i], firstVn, flag)) == -1) return;firstComplete[i] = 1;}t3.append("first集:" + "\n"); // 显示FIRST**for (int i = 0; Vn[i] != null; i++) {t3.append("first(" + Vn[i] + ")={ ");for (int j = 0; first[i][j] != '\0'; j++) {t3.append(first[i][j] + " , ");}t3.append("}" + "\n");}follow = new char[Vnnum][100];String followVn[] = null;followComplete = new int[Vnnum];for (int i = 0; Vn[i] != null; i++) // 求FOLLOW**{flag = 0;followVn = new String[20];if(i==0){}else if ((flag = tianjiaFollow(follow[i], Vn[i], followVn, flag)) == -1)return;followComplete[i] = 1;}t3.append("follow集:" + "\n"); // 显示FOLLOW**for (int i = 0; Vn[i] != null; i++) {t3.append("follow(" + Vn[i] + ")={ ");for (int j = 0; follow[i][j] != '\0'; j++) {t3.append(follow[i][j] + " , ");}t3.append("}" + "\n");}select = new char[P.size()][100];for (int i = 0; i < P.size(); i++) // 求SELECT**{flag = 0;tianjiaSelect(select[i], (String) P.elementAt(i), flag);}t3.append("select集:" + "\n"); // 显示SELECT**for (int i = 0; i < P.size(); i++) {t3.append("select(" + (String) P.elementAt(i) + ")={ ");for (int j = 0; select[i][j] != '\0'; j++) {t3.append(select[i][j] + " , ");}t3.append("}" + "\n");}for (int i = 0; Vn[i] != null; i++)// 判断select交集是否为空{int biaozhi = 0;char save[] = new char[100];for (int j = 0; j < P.size(); j++) {String t = (String) P.elementAt(j);if (t.substring(0, 1).equals(Vn[i])) {for (k = 0; select[j][k] != '\0'; k++) {if (puanduanChar(save, select[j][k])) {save[biaozhi] = select[j][k];biaozhi++;} else// 当有交集时,不为LL(1)文法{t3.append("不是LL(1)文法!!" + "\n");return;}}}}}char Vt[] = new char[100];int biaozhi = 0;for (int i = 0; i < P.size(); i++) {String t = (String) P.elementAt(i);for (int j = 2; j < t.length(); j++)// 提取表达式右侧的终结符存入Vt{if (t.charAt(j) > 'Z' || t.charAt(j) < 'A') {if (puanduanChar(Vt, t.charAt(j))) {Vt[biaozhi] = t.charAt(j);biaozhi++;}}}}if (puanduanChar(Vt, '#'))// 若可推出空集,则将#加入Vt。
FIRST集和FOLLOW集求法

FIRST集和FOLLOW集求法龙书算法:First:(1)、如果X是终结符,那么First(X) = X;(2)、如果X是⾮终结符,且XàY1Y2......Yk是⼀个产⽣式,其中k>=1;那么如果对于某个I, a在First(Yi)中,且#(空串)在所有的First(Y1)…..First(Yi-1)中,就吧a加⼊到First(X)中。
(3)、如果Xà#(空串)是⼀个产⽣式,那么将#加⼊到First(X)中。
Follow:(1)、将$放⼊到Follow(S)中,其中S是开始符号,⽽$是输⼊右端结束的标记。
(2)、如果存在⼀个产⽣式AàaBb,那么First(b)中除#(空串)外地所有符号都在Follow(B)中。
(3)、如果存在⼀个产⽣式AàaB, 或存在AàaBb且First(b)包含#(空串),那么Follow(A)中的所有符号都在Follow(B)中。
⾃⼰理解:First:(看X的产⽣式)(1)、如果X是终结符,那么First(X)= X;(2)、如果X是⾮终结符,且XàY1Y2......Yk,i=1;1)、将First(Yi)加⼊到First(X)中,2)、如果#包含着First(Yi)中,i++,重复1);3)、如果#不包含在First(Yi)中,First(X)计算完成;(3)、如果Xà#(空串)是⼀个产⽣式,那么将#加⼊到First(X)中。
Follow:(看在右边有B的产⽣式)(1)、将$放⼊到Follow(S)中,其中S是开始符号,⽽$是输⼊右端结束的标记。
(2)、如果存在⼀个产⽣式AàaBb,那么First(b)中除#(空串)外地所有符号都在Follow(B)中。
(3)、如果存在⼀个产⽣式AàaB, 或存在AàaBb且First(b)包含#(空串),那么Follow(A)中的所有符号都在Follow(B)中。
编译原理_LL(1)文法源代码(实验三)

一、实验目的及要求1.把握LL(1)分析法的大体原理;2.把握LL(1)分析表的构造方式;3.用LL(1)分析法分析高级语言表达式。
4、了解LL(1)分析器的工作进程。
文法:无二义性的算术表达式的文法(1)把词法分析作为语法分析的子程序实现(5分)(2)独立的语法分析程序(4分)(3)对表达式文法排除左递归、构造LL(1)分析表(4)LL(1)分析表能够直接输入(4分),也能够用程序实现(5分)(5)给一个表达式,给出分析进程(分析栈、输入串、所用规那么)(4分)(6)生成一个棵语法树(5分)用二叉树的形式表示出来二、实验内容及原理一、实验原理(1)、LL(1)文法的概念LL(1)分析法属于确信的自顶向下分析方式。
LL(1)的含义是:第一个L说明自顶向下分析是从左向右扫描输入串,第2个L说明分析进程中将利用最左推导,1说明只需向右看一个符号即可决定如何推导,即选择哪个产生式(规那么)进行推导。
LL(1)文法的判别需要依次计算FIRST集、FOLLOW集和SELLECT集,然后判定是不是为LL(1)文法,最后再进行句子分析。
需要预测分析器对所给句型进行识别。
即在LL(1)分析法中,每当在符号栈的栈顶显现非终极符时,要预测用哪个产生式的右部去替换该非终极符;当显现终结符时,判定其与剩余输入串的第一个字符是不是匹配,若是匹配,那么继续分析,不然报错。
LL(1)分析方式要求文法知足如下条件:关于任一非终极符A的两个不同产生式A→α,A→β,都要知足下面条件:SELECT(A→α)∩SELECT(A→β)=∅(2)、预测分析表构造LL(1)分析表的作用是对当前非终极符和输入符号确信应该选择用哪个产生式进行推导。
它的行对应文法的非终极符,列对应终极符,表中的值有两种:一是产生式的右部的字符串,一是null。
假设用M表示LL(1)分析表,那么M可表示如下:M: VN×VT→P∪{Error}M(A, t) = A→α,当t∈select(A→α) ,不然M(A, t) = Error其中P表示所有产生式的集合。
LL(1)文法分析

LL(1)文法分析:
实现功能:实现了对First集、Follow集和分析表的输出。
在实习中对文法消除左递归和提取左公因子没有实现成功,所以在进行输入时要求用户直接输入LL(1)文法,之后才可以顺利的进行其他操作。
实现过程:由用户输入的文法确定和分别存储非终结符和终结符,存储方式和调用方式为:将文法顺序的存储在二维数组中,之后用到时都是用存储的位置。
之后按照First(A)=从A推导得到的串的首符号的集合,并且遇到空字后向后推移直到遇到第一个非终结符;Follow(A)=紧跟在A右边的终结符号的集合来给出First集和Follow集,若某字符后面只有空字,那么其推导式的左部的Follow集的所有符号都在这个字符的Follow集中。
预测分析表的得出是按照First集和Follow集进行推导的:对于First集中的每个终结符号a,将A->a 加入到analyseTable[i,i+2]中。
如果@(空字)在First集中,那么对于Follow(A)中的每个终结符号b,将A->a加入到analyseTable[A.id,b.id]中。
如果@在First集中,且$在Follow(A)中,也将A->加入到analyseTable[A.id,#]。
测试:
预测分析表:。
实验5-LL(1)语法分析程序的设计与实现(C语言)

实验五LL(1)文法识别程序设计一、实验目的通过LL(1)文法识别程序的设计理解自顶向下的语法分析思想。
二、实验重难点FIRST集合、FOLLOW集合、SELECT集合元素的求解,预测分析表的构造。
三、实验内容与要求实验内容:1.阅读并理解实验案例中LL(1)文法判别的程序实现;2.参考实验案例,完成简单的LL(1)文法判别程序设计。
四、实验学时4课时五、实验设备与环境C语言编译环境六、实验案例1.实验要求参考教材93页预测分析方法,94页图5.11 预测分析程序框图,编写表达式文法的识别程序。
要求对输入的LL(1)文法字符串,程序能自动判断所给字符串是否为所给文法的句子,并能给出分析过程。
表达式文法为:E→E+T|TT→T*F|FF→i|(E)2.参考代码为了更好的理解代码,建议将图5.11做如下标注:/* 程序名称: LL(1)语法分析程序 *//* E->E+T|T *//* T->T*F|F *//* F->(E)|i *//*目的: 对输入LL(1)文法字符串,本程序能自动判断所给字符串是否为所给文法的句子,并能给出分析过程。
/********************************************//* 程序相关说明 *//* A=E' B=T' *//* 预测分析表中列号、行号 *//* 0=E 1=E' 2=T 3=T' 4=F *//* 0=i 1=+ 2=* 3=( 4=) 5=# *//************************************/#include"iostream"#include "stdio.h"#include "malloc.h"#include "conio.h"/*定义链表这种数据类型参见:*/struct Lchar{char char_ch;struct Lchar *next;}Lchar,*p,*h,*temp,*top,*base;/*p指向终结符线性链表的头结点,h指向动态建成的终结符线性链表节点,top和base分别指向非终结符堆栈的顶和底*/char curchar; //存放当前待比较的字符:终结符char curtocmp; //存放当前栈顶的字符:非终结符int right;int table[5][6]={{1,0,0,1,0,0},{0,1,0,0,1,1},{1,0,0,1,0,0},{0,1,1,0,1,1},{1,0,0,1,0,0}};/*存放预测分析表,1表示有产生式,0表示无产生式。
编译原理课程设计-LL(1)语法分析器的构造

LL(1)语法分析器的构造摘要语法分析的主要任务是接收词法分析程序识别出来的单词符由某种号串,判断它们是否语言的文法产生,即判断被识别的符号串是否为某语法部分。
一般语法分析常用自顶向下方法中的LL分析法,采用种方法时,语法分程序将按自左向右的顺序扫描输入的的符号串,并在此过程中产生一个句子的最左推导,即LL是指自左向右扫描,自左向右分析和匹配输入串。
经过分析,我们使用VC++作为前端开发工具,在分析语法成分时比较方便直观,更便于操作。
运行程序的同时不断修正改进程序,直至的到最优源程序。
关键字语法分析文法自顶向下分析 LL(1)分析最左推导AbstractGrammatical analysis of the main tasks was to receive lexical analysis procedure to identify the words from a website, string, and judge whether they have a grammar of the language, that is, judging by the series of symbols to identify whether a grammar part. General syntax analysis commonly used top-down methods of LL analysis, using methods, Grammar hours will be from the procedures of the order left-to-right scanning input string of symbols, and in the process produced one of the most left the sentence is derived, LL is scanned from left to right, From left to right analysis and matching input strings. After analysis, we use VC + + as a front-end development tool for the analysis of syntax ingredients more convenient visual, more easy to operate. Operational procedures at the same time constantly improving procedures, until the source of optimal .Key WordsGrammatical analysis grammar Top-down analysis LL (1) AnalysisMost left Derivation目录摘要 (1)引言 (3)第一章设计目的 (4)第二章设计的内容和要求 (5)2.1 设计内容 (5)2.2 设计要求 (5)2.3 设计实现的功能 (5)第三章设计任务的组织和分工 (6)3.1 小组的任务分工 (6)3.2 本人主要工作 (6)第四章系统设计 (9)4.1 总体设计 (9)4.2 详细设计 (9)第五章运行与测试结果 (22)5.1 一组测试数据 (22)5.2 界面实现情况 (23)第六章结论 (27)课程设计心得 (28)参考文献 (29)致谢 (30)附录(核心代码清单) (31)引言编译器的构造工具是根据用户输入的语言的文法,编译器的构造工具可以生成程序来处理以用户输入的文法书写的文本。
first集合和follow集合的求法

first集合和follow集合的求法编译原理是计算机专业中的重要学科,其中语法分析是编译原理的基础。
而语法分析器(Parser)的核心就是构建语法分析表格。
而在构建语法分析表格的过程中,first集合和follow集合的求法是一个非常重要的问题,本文就将详细介绍first集合和follow集合的求法。
一、first集合first集合指的是文法中每个非终结符号的经过一次推导得到的所有终结符号的集合,也就是最小前缀(First)的集合。
例如对于一个简单的文法表达式E→E+T|T,其中E和T是非终结符号,+是终极符号。
那么开始寻找E的first集合时,我们应该先判断E能够推导出哪些符号,根据文法表达式,E可以推导出E+T和T。
接着我们可以判断E+T和T 所能推导出的所有终结符号,并将这些终结符号加入到E的first集合中。
具体步骤可以参考下面的推导过程:E → E + TE → TT → a那么最终E的first集合就是{a,+}。
二、follow集合follow集合指的是文法中每个非终结符号在所有推导过程中后跟的符号的集合。
例如对于一个简单的文法表达式E→E+T|T,其中E和T是非终结符号,+是终极符号。
求解E的follow集合时,首先要考虑的是E出现在了哪些地方。
通过分析E在文法表达式中的位置,我们可以发现E出现在了三种不同的情况下:1. E是文法的起始符号,此时E的follow集合中必须包含结束符$。
2. E出现在某些规则的右侧,此时E的follow集合中必须包含右侧的符号的first集合,但是需要注意的是,如果推导出空串,则应该将右侧的非终结符号所在位置的follow集合添加进来。
3. E的右侧是其所在规则的最末尾,此时需要将E所在规则的左侧符号所在位置的follow集合添加到E的follow集合中。
根据以上三种情况,我们可以结合上面的文法表达式来推导出E的follow集合。
具体步骤可以参考下面的推导过程:S → EE → E + TE → TT → a1. $ ∈ follow(E)2. follow(T) = {+, $}follow(E) = first(T) ∪ {+, $}follow(E) = {+, a, $}3. follow(E) = {+, $}那么最终E的follow集合就是{+, a, $}。
求文法的集first 和follow集

#include<iostream>#include"string.h"#define MAX 100using namespace std;//产生式结构体struct product{int rl;char l,r[20];}p[100];//first 和follow 集struct set{int n;//元素数量char elm[100];}first[MAX],follow[MAX];int table[MAX][MAX];//预测分析表char v[100],t[100];//变量和终结符int n,vnum,tnum;//产生式数量,变量数量和终结符数量//判断是否为终结符inline bool isterminal(char x){if(x>='A'&&x<='Z')return false;return true;}//判断符号x是否从未出现过bool ex(char x){int i;if(isterminal(x)){for(i=1;i<=tnum;i++)if(t[i]==x) return true;return false;}for(i=1;i<=vnum;i++)if(v[i]==x) return true;return false;}//读入文法void load(){int i,j,k;char tmp[25];//printf("输入产生式的数量:"); scanf("%d",&n);for(vnum=tnum=0,i=1;i<=n;i++) {scanf("%s",tmp);p[i].l=tmp[0];if(!ex(tmp[0])) v[++vnum]=tmp[0]; for(k=0,j=3;tmp[j];j++){p[i].r[k++]=tmp[j];if(isterminal(tmp[j])){if(!ex(tmp[j]))t[++tnum]=tmp[j];}else if(!ex(tmp[j]))v[++vnum]=tmp[j];}p[i].r[k]=0,p[i].rl=k-1;}t[++tnum]=v[++vnum]='#';}//输出用户输入的文法void show(){int i;for(i=1;i<=n;i++)printf("%c->%s\n",p[i].l,p[i].r);}//把符号x变为对应的编号int cid(char x){int i;if(!isterminal(x)){for(i=1;i<=vnum;i++)if(v[i]==x) return i;}for(i=1;i<=tnum;i++)if(t[i]==x) return i+1000;return -1;}//判断集合st里面是否包含符号idtbool inclu(struct set &st,char idt){int i;for(i=1;i<=st.n;i++)if(st.elm[i]==idt)return true;return false;}//把符号e添加到集合st里面inline void add(struct set &st,char e){st.n++;st.elm[st.n]=e;}//求first集void makefirst(){int i,j,k,idl,idr;bool inc;inc=true;while(inc){inc=false;for(i=1;i<=n;i++) //遍历所有产生式{idl=cid(p[i].l);for(j=0;p[i].r[j];j++){idr=cid(p[i].r[j]);//如果当前为终结符//并且first[idl]中不包含这个终结符就把这个终结符加入first[idl] if(idr>1000){if(!inclu(first[idl],p[i].r[j])){add(first[idl],p[i].r[j]);inc=true;}break;}//否则把该变量的first集里面的元素加入first[idl]else{for(k=1;k<=first[idr].n;k++)if(!inclu(first[idl],first[idr].elm[k])){add(first[idl],first[idr].elm[k]);inc=true;}}//.....if(!inclu(first[idl],'~')) break;}}// 若idl可以转换为空,则‘~’应属于first[idl]if(p[i].r[j]==0&&!inclu(first[idl],'~')){add(first[idl],'~');inc=true;}}}}//输出集合,flag用于表示是first还是followvoid print(struct set *st,int flag){int i,j;char *s;puts("\n");flag==0? s="FIRST":s="FOLLOW";for(i=1;i<=vnum;i++){printf("%s(%c): ",s,v[i]);for(j=1;j<=st[i].n;j++)printf("%c ",st[i].elm[j]);puts("");}}//求follow集void makefollow(){int i,j,k,idl,idr,idf;bool flag,inc=true;add(follow[1],'#');//把结束标志"#"加入起始符的follow集while(inc){inc=false;for(i=1;i<=n;i++)idl=cid(p[i].l);for(flag=true,j=p[i].rl;j>=0;j--){idr=cid(p[i].r[j]);if(idr>1000){flag=false; continue;}if(flag){for(k=1;k<=follow[idl].n;k++){if(!inclu(follow[idr],follow[idl].elm[k])){add(follow[idr],follow[idl].elm[k]);inc=true;}}}if(j<p[i].rl) idf=cid(p[i].r[j+1]);else continue;if(idf>1000){if(!inclu(follow[idr],p[i].r[j+1]))add(follow[idr],p[i].r[j+1]);continue;}for(k=1;k<=first[idf].n;k++){if(!inclu(follow[idr],first[idf].elm[k])&&first[idf].elm[k]!='~') {add(follow[idr],first[idf].elm[k]);inc=true;}}}}}}void maketable(){int i,j,k,idl,idr,idt;char ch;bool flag;memset(table,0,sizeof(table));table[vnum][tnum]=-1;for(i=1;i<=n;i++){idl=cid(p[i].l);for(j=0;j<=p[i].rl;j++){ch=p[i].r[j];idr=cid(ch);if(idr>1000){idr-=1000;if(ch!='~'){if(table[idl][idr]) goto end;table[idl][idr]=i;}else{for(k=1;k<=follow[idl].n;k++){idt=cid(follow[idl].elm[k])-1000;if(table[idl][idt]) goto end;table[idl][idt]=i;}}break;}for(flag=false,k=1;k<=first[idr].n;k++) {idt=cid(first[idr].elm[k])-1000;if(first[idr].elm[k]=='~') flag=true; if(table[idl][idt]) goto end;table[idl][idt]=i;}if(!flag) break;}if(j>p[i].rl){for(k=1;k<=follow[idl].n;k++){idt=cid(follow[idl].elm[k])-1000;if(table[idl][idt]) goto end;table[idl][idt]=i;}}}return;end: printf("It's not a LL(1) language,and if you want to use this program to compile you may get a wrong answer!\n");return;}void compile(char *exp){int i,j,top,idl,idr,t,step[MAX],c=0;char stack[MAX];top=1;t=strlen(exp);stack[0]='#',stack[1]=v[1];exp[t]='#',exp[t+1]=0;for(i=0;;){idl=cid(stack[top]);idr=cid(exp[i])-1000;if(idl>1000) goto fail;t=table[idl][idr];if(t==0) goto fail;step[++c]=t;for(top--,j=p[t].rl;j>=0;j--){if(p[t].r[j]!='~')stack[++top]=p[t].r[j];}while(top>=0&&exp[i]&&stack[top]==exp[i]){if(stack[top]=='#') goto pass;top--,i++;}}pass: printf("Accept!\n");for(i=1;i<=c;i++)printf("%c->%s\n",p[step[i]].l,p[step[i]].r);return;fail: printf("Compile Error!\n");return;}int main(){char exp[MAX];freopen("LL(1).txt","r",stdin); load();show();makefirst();print(first,0);makefollow();print(follow,1);maketable();while(scanf("%s\n",exp)!=EOF) compile(exp);return 0;}。
编译原理实验报告FIRST集和FOLLOW集

编译原理实验报告实验名称计算first集合和follow集合实验时间院系计算机科学和技术班级软件工程1班学号姓名输入:任意的上下文无关文法。
输出:所输入的上下文无关文法一切非终结符的first 集合和follow 集合。
2. 实验原理设文法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 后的终结符号的集合。
FOLLOW 集可按下列方法求得:(1) 对于文法G[S]的开始符号S ,有#∈FOLLOW (S );(2) 若文法G[S]中有形如B →xAy 的规则,其中x ,y ∈V *,则FIRST(y )-{ε}∈FOLLOW (A );(3) 若文法G[S]中有形如B →xA 的规则,或形如B →xAy 的规则且ε∈FIRST (y ),其中x ,y ∈V *,则FOLLOW (B )∈FOLLOW (A );计算first集合和follow集合4.实验心得通过上机实验我对文法符号的FIRST集和FOLLOW集有了更深刻的理解,已经熟练的掌握了求解的思想和方法,同时也锻炼了自己的动手解决问题的能力,对编程能力也有所提高。
编译

四、综合题1. 已知文法 G(E)E →T|E+TT→F|T *FF →(E)|i(1)给出句型(T *F+i)的最右推导;(2)给出句型(T *F+i)的短语、素短语。
解:(1) 最右推导:E ->T->F->(E)->(E + T)->(E + F)->(E + i)->(T+i)->(T*F+i)(2) 短语:(T*F+i) ,T*F+i ,T*F,i素短语:T*F,i2. 构造正规式 1(0|1)*101 相应的 DFA 。
解:先构造 NFA :确定化:重新命名,令 AB 为 B、AC 为 C、ABY 为 D 得:所以,可得 DFA 为:3. 文法:S->MH|aH ->LSo| εK ->dML| εL ->eHfM->K|bLM判断 G 是否为 LL(1) 文法,如果是,构造 LL(1) 分析表。
解:各符号的 FIRST 集和 FOLLOW集为:预测分析表由于预测分析表中无多重入口,所以可判定文法是 LL(1)的。
4. 对下面的文法 G :E ->TE'E'->+E| εT ->FT'T' ->T| εF-> PF'F'-> *F'| εP->(E)|a|b|^(1)计算这个文法的每个非终结符的 FIRST 集和 FOLLOW 集。
(4 分)(2) 证明这个方法是 LL(1) 的。
(4 分)(3) 构造它的预测分析表。
(2 分)解:(1)计算这个文法的每个非终结符的 FIRST 集和 FOLLOW集。
FIRST 集合有:FIRST(E)=FIRST(T)=FIRST(F)=FIRST(P)={(,a,b,^};FIRST(E')={+,ε }FIRST(T)=FIRST(F)=FIRST(P)={(,a,b,^};FIRST(T')=FIRST(T)U{ε}={(,a,b,^, ε};FIRST(F)=FIRST(P)={(,a,b,^};FIRST(F')=FIRST(P)={*, ε};FIRST(P)={(,a,b,^};FOLLOW 集合有:FOLLOW(E)={),#};FOLLOW(E')=FOLLOW(E)={),#}; FOLLOW(T)=FIRST(E')UFOLLOW(E)={+,),#};//不包含εFOLLOW(T')=FOLLOW(T)=FIRST(E')UFOLLOW(E)={+,),#};FOLLOW(F)=FIRST(T')UFOLLOW(T)={(,a,b,^,+,),#};//不包含εFOLLOW(F')=FOLLOW(F)=FIRST(T')UFOLLOW(T)={(,a,b,^,+,),#};FOLLOW(P)=FIRST(F')UFOLLOW(F)={*,(,a,b,^,+,),#};//不包含ε(2)证明这个方法是 LL(1)的。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
构造预测分析表源程序:#include<stdlib.h>#include<stdio.h>#include<string.h>/*******************************************/int count=0; /*分解的产生式的个数*/int number; /*所有终结符和非终结符的总数*/char start; /*开始符号*/char termin[50]; /*终结符号*/char non_ter[50]; /*非终结符号*/char v[50]; /*所有符号*/char left[50]; /*左部*/char right[50][50]; /*右部*/char first[50][50],follow[50][50]; /*各产生式右部的FIRST和左部的FOLLOW 集合*/char first1[50][50]; /*所有单个符号的FIRST集合*/char select[50][50]; /*各单个产生式的SELECT集合*/char f[50],F[50]; /*记录各符号的FIRST和FOLLOW是否已求过*/char empty[20]; /*记录可直接推出@的符号*/char TEMP[50]; /*求FOLLOW时存放某一符号串的FIRST集合*/ int validity=1; /*表示输入文法是否有效*/int ll=1; /*表示输入文法是否为LL(1)文法*/int M[20][20]; /*分析表*/char choose; /*用户输入时使用*/char empt[20]; /*求_emp()时使用*/char fo[20]; /*求FOLLOW集合时使用*//*******************************************判断一个字符是否在指定字符串中********************************************/int in(char c,char *p){int i;if(strlen(p)==0)return(0);for(i=0;;i++){if(p[i]==c)return(1); /*若在,返回1*/if(i==strlen(p))return(0); /*若不在,返回0*/}}/******************************************* 得到一个不是非终结符的符号********************************************/ char c(){char c='A';while(in(c,non_ter)==1)c++;return(c);}/******************************************* 分解含有左递归的产生式********************************************/ void recur(char *point){ /*完整的产生式在point[]中*/int j,m=0,n=3,k;char temp[20],ch;ch=c(); /*得到一个非终结符*/k=strlen(non_ter);non_ter[k]=ch;non_ter[k+1]='\0';for(j=0;j<=strlen(point)-1;j++){if(point[n]==point[0]){ /*如果'|'后的首符号和左部相同*/ for(j=n+1;j<=strlen(point)-1;j++){while(point[j]!='|'&&point[j]!='\0')temp[m++]=point[j++];left[count]=ch;memcpy(right[count],temp,m);right[count][m]=ch;right[count][m+1]='\0';m=0;count++;if(point[j]=='|'){n=j+1;break;}}}else{ /*如果'|'后的首符号和左部不同*/ left[count]=ch;right[count][0]='@';right[count][1]='\0';count++;for(j=n;j<=strlen(point)-1;j++){if(point[j]!='|')temp[m++]=point[j];else{left[count]=point[0];memcpy(right[count],temp,m); right[count][m]=ch;right[count][m+1]='\0';printf(" count=%d ",count);m=0;count++;}}left[count]=point[0];memcpy(right[count],temp,m); right[count][m]=ch;right[count][m+1]='\0';count++;m=0;}}}void non_re(char *point){int m=0,j;char temp[20];for(j=3;j<=strlen(point)-1;j++) {if(point[j]!='|')temp[m++]=point[j];else{left[count]=point[0];memcpy(right[count],temp,m); right[count][m]='\0';m=0;count++;}}left[count]=point[0];memcpy(right[count],temp,m);right[count][m]='\0';count++;m=0;}/*******************************************读入一个文法********************************************/char grammer(char *t,char *n,char *left,char right[50][50]) {char vn[50],vt[50];char s;char p[50][50];int i,j,k;printf("请输入文法的非终结符号串:");scanf("%s",vn);getchar();i=strlen(vn);memcpy(n,vn,i);n[i]='\0';printf("请输入文法的终结符号串:");scanf("%s",vt);getchar();i=strlen(vt);memcpy(t,vt,i);t[i]='\0';printf("请输入文法的开始符号:");scanf("%c",&s);getchar();printf("请输入文法产生式的条数:");scanf("%d",&i);getchar();for(j=1;j<=i;j++){printf("请输入文法的第%d条(共%d条)产生式:",j,i); scanf("%s",p[j-1]);getchar();}for(j=0;j<=i-1;j++)if(p[j][1]!='-'||p[j][2]!='>'){ printf("\ninput error!");validity=0;return('\0');} /*检测输入错误*/for(k=0;k<=i-1;k++){ /*分解输入的各产生式*/ if(p[k][3]==p[k][0])recur(p[k]);elsenon_re(p[k]);}return(s);}/*******************************************将单个符号或符号串并入另一符号串********************************************/void merge(char *d,char *s,int type){ /*d是目标符号串,s是源串,type=1,源串中的' @ '一并并入目串; type=2,源串中的' @ '不并入目串*/int i,j;for(i=0;i<=strlen(s)-1;i++){if(type==2&&s[i]=='@');else{for(j=0;;j++){if(j<strlen(d)&&s[i]==d[j])break;if(j==strlen(d)){d[j]=s[i];d[j+1]='\0';break;}}}}}/*******************************************求所有能直接推出@的符号********************************************/ void emp(char c){ /*即求所有由' @ '推出的符号*/ char temp[10];int i;for(i=0;i<=count-1;i++){if(right[i][0]==c&&strlen(right[i])==1){temp[0]=left[i];temp[1]='\0';merge(empty,temp,1);emp(left[i]);}}}/*******************************************求某一符号能否推出' @ '********************************************/int _emp(char c){ /*若能推出,返回1;否则,返回0*/ int i,j,k,result=1,mark=0;char temp[20];temp[0]=c;temp[1]='\0';merge(empt,temp,1);if(in(c,empty)==1)return(1);for(i=0;;i++){if(i==count)return(0);if(left[i]==c) /*找一个左部为c的产生式*/ {j=strlen(right[i]); /*j为右部的长度*/ if(j==1&&in(right[i][0],empty)==1)return(1);else if(j==1&&in(right[i][0],termin)==1)return(0);else{for(k=0;k<=j-1;k++)if(in(right[i][k],empt)==1)mark=1;if(mark==1)continue;else{for(k=0;k<=j-1;k++){result*=_emp(right[i][k]);temp[0]=right[i][k];temp[1]='\0';merge(empt,temp,1);}}}if(result==0&&i<count)continue;else if(result==1&&i<count)return(1);}}}/******************************************* 判断读入的文法是否正确********************************************/int judge(){int i,j;for(i=0;i<=count-1;i++){if(in(left[i],non_ter)==0){ /*若左部不在非终结符中,报错*/printf("\nerror1!");validity=0;return(0);}for(j=0;j<=strlen(right[i])-1;j++){if(in(right[i][j],non_ter)==0&&in(right[i][j],termin)==0&&right[i][j]!='@') { /*若右部某一符号不在非终结符、终结符中且不为' @ ',报错*/ printf("\nerror2!");validity=0;return(0);}}}return(1);}/*******************************************求单个符号的FIRST********************************************/void first2(int i){ /*i为符号在所有输入符号中的序号*/ char c,temp[20];int j,k,m;c=v[i];char ch='@';emp(ch);if(in(c,termin)==1) /*若为终结符*/{first1[i][0]=c;first1[i][1]='\0';}else if(in(c,non_ter)==1) /*若为非终结符*/{for(j=0;j<=count-1;j++){if(left[j]==c){if(in(right[j][0],termin)==1||right[j][0]=='@') {temp[0]=right[j][0];temp[1]='\0';merge(first1[i],temp,1);}else if(in(right[j][0],non_ter)==1){if(right[j][0]==c)continue;for(k=0;;k++)if(v[k]==right[j][0])break;if(f[k]=='0'){first2(k);f[k]='1';}merge(first1[i],first1[k],2);for(k=0;k<=strlen(right[j])-1;k++) {empt[0]='\0';if(_emp(right[j][k])==1&&k<strlen(right[j])-1) {for(m=0;;m++)if(v[m]==right[j][k+1])break;if(f[m]=='0'){first2(m);f[m]='1';}merge(first1[i],first1[m],2);}else if(_emp(right[j][k])==1&&k==strlen(right[j])-1) {temp[0]='@';temp[1]='\0';merge(first1[i],temp,1);}elsebreak;}}}}}f[i]='1';}/*******************************************求各产生式右部的FIRST********************************************/void FIRST(int i,char *p){int length;int j,k,m;char temp[20];length=strlen(p);if(length==1) /*如果右部为单个符号*/ {if(p[0]=='@'){if(i>=0){first[i][0]='@';first[i][1]='\0';}else{TEMP[0]='@';TEMP[1]='\0';}}else{for(j=0;;j++)if(v[j]==p[0])break;if(i>=0){memcpy(first[i],first1[j],strlen(first1[j])); first[i][strlen(first1[j])]='\0';}else{memcpy(TEMP,first1[j],strlen(first1[j]));TEMP[strlen(first1[j])]='\0';}}}else /*如果右部为符号串*/ {for(j=0;;j++)if(v[j]==p[0])break;if(i>=0)merge(first[i],first1[j],2);elsemerge(TEMP,first1[j],2);for(k=0;k<=length-1;k++){empt[0]='\0';if(_emp(p[k])==1&&k<length-1){for(m=0;;m++)if(v[m]==right[i][k+1])break;if(i>=0)merge(first[i],first1[m],2);elsemerge(TEMP,first1[m],2);}else if(_emp(p[k])==1&&k==length-1) {temp[0]='@';temp[1]='\0';if(i>=0)merge(first[i],temp,1);elsemerge(TEMP,temp,1);}else if(_emp(p[k])==0)break;}}}/*******************************************求各产生式左部的FOLLOW********************************************/ void FOLLOW(int i){int j,k,m,n,result=1;char c,temp[20];c=non_ter[i]; /*c为待求的非终结符*/ temp[0]=c;temp[1]='\0';merge(fo,temp,1);if(c==start){ /*若为开始符号*/temp[0]='#';temp[1]='\0';merge(follow[i],temp,1);}for(j=0;j<=count-1;j++){if(in(c,right[j])==1) /*找一个右部含有c的产生式*/{for(k=0;;k++)if(right[j][k]==c)break; /*k为c在该产生式右部的序号*/for(m=0;;m++)if(v[m]==left[j])break; /*m为产生式左部非终结符在所有符号中的序号*/ if(k==strlen(right[j])-1){ /*如果c在产生式右部的最后*/if(in(v[m],fo)==1){merge(follow[i],follow[m],1);continue;}if(F[m]=='0'){FOLLOW(m);F[m]='1';}merge(follow[i],follow[m],1);}else{ /*如果c不在产生式右部的最后*/ for(n=k+1;n<=strlen(right[j])-1;n++){empt[0]='\0';result*=_emp(right[j][n]);}if(result==1){ /*如果右部c后面的符号串能推出^*/ if(in(v[m],fo)==1){ /*避免循环递归*/merge(follow[i],follow[m],1);continue;}if(F[m]=='0'){FOLLOW(m);F[m]='1';}merge(follow[i],follow[m],1);}for(n=k+1;n<=strlen(right[j])-1;n++)temp[n-k-1]=right[j][n]; temp[strlen(right[j])-k-1]='\0';FIRST(-1,temp);merge(follow[i],TEMP,2);}}}F[i]='1';}/*******************************************判断读入文法是否为一个LL(1)文法********************************************/int ll1(){int i,j,length,result=1;char temp[50];for(j=0;j<=49;j++){ /*初始化*/first[j][0]='\0';follow[j][0]='\0';first1[j][0]='\0';select[j][0]='\0';TEMP[j]='\0';temp[j]='\0';f[j]='0';F[j]='0';}for(j=0;j<=strlen(v)-1;j++)first2(j); /*求单个符号的FIRST集合*/ printf("\n各非终结符导出的first集:");for(j=0;j<=strlen(v)-1;j++)printf("%c:%s ",v[j],first1[j]);printf("\n能导空的非终结符集合:%s",empty); // printf("\n_emp:");//for(j=0;j<=strlen(v)-1;j++)// printf("%d ",_emp(v[j]));for(i=0;i<=count-1;i++)FIRST(i,right[i]); /*求FIRST*/for(j=0;j<=strlen(non_ter)-1;j++){ /*求FOLLOW*/ if(fo[j]==0){fo[0]='\0';FOLLOW(j);}}//printf("\nfirst:");//for(i=0;i<=count-1;i++)// printf("%s ",first[i]);printf("\nfollow集合:");for(i=0;i<=strlen(non_ter)-1;i++)for(i=0;i<=count-1;i++){ /*求每一产生式的SELECT集合*/ memcpy(select[i],first[i],strlen(first[i]));select[i][strlen(first[i])]='\0';for(j=0;j<=strlen(right[i])-1;j++)result*=_emp(right[i][j]);if(strlen(right[i])==1&&right[i][0]=='@')result=1;if(result==1){for(j=0;;j++)if(v[j]==left[i])break;merge(select[i],follow[j],1);}}printf("\nselect集合顺序是:");for(i=0;i<=count-1;i++)memcpy(temp,select[0],strlen(select[0]));temp[strlen(select[0])]='\0';for(i=1;i<=count-1;i++){ /*判断输入文法是否为LL(1)文法*/ length=strlen(temp);if(left[i]==left[i-1]){merge(temp,select[i],1);if(strlen(temp)<length+strlen(select[i]))return(0);}else{temp[0]='\0';memcpy(temp,select[i],strlen(select[i]));temp[strlen(select[i])]='\0';}}return(1);}/*******************************************构造分析表M********************************************/ void MM(){int i,j,k,m;for(i=0;i<=19;i++)for(j=0;j<=19;j++)M[i][j]=-1;i=strlen(termin);termin[i]='#'; /*将#加入终结符数组*/ termin[i+1]='\0';for(i=0;i<=count-1;i++){for(m=0;;m++)if(non_ter[m]==left[i])break; /*m为产生式左部非终结符的序号*/for(j=0;j<=strlen(select[i])-1;j++){if(in(select[i][j],termin)==1){for(k=0;;k++)if(termin[k]==select[i][j])break; /*k为产生式右部终结符的序号*/ M[m][k]=i;}}}}/*******************************************判断符号串是否是该文法的句型********************************************/void syntax(){int i,j,k,m,n,p,q;char ch;char S[50],str[50];printf("请输入该文法的句型:"); scanf("%s",str);getchar();i=strlen(str);str[i]='#';str[i+1]='\0';S[0]='#';S[1]=start;S[2]='\0';j=0;ch=str[j];while(1){if(in(S[strlen(S)-1],termin)==1){if(S[strlen(S)-1]!=ch){printf("该符号串不是文法的句型!");return;}else if(S[strlen(S)-1]=='#'){printf("该符号串是文法的句型."); return;}else{S[strlen(S)-1]='\0';j++;ch=str[j];}}else{for(i=0;;i++)if(non_ter[i]==S[strlen(S)-1])break;for(k=0;;k++){if(termin[k]==ch)break;if(k==strlen(termin)){printf("词法错误!");return;}}if(M[i][k]==-1){printf("语法错误!");return;}else{m=M[i][k];if(right[m][0]=='@')S[strlen(S)-1]='\0';else{p=strlen(S)-1;q=p;for(n=strlen(right[m])-1;n>=0;n--) S[p++]=right[m][n]; S[q+strlen(right[m])]='\0';}}}printf("S:%s str:",S);for(p=j;p<=strlen(str)-1;p++)printf("%c",str[p]);printf(" \n");}}/******************************************* 一个用户调用函数********************************************/void menu(){syntax();printf("\n是否继续?(y or n):");scanf("%c",&choose);getchar();while(choose=='y'){menu();}}/*******************************************主函数********************************************/void main(){int i,j;start=grammer(termin,non_ter,left,right); /*读入一个文法*/printf("count=%d",count);printf("\n开始符号为:%c",start);strcpy(v,non_ter);strcat(v,termin);printf("\n所有符号集为:%s",v);printf("\n非终结符集合:{%s",non_ter); printf("}");printf("\n终结符集合:{%s",termin); printf("}");printf("\n文法所有右边表达式依次是:");for(i=0;i<=count-1;i++)printf("%s ",right[i]);printf("\n文法所有左边开始符依次是:"); for(i=0;i<=count-1;i++)printf("%c ",left[i]);if(validity==1)validity=judge();//printf("\nvalidity=%d",validity);if(validity==1)ll=ll1();// printf("\nll=%d",ll);if(ll==0)printf("\n该文法不是一个LL1文法!");else{printf("\n该文法是一个LL(1)文法!");MM();// printf("\n");//for(i=0;i<=19;i++)// for(j=0;j<=19;j++)// if(M[i][j]>=0)//printf("M[%d][%d]=%d ",i,j,M[i][j]); menu();}}运行结果:。