1、实验目的与内容

输入:一个正则表达式(例如“(a|b)*abb”)

输出:对应的一个NFA的mermaid语法

graph LR
0((0))-->|a| 1((1))
1((1))-->|$| 5((5))
2((2))-->|b| 3((3))
3((3))-->|$| 5((5))
4((4))-->|$| 2((2))
4((4))-->|$| 0((0))
5((5))-->|$| 4((4))
5((5))-->|$| 7((7))
6((BEG))-->|$| 4((4))
6((BEG))-->|$| 7((7))
7((7))-->|$| 8((8))
8((8))-->|a| 9((9))
9((9))-->|$| 10((10))
10((10))-->|b| 11((11))
11((11))-->|$| 12((12))
12((12))-->|b| 13((END))

对应的NFA:

a
$
b
$
$
$
$
$
$
$
$
a
$
b
$
b
0
1
2
3
4
5
BEG
7
8
9
10
11
12
END

2、程序总体设计思路和框架

总程序分为两份代码,第一份代码将一个正则表达式(regular expression)转化为后缀形式,第二份代码把得出的后缀式转化为NFA并且输出对应NFA图形的mermaid语法。

如下图:

ReExToNFA.cpp

ReExToNFA2.cpp

一个正则表达式中有四种运算符号:闭包运算符(*)、连接符(.)、或运算符(|)和括号,运算符的优先级依次递减,在中缀形式中连接符通常被省略,所以构建出的后缀式要补上连接符。得到后缀式之后,使用MYT算法根据后缀式构建NFA。

这里简单讲一下中缀转后缀的算法流程:

yes
no
yes
no
建个栈
遍历中缀式,取字符ch
ch是操作数(operand)
输出
ch是操作符(operator)
栈空?
优先:ch>top?
ch入栈
pop,输出

3、主要的数据结构和流程描述

结构体NFA的定义如下:

struct NFA
{
    set<int> Q;                  //状态集合
    set<char> sigma;             //输入字符集合
    vector<int> delta[128][128]; //状态转化函数,一个状态对应的一个输入可能有两个以上的转移,使用数组不行!!!
    int q0;                      //开始状态
    set<int> F;                  //接收状态的集合
    NFA()                        //构造函数
    {
        //memset(delta, -1, sizeof(delta));//-1表示为此状态没有此输入。
    }
} NFAinstance;

注释中已经有了详尽的说明。

4、测试结果与说明

测试结果以及输出见第2部分。

5、实验收获与反思

本实验的关键点在于:

1、中缀式到后缀式的转化。

2、MYT算法将后缀式转化为NFA。

解决了这两个问题,代码就能顺利写出了。

附录

ReExToNFA.cpp

ReExToNFA.cpp2

参考资料

[1] 中缀表达式转换为后缀表达式

[2] 《编译器设计原理》,西安电子科技大学出版社,谌志群,王荣波,黄孝喜

[3] 基于MYT算法和自顶向下算法从正则表达式到NFA

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