实现正则表达式很困难。以有趣的方式努力,让我想分享经验教训。因此这个系列,简称QRS。
关注我的Quamina开源模式匹配项目的人会注意到最近没有更新和对话。这是因为,根据问题#66 ,我正在努力添加相当完整的正则表达式匹配。
个人笔记
事实证明这很难。要么这是我多年来必须破解的最难的难题,要么我的高龄正在削弱我的技能。我需要几周时间才能进行第一个增量发布。任何;从这项工作中学到的经验仍然让我感到新鲜和迷人,并让我有分享的冲动。
我希望只要我的精神还存在,我就能保留这种冲动。其实我希望能保留做软件的能力。由于种种原因,近年来我的个人压力很大。从我的成人责任中抽出时间来处理可执行的抽象一直是我理智的支柱。
无论如何,通常当我编码时我会在博客上写相关内容,但到目前为止我还没有,因为工作尚未完成。然后我意识到它太大了,解决了太多不同的问题,不能只是一个整体,因此这个迷你系列。
[不知道什么是正则表达式的读者现在可能应该关闭此选项卡。不要感到内疚,不是全职计算专业人员的人不应该知道更少的关心。]
[注释:在本系列中我会说“Regexp”或者只是“RE”。]
我知道我至少要写三篇文章:
-
解析 RE 语法。 (已经写好了!)
-
代表已解析的 RE。
-
为 RE 实现基于 UTF-8 的自动机。
目前,我认为工作中最难的部分是#1,解析。 (也许那是因为我还没有真正深入研究#3。)如果最终系列只有三个部分,我会感到惊讶。
现在,介绍性材料。
哪些正则表达式?
它们有很多种口味。我正在实现的是I-Regexp, RFC 9485 。细心的读者会注意到我共同编辑了该 RFC,并且我很高兴地承认存在偏见。
I-Regexp 基本上是XSD 正则表达式的子集(选择子集是因为它们有一个很好的干净的不可变规范),它很像好的 ol’PCRE(Perl 兼容的正则表达式)。除了:
-
它们的设计假设它们仅用于匹配字符串并返回“是”或“否”答案。
-
它们是锚定的,也就是说(与 PCRE 不同)它们都被假定以
^
开头并以$
结尾。 -
它们省略了流行的单字符转义符,例如
\w
和\S
因为它们在 Unicode 上下文中是粗略的。 -
他们没有捕获组或反向引用。
-
它们不支持字符类减法,例如
[azmp]
。
如果您感兴趣的是问“字段值匹配吗?”,我会声称他们达到了非常有用的 80/20 分。这当然是夸米纳感兴趣的事情。
项目策略
我完全不会尝试以大爆炸的方式完成这一切。我现在有了一个可靠的 RE 解析器(这很难!),它可以识别十种不同的 RE 功能,范围从.
到(a+b*c?).|d[ef]{3,9}\?\P{Lu}
中的所有内容。我计划一次推出一项功能,但拒绝接受使用我尚未实现的功能的 RE。
再次取消反斜杠
去看看Unbackslash 。 Tl;dr:在处理 JSON 的 Go 软件中处理标准 RE 转义字符\
是非常痛苦的。因为 Go 和 JSON 都使用\
进行转义,并且您的单元测试最终会填满\\
和\\\\\\\\
并变得非常难以阅读。因此,在发布该博客文章并在 Mastodon 上进行民意调查后, ~
是新的\
。这样上面的 RE 就变成(a+b*c?).|d[ef]{3,9}~?~P{Lu}
。
你可以不喜欢它。但我请求你不要按下那个让我下地狱的大按钮,直到你尝试为你希望 Quamina 处理的 RE 编写一些单元测试。
回到策略:第一个功能将是那个可爱的小点运算符。因此……
测验
只是为了好玩,这是一个智力挑战。假设您正在构建一个一次字节状态机来处理 UTF-8 文本。大概需要多少个州才能匹配.
,即任何单个 Unicode 代码点?我所说的“匹配”是指拒绝任何不匹配的字节序列,当它匹配时,消耗足够的字节以使您位于.
并准备好开始匹配接下来的内容。
我想我已经找到了正确的答案。这让我感到惊讶,所以我仍在进行理智检查,但我认为我是对的。我确信问题并不像看起来那么简单。
原文: https://www.tbray.org/ongoing/When/202x/2024/12/12/Quamina-Regular-Expression-Series