nfa转dfa,正式完成

为了加速转换的处理,我压缩了符号表。具体算法参考任何一本与编译或者自动机相关的书籍。

这里的核心问题是处理传递性闭包,transitive closure,这个我目前采取的是最简单的warshall算法,虽然是4次的复杂度,但是由于我构建nfa的时候并没有采取标准的方法,使得nfa的节点减少很多。ps,上上篇所说的re转nfa,我这里有一个修改,就是对于or转换,不再增加节点,而是只增加两条空转换边。

相关代码如下

  1 #include "nfa_process.h"
  2 //首先在原来的nfa节点中,把最后的那个正则的开始节点可达的那些节点提取出来,相当于又一次的拷贝
  3 p_edge_list nfa_to_dfa[100];//这个当作新的nfa图
  4 int nfa_min_node_number=0;//注意nfa_min_node_number使用的时候是从1开始的,他的值表示已经使用到了多少
  5 #define BYTE_MASK 0x80
  6 typedef struct _dfa_edge
  7 {
  8     struct _dfa_edge* next;
  9     char label;
 10     int dest_dfa_index;
 11 }dfa_edge,*pdfa_edge;
 12 typedef struct _dfa_node
 13 {
 14     pdfa_edge begin;
 15     int dfa_hash_number;
 16 }dfa_node,*pdfa_node;
 17 dfa_node current_dfa_table[400];//由于dfa的数量很大,所以开一个400的数组
 18 int dfa_node_number=0;//dfa_node_number作为一个全局变量,用来标号使用
 19 int current_dfa_node=0;//这个用来表示处理到了那一个dfa节点了
 20 typedef struct _dfa_hash
 21 {
 22     int in_use;
 23     char* name;
 24     int dfa_node_pointer;
 25 }dfa_hash;
 26 dfa_hash dfa_hash_table[400];//400的理由同上,这里刚好397是一个质数
 27 void ntd_add_edge(int ntd_node_begin,int ntd_node_end,char label)//在压缩过的 nfa中加边 
 28 {
 29     p_edge_list temp_pnode=malloc(sizeof(struct _graph_edge_list));
 30     temp_pnode->label_of_edge=label;
 31     temp_pnode->destination_number=ntd_node_end;
 32     temp_pnode->next_edge=nfa_to_dfa[ntd_node_begin];
 33     nfa_to_dfa[ntd_node_begin]=temp_pnode;
 34 }
 35 int dfa_into_hash(char* input_name,int dfa_node_index,int byte_of_bitmap)//这里是hash插入函数
 36 {
 37     int for_i;
 38     unsigned int result;
 39     int counter;
 40     char* hash_name;
 41     result=0;
 42     for(for_i=0;for_i<byte_of_bitmap;for_i++)
 43     {
 44         result+=(unsigned int) input_name[for_i];
 45     }
 46     result=result%397;
 47     counter=0;
 48     while(counter<397)
 49     {
 50         if(dfa_hash_table[result].in_use==0)
 51         {
 52             dfa_hash_table[result].dfa_node_pointer=dfa_node_index;
 53             dfa_hash_table[result].in_use=1;
 54             hash_name=malloc(sizeof(char)*(byte_of_bitmap+1));
 55             strcpy(hash_name,input_name);
 56             dfa_hash_table[result].name=hash_name;
 57             return result;
 58         }
 59         result=(result+1)%397;
 60         counter++;
 61     }
 62     return -1;
 63 }
 64 int search_dfa_hash(char* input_name)//对于一个名字寻找是否已经在表中
 65 {
 66     int for_i;
 67     unsigned int result;
 68     int counter;
 69     int byte_of_bitmap=strlen(input_name);
 70     result=0;
 71     for(for_i=0;for_i<byte_of_bitmap;for_i++)
 72     {
 73         result+=(unsigned int) input_name[for_i];
 74     }
 75     result=result%397;
 76     counter=0;
 77     while(counter<397)
 78     {
 79         if(dfa_hash_table[result].in_use==0)
 80         {
 81             return -1;
 82         }
 83         else
 84         {
 85             if(!strcmp(dfa_hash_table[result].name,input_name))
 86             {
 87                 return result;
 88             }
 89             else
 90             {
 91                 result=(result+1)%397;
 92                 counter++;
 93             }
 94         }
 95     }
 96     return -1;
 97 }
 98 
 99 
