为了加速转换的处理,我压缩了符号表。具体算法参考任何一本与编译或者自动机相关的书籍。
这里的核心问题是处理传递性闭包,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]¤t_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