我已经为Mark Byers点了一个+1,但据我记得,这篇论文并没有详细说明正则表达式匹配的工作原理,只是解释了为什么一种算法很差而另一种算法好得多。也许链接中有相关内容?
我将专注于好的方法——创建有限状态自动机。如果你限制自己只使用没有最小化的确定性自动机,那么这并不太困难。
我将(非常快速地)描述
现代编译器设计中采用的方法。
想象你有以下正则表达式...
a (b c)* d
这些字母代表要匹配的文字。星号 * 通常表示零个或多个重复。基本思想是根据点规则推导出状态。我们将零状态视为尚未匹配任何内容的状态,因此点放在最前面...
0 : .a (b c)* d
唯一可能的匹配是 'a',因此我们推导出的下一个状态是...
1 : a.(b c)* d
现在我们有两种可能性 - 匹配'b'(如果至少重复了'bc')或匹配'd'。注意 - 我们基本上在进行二元图搜索(深度优先或广度优先或其他),但我们是在搜索时发现二元图的。假设采用广度优先策略,我们需要将其中一个情况排队以供稍后考虑,但我将在此忽略此问题。无论如何,我们已经发现了两个新状态...
2 : a (b.c)* d
3 : a (b c)* d.
第三状态是一个结束状态(可能不止一个)。对于第二状态,我们只能匹配“c”,但在点后面的位置需要小心处理。我们得到了“a。(b c)* d” - 这与第一状态相同,因此我们不需要新的状态。
如果我没记错,在现代编译器设计中的方法是当你遇到运算符时翻译规则,以简化点的处理。状态1将被转换为...
1 : a.b c (b c)* d
a.d
换句话说,你的下一个选择是匹配第一个重复项或跳过重复项。从这个状态开始的下一个状态等价于状态2和3。这种方法的优点是可以丢弃所有过去的匹配('.'之前的所有内容)因为只关心未来的匹配。通常这会产生一个较小的状态模型(但不一定是最小的)。
编辑:如果您丢弃已匹配的详细信息,则状态描述是表示从此处开始可能发生的字符串集合的表示形式。
就抽象代数而言,这是一种集合闭包。代数基本上是具有一个(或多个)运算符的集合。我们的集合是状态描述的集合,我们的运算符是转换(字符匹配)。闭合集是应用任何运算符到集合中的任何成员总是产生另一个在集合中的成员的集合。集合的闭包是最小的较大集合,它是封闭的。因此,基本上,从明显的起始状态开始,我们正在构建相对于我们的转换运算符集合封闭的最小状态集合 - 可达状态的最小集合。
这里的“最小”是指闭包过程 - 可能存在一个更小的等效自动机,通常称为最小自动机。
有了这个基本想法,就不太难说“如果我有两个表示两组字符串集合的状态机,如何推导出第三个表示并集”(或交集,或集合差异...)。您的状态表示将是来自每个输入自动机的当前状态(或一组当前状态),以及可能的其他细节,而不是点规则。
如果您的常规语法变得复杂,可以进行最小化。基本思想相对简单。将所有状态分组为一个等价类或“块”。然后,反复测试是否需要拆分块(状态实际上不是等效的),并针对特定转换类型进行处理。如果特定块中的所有状态都可以接受相同字符的匹配,并在此过程中到达相同的下一块,则它们是等效的。
Hopcroft算法是处理这种基本思想的有效方法。
关于最小化的一个特别有趣之处是,每个确定性有限自动机都具有精确的一种最小形式。此外,Hopcrofts算法将生成相同的表示形式,即使它从哪个大型案例的哪个表示开始。也就是说,这是一种“规范”表示,可用于导出哈希或进行任意但一致的排序。这意味着您可以使用最小自动机作为容器键。
以上内容可能在定义方面有些粗略,因此在使用这些术语之前,请确保自己查找任何术语,但是运气好的话,这会给您提供一个基本思想的公平快速介绍。
顺便说一句-浏览
Dick Grunes site的其余部分-他有一本免费的解析技术PDF书。《现代编译器设计》第一版在我看来相当不错,但正如您所看到的,第二版即将出版。
(foo|bar)(baz)+
应该翻译为要么是 "foo" 或者 "bar",然后是一个或多个 "baz"
。Pyparsing (http://pyparsing.wikispaces.com/Documentation) 可以帮助实现这个项目。 - Katriel