100 int hamming_distance(int input)//用来得到一个4字节的int里面二进制存在1的个数
101 {
102     int temp=(unsigned)input;
103     temp=(temp&0x55555555)+((temp>>1)&0x55555555);
104     temp=(temp&0x33333333)+((temp>>2)&0x33333333);
105     temp=(temp&0x0f0f0f0f)+((temp>>4)&0x0f0f0f0f);
106     temp=(temp&0x00ff00ff)+((temp>>8)&0x00ff00ff);
107     temp=(temp&0x0000ffff)+((temp>>16)&0x0000ffff);
108     return temp;
109 }
110 int length_of_char_set(void)//计算字符表中的字符的种类
111 {
112     int result=0;
113     result+=hamming_distance(alpha_table.char_one);
114     result+=hamming_distance(alpha_table.char_two);
115     result+=hamming_distance(alpha_table.char_three);
116     result+=hamming_distance(alpha_table.char_four);
117     return result;
118 }
119 
120  minimize_char_set(char* mini_set)//压缩字母表
121 {
122     int for_i;
123     unsigned int another_mask;
124     int mini_set_counter;
125     mini_set_counter=for_i=0;
126     for(for_i=0;for_i<32;for_i++)
127     {
128         if(for_i!=17)//对于空转换字符我们不予理睬
129         {
130             another_mask=MASK>>for_i;
131             if(alpha_table.char_one&another_mask)
132             {
133                 
134                 mini_set[mini_set_counter++]=for_i;
135             }
136         }
137     }
138     for(for_i=32;for_i<64;for_i++)
139     {
140         another_mask=MASK>>(for_i%32);
141         if(alpha_table.char_two&another_mask)
142         {
143             mini_set[mini_set_counter++]=for_i;
144         }
145     }
146     for(for_i=64;for_i<96;for_i++)
147     {
148         another_mask=MASK>>(for_i%32);
149         if(alpha_table.char_three&another_mask)
150         {
151             mini_set[mini_set_counter++]=for_i;
152         }
153     }
154     for(for_i=96;for_i<128;for_i++)
155     {
156         another_mask=MASK>>(for_i%32);
157         if(alpha_table.char_four&another_mask)
158         {
159             mini_set[mini_set_counter++]=for_i;
160         }
161     }
162 }
163 
164 void set_dfa_bit(char* begin,int index)//在转换节点中设置第index位,这里是从1开始数的,所以要注意
165 {
166     char* temp;
167     index=index-1;
168     temp=begin+index/8;
169     index=index%8;
170     *temp=(unsigned char)(*temp)|(BYTE_MASK>>index);
171 }
172 int extract_nfa(void)//这里是压缩nfa,实质上与前面那个nfa复制函数是一样的 
173 //返回开始节点的索引,为了nfa转dfa用
174 {
175     int nfa_begin_number;
176     int nfa_end_number;

177     int copy_destination;
178     int offset;
179     int original_token;
180     char copy_label;
181     p_edge_list pcopy;
182     original_token=token_node_number-1;
183     nfa_begin_number=token_node[original_token].bottom;
184     nfa_end_number=token_node[original_token].top;
185     offset=nfa_min_node_number-nfa_begin_number+1;//因为这样要考虑下一个节点
186     for(nfa_begin_number;nfa_begin_number<=nfa_end_number;nfa_begin_number++)//开始复制图
187     {
188         pcopy=nfa_node[nfa_begin_number];
189         nfa_min_node_number++;
190         nfa_node[nfa_min_node_number]=NULL;
191         while(pcopy!=NULL)
192         {
193             copy_label=pcopy->label_of_edge;
194             copy_destination=pcopy->destination_number+offset;
195             ntd_add_edge(nfa_min_node_number,copy_destination,copy_label);
196             pcopy=pcopy->next_edge;
197         }
198     }
199     return token_node[original_token].begin+offset;
200 }
201 void tackle_dfa_label(char* output,int dfa_current,char input_label,int node_size)//处理边转换
202 {
203     char* current_nfa_set;
204     int for_i;
205     int dfa_hash_index;
206     p_edge_list temp_edge;//这里处理的还是nfa的边
207     char temp_label=input_label;
208     dfa_hash_index=current_dfa_table[dfa_current].dfa_hash_number;
209     current_nfa_set=dfa_hash_table[dfa_hash_index].name;
210     for(for_i=0;for_i<node_size;for_i++)//对于这个位图中的每一位进行遍历
211     {
212         if((BYTE_MASK>>(for_i%8))&(current_nfa_set[for_i/8]))//如果这个位有效
213         {
214             temp_edge=nfa_to_dfa[for_i+1];//注意这里要加1,因为 nfa_to_table的索引是从1开始的
215             while(temp_edge!=NULL)
216             {
217                 if(temp_edge->label_of_edge==temp_label)
218                 {
219                     set_dfa_bit(output,temp_edge->destination_number);//注意这里不需要减1
220                 }
221                 temp_edge=temp_edge->next_edge;
222             }
223         }
224     }
225 }
226 
227 
228 int is_label_null(char* input_name,int name_size_inbyte)//判断一个label是否为空
229 {
230     int result;
231     int for_i;
232     result=0;
233     for(for_i=0;for_i<name_size_inbyte;for_i++)
234     {
235         result+=(unsigned char)(input_name[for_i]);
236     }
237     if(result==0)
238     {
239         return 1;
240     }
241     else
242     {
243         return 0;
244     }
245 }
246 void extend_dfa_label(char* input_name,int node_size,int* result_null_access)//这里对结果进行空扩展
247 {
248     char* extend_temp;
249     int for_i,for_j;
250     int size_in_byte;
251     unsigned char current_mask;
252     int for_k;
253     size_in_byte=(node_size+7)/8;
254     for_j=0;
255     extend_temp=malloc(sizeof(char)*(size_in_byte));//临时的位图
256     for(for_i=0;for_i<size_in_byte;for_i++)
257     {
258         extend_temp[for_i]=0;
259     }
260     while(for_j<node_size)
261     {
262         current_mask=BYTE_MASK>>(for_j%8);
263         if(input_name[for_j/8]&current_mask)
264         {
265             for(for_k=0;for_k<node_size;for_k++)
266             {
267                 if(result_null_access[for_j*node_size+for_k])
268                 {
269                     set_dfa_bit(extend_temp,for_k+1);//for_k处在for_k+1位那里
270                 }
271             }
272         }
273         for_j++;
274     }
275     for(for_k=0;for_k<size_in_byte;for_k++)//然后把结果复制回去
276     {
277         input_name[for_k]=extend_temp[for_k];
278     }
279     free(extend_temp);
280 }
281 
282 void null_transition(int index_of_begin)//这里是nfa转dfa的主函数 
283 {
284     int nfa_begin=index_of_begin;//获得起始节点
285     int node_size=nfa_min_node_number;
286     int for_i,for_k,for_m;
287     int for_j;
288     //现在需要求传递性闭包,目前为了省事采取四次方的算法,虽然可以做点优化,但是是以空间复杂度为代价的
289     //由于是四次方的算法,所以我们需要尽量减少节点的数目,幸运的是,我自己定义的转换规则几乎很少去增加节点
290     //这样将大大的有利于效率
291     //计算传递性闭包,我采用warshall算法,直接开一个n*n的矩阵
292     int* origin_null_access=malloc(sizeof(int)*node_size*node_size);
293     int* result_null_access=malloc(sizeof(int)*node_size*node_size);
294     int node_size_byte=(node_size+7)/8;
295     int alpha_size;
296     char* mini_alpha_set;
297     int search_result;
298     char label;
299     pdfa_edge temp_dfa_edge;
300     char* begin_nfa_set=malloc(sizeof(char)*(node_size_byte+1));
301     char* temp_nfa_set=malloc(sizeof(char)*(node_size_byte+1));
302     for(for_i=0;for_i<node_size*node_size;for_i++)
303     {
304         origin_null_access[for_i]=0;
305         result_null_access[for_i]=0;
306     }
307     for(for_i=0;for_i<=node_size_byte;for_i++)//malloc注意一定要自己去初始化值,否则默认为0xcd操
308     {
309         begin_nfa_set[for_i]=0;
310         temp_nfa_set[for_i]=0;
311     }
312     for(for_i=1;for_i<=node_size;for_i++)//初始化矩阵
313         //注意矩阵下标是从0开始的,而节点下标是从1开始的,引用的时候要小心
314     {
315         p_edge_list temp;
316         temp=nfa_to_dfa[for_i];
317         origin_null_access[(for_i-1)*node_size+for_i-1]=1;//由于自己对自己总是可达的
318         result_null_access[(for_i-1)*node_size+for_i-1]=1;//同上
319         while(temp!=NULL)
320         {
321             if(temp->label_of_edge==(char)17)
322             {
323                 origin_null_access[(for_i-1)*node_size+temp->destination_number-1]=1;
324                 result_null_access[(for_i-1)*node_size+temp->destination_number-1]=1;
325             }
326             temp=temp->next_edge;
327         }
328     }
329     //初始化基础的可达矩阵
330     for(for_i=1;for_i<node_size;for_i++)//这里之所以迭代node_size-1次,因为最长简单路径不超过node_size-1条边。
331     {
332         for(for_j=0;for_j<node_size;for_j++)
333         {
334             for(for_k=0;for_k<node_size;for_k++)
335             {
336                 int temp=0;
337                 for(for_m=0;for_m<node_size;for_m++)
338                 {
339                     temp+=result_null_access[for_j*node_size+for_m]*origin_null_access[for_m*node_size+for_k];
340                 }
341                 if(temp>0)
342                 {
343                     result_null_access[for_j*node_size+for_k]=1;//可联通
344                 }
345                 else
346                 {
347                     result_null_access[for_j*node_size+for_k]=0;//不可联通
348                 }
349             }
350         }
351     }
352     //至此邻接矩阵已经构建完成,现在我们把它变成邻接位图
353     node_size_byte=(node_size+7)/8;
354     //现在来处理字符集,注意,这里不包括空转换字符
355     alpha_size=length_of_char_set();
356     mini_alpha_set=malloc(sizeof(char)*alpha_size);
357     for(for_i=0;for_i<alpha_size;for_i++)
358     {
359         mini_alpha_set[for_i]=0;
360     }
361     minimize_char_set(mini_alpha_set);
362     //这里用位图来表示每个点的在某一个字符上的转换集合
363     //加上一个\0,是为了使他变成字符串,这样就好比较了
364     //方便了hash和二叉树的插入和寻找
365     for(for_i=0;for_i<node_size;for_i++)
366     {
367         if(result_null_access[(nfa_begin-1)*node_size+for_i]==1)//我擦,这里忘了减少1了
368         {
369             set_dfa_bit(begin_nfa_set,for_i+1);
370         }
371     }
372     dfa_node_number++;
373     current_dfa_table[dfa_node_number].begin=NULL;
374     current_dfa_table[dfa_node_number].dfa_hash_number=dfa_into_hash(begin_nfa_set,dfa_node_number,node_size_byte+1);
375     current_dfa_node=0;
376     while(current_dfa_node<dfa_node_number)
377     {
378         current_dfa_node++;
379         
380         for(for_j=0;for_j<alpha_size;for_j++)
381         {
382             for(for_i=0;for_i<=node_size_byte;for_i++)
383             {
384                 temp_nfa_set[for_i]='\0';
385             }//清空上次的结果 
386             label=mini_alpha_set[for_j];//设定转换条件
387             tackle_dfa_label(temp_nfa_set,current_dfa_node,label,node_size);//进行边转换
388             extend_dfa_label(temp_nfa_set,node_size,result_null_access);//进行空扩展
389             if(!is_label_null(temp_nfa_set,node_size_byte))//如果在这个字符上有转换
390             {
391                 search_result=search_dfa_hash(temp_nfa_set);
392                 if(search_result==-1)//如果为新的状态,则为之建立一个新的节点
393                 {
394                     dfa_node_number++;
395                     current_dfa_table[dfa_node_number].begin=NULL;
396                     current_dfa_table[dfa_node_number].dfa_hash_number=dfa_into_hash(temp_nfa_set,dfa_node_number,node_size_byte+1);
397                     temp_dfa_edge=malloc(sizeof(struct _dfa_edge));//建立一条新的边
398                     temp_dfa_edge->next=current_dfa_table[current_dfa_node].begin;
399                     temp_dfa_edge->label=mini_alpha_set[for_j];
400                     temp_dfa_edge->dest_dfa_index=dfa_node_number;
401                     printf("add an node %d\n",dfa_node_number);
402                     printf("add an edge from %d to %d with label %c\n",current_dfa_node,dfa_node_number,mini_alpha_set[for_j]);
403                 }
404                 else//如果已经存在这个状态
405                 {
406                     temp_dfa_edge=malloc(sizeof(struct _dfa_edge));
407                     temp_dfa_edge->next=current_dfa_table[current_dfa_node].begin;
408                     temp_dfa_edge->label=mini_alpha_set[for_j];
409                     temp_dfa_edge->dest_dfa_index=dfa_hash_table[search_result].dfa_node_pointer;
410                     printf("add an edge from %d to %d with label %c\n",current_dfa_node,temp_dfa_edge->dest_dfa_index,mini_alpha_set[for_j]);
411                 }
412                 current_dfa_table[current_dfa_node].begin=temp_dfa_edge;//修改邻接表 
413             }//对于单字符处理完毕
414         }//对于所有的字符都处理完毕了
415     }//对于当前节点处理完毕,但是可能还有其他节点,继续迭代。
416 }//他妈的 nfa转dfa全都处理完毕 
417 void show_dfa_table(void)//输出邻接表
418 { 
419     int for_i;
420     pdfa_edge temp_dfa_edge;
421     for(for_i=1;for_i<=dfa_node_number;for_i++)
422     {
423         temp_dfa_edge=current_dfa_table[for_i].begin;
424         while(temp_dfa_edge!=NULL)
425         {
426             printf("there is an dfa edge from %d to %d with label %c\n",for_i,temp_dfa_edge->dest_dfa_index,temp_dfa_edge->label);
427             temp_dfa_edge=temp_dfa_edge->next;
428         }
429     }
430 }
431 
432 
433 
434 
435 
436     

 

你可能感兴趣的:(df)