正则转nfa:完成
太累了,感觉不会再爱了。问题已经解决,具体的懒得说了。
#include "regular_preprocess.h"
//这个版本终于要上nfa了,好兴奋啊
//由于连个节点之间可能有多条边,所以只能用邻接表来存储了
//注意这里是有向图
//对于每一个token,这里都会生成一个或多个图节点
//但是每个token会附带另外的两个域,即这个token的开始节点和结束节点
//因为内部节点对于外部来说是不可连接的,所以不需要暴露
//这里有一个难题,就是空转换如何表示,这里我们必须找一个不可打印字符来代表空转换
//楼主自己查了一下asc码表,选择了17号字符,因为
//鬼才知道那个字符啥意思,而且看描述c语言里面也不会出现这个字符吧,
//我看了看键盘,非常欣慰,因为我不知道怎么打出17号字符
//好,就选它了。
//对于nfa图,这里维护了一个图节点的数组,而这个数组组成了邻接表
typedef struct _graph_edge_list
{
struct _graph_edge_list* next_edge;//下一条边的指针
char label_of_edge;//这个是转换条件
int destination_number;//有向边的终点,直接用这个点在图节点数组中的索引来表示
}graph_edge_list,*p_edge_list;
typedef struct _node_for_token//对于每一个token,我们记录他的进入节点和终止节点,为了连接成大的nfa图用
{
int begin;//当前节点的开始节点的标号
int end;//当前节点的结束节点的标号
int top;//这个是当前节点的nfa的最大标号
int bottom;//这个是当前节点的nfa的最小标号
}node_for_token,pnode_for_token;
node_for_token token_node[];
//每一个token对应一个节点,所以100就够了,当然现在处理的是小的输入
//注意这里有一个特殊的地方,对于括号运算符,他的内容与他的子节点的内容是一样的
//而对于假名操作符,他的内容与他的子节点的内容也是一样的,但是他的内容永远都不会被其他节点所利用
//因为在生成token的过程中,入栈的是他所代表的子节点,所以他的token是不会被其他的token所引用的
//还有一个最需要注意的地方,就是每一个token都有其相对应的token_node,而且这两者的索引都相同
//这个设定便利了nfa的处理,同时也就造就了上面说的括号与中括号的特殊性
int token_node_number=;//这里是用来遍历整个token表的,每增加1,则token_node的内容增加1
p_edge_list nfa_node[];//因为生成nfa的过程中可能生成四倍于输入的节点,所以定为这么大
int nfa_node_number=;//这个是nfa中的节点的标号
void add_edge(int nfa_node_begin,int nfa_node_end,char label)//添加边的函数
{
p_edge_list temp_pnode=malloc(sizeof(struct _graph_edge_list));
temp_pnode->label_of_edge=label;
temp_pnode->destination_number=nfa_node_end;
temp_pnode->next_edge=nfa_node[nfa_node_begin];
nfa_node[nfa_node_begin]=temp_pnode;
}
void nfa_cope_alias(void)
{
int nfa_begin_number;
int nfa_end_number;
int copy_destination;
int offset;
int original_token;
char copy_label;
p_edge_list pcopy;
original_token=reg_pattern_table[token_node_number].origin_number;
nfa_begin_number=token_node[original_token].bottom;
nfa_end_number=token_node[original_token].top;
offset=nfa_node_number-nfa_begin_number+;//因为这样要考虑下一个节点
token_node[token_node_number].bottom=nfa_node_number+;
token_node[token_node_number].top=nfa_end_number+offset;
token_node[token_node_number].begin=offset+token_node[original_token].begin;
token_node[token_node_number].end=offset+token_node[original_token].end;
for(nfa_begin_number;nfa_begin_number<=nfa_end_number;nfa_begin_number++)
{
pcopy=nfa_node[nfa_begin_number];
nfa_node_number++;
nfa_node[nfa_node_number]=NULL;
while(pcopy!=NULL)
{
copy_label=pcopy->label_of_edge;
copy_destination=pcopy->destination_number+offset;
add_edge(nfa_node_number,copy_destination,copy_label);
pcopy=pcopy->next_edge;
}
}
} void generate_nfa_node(void)
{
int reg_pattern_left;
int reg_pattern_right;
int reg_pattern_origin;
int for_i,for_j;
int add_edge_from,add_edge_to;
//这里建立节点的时候,是从低往高来遍历标号,来生成节点的
//因为我们在生成token的时候,保证了低标号的不会引用高标号的token,因此是一个拓扑排序
while(token_node_number<=name_number)
{
switch(reg_pattern_table[token_node_number].type)
{
case closure:
//对于闭包运算,我们可以直接将子节点的开始节点与结束节点之间添加两条空边
//不过这两条边的方向相反 ,偷懒哈哈
reg_pattern_origin=reg_pattern_table[token_node_number].sub;
add_edge_from=token_node[reg_pattern_origin].begin;
add_edge_to=token_node[reg_pattern_origin].end;
add_edge(add_edge_from,add_edge_to,(char));
add_edge(add_edge_to,add_edge_from,(char));
token_node[token_node_number].begin=add_edge_from;
token_node[token_node_number].end=add_edge_to;
token_node[token_node_number].bottom=token_node[reg_pattern_origin].bottom;
token_node[token_node_number].top=token_node[reg_pattern_origin].top;
token_node_number++;
//处理下一个token_node
break;
case cat:
//对于cat节点,那就非常简单了,只需要在原来的左节点的结束点与右节点的开始点之间连一条边
//然后设置一下当前token_node的开始节点和结束节点
//然后token_node_number加一,由于这里没有生成新的nfa节点,所以nfa_node_number不变
//对于双目运算符,左边的节点总是被先建立起来,所以左边的标号最大的节点会小于右边的编号最小的
reg_pattern_left=reg_pattern_table[token_node_number].left;
reg_pattern_right=reg_pattern_table[token_node_number].right;
token_node[token_node_number].begin=token_node[reg_pattern_left].begin;
token_node[token_node_number].end=token_node[reg_pattern_right].end;
token_node[token_node_number].bottom=token_node[reg_pattern_left].bottom;
token_node[token_node_number].top=token_node[reg_pattern_right].top;
add_edge_from=token_node[reg_pattern_left].end;
add_edge_to=token_node[reg_pattern_right].begin;
add_edge(add_edge_from,add_edge_to,(char));
token_node_number++;
break;
case or:
//对于或的处理,我们这里修改了以前的实现,不再增加节点,而只是增加两条空转换边。
reg_pattern_left=reg_pattern_table[token_node_number].left;
reg_pattern_right=reg_pattern_table[token_node_number].right;
add_edge_from=token_node[reg_pattern_left].begin;
add_edge_to=token_node[reg_pattern_right].begin;
add_edge(add_edge_from,add_edge_to,(char));
add_edge_from=token_node[reg_pattern_right].end;
add_edge_to=token_node[reg_pattern_left].end;
add_edge(add_edge_from,add_edge_to,(char));
token_node[token_node_number].begin=token_node[reg_pattern_left].begin;
token_node[token_node_number].end=token_node[reg_pattern_left].end;
token_node[token_node_number].bottom=token_node[reg_pattern_left].bottom;
token_node[token_node_number].top=token_node[reg_pattern_right].top;
token_node_number++;
break;
case parenthesis:
//对于括号,直接初始化他的开始节点和结束节点就行了,反正也没人会用它了
token_node[token_node_number].begin=token_node[reg_pattern_table[token_node_number].sub].begin;
token_node[token_node_number].end=token_node[reg_pattern_table[token_node_number].sub].end;
token_node[token_node_number].bottom=token_node[reg_pattern_table[token_node_number].sub].bottom;
token_node[token_node_number].top=token_node[reg_pattern_table[token_node_number].sub].top;
token_node_number++;
break;
case alias:
//对于假名,这里需要重新拷贝nfa
nfa_cope_alias();
token_node_number++;
break;
case literal_char:
//对于单字符,直接新建两个节点,然后在这两个节点中建立一条边
//然后初始化token_node,这里nfa上限和下限非常好处理,因为只有两个节点
nfa_node_number++;
nfa_node[nfa_node_number]=NULL;
token_node[token_node_number].end=nfa_node_number;
add_edge_to=nfa_node_number;
token_node[token_node_number].bottom=nfa_node_number;
nfa_node_number++;
nfa_node[nfa_node_number]=NULL;
token_node[token_node_number].begin=nfa_node_number;
add_edge_from=nfa_node_number;
add_edge(add_edge_from,add_edge_to,reg_pattern_table[token_node_number].value);
token_node[token_node_number].top=nfa_node_number;
token_node_number++;
break;
case set_of_char:
for_i=reg_pattern_table[token_node_number].begin;
for_j=reg_pattern_table[token_node_number].end;
nfa_node_number++;
//增加一个节点,当前是作为尾节点
token_node[token_node_number].end=nfa_node_number;
token_node[token_node_number].bottom=nfa_node_number;
nfa_node[nfa_node_number]=NULL;
add_edge_to=nfa_node_number;
nfa_node_number++;
//增加一个节点,作为头节点
add_edge_from=nfa_node_number;
token_node[token_node_number].begin=nfa_node_number;
nfa_node[nfa_node_number]=NULL;
token_node[token_node_number].top=nfa_node_number;
for(for_i;for_i<=for_j;for_i++)
{
//对于字符集里面的每个字符,都需要增加一条边
add_edge(add_edge_from,add_edge_to,(char)for_i);
}
token_node_number++;
break;
case maybe_exist:
//处理问号运算符,其实这个就比较简单了,只需要在子表达式的头节点与尾节点之间加一条空边
reg_pattern_origin=reg_pattern_table[token_node_number].sub;
add_edge_from=token_node[reg_pattern_origin].begin;
add_edge_to=token_node[reg_pattern_origin].end;
token_node[token_node_number].begin=token_node[reg_pattern_origin].begin;
token_node[token_node_number].end=token_node[reg_pattern_origin].end;
token_node[token_node_number].bottom=token_node[reg_pattern_origin].bottom;
token_node[token_node_number].top=token_node[reg_pattern_origin].top;
add_edge(add_edge_from,add_edge_to,(char));
token_node_number++;
break;
case one_or_more:
//在这里,直接从子节点的尾节点发出一条边到子节点的开始节点
reg_pattern_origin=reg_pattern_table[token_node_number].sub;
token_node[token_node_number].end=token_node[reg_pattern_origin].end;
add_edge_to=token_node[token_node_number].begin=token_node[reg_pattern_origin].begin;
token_node[token_node_number].bottom=token_node[reg_pattern_origin].bottom;
token_node[token_node_number].top=token_node[reg_pattern_origin].top;
add_edge_from=token_node[reg_pattern_origin].end;
add_edge(add_edge_from,add_edge_to,(char));
token_node_number++;
break;
default:
printf("a type can't be recognised, please check\n");
token_node_number++;
break;
}
}
} void show_nfa_table(void)
{
int final_re;
int nfa_final_bottom;
int nfa_final_top;
p_edge_list traverse;
char label;
int destination;
final_re=token_node_number-;
nfa_final_bottom=token_node[final_re].bottom;
nfa_final_top=token_node[final_re].top;
for(nfa_final_bottom;nfa_final_bottom<=nfa_final_top;nfa_final_bottom++)
{
traverse=nfa_node[nfa_final_bottom];
while(traverse!=NULL)
{
destination=traverse->destination_number;
label=traverse->label_of_edge;
if(label==(char))
{
printf("there is an edge from %d to %d with null_transition\n",nfa_final_bottom,destination);
}
else
{
printf("there is an edge from %d to %d with label %c\n",nfa_final_bottom,destination,label);
}
traverse=traverse->next_edge;
}
}
printf("the nfa_graph begins with %d\n",token_node[final_re].begin);
printf("the nfa_graph ends with %d\n",token_node[final_re].end);
}
正则转nfa:完成的更多相关文章
- 正则转nfa:bug出现。
本人写的一个正则到nfa的bug 刚写完前面的那篇,自己用脑子过了一下,发现了一个bug.具体情况如下. 这个bug的产生条件是多次调用假名的时候,每次调用都会修改假名的nfa图.直接这么说不好理解, ...
- 正则转nfa:bug消除
正则到nfabug的解决方法 前面提到了这个bug,为了解决这个bug,我们必须在每次引用到一个假名的时候,都构建一个拷贝.现在假设我们遇到了一个假名,并得到了他的开始节点和结束节点,当前的难题就是构 ...
- 最初步的正则表达式引擎:nfa的转换规则。
[在此处输入文章标题] 正则到nfa 前言 在写代码的过程中,本来还想根据龙书上的说明来实现re到nfa的转换.可是写代码的时候发现,根据课本来会生成很多的无用过渡节点和空转换边,需要许多的代码.为了 ...
- 正则表达式引擎:nfa的转换规则。
正则表达式引擎:nfa的转换规则. 正则到nfa 前言 在写代码的过程中,本来还想根据龙书上的说明来实现re到nfa的转换.可是写代码的时候发现,根据课本来会生成很多的无用过渡节点和空转换边,需要许多 ...
- [转]Web应用防火墙WAF详解
通过nginx配置文件抵御攻击 0x00 前言 大家好,我们是OpenCDN团队的Twwy.这次我们来讲讲如何通过简单的配置文件来实现nginx防御攻击的效果. 其实很多时候,各种防攻击的思路我们都明 ...
- WAF的实现
文章来源:http://danqingdani.blog.163.com/blog/static/1860941952014101723845500/ 本篇文章从WAF产品研发的角度来YY如何实现一款 ...
- 如何打造一款可靠的WAF
之前写了一篇<WAF防御能力评测及工具>,是站在安全运维人员选型WAF产品的角度来考虑的(优先从测试角度考虑是前职业病,毕竟当过3年游戏测试?!).本篇文章从WAF产品研发的角度来YY如何 ...
- atitit.词法分析原理 词法分析器 (Lexer)
atitit.词法分析原理 词法分析器 (Lexer) 1. 词法分析(英语:lexical analysis)1 2. :实现词法分析程序的常用途径:自动生成,手工生成.[1] 2 2.1. 词法分 ...
- js 正则学习小记之NFA引擎
原文:js 正则学习小记之NFA引擎 之前一直认为自己正则还不错,在看 次碳酸钴,Barret Lee 等大神都把正则玩的出神入化后发现我只是个战五渣. 求抱大腿,求大神调教. 之前大致有个印象,正 ...
随机推荐
- mahout算法源码分析之Itembased Collaborative Filtering(四)共生矩阵乘法
Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 经过了SimilarityJob的计算共生矩阵后,就可以开始下面一个过程了,这个过程主要是共生矩阵的乘法 ...
- 关于App.config配置文件
今天在做复习的时候,突然发现自己无法读取配置文件中的数据库连接字符串,而且检查了半天也没找出原因,最后求助万能的度娘才得以解决—— 1.App.config配置文件在项目中添加后不要修改名称,否则会出 ...
- (剑指Offer)面试题15:链表中倒数第k个结点
题目: 输入一个链表,输出该链表中倒数第k个结点. 例如:链表中有6个结点,从头到尾依次为1,2,3,4,5,6,则该链表的倒数第3个结点为4. 链表结点定义: struct ListNode{ in ...
- HDU 1695 GCD 容斥
GCD 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=1695 Description Given 5 integers: a, b, c, d, k ...
- C++ Code_TabControl
主题 1. 选项卡控件基础 2. 显示图标的选项卡 3. 选项卡控件高级 4. 5. 属性 选项卡控件基础 1.插入1个对话框,新建1个类 CCDialog1,1 个对话框对应一个 ...
- [Node.js] CommonJS Modules
CoomonJS modules provide a clean syntax for importing dependencies. This lesson will take a look at ...
- JDBC连接MySQL数据库及演示样例
JDBC是Sun公司制定的一个能够用Java语言连接数据库的技术. 一.JDBC基础知识 JDBC(Java Data Base Connectivity,java数据库连接)是一种用 ...
- errno多线程安全(转载)
一.errno的由来 在C编程中,errno是个不可缺少的变量,特别是在网络编程中.如果你没有用过errno,那只能说明你的程序不够健壮.当然,如果你是WIN32平台的GetLastError ...
- [原创]SQL SERVER 2008R2 技术总结专题目录索引
前言: 在工作中使用了SQL SERVER 2008R2已经很长一段时间了,工作中自己也有个蛮好的习惯:总是喜欢将碰到的一些问题.技术方案等记录下来,现在越积越多,最近也比较轻松了,准备整理 ...
- 【转】如何使用KeyChain保存和获取UDID
本文是iOS7系列文章第一篇文章,主要介绍使用KeyChain保存和获取APP数据,解决iOS7上获取不变UDID的问题.并给出一个获取UDID的工具类,使用方便,只需要替换两个地方即可. 一.iOS ...