• 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 }//
  • 相关阅读:
    BZOJ3064: Tyvj 1518 CPU监控
    BZOJ3160: 万径人踪灭
    BZOJ3527: [Zjoi2014]力
    BZOJ2194: 快速傅立叶之二
    FFT模板
    Splay树再学习
    POJ2406 Power Strings KMP算法
    POJ2752 Seek the Name,Seek the Fame KMP算法
    POJ3461 Oulipo KMP算法
    POJ2004 Mix and build Trie树? dp?
  • 原文地址:https://www.cnblogs.com/huangfeidian/p/3174224.html
Copyright © 2020-2023  润新知