正则转nfa:完成

太累了,感觉不会再爱了。问题已经解决,具体的懒得说了。

  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
");
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
",nfa_final_bottom,destination);
240             }
241             else
242             {
243                 printf("there is an edge from %d to %d with label %c
",nfa_final_bottom,destination,label);
244             }
245             traverse=traverse->next_edge;
246         }
247     }
248     printf("the nfa_graph begins with %d
",token_node[final_re].begin);
249     printf("the nfa_graph ends with %d
",token_node[final_re].end);
250 }
原文地址:https://www.cnblogs.com/huangfeidian/p/3154477.html