最初步的正则表达式引擎:生成nfa

这个版本修改了前面版本的两个个bug。

第一个:识别到字符集的时候,只是将name_number加1,却并不对reg_pattern_table[name_number]进行初始化。

第二个:识别到假名的时候,并不为他分配一个name_number,而只是在hash表中为其分配一个表项。

现在,当识别到这两个的时候,都会为之分配一个name_number,并在reg_pattern_table中正确的初始化。

相关的修改的代码都在tackle_particle()函数中。

还有对tackle_cat()函数的定义移动到了tackle_invisible_cat()函数的前面。

另外一个重大的修改就是,将当前文件分割为多文件了,原来的正则处理部分划分为reg_preprocess.h。

而新的nfa生成部分为nfa_preocess.h。

nfa_process.h基本生成了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 lable_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 }node_for_token,pnode_for_token;
 25 node_for_token token_node[100];
 26 //每一个token对应一个节点,所以100就够了,当然现在处理的是小的输入
 27 //注意这里有一个特殊的地方,对于括号运算符,他的内容与他的子节点的内容是一样的
 28 //而对于假名操作符,他的内容与他的子节点的内容也是一样的,但是他的内容永远都不会被其他节点所利用
 29 //因为在生成token的过程中,入栈的是他所代表的子节点,所以他的token是不会被其他的token所引用的
 30 //还有一个最需要注意的地方,就是每一个token都有其相对应的token_node,而且这两者的索引都相同
 31 //这个设定便利了nfa的处理,同时也就造就了上面说的括号与中括号的特殊性
 32 int token_node_number=1;//这里是用来遍历整个token表的,每增加1,则token_node的内容增加1
 33 p_edge_list nfa_node[400];//因为生成nfa的过程中可能生成四倍于输入的节点,所以定为这么大
 34 int nfa_node_number=0;//这个是nfa中的节点的标号
 35 void add_edge(int nfa_node_begin,int nfa_node_end,char label)//添加边的函数 
 36 {
 37     p_edge_list temp_pnode=malloc(sizeof(struct _graph_edge_list));
 38     temp_pnode->lable_of_edge=label;
 39     temp_pnode->destination_number=nfa_node_end;
 40     temp_pnode->next_edge=nfa_node[nfa_node_begin];
 41     nfa_node[nfa_node_begin]=temp_pnode;
 42 }
 43 void generate_nfa_node(void)
 44 {
 45     int reg_pattern_left;
 46     int reg_pattern_right;
 47     int reg_pattern_origin;
 48     int for_i,for_j;
 49     int add_edge_from,add_edge_to;
 50     //这里建立节点的时候,是从低往高来遍历标号,来生成节点的
 51     //因为我们在生成token的时候,保证了低标号的不会引用高标号的token,因此是一个拓扑排序
 52     while(token_node_number<name_number)
 53     {
 54         switch(reg_pattern_table[token_node_number].type)
 55         {
 56         case closure:
 57             //对于闭包运算,我们可以直接将子节点的开始节点与结束节点之间添加两条空边
 58             //不过这两条边的方向相反 ,偷懒哈哈 
 59             reg_pattern_origin=reg_pattern_table[token_node_number].sub;
 60             add_edge_from=token_node[reg_pattern_origin].begin;
 61             add_edge_to=token_node[reg_pattern_origin].end;
 62             add_edge(add_edge_from,add_edge_to,(char)17);
 63             add_edge(add_edge_to,add_edge_from,(char)17);
 64             token_node[token_node_number].begin=add_edge_from;
 65             token_node[token_node_number].end=add_edge_to;
 66             token_node_number++;
 67             //处理下一个token_node
 68             break;
 69         case cat:
 70             //对于cat节点,那就非常简单了,只需要在原来的左节点的结束点与右节点的开始点之间连一条边
 71             //然后设置一下当前token_node的开始节点和结束节点
 72             //然后token_node_number加一,由于这里没有生成新的nfa节点,所以nfa_node_number不变
 73             reg_pattern_left=reg_pattern_table[token_node_number].left;
 74             reg_pattern_right=reg_pattern_table[token_node_number].right;
 75             token_node[token_node_number].begin=token_node[reg_pattern_left].begin;
 76             token_node[token_node_number].end=token_node[reg_pattern_right].end;
 77             add_edge_from=token_node[reg_pattern_left].end;
 78             add_edge_to=token_node[reg_pattern_right].begin;
 79             add_edge(add_edge_from,add_edge_to,(char)17);
 80             token_node_number++;
 81             break;
 82         case or:
 83             //对于or节点,需要增加两个节点和四条边,郁闷啊
 84             reg_pattern_left=reg_pattern_table[token_node_number].left;
 85             reg_pattern_right=reg_pattern_table[token_node_number].right;
 86             nfa_node_number++;
 87             //建立这个token_node的头节点,以及初始化他的邻接表
 88             token_node[token_node_number].begin=nfa_node_number;
 89             nfa_node[nfa_node_number]=NULL;
 90             add_edge_from=nfa_node_number;
 91             add_edge_to=token_node[reg_pattern_left].begin;
 92             add_edge(add_edge_from,add_edge_to,(char)17);
 93             add_edge_to=token_node[reg_pattern_right].begin;
 94             add_edge(add_edge_from,add_edge_to,(char)17);
 95             nfa_node_number++;
 96             //建立这个token_node的尾节点,以及增加两条指向他的边
 97             token_node[token_node_number].end=nfa_node_number;
 98             nfa_node[nfa_node_number]=NULL;
 99             add_edge_to=nfa_node_number;
100             add_edge_from=token_node[reg_pattern_left].end;
101             add_edge(add_edge_from,add_edge_to,(char)17);
102             add_edge_from=token_node[reg_pattern_right].begin;
103             add_edge(add_edge_from,add_edge_to,(char)17);
104             token_node_number++;
105             break;
106         case parenthesis:
107             token_node[token_node_number].begin=token_node[reg_pattern_table[token_node_number].sub].begin;
108             token_node[token_node_number].end=token_node[reg_pattern_table[token_node_number].sub].end;
109             token_node_number++;
110             break;
111         case  alias:
112             //对于假名,直接初始化他的开始节点和结束节点就行了,反正也没人会用它了
113             token_node[token_node_number].begin=token_node[reg_pattern_table[token_node_number].origin_number].begin;
114             token_node[token_node_number].end=token_node[reg_pattern_table[token_node_number].origin_number].end;
115             token_node_number++;
116             break;
117         case literal_char:
118             //对于单字符,直接新建两个节点,然后在这两个节点中建立一条边
119             //然后初始化token_node
120             nfa_node_number++;
121             nfa_node[nfa_node_number]=NULL;
122             token_node[token_node_number].end=nfa_node_number;
123             add_edge_to=nfa_node_number;
124             nfa_node_number++;
125             nfa_node[nfa_node_number]=NULL;
126             token_node[token_node_number].begin=nfa_node_number;
127             add_edge_from=nfa_node_number;
128             add_edge(add_edge_from,add_edge_to,reg_pattern_table[token_node_number].value);
129             token_node_number++;
130             break;
131         case set_of_char:
132             for_i=reg_pattern_table[token_node_number].begin;
133             for_j=reg_pattern_table[token_node_number].end;
134             nfa_node_number++;
135             //增加一个节点,当前是作为尾节点
136             token_node[token_node_number].end=nfa_node_number;
137             nfa_node[nfa_node_number]=NULL;
138             add_edge_to=nfa_node_number;
139             nfa_node_number++;
140             //增加一个节点,作为头节点
141             add_edge_from=nfa_node_number;
142             token_node[nfa_node_number].begin=nfa_node_number;
143             nfa_node[nfa_node_number]=NULL;
144             for(for_i;for_i<=for_j;for_i++)
145             {
146                 //对于字符集里面的每个字符,都需要增加一条边
147                 add_edge(add_edge_from,add_edge_to,(char)for_i);
148             }
149             token_node_number++;
150             break;
151         case maybe_exist:
152             //处理问号运算符,其实这个就比较简单了,只需要在子表达式的头节点与尾节点之间加一条空边
153             reg_pattern_origin=reg_pattern_table[token_node_number].sub;
154             add_edge_from=token_node[reg_pattern_origin].begin;
155             add_edge_to=token_node[reg_pattern_origin].end;
156             add_edge(add_edge_from,add_edge_to,(char)17);
157             token_node_number++;
158             break;
159         case one_or_more:
160             //这种情况下,我另外建立一个节点当作本token的尾节点
161             //然后添加两条空边,起点都是子节点的尾节点,终点一个是子节点的开始节点 
162             //另外一个就是当前节点的尾节点 
163             nfa_node_number++;
164             //这个节点是作为当前节点的尾节点 
165             nfa_node[nfa_node_number]=NULL;
166             token_node[token_node_number].end=nfa_node_number;
167             add_edge_to=nfa_node_number;
168             reg_pattern_origin=reg_pattern_table[token_node_number].sub;
169             add_edge_from=token_node[reg_pattern_origin].end;
170             add_edge(add_edge_from,add_edge_to,(char)17);
171             add_edge_to=token_node[reg_pattern_origin].begin;
172             add_edge(add_edge_from,add_edge_to,(char)17);
173             token_node_number++;
174             break;
175         default:
176             printf("a type can't be recognised, please check
");
177             token_node_number++;
178             break;
179         }
180     }
181 }
原文地址:https://www.cnblogs.com/huangfeidian/p/3152300.html