如何在有限状态自动机中模拟后向引用,超前查找和向后查找?


26

我创建了一个简单的正则表达式词法分析器和解析器,以获取一个正则表达式并生成其解析树。对于基本的正则表达式,从此解析树创建非确定性有限状态自动机相对简单。但是,我似乎无法全神贯注于如何模拟反向引用,先行和后备。

从我在紫色龙书中读到的内容中,我了解到,要模拟一个先行,当且仅当匹配项后跟正则表达式匹配项时,才匹配正则表达式,您将创建一个不确定的有限元状态自动机,其中替换为。是否可以创建相同的确定性有限状态自动机?r s / εr/srs/ε

模拟否定的前瞻性和后瞻性怎么样?如果您将我链接到描述如何详细执行此操作的资源,我将不胜感激。



Answers:


21

首先,反向引用可以被有限自动机,因为它们允许你来形容非正规语言模拟。例如,([ab]^*)\1匹配,这甚至不是上下文无关。{www{a,b}}

在有限自动机的世界中,前瞻性和后瞻性并没有什么特别的,因为我们在这里只匹配整个输入。因此,“只检查却不消耗”的特殊语义是没有意义的。您只需串联和/或相交检查和使用表达式,然后使用生成的自动机。这个想法是在“使用”输入并将结果存储在状态中时检查前向或后向表达式。

在实现正则表达式时,您希望通过自动机运行输入并获取匹配项的开始和结束索引。这是一个非常不同的任务,因此实际上并没有构造有限自动机。您可以构建自动机,就好像使用了前向或后向表达式一样,并更改了索引存储响应。相应地进行报告。

例如,回头看。我们可以通过与隐式使用的“全部匹配”正则表达式同时执行检查正则表达式来模仿正则表达式的语义。仅从后向表达式的自动机处于最终状态的状态中,才能输入受保护表达式的自动机。例如,正则表达式/(?=c)[ab]+/(假设是完整字母)-请注意,它会转换为正则表达式{ a b c } c { a b } + { a b c }{a,b,c} -可以与{a,b,c}c{a,b}+{a,b,c}

在此处输入图片说明
[ 来源 ]

而你将不得不

  • 每当您输入q 2(最初或从q 2)将当前索引存储为并且iq2q2
  • 每当您点击(离开)q 2时,报告从到当前索引(1)的(最大)匹配。i1q2

注意自动机的左侧部分如何为规范自动机的并行自动机[abc]*c(迭代),分别。

ijij

请注意,不确定性是固有的:主自动机和超前/后退自动机可能会重叠,因此您必须存储它们之间的所有转换,以便以后报告匹配的转换或回溯。


11

关于实现正则表达式引擎背后的实用问题的权威参考是Russ Cox撰写的三篇博客文章系列。如此处所述,由于反向引用使您的语言变得不规则,因此可以使用backtracking来实现它们。

像正则表达式模式匹配引擎的许多功能一样,前瞻性和后顾之忧不太适合确定字符串是否为语言成员的范式。通常使用正则表达式来搜索较大字符串中的子字符串。“匹配”是属于该语言的子字符串,返回值是较大字符串中子字符串的起点和终点。

先行和后退的目的不是引入匹配非常规语言的功能,而是调整引擎报告匹配子字符串的起点和终点的位置。

我依靠http://www.regular-expressions.info/lookaround.html上的描述。支持此功能的正则表达式引擎(Perl,TCL,Python,Ruby等)似乎都是基于回溯的(即,它们支持的语言集比常规语言要大得多)。他们似乎将此功能实现为回溯的相对“简单”扩展,而不是尝试构造真正的有限自动机来执行任务。

积极向前

对于语法正向前查找(?=正则表达式)。因此,例如,q(?=u)相匹配q,只有当它后面u,但不匹配u。我想他们会通过回溯的方式来实现这一目标。在正向超前前为表达式创建FSM。当匹配时,记住它的结束位置并开始一个新的FSM,该FSM表示正向超前的表达式。如果匹配,则您有一个“匹配”,但匹配在正超前匹配开始的位置之前“结束”。

唯一没有回溯就很难的部分是,您需要记住输入中超前开始的点,并在完成匹配后将输入磁带移回到该位置。

负前瞻

否定超前的语法是(?!regex)。因此,例如,仅在不带时q(?!u)匹配。这可以是后面跟一些其他字符,也可以是字符串的结尾。我想这是通过为先行表达式创建NFA来实现的,然后仅在NFA无法匹配后续字符串时才成功。quqq

如果要在不依赖回溯的情况下执行此操作,则可以否定超前表达的NFA,然后以与对待正向超前相同的方式对其进行处理。

正向后看

(?<=)(?=q)uuqqnnn

通过将“以regex结尾的字符串”与lookexhind运算符之前的regex的任何部分相交,您也许可以实现此功能而无需回溯。但是,这将很棘手,因为向后的正则表达式可能需要向后看,而不是当前输入的开始。

负向后看

对于语法负回顾后(?<!正则表达式)。因此,例如,(?<!q)u匹配u,但前提是它前面没有q。因此它将与uin umbrellauin相匹配doubt,但与uin 不匹配quick。再次,这似乎是通过计算regex的长度,备份那么多字符,使用regex测试匹配来完成的,但是如果lookbehind匹配,整个匹配失败。

通过排除正则表达式,然后执行与进行正向后看相同的操作,您可以实现此功能而无需回溯。


5

至少对于反向引用,这是不可能的。例如,正则表达式(.*)\1表示不规则的语言。这意味着不可能创建一个可以识别这种语言的有限自动机(无论是否具有确定性)。如果您想正式证明这一点,则可以使用泵引理


4

我一直在自己研究这个问题,您应该可以使用Alternate Finite Automaton实现超前。遇到前瞻时,您将不确定地运行前瞻和表达式的其余部分,仅当两个路径接受时才接受。您可以以合理的放大率将AFA转换为NFA(从而转换为DFA),尽管我尚未验证明显的构造与捕获组的配合是否很好。

在不回溯的情况下,完全有可能实现固定宽度的向后查找。令n为宽度。从NFA中开始向后看的点开始,您将向后看的状态分开,以使进入后向的每条路径都以n个字符的值结尾,这些状态进入后向。然后,将前瞻性添加到这些状态的开头(如果需要,立即将子图从AFA编译为NFA)。

正如其他人提到的那样,反向引用不是常规的,因此它们不能通过有限的自动机来实现。实际上,它们是NP完整的。在我正在研究的实现中,快速的是/否匹配是最重要的,因此我选择根本不实现反向引用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.