编译原理之正则表达式转NFA
本文转载自http://chriszz.sinaapp.com/?p=257
输入一个正则表达式,输出一个NFA。
我的做法:输入一个字符串表示正则,输出则是把输出到一个.dot文件中并将dot文件编译成pdf,fedora需要sudo yum install dot,然后evince XXX.pdf就可以查看生成的NFA了。
具体算法是按照龙书上的Tompson算法来的。
废话不多说,放码过来:
/*
Author:ChrisZZ(zchrissirhcz@gmail.com)
Time:2013-12-25 14:13:09
输入:正则表达式
输出:自动机
算法步骤:
1.把正则表达式转化为后缀表达式
2.把后缀表达式转化为NFA
3.用dot语言把NFA输出到PDF
参考:
1.Regular Expression Matching Can Be Simple And Fast
http://swtch.com/~rsc/regexp/regexp1.html
2.龙书 chap3.7.4 从正则表达式构造NFA
3.YCC学长的project中dot语言的使用
其他说明:
1.需要安装dot,并添加到系统path中
2.在windows下运行时,控制台因为编码不支持可能导致中文提示无法显示
*/
#include <iostream>
#include <string>
#include <stdio.h>
#include <stack>
#include <string.h>
#include <stdexcept>
#include <stdlib.h> using namespace std; const int Match = 256;
const int Split = 257;//表示epsilon分支 struct Paren{//括号结构体
int natom;
int nalt;
}; string re2post(string re){
Paren paren;//括号
stack<struct Paren>parenStk;
string postExpr="";
int i, len=re.length();
int nalt=0, natom=0;
const string invalidRegExp = "非法的正则表达式";
for(i=0; i<len; i++){
if(isspace(re[i])) continue;
if(isalpha(re[i])){
if(natom>1){
natom--;
postExpr = postExpr + '.';
}
natom++;
postExpr = postExpr + re[i];
}
else if(re[i]=='('){
if(natom>1){
postExpr = postExpr + '.';
}
paren.natom = natom;
paren.nalt = nalt;
parenStk.push(paren);
nalt = 0;
natom = 0;
}
else if(re[i]==')'){
if(natom==0 || parenStk.empty())
throw runtime_error(invalidRegExp+":括号不匹配");
while(--natom>0){//比如((a|b)(c|d))模式,当上一次匹配完倒数第二个右括号后,natom为2,需要添加'.'
postExpr = postExpr + '.';
}
while(nalt-->0){
postExpr = postExpr + '|';
}
paren=parenStk.top();
parenStk.pop();
natom = paren.natom;
nalt = paren.nalt;
natom++;
}
else if(re[i]=='*'){
if(natom==0)
throw runtime_error(invalidRegExp+":提前出现'*'");
postExpr = postExpr + re[i];
}
else if(re[i]=='|'){
if(natom==0) throw runtime_error(invalidRegExp+":提前出现'|'");
while(--natom>0){
postExpr = postExpr + '.';
}
nalt++;
}
else
throw runtime_error(invalidRegExp);
}
if(!parenStk.empty())
throw runtime_error(invalidRegExp+":括号不匹配");
while(--natom>0){
postExpr = postExpr + '.';
}
while(nalt-->0){
postExpr = postExpr + '|';
}
return postExpr;
} class NFA; /*
* c<256表示edge权重为c;
* c=256表示终结状态,匹配成功
* c=257表示分支(split)
*/
class State{
friend class NFA;
friend void nfa2graph(State* head, const string& re);
public:
State(int c=256, State* out=NULL, State* out1=NULL){
this->c = c;
this->out = out;
this->out1 = out1;
this->id = 0;
}
void setId(int id){
this->id = id;
} private:
int c;
int id;//状态的编号
State* out;//从本状态出去的状态集合的头指针
State* out1;//两个分支的情况
}; class NFA{
public:
NFA(){
head = NULL;
tail = NULL;
}
NFA(const int& c){
tail = new State(Match, NULL, NULL);
head = new State(c, tail, NULL);
}
void doCat(NFA& nfa){
tail->out = nfa.head;
tail->c = Split;
tail = nfa.tail;
nfa.head = NULL;
nfa.tail = NULL;
}
void doUnion(NFA& nfa){
State* newHead = new State(Split, head, nfa.head);
State* newTail = new State(Match, NULL, NULL);
tail->c = Split;
tail->out = newTail;
nfa.tail->c = Split;
nfa.tail->out = newTail;
tail = newTail;
head = newHead;
nfa.head = NULL;
nfa.tail = NULL;
}
void doStar(){
State* newTail = new State(Match, NULL, NULL);
State* newHead = new State(Split, head, newTail);
tail->c = Split;
tail->out = newTail;
tail->out1 = head;
tail = newTail;
head = newHead;
} void nfa2graph(const string& re){
char myfile[100];
printf("请输入一个文件名,用来保存生成的NFA-graph(不必提供后缀):\n");
scanf("%s", myfile);
printf("已将DOT文件存储在\"%s.dot\",\n", myfile);
printf("PDF文件则存储在\"%s.dot.pdf\".\n", myfile);
int i;
while(myfile[i]!='\0')
i++;
myfile[i] = '.';
myfile[i+1] = 'd';
myfile[i+2] = 'o';
myfile[i+3] = 't';
myfile[i+4] = '\0'; FILE *file = fopen(myfile, "w"); fputs("digraph {\n", file);
fputs("\t\"", file);
int len=re.length();
for(i=0; i<len; i++){
fprintf(file, "%c", re[i]);
} fputs("\" [shape = plaintext]\n", file);
fputs("\trankdir = LR\n", file);
fputs("\t\"\" [shape = point]\n", file);
fputs("\t\"\" -> 1 [label = Start]\n\n", file); int id = 1; char circle[2000];
memset(circle, 0, sizeof(circle));
State* p;
stack<State*> staStk; head->setId(id++);
staStk.push(head); while(!staStk.empty()){
p = staStk.top();
staStk.pop();
char flag = 1;
cout << "p->c=" << p->c << endl;
if(p->c < Match){
cout << "p->out->id=" << p->out->id << endl;
if(p->out->id==0){
p->out->id = id++;
cout << "id=" << id << endl; }
else
flag = 0;
fprintf(file, "\t%d -> %d [label = \"%c\"]\n", p->id, (p->out)->id, p->c);
State *what = p->out;
if(flag) //push(*what);
staStk.push(what);
} else if(p->c == Match){
circle[p->id] = 1;
} else{ //对应Split的情形
if(p->out->id==0)
p->out->id = id++;
else
flag = 0;
fprintf(file, "\t%d -> %d [label = <ε>]\n", p->id, p->out->id);
State *what = p->out;
if(flag) staStk.push(what); if(p->out1!=NULL){
flag = 1; if(p->out1->id==0)
p->out1->id = id++;
else
flag = 0;
fprintf(file, "\t%d -> %d [label = <ε>]\n", p->id, p->out1->id);
what = p->out1;
if(flag) staStk.push(what);
}
}
} for(i=1; i<id; i++){
fprintf(file, "\t%d [shape = circle", i);
if(circle[i])
fputs(", peripheries = 2", file);
fprintf(file, "]\n");
} fputs("}", file);
fclose(file); char cmd[108];
sprintf(cmd, "dot %s -O -Tpdf", myfile);
if(system(cmd)==0){
printf("成功生成pdf图像!\n");
//printf("Linux用户可以使用evince file.pdf &命令打开~\n");
}
else
printf("悲剧!生成pdf图像时出现错误..\n");
}
private:
State* head;
State* tail;
}; NFA post2nfa(const string& postExpr){
stack<NFA> nfaStk;
NFA e1, e2, e;
int i, len=postExpr.length();
for(i=0; i<len; i++){
switch(postExpr[i]){
case '.':
e2 = nfaStk.top();
nfaStk.pop();
e1 = nfaStk.top();
nfaStk.pop();
e1.doCat(e2);
nfaStk.push(e1);
break;
case '|':
e2 = nfaStk.top();
nfaStk.pop();
e1 = nfaStk.top();
nfaStk.pop();
e1.doUnion(e2);
nfaStk.push(e1);
break;
case '*':
e = nfaStk.top();
nfaStk.pop();
e.doStar();
nfaStk.push(e);
break;
default://
NFA alpha(postExpr[i]);
nfaStk.push(alpha);
}
}
e = nfaStk.top();
nfaStk.pop();
if(!nfaStk.empty())
throw runtime_error("未知错误");
return e;
} int main(){
string re;
while(true){
cout << "请输入一个正则表达式:\n";
cin >> re;
string postExpr = re2post(re);
cout << "postExpr is : " << postExpr << endl;
NFA nfa = post2nfa(postExpr);
nfa.nfa2graph(re);
cout << "继续吗?(y/n)\n" << endl;
char c;
cin >> c;
while(c!='y' && c!='n'){
cout << "请输入'y'或'n'.\n";
c=getchar();
}
if(c=='n')
break;
}
cout << "Bye~\n";
return 0;
}
编译原理之正则表达式转NFA的更多相关文章
- 正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——3 计算4个函数
整个引擎代码在github上,地址为:https://github.com/sun2043430/RegularExpression_Engine.git nullable, firstpos, la ...
- 编译原理-NFA构造DFA
本题摘自北邮的编译原理与技术. 首先,根据此图构造状态转换表 表中第一列第一行表示从第一个符号B通过任意个空转换能到达的节点,Ia表示由此行的状态数组({B,5,1}可以看作0状态)经过一个a可以到达 ...
- 编译原理--NFA/DFA
现成的, 讲义: https://www.cnblogs.com/AndyEvans/p/10240790.html https://www.cnblogs.com/AndyEvans/p/10241 ...
- 编译原理-词法分析05-正则表达式到DFA-01
编译原理-词法分析05-正则表达式到DFA 要经历 正则表达式 --> NFA --> DFA 的过程. 0. 术语 Thompson构造Thompson Construction 利用ε ...
- Compiler Theory(编译原理)、词法/语法/AST/中间代码优化在Webshell检测上的应用
catalog . 引论 . 构建一个编译器的相关科学 . 程序设计语言基础 . 一个简单的语法制导翻译器 . 简单表达式的翻译器(源代码示例) . 词法分析 . 生成中间代码 . 词法分析器的实现 ...
- Stanford公开课《编译原理》学习笔记(1~4课)
目录 一. 编译的基本流程 二. Lexical Analysis(词法分析阶段) 2.1 Lexical Specification(分词原则) 2.2 Finite Automata (典型分词算 ...
- 编译原理_P1004
龙书相关知识点总结 //*************************引论***********************************// 1. 编译器(compiler):从一中语言( ...
- 跟vczh看实例学编译原理——三:Tinymoe与无歧义语法分析
文章中引用的代码均来自https://github.com/vczh/tinymoe. 看了前面的三篇文章,大家应该基本对Tinymoe的代码有一个初步的感觉了.在正确分析"print ...
- 跟vczh看实例学编译原理——二:实现Tinymoe的词法分析
文章中引用的代码均来自https://github.com/vczh/tinymoe. 实现Tinymoe的第一步自然是一个词法分析器.词法分析其所作的事情很简单,就是把一份代码分割成若干个tok ...
随机推荐
- 使用traefik作为kubernetes的ingress
目录 说明 部署 创建一个独立的命名空间 配置rbac授权 配置secret 创建一个configmap用于存放traefik的配置文件 配置traefik的deployment文件 配置服务 通过p ...
- GO_01:Mac之Go语言Idea环境配置
声明:本人所使用的是Mac Pro 安装开始 1. 首先将 GO 基础组件安装好,就好似 java 中的 jdk.当然,安装的时候需要到官网去下载,这一步难倒了好多无法FQ的同学们,故这里我将我这边下 ...
- Hadoop基础-Apache Avro串行化的与反串行化
Hadoop基础-Apache Avro串行化的与反串行化 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Apache Avro简介 1>.Apache Avro的来源 ...
- 数据分析与展示---Matplotlib入门
简介: 一:Matplotlib库的介绍 (一)简单使用 二:区域划分subplot 三:plot函数 四:pyplot的中文显示 (一)方法一:修改rcParams参数 (二)方法二(推荐),在有中 ...
- 2015/12/12 考了PAT,又回来玩Python了。
上次写飞机大战的坑还没填完,然后就有好长时间没有更新博客了.可能大家在疑惑我在干什么... 其实不是有意暂停博客更新的,十一月学校里有两个考试要准备,然后有好多实验要做.平时的课余时间本来就不是很多了 ...
- alertdialog 设置最大高度
设置最大高度,有很多方法,我个人比较喜欢的是下面这种方式 ,这里的view即添加到 view.addOnLayoutChangeListener(new View.OnLayoutChangeList ...
- sparse representation 与sparse coding 的区别的观点
@G_Auss: 一直觉得以稀疏为目标的无监督学习没有道理.稀疏表示是生物神经系统的一个特性,但它究竟只是神经系统完成任务的副产物,还是一个优化目标,没有相关理论,这里有推理漏洞.实际上,稀疏目标只能 ...
- 远程连接工具PuTTY和MTPuTTY
PuTTY是一个Telnet.SSH.rlogin.纯TCP以及串行接口连接软件 官网 http://www.chiark.greenend.org.uk/~sgtatham/putty/ putty ...
- Linux IO调度算法
Linux IO调度算法 操作系统的调度 CPU调度 CPU scheduler IO调度 IO scheduler IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移 ...
- Python文件操作-文件的增删改查
需求:对文件进行增删改查 由于时间原因,本次代码没有增加任何注释,如有疑问,请联系编辑者:闫龙 其实我也是醉了,看着这些个代码,我脑袋也特么大了,没办法,大神说了,不让用新知识,只可以使用学过的,所以 ...