如何在词法分析器生成器中高效实现最长匹配?

8
我想学习如何编写像flex这样的词法分析器生成器。我一直在阅读“编译原理:技术、原理和工具”(即“龙书”),对flex的工作原理有了基本的了解。
我的初始方法是:用户将提供一个正则表达式的哈希映射,将一个正则表达式映射到一个标记枚举。程序将按照给定顺序循环遍历每个正则表达式,并查看它们是否与字符串的开头匹配(我可以在每个正则表达式的开头添加^来实现此目的)。如果匹配成功,我可以将该正则表达式的标记添加到程序的标记列表中。
我的第一个问题是,这是最有效的方法吗?目前我必须循环遍历每个正则表达式,但从理论上讲,我可以从组合所有正则表达式中构造一个DFA,并更有效地进行步进。然而,创建这个DFA会有一些开销。
我的第二个问题是,我该如何实现最长匹配字符串的选择器,就像flex一样?也就是说,我想将ifa匹配为标识符,而不是关键字if后面跟着字母a。我没有看到任何有效的使用正则表达式的方法。我想我必须遍历所有的正则表达式,尝试匹配它们,如果有多个匹配结果,就选择最长的一个。然而,如果我将正则表达式转换为DFA(也就是我的自己的DFA数据结构),那么我可以继续在字符上进行步进,直到DFA上没有更多的可能性转移边。此时,我可以将我通过接受状态的最后一次经过作为一个标记的实际匹配,因为这应该是最长的匹配。
我的两个问题都指向编写自己的从正则表达式到DFA的翻译器。这是必需的吗,还是我仍然可以使用普通的正则表达式(由标准库实现),并获得最长的匹配?
编辑:我没有提到我正在使用哪个正则表达式引擎,因为我想要一个通用的答案,但我正在使用Rust的正则表达式库:http://static.rust-lang.org/doc/master/regex/index.html

你使用的正则表达式引擎是哪个?POSIX 引擎遵循最左最长规则,类似 Perl 的引擎通常使用最左匹配(因此,您可以通过在正则表达式中首先放置较长的子匹配项来确保它是最长的)。 - Tim Pietzcker
1个回答

10

时间上,将所有的正则表达式编译成一个能够并行匹配所有模式的自动机要更有效率。然而,这可能会导致空间使用量大幅增加(DFA相对于模式大小可以具有指数级别的状态数),因此值得研究是否会造成影响。

通常,实现最大匹配(尽可能匹配最长字符串)的方式是按照正常方式运行匹配自动机。跟踪找到的最后一次匹配的索引。当自动机进入死状态并停止时,您可以输出从标记点之前的令牌开头到匹配点的子字符串,然后在输入序列中跳回匹配完成后的位置。这可以相当高效地完成,几乎没有任何减速。

如果有帮助的话,这里有一些我教授的编译器课程讲义,探讨了扫描技术。

希望这有所帮助!


1
但是,如果您使用正则表达式“1 |(1 * 2)”来匹配字符串“1111111 ..... 1”,DFA将在不进入死状态的情况下运行到输入的末尾。因此,它每次都会读取到输入的末尾并跳回。运行时间为O(n ^ 2)。是否可以使用DFA在O(n)时间内完成字符串匹配? - zzh1996
你说得完全正确,上次我教编译器课程时,我实际上把这个作为练习题了!不过我现在脑海中并不确定如何使其达到最坏情况的效率,但实际上这是一种非常快速的方法,很少会退化。 - templatetypedef
当我尝试检索与此答案链接的讲座幻灯片时,我收到了“访问被拒绝”的错误提示。 - Andreas Abel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接