注:代码生成的项目集规范簇、ACTION GOTO表的顺序可能和课本、教材、参考答案的顺序不同,但这并不影响分析过程的正确性,毕竟机器是按规律办事的😄
主函数已经把功能分的很清晰,代码注释也很详细,如阅读代码,可先在主函数查看,再到子函数逐一查看
演示
演示所用文法和句子
文法
E->E-T
E->T
T->T*F
T->F
F->-F
F->a
待分析句子
a–a*a
(1)根据提示输入文法的个数
(2)输入文法
回车,然后依此输入文法
(3)扩展文法的生成、项目集规范簇的生成、ACTION GOTO表的生成
回车后扩展文法的生成、项目集规范簇的生成、ACTION GOTO表的生成
扩展文法
项目集规范簇
ACTION GOTO表
(3)分析句子
根据提示输入分析串
(4)生成分析过程
输入分析串后回车
思路
其实思路比较简单,只是实现起来较为繁琐。
1.输入文法
2.进行项目集规范簇的构建
(1)采用链表进行生成存储,因为生成个数不确定
(2)采用双重循环,外循环进行In的遍历,内循环进行项目的遍历
(3)结束条件为遍历到最后一个项目为止,也就是无法再生成
3.根据项目集规范簇生成ACTION表和GO表
(1)将输入的文法进行拆分为终结符与非终结符,非终结符做action表的行标,终结符做goto表的行标
(2)将项目集规范簇的In中的n做列标
(3)在action表中用正数代表移进,负数代表归约,999代表接受,goto表用数字代表是哪条文法
(4)双重循环遍历项目集规范簇进行action goto表的生成
(5)外循环遍历每个I,内循环查询这个I是哪些I生成的,因为一个I可以通过多个I生成
4.通过查找ACTION和GO表进行句子的分析
源代码
#include<stdio.h> #include<string.h> #include<stdlib.h> typedef struct GLR_CSS{ int num;//条的编号 char left;//产生式左边 char right[30];//产生式右边 int dot;//用于存储点的位置 struct GLR_CSS *next; }CSS; typedef struct GLR{ int I_01;//存放自己是I几 int I_02;//存放是经过I几通过输入c变换过来的 char c; //I01输入c到I02 struct GLR_CSS *css;//头指针 int num;//产生式的个数 struct GLR *next; }LR; char g[100][20]; int n;//文法的条数 char at[100];//存储终结符 int at_num=0; //用于记录at数组的长度 char gt[100];//存储非终结符 char gt_01[100][2];//第一个存储非终结符,第二个存储记录 int gt_num=0;//用于记录gt数组的长度 LR lr;//用于存储输入的文法 LR *I; LR *Ips;//存储头结点 LR *Ipf;//存储最后一个结点 int act_gt[100][100]; char str[100];//需要分析的输入串 int str_num;//分析串栈的指针 int fhz[100];//符号栈 int fhz_num;//符号栈的指针 //接收输入的文法 void get_g(){ int i; printf("请输入文法的条数:"); scanf("%d",&n); getchar(); for(i=0;i<n;i++){ printf("(%d)",i+1); gets(g[i]); } } //查重action表 void action_01(char c){ if(at_num==0){//没有直接放 at[at_num]=c; at_num++; }else{//有,查重 int i; int flag=0; for(i=0;i<at_num;i++){ if(at[i]==c){ flag=1; } } if(flag==0){//没有 at[at_num]=c; at_num++; } } } //找出action表 void action(){//找action序列(非大写) int i; //遍历每一条 int j;//遍历每一条的每一个 int len; for(i=0;i<n;i++){ len=strlen(g[i]); for(j=0;j<len;j++){ if(g[i][j]=='-'&&g[i][j+1]=='>'){//排除->(排除箭头,也就是“定义为”) j++; continue; }else{ if(g[i][j]<65||g[i][j]>90){//终结符的范围 action_01(g[i][j]); //放入at数组,//该函数用于检测是否重复 } } } } at[at_num]='#'; at_num++; at[at_num]='\0'; } //查重goto表 void gotoo_01(char c){ if(gt_num==0){//没有直接放 gt[gt_num]=c; gt_num++; }else{//有,查重 int i; int flag=0; for(i=0;i<gt_num;i++){ if(gt[i]==c){ flag=1; } } if(flag==0){//没有 gt[gt_num]=c; gt_num++; } } } //找出goto表 void gotoo(){//找goto序列(大写) int i; //遍历每一条 int j;//遍历每一条的每一个 int len; for(i=0;i<n;i++){ len=strlen(g[i]); for(j=0;j<len;j++){ if(g[i][j]=='-'&&g[i][j+1]=='>'){//排除->(排除箭头,也就是“定义为”) j++; continue; }else{ if(g[i][j]>=65&&g[i][j]<=90){//非终结符的范围 gotoo_01(g[i][j]); //放入gt数组,//该函数用于检测是否重复 } } } } gt[gt_num]='\0'; } void init_gt_01(){ int i; for(i=0;i<gt_num;i++){ gt_01[i][0]=gt[i]; gt_01[i][1]=0;//未检测时为0; } } //测试,输出结构体 void f(CSS *p){ int i=1; while(p!=NULL){ printf("(%d)%c->%s\n",i,p->left,p->right); printf("点:%d\n",p->dot); p=p->next; i++; } } //将所有文法存储在lr变量中 void lr_save(){ lr.num=n;//存储初始文法的条数 int i,j; int len; CSS *cssp,*cssk,*csst; cssp=(CSS*)malloc(sizeof(CSS));//头结点 cssk=cssp; for(i=0;i<n;i++){ len=strlen(g[i]); csst=(CSS*)malloc(sizeof(CSS)); csst->num=i+1; //存储是第几条文法 csst->left=g[i][0];//存储左边 int k=0; for(j=3;j<len;j++){ csst->right[k]=g[i][j]; k++; } csst->right[k]='\0'; cssk->next=csst; cssk=cssk->next; } cssk->next=NULL; lr.css=cssp; //fff();//测试,输出函数 } //初始化I0;并初始头结点 void into_I0(){ //空结点 Ips=(LR*)malloc(sizeof(LR)); //临时结点 LR *Ipt; Ipt=(LR*)malloc(sizeof(LR)); //Ipt->num=1;//文法条数 //初始化第一个数据结点 Ipt->I_01=0;//I0 //定义语句的临时结点 ,和转移结点 CSS *csst,*csss; //定义遍历的结点 CSS *cssk; cssk=lr.css->next; //语句的第一个空结点 Ipt->css=(CSS*)malloc(sizeof(CSS)); csss=Ipt->css; //语句初始个数 Ipt->num=0; int bt=-1; / //E'->E bt++;//表达式的序号 csst =(CSS*)malloc(sizeof(CSS)); //表达式的序号 csst->num=bt; //点的位置 csst->dot=0; //表达式左边 csst->left='S'; //表达式右边 csst->right[0]=cssk->left; csst->right[1]='\0'; csss->next=csst; csss=csss->next; //cssk=cssk->next; Ipt->num++;//语句条数加1 / while(cssk!=NULL){ bt++;//表达式的序号 csst =(CSS*)malloc(sizeof(CSS)); //表达式的序号 csst->num=bt; //点的位置 csst->dot=0; //表达式左边 复制 csst->left=cssk->left; //表达式右边复制 strcpy(csst->right,cssk->right); csss->next=csst; csss=csss->next; cssk=cssk->next; Ipt->num++;//语句条数加1 } csss->next=NULL; Ips->next=Ipt;//头 Ipf=Ips->next;//指向尾巴 Ipf->next=NULL; //fff(); } int jc_gt_01(char c){//c处为0返回1,否则返回0 int i; for(i=0;i<gt_num;i++){ if(gt_01[i][0]==c){ if(gt_01[i][1]==0){ return 1; }else{ return 0; } } } return 0; } void fz_gt_01(int c){ int i; for(i=0;i<gt_num;i++){ if(gt_01[i][0]==c){ gt_01[i][1]=1; } } } void create_I(LR *lrr,CSS *css){ //清空gt_01数组 init_gt_01(); char s=css->right[css->dot];//当前移入的符号 //(1)遍历lrr结点所有表达式,生成新的I结点 //开辟I结点存储空间 LR *ln=(LR*)malloc(sizeof(LR)); ln->num=0;//文法初始条数 // 生成新表达式的csss CSS *csss,*csst; csss=(CSS*)malloc(sizeof(CSS));//头结点 ln->css=csss;//链上带头结点 csss->next=NULL; //遍历每条表达式的临时变量 CSS *lt=lrr->css->next; while(lt!=NULL){ if(lt->right[lt->dot]==s){//点后面是要移入的符号 csst=(CSS*)malloc(sizeof(CSS));//开辟一个表达式的存储空间 csst->next=NULL;//next复制NULL csst->left=lt->left; strcpy(csst->right,lt->right); csst->dot=lt->dot+1; csss->next=csst; csss=csss->next; ln->num++; } lt=lt->next; } //(2)判断新结点的每个表达式后是否是终结符,若不是则新添加 //csss目前指向最后一个 CSS *csss01=ln->css->next; CSS *csss02; //char pre='a'; while(csss01!=NULL){//循环目前生成的表达式 if(csss01->right[csss01->dot]>=65&&csss01->right[csss01->dot]<=90&&jc_gt_01(csss01->right[csss01->dot])==1){//非终结符判断条件 //printf("ssssssssssss\n"); csss02=Ips->next->css->next;//I0的所有表达式 while(csss02!=NULL){ if(csss02->left==csss01->right[csss01->dot]){ csst=(CSS*)malloc(sizeof(CSS));//开辟一个表达式的存储空间 csst->next=NULL;//next复制NULL csst->left=csss02->left; strcpy(csst->right,csss02->right); csst->dot=csss02->dot; csss->next=csst; csss=csss->next; ln->num++; } csss02=csss02->next; } fz_gt_01(csss01->right[csss01->dot]); } csss01=csss01->next; } //(3)查重 LR *Ipt01=Ips->next; CSS *csss03; CSS *csss04; int flag=0; while(Ipt01!=NULL){ if(Ipt01->num==ln->num){//语法条数相等才有相同的可能性 csss03=Ipt01->css->next; flag=0; while(csss03!=NULL){ csss04=ln->css->next; while(csss04!=NULL){ if(csss04->left==csss03->left&&csss04->dot==csss03->dot&&strcmp(csss03->right,csss04->right)==0){ flag++; } csss04=csss04->next; } csss03=csss03->next; } if(flag==ln->num){//重复了 return; } } Ipt01=Ipt01->next; } //加到末尾 ln->I_01=Ipf->I_01+1; ln->I_02=lrr->I_01; ln->c=s; Ipf->next=ln; Ipf=Ipf->next; Ipf->next=NULL; /*printf("-------------------\n"); printf("这是I(%d)\n",ln->I_01); printf("I(%d)输入%c变成\n",ln->I_02,ln->c); printf("表达式为:\n"); f(ln->css->next); printf("-------------------\n"); */ } //将所有项目I0---In列出 void lr_analyse(){ LR *Ipt_01;//用于遍历 LR *Ipt_02; //用于开辟新结点 CSS *csst_01;//遍历 CSS *csst_02; Ipt_01=Ips->next; // lr_analyse_exit()==1 while(1){//项目列表的最后一个所有语法的点都在最后面则退出 ,遍历I列表,每次一个 csst_01=Ipt_01->css->next;//In的第一个产生式子的位置 while(csst_01!=NULL){//当前I的第j个表达式点后移,产生新的 //移动点 // //printf("222222222\n"); if(csst_01->dot==strlen(csst_01->right)){//已经完了 csst_01=csst_01->next; continue; }else{ //printf("-------当前遍历的是I(%d)的%c->%s语句-------\n",Ipt_01->I_01,csst_01->left,csst_01->right); create_I(Ipt_01,csst_01);//传入当前I的所有表达式的头,和当前语句 } csst_01=csst_01->next; } //printf("11111111111122222222222222222222222222"); if(Ipt_01->I_01==Ipf->I_01){ break; }else{ Ipt_01=Ipt_01->next; //printf("11111111111111111111111111\n"); } } } void acc_form(){ act_gt[1][strlen(at)-1]=999; } //检测是不是lt02移入lt01->c,变成lt01 void goto_form_01(LR *lt01,LR *lt02,char c){ CSS *csst01; csst01=lt02->css->next;//lt02的第一条语句 //移入后的新语句头 CSS *csst02; csst02=(CSS*)malloc(sizeof(CSS)); csst02->next=NULL; //移动临时变量 CSS *csst03,*csst04; csst03=csst02; while(csst01!=NULL){ if(csst01->right[csst01->dot]==c){ //printf("77888888999\n"); //申请空间 csst04=(CSS*)malloc(sizeof(CSS)); //填写表达式左边数据 csst04->left=csst01->left; //右边数据 strcpy(csst04->right,csst01->right); //点 csst04->dot=csst01->dot+1; csst04->next=NULL; csst03->next=csst04; csst03=csst03->next; } csst01=csst01->next; } //查看csst02是否与lt01表达式相同 int flag=0; csst02=csst02->next; /*printf("=======这是csst02=======\n"); f(csst02); printf("=======这是csst02=======\n");*/ CSS *csst05; while(csst02!=NULL){ csst05=lt01->css->next; while(csst05!=NULL){ //printf("777777777\n"); if(csst05->left==csst02->left&&csst05->dot==csst02->dot&&strcmp(csst05->right,csst02->right)==0){ flag++; } csst05=csst05->next; } csst02=csst02->next; } //printf("flag==%d,lt01->num=%d\n",flag,lt01->num); if(flag==lt01->num){ //说明lt02移入c,变成lt01 //纵坐标 int h=lt02->I_01; //横坐标 int x; int i; for(i=0;i<gt_num;i++){ if(gt[i]==c){ x=i; } } act_gt[h][at_num+x]=lt01->I_01; } //移入 } void goto_form(){ //遍历除I0所有LR->c为大写的 LR *lt01; lt01=Ips->next->next;//I1 LR *lt02;//I0 while(lt01!=NULL){ if(lt01->c>=65&<01->c<=90){//lt01是由某个状态移入c变成的 lt02=Ips->next;//从I0,开始 while(lt02!=NULL){ //检测是不是lt02移入lt01->c,变成lt01 goto_form_01(lt01,lt02,lt01->c);//并填写goto表 lt02=lt02->next; } } lt01=lt01->next; } } void action_r_form_01(int h,CSS *csst){//(纵坐标,当前表达式)在I0中找到,并填表 CSS *csst01=Ips->next->css; while(csst01!=NULL){ if(csst01->left==csst->left&&strcmp(csst01->right,csst->right)==0){ int num=csst01->num*(-1);//编号 //填表 int i; for(i=0;i<at_num;i++){ act_gt[h][i]=num; } } csst01=csst01->next; } } void action_r_form(){ //遍历I1开始 //如果In的某条文法的点在最后,则在I0的css中找对应的编号 ,(除left=S的情况,因为这个是acc) LR *lt01=Ips->next->next; CSS *csst01; while(lt01!=NULL){ csst01=lt01->css->next; while(csst01!=NULL){ if(csst01->left!='S'&&csst01->dot==strlen(csst01->right)){ action_r_form_01(lt01->I_01,csst01);//在I0中找到,并填表 } csst01=csst01->next; } lt01=lt01->next; } } //检测是不是lt02移入lt01->c,变成lt01 void action_s_form_01(LR *lt01,LR *lt02,char c){ init_gt_01();//清空gt_01数组 CSS *csst01; csst01=lt02->css->next;//lt02的第一条语句 //移入后的新语句头 CSS *csst02; csst02=(CSS*)malloc(sizeof(CSS)); csst02->next=NULL; //移动临时变量 CSS *csst03,*csst04; csst03=csst02; while(csst01!=NULL){ if(csst01->right[csst01->dot]==c){ //printf("77888888999\n"); //申请空间 csst04=(CSS*)malloc(sizeof(CSS)); //填写表达式左边数据 csst04->left=csst01->left; //右边数据 strcpy(csst04->right,csst01->right); //点 csst04->dot=csst01->dot+1; csst04->next=NULL; csst03->next=csst04; csst03=csst03->next; //printf("%c",csst04->right[csst04->dot]); } //printf("qqqqqqqqqqqqqqqqqqqq\n"); csst01=csst01->next; } //将csst02中的非终结符在I0中找出,现在csst03所指最后一个表达式 CSS *csst02_01=csst02->next; while(csst02_01!=NULL){ char cc=csst02_01->right[csst02_01->dot]; if(cc>=65&&cc<=90&&jc_gt_01(cc)==1){ //遍历I0 CSS *cssi0=Ips->next->css->next; while(cssi0!=NULL){ if(cssi0->left==cc){ //申请空间 csst04=(CSS*)malloc(sizeof(CSS)); //填写表达式左边数据 csst04->left=cssi0->left; //右边数据 strcpy(csst04->right,cssi0->right); //点 csst04->dot=cssi0->dot; csst04->next=NULL; csst03->next=csst04; csst03=csst03->next; } cssi0=cssi0->next; } fz_gt_01(cc); } csst02_01=csst02_01->next; } //查看csst02是否与lt01表达式相同 int flag=0; csst02=csst02->next; /*printf("=======这是csst02=======\n"); f(csst02); printf("=======这是csst02=======\n");*/ /*if(lt01->I_01==3){ printf("这是I3,,I%d正在与他比较\n",lt02->I_01); printf("生成的东西为:\n"); f(csst02); printf("%c\n",lt01->c); }*/ CSS *csst05; while(csst02!=NULL){ csst05=lt01->css->next; while(csst05!=NULL){ //printf("777777777\n"); if(csst05->left==csst02->left&&csst05->dot==csst02->dot&&strcmp(csst05->right,csst02->right)==0){ flag++; } csst05=csst05->next; } csst02=csst02->next; } //printf("flag==%d,lt01->num=%d\n",flag,lt01->num); if(flag==lt01->num){ //说明lt02移入c,变成lt01 //纵坐标 int h=lt02->I_01; //横坐标 int x; int i; for(i=0;i<at_num;i++){ if(at[i]==c){ x=i; } } act_gt[h][x]=lt01->I_01; } //移入 } void action_s_form(){ //遍历除I0所有LR->c为非大写的,也就是终结符 LR *lt01; lt01=Ips->next->next;//I1 LR *lt02;//I0 while(lt01!=NULL){ if(lt01->c<65||lt01->c>90){//lt01是由某个状态移入c变成的 lt02=Ips->next;//从I0,开始 while(lt02!=NULL){ //检测是不是lt02移入lt01->c,变成lt01 action_s_form_01(lt01,lt02,lt01->c);//并填写goto表 lt02=lt02->next; } } lt01=lt01->next; } } void output_action_goto(){ int i; int j; int k; int q; int w; printf("--------ACTION-------------------GOTO-------\n"); printf(" 状态"); for(q=0;q<at_num;q++){ printf("%5c",at[q]); } for(k=0;k<gt_num;k++){//goto表的表头 printf("%5c",gt[k]); } printf("\n"); for(i=0;i<=Ipf->I_01;i++){ printf("%5d",i);//状态 //输出action表的值 for(w=0;w<at_num;w++){ if(act_gt[i][w]==999){ printf(" acc"); continue; } if(act_gt[i][w]<0){ printf(" R%d",act_gt[i][w]*-1); continue; } if(act_gt[i][w]>0){ printf(" S%d",act_gt[i][w]); continue; } printf("%5c",' '); } //输出goto表的值 for(j=0;j<gt_num;j++){ if(act_gt[i][at_num+j]==0){ printf("%5c",' '); }else{ printf("%5d",act_gt[i][at_num+j]); } } printf("\n"); } } void anction_goto_form(){ //(1)填写acc-->999 acc_form(); //(2) 填写goto()表,输入大写 goto_form(); //(3)填写r负数 action_r_form(); //printf("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww\n"); //(4)填写s正数 action_s_form(); //printf("-----------------%d\n",lt->I_01); output_action_goto();//输出acion_goto表 } void output_lr_analyse(){ LR *lt=Ips->next; while(lt!=NULL){ printf("当前是I(%d),有%d条语句,输入字符是%c\n",lt->I_01,lt->num,lt->c); printf("语句为:\n"); f(lt->css->next); lt=lt->next; } } void get_string(){ printf("请输入分析串:"); gets(str); str[strlen(str)+1]='\0'; str[strlen(str)]='#'; //puts(str); } int jc_lr_form(int h,char c){ int i; int flag=0;//记录是action表的内容还是goto表的内容 int x; //遍历action for(i=0;i<at_num;i++){ if(at[i]==c){ x=i; flag=1;//action表 break; } } for(i=0;i<gt_num;i++){ if(gt[i]==c){ x=at_num+i; flag=2; break; } } if(flag==1){//在action中 if(act_gt[h][x]>0&&act_gt[h][x]!=32){ //S return act_gt[h][x]; } if(act_gt[h][x]<0){ return act_gt[h][x]; //r } if(act_gt[h][x]==999){ //接受 return 9999; } if(act_gt[h][x]==32){ //出错 return 2222; } } if(flag==2){//说明在goto表中 if(act_gt[h][x]!=32){//状态转移 return act_gt[h][x]+1000; }else{ //出错 return 2222; } } if(flag==0){ //出错 return 2222; } } void out_fhz(){ printf("符号栈:"); int i; for(i=0;i<=fhz_num;i++){ if(fhz[i]<=0){ printf("%d",fhz[i]*-1); }else{ //printf("ggggggggggggggggggggggggggggggggg"); printf("%c",fhz[i]); } } printf(" "); } void out_str(){ printf("当前输入:"); int i; for(i=str_num;i<strlen(str);i++){ printf("%c",str[i]); } printf(" "); } //找标号为num的表达式 CSS* guiyue(int num){ CSS *p=Ips->next->css->next; while(p!=NULL){ if(p->num==num){ return p; } p=p->next; } } void lr_process(){ //初始符号栈#0 fhz[fhz_num]='#'; //printf("------>%c",fhz[fhz_num]); fhz_num++; fhz[fhz_num]=0*(-1);//状态(用负数来表示状态,将状态与符号区分开来) //fhz_num++; while(1){ //查看符号栈栈顶元素,和串的栈顶元素 //查分析表 int sr_num=jc_lr_form(fhz[fhz_num]*-1,str[str_num]);//(状态,输入) //sr_num==2222//出差错 if(sr_num==2222){ printf("出错,不可识别\n"); break; } //sr_num>0&&sr_num<1000 移进 if(sr_num>0&&sr_num<1000){ out_fhz();//输出栈内容 out_str(); //当前输入 if(sr_num==999){ printf("接收accept\n"); break; }else{ printf("移进:S%d\n",sr_num); } //先移入符号,再移入状态 //移入符号 fhz_num++;//符号栈指针+1 fhz[fhz_num]=str[str_num]; str_num++;//输入串指针后移 //移入状态 fhz_num++; fhz[fhz_num]=sr_num*-1; } if(sr_num<0){//归约 out_fhz();//输出栈内容 out_str(); //当前输入 //在I0中找标号为sr_num*-1的 //sr_num*-1,要归约的表达式 CSS *csst01=guiyue(sr_num*-1); printf("归约:R%d(%c->%s)\n",sr_num*-1,csst01->left,csst01->right); //fhz指针后移一位,换成csst01->left fhz_num=fhz_num-2*strlen(csst01->right)+1; fhz[fhz_num]=csst01->left; int zt=jc_lr_form(fhz[fhz_num-1]*-1,fhz[fhz_num]); //printf("%d\n",zt); if(zt>1000&&zt<2000){ fhz_num++; fhz[fhz_num]=(zt-1000)*-1; }else{ if(zt==9999){ printf("acc,接收\n"); break; }else{ printf("出错,不可识别\n"); break; } } } //sr_num==9999 //接受 if(sr_num==9999){ printf("acc,接收\n"); break; } } } int main(){ get_g();//文法的输入 action();//找action序列(非大写) gotoo();//找大写非终结符 init_gt_01();//初始化gt_01表 lr_save(); //存储输入的文法在变量lr中 into_I0();//初始化I0;并初始头结点 printf("===================开始分析=====================\n"); printf("=====================I(0)为=====================\n"); f(Ips->next->css->next); printf("================================================\n"); lr_analyse(); //项目集规范族 Ips是头指针 //清屏函数 //system("cls"); output_lr_analyse();//输出项目集 printf("======================ACTION GOTO表==========================\n"); anction_goto_form();//构造LR分析表,并输出 //输入分析串 get_string(); lr_process(); return 0; }