Regex 如何在lexer生成器中有效地实现最长匹配?

Regex 如何在lexer生成器中有效地实现最长匹配?,regex,language-agnostic,programming-languages,lexical-analysis,dfa,Regex,Language Agnostic,Programming Languages,Lexical Analysis,Dfa,我对学习如何编写像flex这样的lexer生成器很感兴趣。我一直在读《编译器:原理、技术和工具》(龙之书),我对flex的工作原理有了基本的了解 我的初始方法是:用户将提供一个正则表达式的哈希映射,将正则表达式映射到令牌枚举。程序将按照给定的顺序逐个循环遍历正则表达式,查看它们是否匹配字符串的开头(我可以在每个正则表达式的开头添加一个^,以实现这一点)。如果他们这样做,我可以将该正则表达式的令牌添加到程序的令牌列表中 我的第一个问题是,这是最有效的方法吗?目前,我必须循环遍历每个正则表达式,但理

我对学习如何编写像flex这样的lexer生成器很感兴趣。我一直在读《编译器:原理、技术和工具》(龙之书),我对flex的工作原理有了基本的了解

我的初始方法是:用户将提供一个正则表达式的哈希映射,将正则表达式映射到令牌枚举。程序将按照给定的顺序逐个循环遍历正则表达式,查看它们是否匹配字符串的开头(我可以在每个正则表达式的开头添加一个
^
,以实现这一点)。如果他们这样做,我可以将该正则表达式的令牌添加到程序的令牌列表中

我的第一个问题是,这是最有效的方法吗?目前,我必须循环遍历每个正则表达式,但理论上,我可以从所有合并的正则表达式构建DFA,并更有效地逐步遍历。但是,创建此DFA会有一些开销

我的第二个问题是,我将如何实现最长的匹配字符串绑定断路器,就像flex一样?i、 e,我想匹配
ifa
作为标识符,而不是关键字
if
后跟字母
a
。我看不到任何有效的方法来处理正则表达式。我想我必须循环遍历所有正则表达式,尝试匹配它们,如果有多个匹配,则取最长的结果。但是,如果我将正则表达式转换为DFA(即我自己的DFA数据结构),那么我可以继续单步遍历字符,直到DFA上不再有可能的转换边。在这一点上,我可以将最后一次通过接受状态作为令牌的实际匹配,因为这应该是最长的匹配

我的两个问题都指向编写我自己的从正则表达式到DFA的翻译程序。这是必需的,或者我仍然可以使用普通正则表达式(由标准库实现)高效地完成这项工作,并且仍然可以获得最长的匹配


编辑:我不使用我正在使用的正则表达式引擎,因为我想要一个通用的答案,但我使用的是Rust的正则表达式库:

从时间上看,将所有正则表达式编译成一个单独的自动机,并行匹配所有模式,效率更高。不过,这可能会大大增加空间使用量(相对于模式大小,DFA可以具有指数级的多个状态),因此值得研究这是否会造成伤害

通常,实现最大munch(匹配可以匹配的最长字符串)的方法是正常运行匹配自动机。跟踪您找到的最后一个匹配项的索引。当自动机进入死状态并停止时,您可以从令牌的开始向上通过匹配点输出子字符串,然后在匹配完成后按输入序列跳回该点。这可以非常有效地完成,而且根本不会减速

如果有帮助的话,这将探索扫描技术


希望这有帮助

您使用的是哪个正则表达式引擎?POSIX引擎遵循最左边最长的规则,类似Perl的引擎通常使用最左边的第一个匹配(因此可以通过将较长的子匹配放在正则表达式的第一位来确保它是最长的)。但是如果使用正则表达式“1 |(1*2)”来匹配字符串“1111111…..1”,DFA将运行到输入的末尾,而不会进入死状态。因此,它读取到输入的末尾,每次都跳回。运行时间为O(n^2)。使用DFA可以在O(n)时间内完成字符串匹配吗?你完全正确,上一次我教编译器课程时,我把它作为练习!虽然在实践中,这是一种非常快速的方法,而且很少退化,但我不知道如何使这种最坏的情况变得有效。当我试图检索此答案中链接的演讲幻灯片时,我会被“禁止访问”。