终于是时候写关于我们构建的正则发动机的引擎了。
正如我们已经看到的,解析器由于解析正则表达式而产生AST。
现在,引擎必须检查给定的字符串是否匹配等级。
为此,发动机使用正则表达式的AST表示,提取正则表达式的基本特征/元素,并尝试找到测试字符串是否具有它们。 /p>
引擎
引擎再次是Python类,具有基本方法匹配,它是包含引擎和所有所需辅助方法的封闭。
匹配签名:
def match(self, re: str, string: str)
匹配语义
我们将向匹配方法提供的语义是:
- 它在字符串中的第一次出现时返回
- 如果没有匹配,它将再次尝试将输入字符串移动到右侧(即将字符串一个字符移动到右侧),直到到达字符串的末端。
也就是说,如果输入字符串为abac
,而正则正则是c
,则在第一次尝试abac
the the t the t Match c
上,所以再次尝试,但是这次将abac
转移到右边,所以尝试匹配bac
带有c
,导致另一个失败,因此它再次尝试使用ac
和c
(失败),最后,它与c
一起尝试c
,从而导致匹配,从而返回true。
到目前为止的
match
功能。
如何匹配
match_group
是发动机的最重要方法。
它通过迭代AST节点的孩子而起作用,并在找到GroupNode
或有一些差异时递归地调用自己。
如果找到了OrNode
,它将检查左子女(位置0中的一个)是否匹配,如果没有,则尝试正确的一个。
当找到叶节点(ElementNode
,â€)时,它会尝试匹配。
每场比赛后,匹配字符的str_i
(字符串索引)增加了。
字符串索引设置为位置0(或一般要考虑更多的位置),并且是一个全局变量,但是匹配函数的本地变量。
递归呼叫的机制使您可以轻松地管理树上的弦和字符串中的点。它还使您可以将每个组视为一个单独的实体,从而更轻松地遵循代码执行。
显然,这不是整个引擎,因为如第一篇文章所述,我们将需要一个回溯系统来确保探索与字符串匹配的所有可能路径。
一个小问题
实际上,叶节点和群体的语义是默认情况下将它们与最大的角色相匹配,尽管大多数情况下这很方便,但在某些边缘情况下,这不会导致一个正确答案。
让我们检查这个示例:
如果您有正则a+ab
和一个测试字符串aab
:
- 由此产生的AST将由一个
GroupNode
组成,有三个孩子,所有叶子 - 第一个要匹配的
a
[1, +â[times - 第二次匹配的第二个
a
- 最终的
b
可以完全匹配一次。
在执行我们的匹配函数期间,这将发生:
-
match_group
被称为并开始迭代其子女 - 第一个
a
元素尽可能匹配,所以两次aa
- 然后,引擎试图匹配第二个
a
元素,但它失败了,因为现在唯一的左字符是b
- false将被返回,但我们都知道正确答案是正确的。
这是因为我们对匹配函数的语义给出了。我们希望系统要做的是尝试另一种方法:
- 由于第一个元素可以接受1个或更多的字母
a
,它与其中的两个相匹配,所以我们想告诉它 - 嘿,请尝试一次尝试一下<< /li> - 在这种情况下,第一个
a
将匹配一次,留出足够的空间适合第二个a
元素,然后还有b
元素,我们的第三和最后一个 - 这个过程有效,因此答案是正确的。
此过程称为回溯,将成为下一篇文章的主题,现在,让我们忽略问题。
结论
总的来说,我们涵盖了我们所需的几乎所有内容以解释比赛方法的行为。
我们代码的骨架看起来像这样
match
函数的结构。
在下一篇文章中,我们将介绍回溯有趣的主题,并将完成我们的正则引擎。