Wait the light to fall

在正则表达式内赋值

焉知非鱼

assignment within regex

Richard

提供一种简单的方法来命名和从 regex 中挑选信息,而不必计算括号。

我可以毫不犹豫地说,Raku(以及在其改名之前的Perl 6)已经实现了这个目标-但所有的细节都与提议的不同。

原因有二。

首先,Richard 假设了 Perl 5 的 regex 语法的一个相当直接的扩展,他提出的语法,(?$hours=..) 的命名捕捉是有意义的。相反,Raku regex 语法是一个相当新的东西,所有非字母数字字符都是潜在的元字符,因此要么被使用,要么为某一目的保留。这使得比 (?$name=regex) 更容易的语法可用于命名捕捉。

第二种情况更为深刻。Raku 的设计者们意识到,只有当重用从头开始时,regex 才能真正强大起来。而实现这一点的最好方法就是让 regex 成为一等公民。

我想在这一点上多说几句:考虑一下函数(和闭包)作为现代编程语言中一等公民的力量。Lisp 已经向我们展示了你可以用它们做什么,现在基本上每个编程语言都得到了它们。像 Perl、Ruby、Javascript 和 Python 这样的动态语言是相当早的适配者,像 C#F# 这样的现代静态类型语言也得到了它们;甚至 Java 最终也赶上了。Java 甚至没有函数,只有方法,而现在它有了闭包,你可以到处传递。

依我的愚见,将 regex 提高到一级公民的水平,并引入简洁的调用语法,给 regex 带来了类似的提升。

在以前,人们的常识是不能用 regex 来解析 XML(或其他任意嵌套的语言),因为它们不是计算机科学意义上的常规语言。Perl 5 对此有一些变通方法,但它们太笨拙和啰嗦了,我甚至没有看到它们被多次推荐,我的一般印象是,如果你使用它们,只是因为缺乏好的替代品。

在 Raku 中就不一样了:regex 中的 <subrule> 会调用另一个叫做 subrule 的 regex,所以你就有了递归(以及与 RFC 112 的讨论相关的,命名捕获)。这种递归使 regex 从常规语言进入 Chomsky Hierarchy 中的无上下文语言领域。但比起递归,命名的 regex 更允许更容易的重用、隔离测试以及其他所有一类公民赋予函数的美妙东西。它也让人们对用 regex 解析 XML 和其他语言的感情从 “你是认真的吗”? 变成了 “当然,这是最好的工具”。

调用语法 <subrule> 意味着一个命名捕获,事实证明,这已经很方便了,在现实世界的解析器中,显式的命名捕获(不与调用绑定)其实是非常罕见的。不过一个显式的语法是存在的,它就是 $<capturename>=[...]

这就带来了第二个有趣的地方。RFC 112 并不只是谈论命名捕获,而是暗示它们被直接存储到同名的变量中。

这是有问题的,原因有很多。

  • 作用域。regex 可以在程序的两个非常不同的部分被声明和使用。强制变量在作用域内,会使捕获语法成为一个作用域不必要的大的源变量(我敢说是全局的?),这是一个明显的反模式。

  • 量词。在 RFC 211 语法中,像 (?$char=.)+ 这样匹配字符串 abc 的 regex 会发生什么? $char 里有什么?sigil 意味着一个标量,所以… 也许是最后捕获的 c?然后扔掉所有其他的匹配?听起来不是很好。或者 (?@char=.) 会有一个包含所有捕获的数组,但这样一来,当写一个 regex 时,你必须知道是否有人后来想在一个量词内使用它。也不是很好。

  • 组合。将匹配项绑定到一个变量上,假设 regex 是作为一个顶层结构使用的,而不是更大事情的一部分。

  • 递归。我还需要详细说明吗?可能不需要。

现在在 Raku 中实现的解决方案更适合于一类公民 regex 的世界:对于每个 regex 匹配都有一个 Match 对象。顶级匹配对象存储在变量 $/ 中,所以访问一个命名捕获键是 $/<key>,甚至还有一个简写,$<key>。只是比最初提出的 $key 长了两个字符。

不过这个解决方案在使用在组合的情况下就会大放异彩,比如说。由于命名捕获对应于一个 regex 匹配,它也是一个 Match 对象,所以我们得出一棵匹配树(都是一样的)。或者改写为:一个 regex 匹配已经是一棵语法树。

我认为这个 RFC 是一个很好的例子,它发现了一个真正的痛点和问题,并提出了一个解决方案。这个解决方案的某些方面在语言设计过程中得以幸存,但大部分细节没有,因为语言的变化远不止于它勉强是 Perl 5 加上通过 RFC 的一些扩展。

我从2007年左右开始参与 Perl 6 项目,看着其中的一些转变;对于 regex,大部分的整合和重新设计工作已经完成。我看着这些实现变得更加强大,甚至到处帮了一点小忙。

生活在这个过程中是一种神奇的体验,就像现在的结果一样神奇。