标签:
w NFAModel(); kleenStarNFA.AddStates(innerNFA.States); kleenStarNFA.AddState(newTail); kleenStarNFA.EntryEdge = entry; kleenStarNFA.TailState = newTail; return kleenStarNFA; } |
代码应当是相当直观的,它就是重复上面画图的逻辑,先将克林闭包内部的表达式转换成NFA,再创建一些辅助的外围状态和相应的状态转换。
有了从正则表达式转换到NFA的算法之后,我们还需要NFA到DFA的转换。这个转换算法称作“子集构造”。我们前面说过NFA遇到同一状态发出带有同一符号的不同的边时,能自动猜测转移到哪一边。而子集构造的思想就是不猜测NFA会转移到哪个状态,而是假设NFA能同时处于所有可能的状态。比如,我们重新考虑前面最开始展示的NFA。一开始,这个NFA的初始状态就包含两个ε转换,我们假设NFA能同时处于所有这种ε转换的目标状态上,也就是说它的初始状态其实是三个状态的集合:
我们称这三个状态为初始状态的ε-闭包(ε-closure)。接下来,如果输入了字符a,那么NFA就可以从当前状态的ε-闭包内任何状态开始,通过字符a的边进行状态转换。这时,我们就得到NFA的下一个状态:
接下来再次输入字符a,我们也可以从当前状态集合出发,找到下一个状态集合:
如果字符串到此为止,这时NFA的状态集合中包含了一个接受状态,因此NFA决定接受字符串“aa”。也就是说,这次没有用到猜测能力,就成功地解析了aa这个字符串。这样我们就了解到,一定存在一个DFA,它的每个状态都是NFA状态的的一个子集。下面我简单转述一下虎书中有关子集构造算法。令edge(s, c)表示从状态s沿着标有字符c的边可以达到的所有NFA状态的集合。对状态集合S,closure(S)是从S中的状态出发,无需接受任何字符,只沿ε边就可以达到的状态组成的集合,可以用迭代法来求出:
接下来我们定义输入一个字符之后的动作规则,从NFA状态集合d中的状态出发,输入符号c,所能达到的NFA的新的状态集合记作DFAedge(d, c),它定义为
最后,假设构成语言的字符集是Σ,构造出整个DFA的算法是:
以上代码在理解了子集构造的基本原理之后很容易就能够转换成代码。VBF.Compilers中的NFA->DFA转换代码比较长,我就不贴在这里了,有兴趣的可以到github上自行下载。
将正则表达式通过NFA最后转化为DFA之后,如何进行真正的字符串扫描工作就是水到渠成的工作了。我们下一篇将介绍具体的做法,以及针对Unicode字符集的处理方式。下一篇我还会介绍VBF.Compilers.Scanners类库的基本用法。如果大家不想自己实现整套算法,那么下回就可以参考我的文章,用VBF库制造出任意的词法分析器来。所以,敬请期待下一篇!
此外别忘了关注我的VBF项目:https://github.com/Ninputer/VBF 和我的微博:http://weibo.com/ninputer 多谢大家支持!
标签:
原文地址:http://www.cnblogs.com/Kevin-Bruce/p/4306777.html