first集合及follow集合

前面那片文章生成的语法分析表并不是最优的,因为有些项在遇到错误输入的时候,并不是采取报错,而是执行规约,直到不能再规约的时候才报错。这是不科学的,我们需要在得到错误输入的时候立马报错,为了实现这个功能,我们需要知道某个文法符号的后面可能接的终结文法符号的集合,只有当前的输入终结文法符号处在栈顶文法符号的后缀集合中时,我们才去采取接受规约移进这三个操作,否则报错。

为了得到后缀集合,我们首先需要得到每一个文法符号的开始符号集合。为了得到这个集合,我们需要将这些符号进行拓扑排序,以保证生成体的第一个符号在生成体的头部符号处理前处理。注意这里一定不会出现环,即出现E:->T +R ,T:->E+F这种情况,至于问什么不会出现,我只是凭直觉。还需要说明的一点是,对于左递归文法,则不需要添加自己到自己的边。

在生成开始集合之后,我们再去生成直接后缀集合,即对于A:->B.C这种的产生式,我们可以直接把c的开始符号集合添加到b的后缀集合之中。这里就不需要拓扑排序了,因为我们的first集合已经完全生成了。

生成了直接后缀集合之后,我们再生成间接后缀集合,即 A:->B.这种的,b的后缀依赖于a的后缀,我们又需要去做一次拓扑排序。直觉上是没有环。。。,又是直觉。

注意拓扑排序出来的第一个项一定是开始符号,而开始符号的直接后缀已经得到了,间接后缀需要手工添加输入结束符。后面的处理就是不停的迭代。

