SLR,语法分析表的构建
2013-07-04 19:44
411 查看
太累了,感觉不会再爱了。执行了跟编译原理上的一模一样的例子,输出了正确结果
上面的代码只是为所有的文法符号生成一个标号,并将生成式转化了这些标号。
下面的代码才是核心的生成语法分析表的步骤,感觉总是有点疑问,特别是有些前提没有证明。
#include <stdio.h> #include <malloc.h> #include <string.h> //这个头文件是为了将语法声明的产生式按照调用顺序来构建调用图,并顺便构建反向调用图。 //从而来构建拓扑排序,并用到将来的分析之中。 typedef struct _decl_hash_table { int is_used;//代表是否已经被使用 char* decl_hash_name;//代表名字 int node_index;//代表所代表的节点编号 }decl_hash_table,*pdecl_hash_table; decl_hash_table decl_hash[100];//暂且定位100,其实我们在使用的过程中只会使用其中的97个,因为这个是一个质数 typedef struct _decl_edge { int node_of_dest; struct _decl_edge* next; }decl_edge,*pdecl_edge; typedef struct _phrase { pdecl_edge begin_of_phrase; struct _phrase* next; }phrase; typedef struct _decl_node { phrase* phrase_link;//作为产生体链表的第一个节点的指针 char* name_of_decl;//这个节点的名称,主要是为了输出使用的 }decl_node,*pdecl_node; decl_node decl_node_table[100];//假设为100个节点,可以改 int node_index=0;//全局的用来给节点赋予编号的变量,最后的值代表了有多少个文法符号,结束符不算在里面 int insert_decl_hash(char* name,int input_node_index) //hash表插入函数,如果满了则返回-1 //注意这里为了节省空间,hash表与节点表的名字域公用了一个字符串指针 { int name_total; int counter; int result; int name_for_i; char* new_name; name_for_i=counter=name_total=result=0; while(name[name_for_i]!='\0') { name_total=name_total+name[name_for_i]; name_for_i++; } new_name=malloc(sizeof(char)*(name_for_i+1)); strcpy(new_name,name); result=name_total%97; while(counter<97) { if(decl_hash[result].is_used==0) { decl_hash[result].is_used=1; decl_hash[result].decl_hash_name=new_name; decl_hash[result].node_index=input_node_index; decl_node_table[input_node_index].name_of_decl=new_name;//公用名字指针 return result; } else { result=(result+1)%97; counter++; } } return -1; } int search_decl_hash(char* input_name) { int name_total; int counter; int result; int name_for_i; name_for_i=counter=name_total=result=0; while(input_name[name_for_i]!='\0') { name_total=name_total+input_name[name_for_i]; name_for_i++; } result=name_total%97; while(counter<97) { if(decl_hash[result].is_used==1) { if(!strcmp(decl_hash[result].decl_hash_name,input_name)) { return decl_hash[result].node_index; } else { result=(result+1)%97; counter++; } } else { return -1; } } return -1; } //以上是与hash表有关的函数 //下面则是与建立邻接表相关的数据结构与函数 void tackle_phrase(char* input_phrase) //根据输入来建立所有的产生式,用数字来代替名字 //注意生成链表的时候,为了插入方便,我们是逆序的链表,全都建完之后再逆序一遍然后变成正序的 { int for_i,for_j; int current_tail_node,current_head_node; char temp[20];//起临时的名字作用 phrase* temp_phrase; decl_edge* temp_decl_edge; for_i=0; while(input_phrase[for_i]!=':'&&input_phrase[for_i]!=' ') { temp[for_i]=input_phrase[for_i]; for_i++; }//找到头部的名字 temp[for_i]='\0'; current_head_node=search_decl_hash(temp); if(current_head_node==-1)//如果这个名字是第一次碰到 { node_index++; insert_decl_hash(temp,node_index); current_head_node=node_index; decl_node_table[current_head_node].phrase_link=NULL; printf("%d is the label of %s\n",node_index,temp); }//建立名字 temp_phrase=malloc(sizeof(struct _phrase));//临时的产生体 temp_phrase->begin_of_phrase=NULL; temp_phrase->next=decl_node_table[current_head_node].phrase_link; decl_node_table[current_head_node].phrase_link=temp_phrase; //把当前的产生式链接到产生式头的产生式链表中 while(input_phrase[for_i]==' '||input_phrase[for_i]==':') { for_i++; }//找到第一个可以分析的点 while(input_phrase[for_i]!='\0') { while(input_phrase[for_i]==' ') { for_i++; }//找到可以分析的点 for_j=0; while(input_phrase[for_i]!=' '&&input_phrase[for_i]!='\0') { temp[for_j]=input_phrase[for_i]; for_i++; for_j++; }//再次找到一个名字 temp[for_j]='\0'; current_tail_node=search_decl_hash(temp); if(current_tail_node==-1)//如果是新的名字,则加入到hash中,并为他分配一个标号 { node_index++; current_tail_node=node_index; insert_decl_hash(temp,node_index); decl_node_table[current_tail_node].phrase_link=NULL; printf("%d is the label of %s\n",node_index,temp); } //为当前的产生式链表增加一个元素 temp_decl_edge=malloc(sizeof(struct _decl_edge)); temp_decl_edge->node_of_dest=current_tail_node; temp_decl_edge->next=temp_phrase->begin_of_phrase; temp_phrase->begin_of_phrase=temp_decl_edge; } } void phrase_end(void)//这个函数是为了将终结符加入到符号表中 { char* end="EOF"; decl_node_table[node_index+1].phrase_link=NULL; insert_decl_hash(end,node_index+1); } void reverse_phrase(void) { int for_i; phrase* temp_phrase; pdecl_edge temp_edge,swap_edge,next_edge; for_i=1; for(for_i=1;for_i<=node_index;for_i++) { temp_phrase=decl_node_table[for_i].phrase_link; while(temp_phrase!=NULL) { temp_edge=temp_phrase->begin_of_phrase; swap_edge=NULL; while(temp_edge!=NULL)//由于前面是逆序的链表,现在我们把他转成顺序的链表 { next_edge=temp_edge->next; temp_edge->next=swap_edge; swap_edge=temp_edge; temp_edge=next_edge; } temp_phrase->begin_of_phrase=swap_edge; //链表转换完成 temp_phrase=temp_phrase->next; }//当前节点的所有产生式转换完成 }//所有节点的产生式逆转完成 }
上面的代码只是为所有的文法符号生成一个标号,并将生成式转化了这些标号。
下面的代码才是核心的生成语法分析表的步骤,感觉总是有点疑问,特别是有些前提没有证明。
#include "declar_topological.h" typedef struct _lr_group_node { int node_pointer;//代表产生式头 phrase* current_phrase;//代表当前的产生式体 pdecl_edge current_edge;//代表当前期待的语法单元 }lr_group_node,*plr_group_node;//这里是闭包中的一个项的描述 typedef struct _lr_closure { plr_group_node current_group_node;//这个代表一个项 struct _lr_closure* next;//这个next域将所有的单个项串联起来 }lr_closure,*plr_closure;//这里是一个闭包的结构 plr_closure current_closure[100];//暂且定为100,可以改 int group_index=0;//这里是为了标记群号 typedef struct _transition_node { enum { shift,//移进 reduce,//规约 error,//报错 accept//接受 }action_type; union { struct { int shift_node;//移进的项集 }; struct { int reduce_node;//规约的时候的产生式头 int reduce_length;//规约的时候的产生式体的长度 }; }; }transition_node,*p_transition_node; typedef struct _token_list { struct _token_list* next; int node_number; }token_list,*ptoken_list; ptoken_list list_head=NULL;//节点头 transition_node* transition_table[400];//这里代表了整个转换表 void extend_group(int in_group_index)//扩展项集的函数 { plr_closure temp_group,temp_group_end,temp_group_add; //temp_group是用来遍历所有的项,temp_group_end是用来在遍历过程中表示最后一个项, //temp_group_add跟前面一个合用,来在项集的末尾增加项 plr_group_node temp_group_node,temp_group_node_add; //temp_group_node是用来遍历项,temp_group_node_add用来在增加项的过程中使用 phrase* temp_phrase; //这两个都是用来增加项用的 int graph_node_index; temp_group=current_closure[in_group_index];//得到当前项集的索引 temp_group_end=temp_group;//这里用来得到当前项集的最后一个项 while(temp_group_end->next!=NULL) { temp_group_end=temp_group_end->next; }//寻找到末尾那个节点 while(temp_group!=NULL)//遍历整个项集,必要的时候扩充项集,即闭包 { temp_group_node=temp_group->current_group_node;//获得当前项 if(temp_group_node->current_edge!=NULL)//如果右端还有文法符号,则试图去扩展 { graph_node_index=temp_group_node->current_edge->node_of_dest;//获得当前项所期待的文法符号的索引 if(temp_group_node->node_pointer!=graph_node_index)//如果这个文法符号与当前产生式的文法符号不相同 //相同的时候会造成无限循环,即不停的把这个项添加进当前闭包中,并且无法终止 //由于我们在扩展闭包的过程中是按照拓扑顺序来添加的,所以可以保证在不重复添加同一节点的情况下 //群的大小是有界的 //在不等的情况下,我们可以扩展群 { temp_phrase=decl_node_table[graph_node_index].phrase_link;//获得当前文法符号的第一个产生式 while(temp_phrase!=NULL)//遍历产生式 { temp_group_node_add=malloc(sizeof(struct _lr_group_node)); temp_group_node_add->current_edge=temp_phrase->begin_of_phrase;//设置为开始的那一条边 temp_group_node_add->current_phrase=temp_phrase;//设置为当前产生式的链表头 temp_group_node_add->node_pointer=graph_node_index;//设置为产生式的头 //至此构建了一个项的描述 //开始添加进项集中,准确的来说是末尾 temp_group_add=malloc(sizeof(struct _lr_closure)); temp_group_add->current_group_node=temp_group_node_add; temp_group_add->next=NULL; temp_group_end->next=temp_group_add; temp_group_end=temp_group_add; temp_phrase=temp_phrase->next; }//把当前项的扩展项都添加进去了 }//同上 } temp_group=temp_group->next;//处理下一项 }//所有的扩展项都处理完成了 }//扩展完成 plr_closure closure_transition(int closure_index,int node_index) { plr_closure result_closure,now_closure,temp_closure_add,temp_closure_end,temp_closure; plr_group_node temp_group_node,temp_group_node_add; pdecl_edge temp_edge;//尼玛 ,为什么这么多temp啊,老子要转函数式编程 now_closure=current_closure[closure_index]; result_closure=malloc(sizeof(struct _lr_closure)); result_closure->current_group_node=NULL; result_closure->next=NULL; temp_closure_end=NULL; //把这个新的群初始化为空 temp_closure=now_closure; while(temp_closure!=NULL) { temp_group_node=temp_closure->current_group_node; temp_edge=temp_group_node->current_edge;//注意这里current_edge可能为空,要去判断 if(temp_edge!=NULL)//如果不为空,再去判断是否是希望的节点 { if(temp_edge->node_of_dest==node_index) { temp_group_node_add=malloc(sizeof(struct _lr_group_node)); temp_group_node_add->current_edge=temp_edge->next; temp_group_node_add->current_phrase=temp_group_node->current_phrase; temp_group_node_add->node_pointer=temp_group_node->node_pointer; //现在建立好了一个项,准备插入到这个项集里面去 temp_closure_add=malloc(sizeof(struct _lr_closure)); temp_closure_add->current_group_node=temp_group_node_add; temp_closure_add->next=NULL;//因为是从尾部开始加入的所以就初始化为空 if(temp_closure_end!=NULL)//这里需要判断当前加入的是不是第一个项,因为要特殊处理 { temp_closure_end->next=temp_closure_add; temp_closure_end=temp_closure_add; } else { result_closure->current_group_node=temp_group_node_add; temp_closure_end=result_closure; free(temp_closure_add); } } } temp_closure=temp_closure->next; }//一直遍历整个项链表,有转换的时候就添加 if(result_closure->current_group_node==NULL) { free(result_closure); return NULL; //额,这里忘了释放内存了,调试完再去释放 } else { return result_closure; } } int already_in(plr_closure input_closure) //遍历整个项集来比较,这个方法比较笨,其实可以插入的时候打表 //也可以利用hash或者其他的数据结构来查询 //这个函数是目前问题最大的函数 { int for_i; plr_closure temp_closure,begin_closure; plr_group_node temp_group_node,org_group_node; for(for_i=1;for_i<=group_index;for_i++)//遍历整个项集族 { temp_closure=current_closure[for_i]; begin_closure=input_closure; //都获得第一个项集的指针,这样我们就可以遍历了 while(begin_closure!=NULL)//遍历整个项集,由于当前尚未扩展,所以用这个短的 //当前比较的都是核心项,这里需要担心的是某个项集的核心项包括了当前项集的核心项 //如果出现这种情况,这里的代码就会出现错误 //当前我们先不考虑这种情况,之后再去修改 { temp_group_node=temp_closure->current_group_node; org_group_node=begin_closure->current_group_node; //由于我们保证了添加项集的时候是按照文法符号的引用序来添加的 //所以直接按照链表,一个一个来比较就行了,如果有一个没有匹配上就不需要再去比较剩下的项了 //只需要匹配下一个项集族 //现在判断是否完全匹配 if(temp_group_node->node_pointer==org_group_node->node_pointer)//如果产生式头相同 { if(temp_group_node->current_phrase==org_group_node->current_phrase)//如果产生式的体相同 { if(temp_group_node->current_edge==org_group_node->current_edge)//如果下一个期待的符号相同 { temp_closure=temp_closure->next; begin_closure=begin_closure->next; continue;//进入下次循环 } } } break;//如果这三个不能完全匹配,则跳出当前项集循环判断,进入下一个项集的循环判断 }//当前项集遍历结束 if(begin_closure==NULL) //如果遍历到了末尾,则说明完全匹配了 //这里我们保证了开始时的temp_closure不是空的,因为我们不会插入一个空群 { return for_i;//返回群号,说明这个项集已经存在了 } } //如果所有的项集都无法匹配 return 0;//返回0,说明这个项集还不存在 } void output_transition(void) { int for_i,for_j; transition_node* temp_tran_row; printf(" "); for(for_i=1;for_i<=node_index+1;for_i++) { printf("%-4s ",decl_node_table[for_i].name_of_decl); } printf("\n"); for(for_i=1;for_i<=group_index;for_i++) { temp_tran_row=transition_table[for_i]; printf("%3d ",for_i); for(for_j=1;for_j<=node_index+1;for_j++) { if(temp_tran_row[for_j].action_type==reduce) { printf("r%2d ",(temp_tran_row+for_j)->reduce_node); } else { if(temp_tran_row[for_j].action_type==shift) { printf("s%2d ",(temp_tran_row+for_j)->shift_node); } else { if(temp_tran_row[for_j].action_type==accept) { printf("ac "); } else { printf("err "); } } } } printf("\n"); } } void output_closure(void) { int for_i; int begin; plr_closure temp_closure; plr_group_node temp_group_node; pdecl_edge temp_edge; phrase* temp_phrase; for(for_i=1;for_i<=group_index;for_i++) { temp_closure=current_closure[for_i]; while(temp_closure!=NULL) { temp_group_node=temp_closure->current_group_node; begin=temp_group_node->node_pointer; temp_phrase=temp_group_node->current_phrase; temp_edge=temp_phrase->begin_of_phrase; printf("%s->",decl_node_table[begin].name_of_decl); while(temp_edge!=NULL) { if(temp_edge==temp_group_node->current_edge) { printf("."); } printf("%s",decl_node_table[temp_edge->node_of_dest].name_of_decl); temp_edge=temp_edge->next; } if(temp_group_node->current_edge==NULL) { printf("."); } printf("\n");//当前项输出完成 temp_closure=temp_closure->next; }//当前项集输出完成 printf("closure %d is completed\n",for_i); }//所有项集输出完成 } void graph_to_closure(char* begin_name) { int begin_node_index;//这个作为原来的开始文法符号的索引 int current_group_index;//这个代表当前正在处理的项集 int for_i,is_in; pdecl_edge temp_edge; phrase* temp_phrase; plr_closure temp_group; plr_group_node temp_group_node; transition_node* temp_tran_row;//代表临时的转换行 int recept_group;//当作接收节点 begin_node_index=search_decl_hash(begin_name);//找到开始节点的索引 //开始添加伪节点 temp_edge=malloc(sizeof(struct _decl_edge)); temp_edge->next=NULL; temp_edge->node_of_dest=begin_node_index; temp_phrase=malloc(sizeof(struct _phrase)); temp_phrase->begin_of_phrase=temp_edge; temp_phrase->next=NULL; decl_node_table[0].phrase_link=temp_phrase; insert_decl_hash("dumb",0);//插入哑元 //这里添加了伪开始节点 //下面开始建群 temp_group_node=malloc(sizeof(struct _lr_group_node)); temp_group_node->current_edge=temp_edge; temp_group_node->current_phrase=temp_phrase; temp_group_node->node_pointer=0; temp_group=malloc(sizeof(struct _lr_closure)); temp_group->next=NULL; temp_group->current_group_node=temp_group_node; group_index++; current_closure[group_index]=temp_group;//从1开始标号,注意 //现在开始扩展项集 extend_group(group_index); current_group_index=group_index; while(current_group_index<=group_index)//一个项集一个项集的处理 { //现在对每一个文法单元进行尝试,看是否有转移 //如果有转移,看是否生成的项集已经被包括了 temp_tran_row=malloc(sizeof(struct _transition_node)*(node_index+2)); //这里需要考虑最后的终结符号,所以加1,又由于为了表述方便,我们把开始偏移定为1,所以最后是加2 transition_table[current_group_index]=temp_tran_row; for(for_i=1;for_i<=node_index+1;for_i++)//这里加1是为了考虑终结符号 { temp_group=NULL; temp_group=closure_transition(current_group_index,for_i);//调用子函数来进行项集的生成 if(temp_group!=NULL)//如果不是空群,进行移进 { is_in=already_in(temp_group); if(!is_in)//如果这个项集还没有添加 { group_index++; current_closure[group_index]=temp_group; temp_tran_row[for_i].action_type=shift; temp_tran_row[for_i].shift_node=group_index; extend_group(group_index); } else//如果项集已经添加 { temp_tran_row[for_i].action_type=shift; temp_tran_row[for_i].shift_node=is_in; } } else//如果没有转换,则考虑规约 { temp_tran_row[for_i].action_type=error;//首先写为这个,后面再去修改 //现在开始遍历当前项集,如果当前项集里面存在点在最末尾的项,则按照这个项来规约 //我们默认当前不会产生归约冲突,即当前的是无二义的lr(0)文法 temp_group=current_closure[current_group_index]; while(temp_group!=NULL)//遍历当前项集中所有的项,看是否有可归约项 { temp_group_node=temp_group->current_group_node; if(temp_group_node->current_edge==NULL)//如果这个项可以作为一个归约式 { int length_of_phrase;//这个代表产生式体有多少个符号 temp_phrase=temp_group_node->current_phrase; temp_edge=temp_phrase->begin_of_phrase; length_of_phrase=0; while(temp_edge!=NULL)//获得产生式长度 { length_of_phrase++; temp_edge=temp_edge->next; } temp_tran_row[for_i].reduce_node=temp_group_node->node_pointer; temp_tran_row[for_i].reduce_length=length_of_phrase; temp_tran_row[for_i].action_type=reduce; break; } temp_group=temp_group->next; } //如果到最后都没有找到可以规约的项,则保持这个为error } }//所有文法符号处理完毕 current_group_index++;//当前项集处理完成,开始处理下一个项集 }//所有的项集处理完成 //这里再去添加一个接受状态 temp_tran_row=transition_table[(transition_table[1]+begin_node_index)->shift_node]; (temp_tran_row+node_index+1)->action_type=accept; //现在把这个调整成为了接受状态,可以输出整个转换表了 //现在开始输出 output_closure();//输出所有的项集 output_transition();//输出转换 }//所有的完成 void str_to_list(char* input_str)//将输入字符转变为文法单元,然后以链表串接起来 { char temp[10]; int str_index; int str_len; int node_pointer; ptoken_list temp_list; ptoken_list current_list; str_index=0; while(input_str[str_index]!='\0') { while(input_str[str_index]==' ') { str_index++; } str_len=0; while(input_str[str_index]!=' '&&input_str[str_index]!='\0') { temp[str_len]=input_str[str_index]; str_index++; str_len++; } temp[str_len]='\0'; node_pointer=search_decl_hash(temp); temp_list=malloc(sizeof(struct _token_list)); temp_list->node_number=node_pointer; temp_list->next=NULL; if(list_head==NULL) { list_head=temp_list; current_list=temp_list; } else { current_list->next=temp_list; current_list=temp_list; } } temp_list=malloc(sizeof(struct _token_list)); temp_list->node_number=node_index+1; current_list->next=temp_list; temp_list->next=NULL; } int get_token(void)//从链表中获得一个文法单元 { ptoken_list temp_list; int result_node; result_node=list_head->node_number; temp_list=list_head; list_head=list_head->next; free(temp_list); return result_node; } void putback(int withdraw) { ptoken_list temp_list; temp_list=malloc(sizeof(struct _token_list)); temp_list->node_number=withdraw; temp_list->next=list_head; list_head=temp_list; } //现在建立了转换表 void input_recognise(void ) { int syntax_stack[100];//暂且开这么大 int stack_pointer; int input_token; int temp_token; int reduce_token;//注意这里规约的时候,可能会产生为0的点 int reduce_length; int for_i; int end=0; transition_node* current_tran_node; stack_pointer=1; syntax_stack[1]=1;//把最开始的群号放进去 temp_token=-1;//防止出现为0的情况 input_token=-1; while(1)//一直循环 { if(input_token==-1) { input_token=get_token(); } current_tran_node=transition_table[syntax_stack[stack_pointer]]+input_token;//获得栈顶节点的转换表 switch((current_tran_node->action_type))//判断转换类型 { case accept://接受 printf("acc\n"); input_token=-1; end=1;//设置结束位 break; case shift://移入 stack_pointer++; syntax_stack[stack_pointer]=current_tran_node->shift_node; printf("shift %d\n",current_tran_node->shift_node); input_token=-1;//设置提醒位,提示下次需要读取输入 break; case error://报错 printf("error\n"); end=1;//设置结束位 break; case reduce://如果是规约,那么获得规约长度,然后缩短栈指针, putback(input_token);//返回这个输入 reduce_length=current_tran_node->reduce_length; reduce_token=current_tran_node->reduce_node; stack_pointer=stack_pointer-reduce_length; printf("reduce "); for(for_i=1;for_i<=reduce_length;for_i++) { printf("%d ",syntax_stack[stack_pointer+for_i]); } printf(" to %d \n",reduce_token); input_token=reduce_token; break; default: printf("we are in trouble\n"); break; } if(end!=0) { break;//报错或接受,因此结束 } }//一直循环 }
相关文章推荐
- atitit..sql update语法的词法分析,与语法ast构建
- atitit..sql update语法的词法分析,与语法ast构建
- LR语法分析------LR(0)、SLR(1)
- SLR(1)预测分析表的构建以及对句子的分析步骤
- python ast 语法分析
- Gradle构建工具之Groovy的基本语法
- Spice 分析(1) – 构建 Spice 开发环境
- Ext架构分析(3)--Widget之父Component:构建器
- 语法分析——简单判断条件语句,赋值语句,循环语句
- maven集成与Jenkins构建 - 静态代码分析工具PMD
- 输入输出LL(1)语法分析程序
- 构建一个前端库-架构分析
- 程序中,调用Bison和Flex结合的小例子(语法分析中处理数据)
- 语法分析
- ASP.NET MVC 源码分析(二) —— 从 IRouteBuilder认识路由构建
- 语法分析语句篇
- Lua5.0 语法分析
- C++ 类的多态五(多态的语法本质分析)
- 语法分析(15)...
- LCC编译器的源程序分析(8)语法分析的开始