之所以说是“简要实现”一方面是因为算法不算高深,算法的实现也不精致,甚至连我对其的理解也不够本质。
我只不过不想在工作若干年后还是一个只会打字的程序员。学点什么东西,真正精通点什么东西才对得起喜欢
技术的自己。
附件中的代码粗略实现了《编译原理》龙书中的几个算法。包括解析正则表达式,建立NFA,然后用NFA去匹
配目标字符串;或者从NFA建立DFA,然后匹配。解析正则表达式我用了比较繁琐的方法,有词法和语法分析
过程。词法分析阶段将字符和一些操作符整理出来,语法分析阶段在建立语法树的过程中对应地建立NFA。
当然,因为语法树在这里并没有用处,所以并没有真正地建立。
从正则表达式到NFA比较简单,很多编译原理书里都提到过,如:s|t表达式对应于下面的NFA:
代码中用如下的结构描述状态和状态机中的转换:
#define E_TOK (0)
/* transition */
struct tran
{
char c;
struct state *dest;
struct tran *next;
};
struct state
{
/* a list of transitions */
struct tran *trans;
/* inc when be linked */
int ref;
};
即,每一个状态都有一个转换列表,每个转换都有一个目标状态(即该转换指向的状态)以及转换字符。
貌似通过以上方法建立出来的状态机每个状态最多只会有2个转换?
建立好NFA后,由NFA匹配目标字符串使用了一种构造子集法(《编译原理》3.7.2节):
这个算法里针对NFA的几个操作,如e-closure、move等在由NFA转换DFA时也被用到,因此代码里单独
做了封装(state_oper.c)。这个算法本质上貌似就是一次步进(step)多个状态。
至于由NFA转DFA,则是相对简单的子集构造法:
在我以前编译原理课考试的前一天晚上(你懂的)我就对这些算法颇为疑惑。在以后看各种编译
原理教材时,我始终不懂NFA是怎么转到DFA的。就算懂了操作步骤(我大学同学曾告诉我这些步骤,虽然
不知道为什么要那样做),一段时间后依然搞忘。很喜欢《编译原理》龙书里对这个算法最本质的说明:
源代码我是用GCC手工编译的,连makefile也没有。三个test_XXX.c文件分别测试几个模块。test_match.c
基本依赖除掉test外所有c文件,全部链接在一块即可。当然,就经验而言我知道是没几个人会去折腾我的这些
代码的。这些在china的领导看来对工作有个鸟用的代码读起来我自己也觉得费力,何况,我还不伦不类地用了
不知道算哪个标准的c写了这些。
你不是真想下载。对于这种代码,有BUG是必然的,你也不用在此文若干个月后问我多少行是什么意思,因为
那个时候我也忘了:D。