zoukankan      html  css  js  c++  java
  • 正则转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 }
  • 相关阅读:
    [转] 计算机网络中的服务原语
    VMWare的三种网络连接方式
    Vim常用操作
    [转] 图解单片机下载程序电路原理之USB转串口线、CH340、PL2303、MAX232芯片的使用
    [转] MMU 配置
    [转] C++项目中的extern "C" {}
    数据结构62:表插入排序算法
    数据结构61:2-路插入排序算法
    数据结构60:折半插入排序算法(折半排序算法)
    数据结构59:插入排序算法
  • 原文地址:https://www.cnblogs.com/huangfeidian/p/3154477.html
Copyright © 2011-2022 走看看