太累了,感觉不会再爱了。问题已经解决,具体的懒得说了。
1 #include "regular_preprocess.h" 2 //这个版本终于要上nfa了,好兴奋啊 3 //由于连个节点之间可能有多条边,所以只能用邻接表来存储了 4 //注意这里是有向图 5 //对于每一个token,这里都会生成一个或多个图节点 6 //但是每个token会附带另外的两个域,即这个token的开始节点和结束节点 7 //因为内部节点对于外部来说是不可连接的,所以不需要暴露 8 //这里有一个难题,就是空转换如何表示,这里我们必须找一个不可打印字符来代表空转换 9 //楼主自己查了一下asc码表,选择了17号字符,因为 10 //鬼才知道那个字符啥意思,而且看描述c语言里面也不会出现这个字符吧, 11 //我看了看键盘,非常欣慰,因为我不知道怎么打出17号字符 12 //好,就选它了。 13 //对于nfa图,这里维护了一个图节点的数组,而这个数组组成了邻接表 14 typedef struct _graph_edge_list 15 { 16 struct _graph_edge_list* next_edge;//下一条边的指针 17 char label_of_edge;//这个是转换条件 18 int destination_number;//有向边的终点,直接用这个点在图节点数组中的索引来表示 19 }graph_edge_list,*p_edge_list; 20 typedef struct _node_for_token//对于每一个token,我们记录他的进入节点和终止节点,为了连接成大的nfa图用 21 { 22 int begin;//当前节点的开始节点的标号 23 int end;//当前节点的结束节点的标号 24 int top;//这个是当前节点的nfa的最大标号 25 int bottom;//这个是当前节点的nfa的最小标号 26 }node_for_token,pnode_for_token; 27 node_for_token token_node[100]; 28 //每一个token对应一个节点,所以100就够了,当然现在处理的是小的输入 29 //注意这里有一个特殊的地方,对于括号运算符,他的内容与他的子节点的内容是一样的 30 //而对于假名操作符,他的内容与他的子节点的内容也是一样的,但是他的内容永远都不会被其他节点所利用 31 //因为在生成token的过程中,入栈的是他所代表的子节点,所以他的token是不会被其他的token所引用的 32 //还有一个最需要注意的地方,就是每一个token都有其相对应的token_node,而且这两者的索引都相同 33 //这个设定便利了nfa的处理,同时也就造就了上面说的括号与中括号的特殊性 34 int token_node_number=1;//这里是用来遍历整个token表的,每增加1,则token_node的内容增加1 35 p_edge_list nfa_node[400];//因为生成nfa的过程中可能生成四倍于输入的节点,所以定为这么大 36 int nfa_node_number=0;//这个是nfa中的节点的标号 37 void add_edge(int nfa_node_begin,int nfa_node_end,char label)//添加边的函数 38 { 39 p_edge_list temp_pnode=malloc(sizeof(struct _graph_edge_list)); 40 temp_pnode->label_of_edge=label; 41 temp_pnode->destination_number=nfa_node_end; 42 temp_pnode->next_edge=nfa_node[nfa_node_begin]; 43 nfa_node[nfa_node_begin]=temp_pnode; 44 } 45 void nfa_cope_alias(void) 46 { 47 int nfa_begin_number; 48 int nfa_end_number; 49 int copy_destination; 50 int offset; 51 int original_token; 52 char copy_label; 53 p_edge_list pcopy; 54 original_token=reg_pattern_table[token_node_number].origin_number; 55 nfa_begin_number=token_node[original_token].bottom; 56 nfa_end_number=token_node[original_token].top; 57 offset=nfa_node_number-nfa_begin_number+1;//因为这样要考虑下一个节点 58 token_node[token_node_number].bottom=nfa_node_number+1; 59 token_node[token_node_number].top=nfa_end_number+offset; 60 token_node[token_node_number].begin=offset+token_node[original_token].begin; 61 token_node[token_node_number].end=offset+token_node[original_token].end; 62 for(nfa_begin_number;nfa_begin_number<=nfa_end_number;nfa_begin_number++) 63 { 64 pcopy=nfa_node[nfa_begin_number]; 65 nfa_node_number++; 66 nfa_node[nfa_node_number]=NULL; 67 while(pcopy!=NULL) 68 { 69 copy_label=pcopy->label_of_edge; 70 copy_destination=pcopy->destination_number+offset; 71 add_edge(nfa_node_number,copy_destination,copy_label); 72 pcopy=pcopy->next_edge; 73 } 74 } 75 } 76 77 void generate_nfa_node(void) 78 { 79 int reg_pattern_left; 80 int reg_pattern_right; 81 int reg_pattern_origin; 82 int for_i,for_j; 83 int add_edge_from,add_edge_to; 84 //这里建立节点的时候,是从低往高来遍历标号,来生成节点的 85 //因为我们在生成token的时候,保证了低标号的不会引用高标号的token,因此是一个拓扑排序 86 while(token_node_number<=name_number) 87 { 88 switch(reg_pattern_table[token_node_number].type) 89 { 90 case closure: 91 //对于闭包运算,我们可以直接将子节点的开始节点与结束节点之间添加两条空边 92 //不过这两条边的方向相反 ,偷懒哈哈 93 reg_pattern_origin=reg_pattern_table[token_node_number].sub; 94 add_edge_from=token_node[reg_pattern_origin].begin; 95 add_edge_to=token_node[reg_pattern_origin].end; 96 add_edge(add_edge_from,add_edge_to,(char)17); 97 add_edge(add_edge_to,add_edge_from,(char)17); 98 token_node[token_node_number].begin=add_edge_from; 99 token_node[token_node_number].end=add_edge_to; 100 token_node[token_node_number].bottom=token_node[reg_pattern_origin].bottom; 101 token_node[token_node_number].top=token_node[reg_pattern_origin].top; 102 token_node_number++; 103 //处理下一个token_node 104 break; 105 case cat: 106 //对于cat节点,那就非常简单了,只需要在原来的左节点的结束点与右节点的开始点之间连一条边 107 //然后设置一下当前token_node的开始节点和结束节点 108 //然后token_node_number加一,由于这里没有生成新的nfa节点,所以nfa_node_number不变 109 //对于双目运算符,左边的节点总是被先建立起来,所以左边的标号最大的节点会小于右边的编号最小的 110 reg_pattern_left=reg_pattern_table[token_node_number].left; 111 reg_pattern_right=reg_pattern_table[token_node_number].right; 112 token_node[token_node_number].begin=token_node[reg_pattern_left].begin; 113 token_node[token_node_number].end=token_node[reg_pattern_right].end; 114 token_node[token_node_number].bottom=token_node[reg_pattern_left].bottom; 115 token_node[token_node_number].top=token_node[reg_pattern_right].top; 116 add_edge_from=token_node[reg_pattern_left].end; 117 add_edge_to=token_node[reg_pattern_right].begin; 118 add_edge(add_edge_from,add_edge_to,(char)17); 119 token_node_number++; 120 break; 121 case or: 122 //对于或的处理,我们这里修改了以前的实现,不再增加节点,而只是增加两条空转换边。 123 reg_pattern_left=reg_pattern_table[token_node_number].left; 124 reg_pattern_right=reg_pattern_table[token_node_number].right; 125 add_edge_from=token_node[reg_pattern_left].begin; 126 add_edge_to=token_node[reg_pattern_right].begin; 127 add_edge(add_edge_from,add_edge_to,(char)17); 128 add_edge_from=token_node[reg_pattern_right].end; 129 add_edge_to=token_node[reg_pattern_left].end; 130 add_edge(add_edge_from,add_edge_to,(char)17); 131 token_node[token_node_number].begin=token_node[reg_pattern_left].begin; 132 token_node[token_node_number].end=token_node[reg_pattern_left].end; 133 token_node[token_node_number].bottom=token_node[reg_pattern_left].bottom; 134 token_node[token_node_number].top=token_node[reg_pattern_right].top; 135 token_node_number++; 136 break; 137 case parenthesis: 138 //对于括号,直接初始化他的开始节点和结束节点就行了,反正也没人会用它了 139 token_node[token_node_number].begin=token_node[reg_pattern_table[token_node_number].sub].begin; 140 token_node[token_node_number].end=token_node[reg_pattern_table[token_node_number].sub].end; 141 token_node[token_node_number].bottom=token_node[reg_pattern_table[token_node_number].sub].bottom; 142 token_node[token_node_number].top=token_node[reg_pattern_table[token_node_number].sub].top; 143 token_node_number++; 144 break; 145 case alias: 146 //对于假名,这里需要重新拷贝nfa 147 nfa_cope_alias(); 148 token_node_number++; 149 break; 150 case literal_char: 151 //对于单字符,直接新建两个节点,然后在这两个节点中建立一条边 152 //然后初始化token_node,这里nfa上限和下限非常好处理,因为只有两个节点 153 nfa_node_number++; 154 nfa_node[nfa_node_number]=NULL; 155 token_node[token_node_number].end=nfa_node_number; 156 add_edge_to=nfa_node_number; 157 token_node[token_node_number].bottom=nfa_node_number; 158 nfa_node_number++; 159 nfa_node[nfa_node_number]=NULL; 160 token_node[token_node_number].begin=nfa_node_number; 161 add_edge_from=nfa_node_number; 162 add_edge(add_edge_from,add_edge_to,reg_pattern_table[token_node_number].value); 163 token_node[token_node_number].top=nfa_node_number; 164 token_node_number++; 165 break; 166 case set_of_char: 167 for_i=reg_pattern_table[token_node_number].begin; 168 for_j=reg_pattern_table[token_node_number].end; 169 nfa_node_number++; 170 //增加一个节点,当前是作为尾节点 171 token_node[token_node_number].end=nfa_node_number; 172 token_node[token_node_number].bottom=nfa_node_number; 173 nfa_node[nfa_node_number]=NULL; 174 add_edge_to=nfa_node_number; 175 nfa_node_number++; 176 //增加一个节点,作为头节点 177 add_edge_from=nfa_node_number; 178 token_node[token_node_number].begin=nfa_node_number; 179 nfa_node[nfa_node_number]=NULL; 180 token_node[token_node_number].top=nfa_node_number; 181 for(for_i;for_i<=for_j;for_i++) 182 { 183 //对于字符集里面的每个字符,都需要增加一条边 184 add_edge(add_edge_from,add_edge_to,(char)for_i); 185 } 186 token_node_number++; 187 break; 188 case maybe_exist: 189 //处理问号运算符,其实这个就比较简单了,只需要在子表达式的头节点与尾节点之间加一条空边 190 reg_pattern_origin=reg_pattern_table[token_node_number].sub; 191 add_edge_from=token_node[reg_pattern_origin].begin; 192 add_edge_to=token_node[reg_pattern_origin].end; 193 token_node[token_node_number].begin=token_node[reg_pattern_origin].begin; 194 token_node[token_node_number].end=token_node[reg_pattern_origin].end; 195 token_node[token_node_number].bottom=token_node[reg_pattern_origin].bottom; 196 token_node[token_node_number].top=token_node[reg_pattern_origin].top; 197 add_edge(add_edge_from,add_edge_to,(char)17); 198 token_node_number++; 199 break; 200 case one_or_more: 201 //在这里,直接从子节点的尾节点发出一条边到子节点的开始节点 202 reg_pattern_origin=reg_pattern_table[token_node_number].sub; 203 token_node[token_node_number].end=token_node[reg_pattern_origin].end; 204 add_edge_to=token_node[token_node_number].begin=token_node[reg_pattern_origin].begin; 205 token_node[token_node_number].bottom=token_node[reg_pattern_origin].bottom; 206 token_node[token_node_number].top=token_node[reg_pattern_origin].top; 207 add_edge_from=token_node[reg_pattern_origin].end; 208 add_edge(add_edge_from,add_edge_to,(char)17); 209 token_node_number++; 210 break; 211 default: 212 printf("a type can't be recognised, please check\n"); 213 token_node_number++; 214 break; 215 } 216 } 217 } 218 219 void show_nfa_table(void) 220 { 221 int final_re; 222 int nfa_final_bottom; 223 int nfa_final_top; 224 p_edge_list traverse; 225 char label; 226 int destination; 227 final_re=token_node_number-1; 228 nfa_final_bottom=token_node[final_re].bottom; 229 nfa_final_top=token_node[final_re].top; 230 for(nfa_final_bottom;nfa_final_bottom<=nfa_final_top;nfa_final_bottom++) 231 { 232 traverse=nfa_node[nfa_final_bottom]; 233 while(traverse!=NULL) 234 { 235 destination=traverse->destination_number; 236 label=traverse->label_of_edge; 237 if(label==(char)17) 238 { 239 printf("there is an edge from %d to %d with null_transition\n",nfa_final_bottom,destination); 240 } 241 else 242 { 243 printf("there is an edge from %d to %d with label %c\n",nfa_final_bottom,destination,label); 244 } 245 traverse=traverse->next_edge; 246 } 247 } 248 printf("the nfa_graph begins with %d\n",token_node[final_re].begin); 249 printf("the nfa_graph ends with %d\n",token_node[final_re].end); 250 }