下面是代码。

  1 #include "decl_tackle.h"
  2 typedef struct _first_graph_node
  3 {
  4     int first_symbol;//代表当前文法符号的某个产生式的第一个节点
  5     struct* _first_graph_node next;
  6 }first_graph_node,*pfirst_graph_node;//这里可以当作邻接表来使用
  7 typedef struct _end_graph_node
  8 {
  9     int symbol_head;//代表以当前文法符号为生成式结尾符号的产生式头部符号
 10     struct _end_graph_node* next;
 11 }end_graph_node,pend_graph_node;//这里也是当作邻接表串联起来
 12 int** first_graph_set;//first集
 13 int** follow_graph_set;//follow集
 14 pfirst_graph_node** first_graph;//这里是整个图的邻接表的开始节点
 15 pend_graph_node** end_graph;//这里是整个图的邻接表的开始节点
 16 int number_edge_first=0;//记录开始图里面有多少条边
 17 int number_edge_end=0;//记录结束图里面有多少条边 
 18 //上面两个变量都是为了拓扑排序使用的
 19 //注意这里是间接后缀而不是直接后缀,直接后缀的处理我们在第二遍遍历图的时候处理
 20 //而间接后缀处理需要在直接后缀处理的基础上执行
 21 //而直接后缀处理需要在前缀处理的基础上执行,因此我们需要三趟遍历
 22 void add_first_edge(int index_from,int index_to)//添加一条边到前缀引用图中
 23 {
 24     pfirst_graph_node temp_node_add,temp_node;
 25     if((*(*(first_graph_set+index_from)+index_to)!=1))//如果这个符号还没有添加
 26     {
 27         temp_node=*(first_graph+index_from);
 28         temp_node_add=malloc(sizeof(struct _first_graph_node));
 29         temp_node_add->first_symbol=index_to;
 30         temp_node_add->next=temp_node;
 31         *(first_graph+index_from)=temp_node_add;//添加到邻接表
 32         *(*(first_graph_set+index_from)+index_to)=1;//标记为已经添加过了
 33         number_edge_first++;
 34     }
 35 }
 36 void add_end_edge(int index_from,int index_to)//添加一条边到后缀引用图中
 37 {
 38     pend_graph_node temp_node_add,temp_node;
 39     if((*(*(end_graph_set+index_from)+index_to)!=1))//如果这个符号还没有添加
 40     {
 41         temp_node=*(end_graph+index_from);
 42         temp_node_add=malloc(sizeof(struct _end_graph_node));
 43         temp_node_add->symbol_head=index_to;
 44         temp_node_add->next=temp_node;
 45         *(end_graph+index_from)=temp_node_add;//添加到邻接表
 46         *(*(end_graph_set+index_from)+index_to)=1;//标记为已经添加过了
 47         number_edge_end++;
 48     }
 49 }
 50 
 51 
 52 
 53 void generate_graph(void)//生成first集合,利用数组来表示
 54 //注意这个函数调用是在反转链表之后的
 55 {
 56     int for_i,for_j;
 57     decl_node temp_decl_node;
 58     int* temp_set;
 59     phrase* temp_phrase;
 60     pdecl_edge temp_decl_edge;
 61     first_graph_set=malloc(sizeof(int)*(node_index+2));
 62     end_graph_set=malloc(sizeof(int)*(node_index+2));
 63     for(for_i=0;for_i<node_size+2;for_i++)
 64     {
 65         temp_set=malloc(sizeof(int)*(node_index+2));
 66         for(for_j=0;for_j<node_index+2;for_j++)
 67         {
 68             temp_set[for_j]=0;
 69         }
 70         first_graph_set[for_i]=temp_set;
 71         temp_set=malloc(sizeof(int)*(node_index+2));
 72         for(for_j=0;for_j<node_index+2;for_j++)
 73         {
 74             temp_set[for_j]=0;
 75         }
 76         end_graph_set[for_i]=temp_set;
 77     }//初始化所有的矩阵
 78     first_graph=malloc(sizeof(struct _first_graph_node*)*(node_index+2));//因为这里考虑了偏移量和结束符
 79     end_graph=malloc(sizeof(struct _end_graph_node*)*(node_index+2));//因为这里考虑了偏移量和结束符
 80     for(for_i=0;for_i<node_index+2;for_i++)
 81     {
 82         first_graph[for_i]=NULL;
 83         end_graph[for_i]=NULL;
 84     }//初始化为空,省得犯以前的错误
 85     //现在开始建立这两个图
 86     for(for_i=1;for_i<=node_index;for_i++)
 87     {
 88         temp_decl_node=decl_node_table[for_i];
 89         temp_phrase=temp_decl_node->phrase_link;
 90         while(temp_phrase!=NULL)
 91         {
 92             temp_edge=temp_phrase->begin_of_phrase;
 93             if(temp_edge->node_of_dest!=for_i)//对于左递归进行忽略
 94             {
 95                 add_first_edge(temp_edge->node_of_dest,for_i);//添加一条边到开始图中
 96             }
 97             while(temp_edge->next!=NULL)
 98             {
 99                 temp_edge=temp_edge->next;//寻找到最后一个节点
100             }
101             if(temp_edge->node_of_dest!=for_i)//对于右递归进行忽略
102             {
103                 add_end_edge(for_i,temp_edge->node_of_dest);//添加到后缀引用图中
104             }
105             temp_phrase=temp_phrase->next;
106         }//对于有产生式的文法单元,处理所有的产生式 
107     }//所有文法单元处理完毕
108 }//前缀引用图和后缀引用图处理完毕
109 
110 void generate_first_set(void)//对于引用图进行拓扑排序,然后按照拓扑排序来生成first集合
111 {
112     //first矩阵里面真正有意义的是那些终结文法符号所占据的位,非终结文法符号没啥意思
113     int number_of_edge;
114     int begin_stack_pointer;
115     int* begin_stack;
116     int end_stack_pointer;
117     int* end_stack;
118     int* temp_row,temp_row_add;//用来遍历矩阵的变量
119     pfirst_graph_node temp_node;
120     int* already_out_stack;//代表已经处理过了,已经出栈
121     int for_i,for_j;
122     already_out_stack=malloc(sizeof(int)*(node_index+2));
123     for(for_i=1;for_i<=node_index+1;for_i++)
124     {
125         already_out_stack[for_i]=0;
126     }//初始化
127     begin_stack=malloc(sizeof(int)*(node_index+2));//多申请几个又不会怀孕
128     end_stack=malloc(sizeof(int)*(node_index+2));
129     begin_stack_pointer=end_stack_pointer=0;
130     number_of_edge=number_edge_first;
131     while(number_of_edge>0)//只要还有一条边没有处理
132     {
133         for_i=1;
134         while(first_graph[for_i]==NULL)
135         {
136             for_i++;
137         }//找到一个还有边存在的点
138         begin_stack_pointer++;
139         begin_stack[begin_stack_pointer]=for_i;
140         while(begin_stack_pointer>0)//好像写的有点问题
141         {
142             temp_node=first_graph[begin_stack[begin_stack_pointer]];
143             if(temp_node!=NULL)//如果栈顶的点还有边
144             {
145                 if(already_out_stack[temp_node->symbol_head]==1)//如果这个点已经处理过了
146                 {
147                     first_graph[begin_stack[begin_stack_pointer]]=temp_node->next;//将这条边摘掉
148                     number_of_edge--;
149                     free(temp_node);
150                 }
151                 else//如果还没处理,那就直接入栈
152                 {
153                     begin_stack_pointer++;
154                     begin_stack[begin_stack_pointer]=temp_node->symbol;
155                 }
156             }
157             else//如果没有边,则需要考虑是不是最后一个点
158             {
159                 
160                 if(begin_stack_pointer>1)//如果不是栈底
161                 {
162                     end_stack_pointer++;
163                     end_stack[end_stack_pointer]=begin_stack[begin_stack_pointer];//换栈
164                     already_out_stack[begin_stack[begin_stack_pointer]]=1;//表示已经处理完了
165                     begin_stack_pointer--;
166                     temp_node=first_graph[begin_stack[begin_stack_pointer]];
167                     number_of_edge--;
168                     first_graph[begin_stack[begin_stack_pointer]]=temp_node->next;//把这条边摘除
169                     free(temp);//出栈
170                 }
171                 else//如果这是栈底
172                 {
173                     begin_stack_pointer=0;//直接设置为栈空,不需要另外的措施
174                 }
175             }
176         }//一直循环到当前节点可达的节点都被处理了
177     }//所有的边都处理完毕了
178     end_stack_pointer++;
179     end_stack[end_stack_pointer]=begin_stack[1];//别忘了最后一个节点
180     already_out_stack[begin_stack[1]]=1;
181     //现在按照栈里面的顺序来处理位图
182     while(end_stack_pointer>0)
183     {
184         temp_row=first_graph+end_stack[end_stack_pointer];
185         if(decl_node_table[end_stack[end_stack_pointer]].phrase_link!=NULL)//如果当前的是非终结文法符号
186         {
187             for(for_i=1;for_i<=node_index;for_i++)
188             {
189                 if(decl_node_table[temp_row[for_i]].phrase_link!=NULL)//如果引用的是一个非终结符,则开始合并终结符号
190                 {
191                     temp_row_add=first_graph+temp_row[for_i];
192                     for(for_j=1;for_j<node_index;for_j++)
193                     {
194                         temp_row[for_j]=temp_row[for_j]|temp_row_add[for_j];
195                         //这里我就不去判断是不是终结文法符号了,因为我们保证了这里是一个拓扑排序
196                     }
197                 }
198                 else
199                 {
200                     //对应终结符的时候,则不需要处理
201                 }
202             }//搜索完成
203             end_stack_pointer--;
204         }
205         else//对于终结文法符号,在自己所在的位标注为1
206         {
207             temp_row[end_stack[end_stack_pointer]]=1;
208             end_stack_pointer--;
209         }
210     }//堆栈中的点都处理完成
211     free(begin_stack);
212     free(end_stack);
213     free(already_out_stack);
214     //释放内存的是好孩子
215 }//前缀集合处理完成
216 //现在开始处理后缀集合
217 //下面这个函数来处理直接后缀
218 void direct_end(void)
219 {
220     int* before_row;
221     int* after_row;
222     pdecl_edge before_edge,after_edge;
223     phrase* current_phrase;
224     int for_i,for_j;
225     for(for_i=1;for_i<node_index;for_i++)
226     {
227         current_phrase=decl_node_table[for_i].phrase_link
228         if(current_phrase!=NULL)//如果这里有生成式 ,则进行处理
229         {
230             before_edge=current_phrase->begin_of_phrase;
231             after_edge=before_edge->next;
232             while(after_edge!=NULL)//这个不是空的,则我们需要去处理一个直接后缀
233             {
234                 before_row=follow_graph_set[before_edge->node_of_dest];
235                 if(decl_node_table[after_edge->node_of_dest].phrase_link!=NULL)//如果不是终结文法符号
236                 {
237                     after_row=first_graph_set[after_edge->node_of_dest];//取出后面文法符号的first集合
238                     for(for_j=1;for_j<=node_index;for_j++)//因为是直接后缀,所以先不考虑输入终结符
239                     {
240                         before_row[for_j]=before_row[for_j]|after_row[for_j];//取并集
241                     }
242                     //并集处理完成
243                 }
244                 else
245                 {
246                     before_row[after_edge->node_of_dest]=1;//将这一位置为1就行了 
247                 }
248                 before_edge=after_edge;
249                 after_edge=after_edge->next;
250             }//当前产生式处理完成
251             current_phrase=current_phrase->next;//处理下一个产生式 
252         }//当前产生式头的所有产生式处理完成
253     }//处理下一个产生式头
254     //所有的产生式处理完毕
255 }//直接后缀处理完毕
256 void indirect_follow(int begin_node_index )
257 {
258     //这里又需要走一遍拓扑排序
259     //注意这趟处理的时候需要考虑输入终结符号
260     //由于开始符号可以生成任何符号,所以在拓扑排序完成之后,处于栈顶的一般是开始符号
261     //注意这只是一般情况下,我们并未用理论来证实这个结论,所以还是老老实实的先找到开始符号吧
262     //这个我们采用参数传递的方法
263     *(follow_graph_set[begin_node_index]+node_index+1)=1;//将输入结束符作为开始符号的后缀符号
264     //下面的内容基本就是复制前面生成first集合的代码
265     int number_of_edge;
266     int begin_stack_pointer;
267     int* begin_stack;
268     int end_stack_pointer;
269     int* end_stack;
270     int* temp_row,temp_row_add;//用来遍历矩阵的变量
271     pend_graph_node temp_node;//这里改变了节点类型
272     int* already_out_stack;//代表已经处理过了,已经出栈
273     int for_i,for_j;
274     already_out_stack=malloc(sizeof(int)*(node_index+2));
275     for(for_i=1;for_i<=node_index+1;for_i++)
276     {
277         already_out_stack[for_i]=0;
278     }//初始化
279     begin_stack=malloc(sizeof(int)*(node_index+2));//多申请几个又不会怀孕
280     end_stack=malloc(sizeof(int)*(node_index+2));
281     begin_stack_pointer=end_stack_pointer=0;
282     number_of_edge=number_edge_end;
283     while(number_of_edge>0)//只要还有一条边没有处理
284     {
285         for_i=1;
286         while(first_graph[for_i]==NULL)
287         {
288             for_i++;
289         }//找到一个还有边存在的点
290         begin_stack_pointer++;
291         begin_stack[begin_stack_pointer]=for_i;
292         while(begin_stack_pointer>0)//好像写的有点问题
293         {
294             temp_node=end_graph[begin_stack[begin_stack_pointer]];
295             if(temp_node!=NULL)//如果栈顶的点还有边
296             {
297                 if(already_out_stack[temp_node->symbol_head]==1)//如果这个点已经处理过了
298                 {
299                     end_graph[begin_stack[begin_stack_pointer]]=temp_node->next;//将这条边摘掉
300                     number_of_edge--;
301                     free(temp_node);
302                 }
303                 else//如果还没处理,那就直接入栈
304                 {
305                     begin_stack_pointer++;
306                     begin_stack[begin_stack_pointer]=temp_node->symbol;
307                 }
308             }
309             else//如果没有边,则需要考虑是不是最后一个点
310             {
311                 
312                 if(begin_stack_pointer>1)//如果不是栈底
313                 {
314                     end_stack_pointer++;
315                     end_stack[end_stack_pointer]=begin_stack[begin_stack_pointer];//换栈
316                     already_out_stack[begin_stack[begin_stack_pointer]]=1;//表示已经处理完了
317                     begin_stack_pointer--;
318                     temp_node=first_graph[begin_stack[begin_stack_pointer]];
319                     number_of_edge--;
320                     first_graph[begin_stack[begin_stack_pointer]]=temp_node->next;//把这条边摘除
321                     free(temp);//出栈
322                 }
323                 else//如果这是栈底
324                 {
325                     begin_stack_pointer=0;//直接设置为栈空,不需要另外的措施
326                 }
327             }
328         }//一直循环到当前节点可达的节点都被处理了
329     }//所有的边都处理完毕了
330     end_stack_pointer++;
331     end_stack[end_stack_pointer]=begin_stack[1];//别忘了最后一个节点
332     already_out_stack[begin_stack[1]]=1;
333     //现在按照栈里面的顺序来处理位图
334     while(end_stack_pointer>0)
335     {
336         temp_row=follow_graph_set+end_stack[end_stack_pointer];//
337         for(for_i=1;for_i<=node_index;for_i++)
338         {
339             if(temp_row[for_i]==1&&decl_node_table[for_i].phrase_link!=NULL)//如果这里依赖一个非终结符
340             {
341                 temp_row_add=follow_graph_set+for_i;
342                 for(for_j=1;for_j<=node_index+1;for_j++)//注意这里需要考虑输入终结符
343                 {
344                     temp_row[for_j]=temp_row[for_j]|temp_row_add[for_j];
345                 }//合并完成
346             }//此符号处理完成
347         }//所有的处理完成
348         end_stack_pointer--;//堆栈减1
349     }//堆栈空
350     //现在所有的后缀信息已经处理完毕
351     free(begin_stack);
352     free(end_stack);
353     free(already_out_stack);
354     //释放内存的是好孩子
355 }//

 

posted @ 2013-07-05 19:58  huangnima  阅读(1101)  评论(2编辑  收藏  举报