• 最初步的正则表达式引擎:生成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 }
  • 相关阅读:
    Bootstrap Table
    XML CDATA识别“<,>”
    LigerUI之Grid使用详解(一)——显示数据 --分页
    Oracle中对XMLType的简单操作(extract、extractvalue...)
    Qt实现应用程序单实例运行--LocalServer方式
    Live m3u8播放3个文件自动停止问题
    markdown
    node.js 知识记录
    .NET 高级架构师 WEB架构师 ------时间 总结 专注
    .NET 高级架构师 WEB架构师 ------走正确的路
  • 原文地址:https://www.cnblogs.com/huangfeidian/p/3152300.html
Copyright © 2020-2023  润新知