regex < .ident>perl 6语法中的函数/捕获

vaj7vani  于 2023-10-22  发布在  Perl
关注(0)|答案(2)|浏览(103)

在阅读perl6(https://github.com/supernovus/exemel/blob/master/lib/XML/Grammar.pm6)的XML语法时,我在理解下面的标记时遇到了一些困难。

token pident {
  <!before \d> [ \d+ <.ident>* || <.ident>+ ]+ % '-'
}

更具体<.ident>地说,ident没有其他定义,所以我假设它是一个保留术语。虽然我在perl6.org上找不到合适的定义。有人知道这是什么意思吗?

mepcadol

mepcadol1#

    • 这个答案的其余部分是为那些想要了解更多关于一般内置规则和/或特别深入ident的人准备的。

<.ident>功能/捕获
由于.<.ident>只匹配,它不捕获[1]。对于这个答案的其余部分,我通常会省略.,因为除了捕获方面之外,它对规则的含义没有任何影响。
就像你可以在编程语言中调用(也就是“call”)另一个函数的声明中的一个函数一样,你也可以在另一个规则的声明中调用一个规则/token/regex/方法(以下我通常只使用术语“规则”)。<foo>是用于调用名为foo的规则的语法;所以<ident>调用一个名为ident的(方法)。
在我写这篇文章的时候,XML::Grammar语法本身并没有定义/声明一个名为ident的规则。这意味着调用最终被分派到一个具有该名称的 * 内置 * 声明。
内置的ident规则的作用与声明为:

token ident {
    [ <alpha> ]
    [ <alnum> ]*
}

官方预定义字符类文档应该提供<alpha><alnum>的精确定义。或者,有关的细节也将在本答复的后面部分提供。
底线是ident匹配一个或多个“字母数字”字符串,除了第一个字符不能是“数字”。
因此,abcdef123都匹配,而123abc不匹配。

剩下的答案

对于那些有兴趣了解细节的人,我写了以下几个部分:

  • Raku(标准语言和类详细信息)**
  • Rakudo(高级别实施)**
    *NQP(中级实施)
  • MoarVM(低级实现)**
    *ident的规格和“规格”
    *<ident>、“字符类”和“标识符”的文档(更正)
    ***ident vs Raku * 标识符 *

Raku(标准语言和类详细信息)

XML::Grammar是用户定义的Raku语法。一个Raku语法是一个类。(“语法实际上只是稍微特殊化的类”)。
一个Raku rule是一个regex是一个方法:

grammar foo { rule ident { ... } }

say foo.^lookup('ident').WHAT; # (Regex)
say Regex ~~ Method;           # True

语法中的规则 call,如<ident>,通常是在语法上调用.parse或类似的结果。.parse调用根据语法中的规则匹配输入字符串。
当在匹配过程中计算XML::Grammar中出现的<ident>时,结果是对XML::Grammar的示例进行ident方法(规则)调用(如果.parse调用只是一个类型对象,则会创建其调用对象的示例)。
因为XML::Grammar本身并没有定义一个同名的规则/方法,所以ident调用是根据标准的方法解析,呃,规则来调度的。(我在这里使用的“规则”一词是通用的非Raku特定意义上的。啊,语言。)
在Raku中,任何使用grammar foo { ... }形式的声明创建的类都会自动从Grammar类继承,而Grammar类又从Match类继承:

say .^mro given grammar foo {} # ((foo) (Grammar) (Match) (Capture) (Cool) (Any) (Mu))

identMatch类中。

Rakudo(高级实现)

在Rakudo编译器中,the Match classdoes的作用是NQPMatchRole
这个NQPMatchRoleident的最高级别实现。

NQP(中级实现)

NQPMatchRole是用nqp语言编写的,nqp语言是Raku的一个子集,用于引导完整的Raku,也是NQP的核心,一个编译器工具包。
ident声明中提取并重新格式化最突出的代码,first 字符的匹配归结为:

nqp::ord($target, $!pos) == 95
|| nqp::iscclass(nqp::const::CCLASS_ALPHABETIC, $target, $!pos)

如果 * 第一个 * 字符是 * _95是下划线的ASCII码/ Unicode码点)* 或 * 匹配NQP中定义的字符类CCLASS_ALPHABETIC的字符,则匹配。
另一个突出的代码是:

nqp::findnotcclass( nqp::const::CCLASS_WORD

这匹配字符类CCLASS_WORD中的零个或多个 * 后续 * 字符。
search of NQP for CCLASS_ALPHABETIC显示了几个匹配项。最有用的似乎是an NQP test file。虽然这个文件清楚地表明CCLASS_WORDCCLASS_ALPHABETIC的超集,但它并没有清楚地表明这些类实际上匹配什么。
NQP的目标是多个“后端”或具体的虚拟机。考虑到Rakudo/NQP文档/测试相对缺乏这些规则和字符类实际匹配的内容,人们必须查看它的一个后端来验证什么是什么。

MoarVM(底层实现)

MoarVM是唯一官方支持的后端。
search of MoarVM for CCLASS显示了几个匹配项。
重要的一个似乎是ops.c,它包含一个switch (cclass)语句,该语句又包含对应于NQP类似命名的常量的MVM_CCLASS_ALPHABETICMVM_CCLASS_WORD的情况。
根据代码的注解:
CCLASS_ALPHABETIC当前匹配的字符与完整的Raku或NQP <:L>规则完全相同,即Unicode将这些字符归类为“字母”。
我认为这意味着<alpha>等效于CCLASS_ALPHABETIC_(下划线)的并集。

CCLASS_WORD匹配相同的加上<:Nd>,即十进制数字(在任何人类语言中,不仅仅是英语)。
我认为这意味着Raku / NQP <alnum>规则等价于CCLASS_WORD

ident的规格和“规格”

Raku的 * 官方 * 规范体现在 roast 中[2]。
search of roast for ident显示了几个匹配项。
大多数使用<ident>只是偶然的,作为测试其他东西的一部分。规范要求它们按照图示工作,但是通过查看附带的用法,您不会理解<ident>应该做什么。
三个测试清楚地测试了<ident>本身。其中一个基本上是多余的,剩下两个。我没有看到这两个匹配的6.c6.c.errata版本之间的变化:
来自S 05-质量/单位吨:

ok ('2+3 ab2' ~~ /<ident>/) && matchcheck($/, q/mob<ident>: <ab2 @ 4>/), 'capturing builtin <ident>';

ok测试它的第一个参数返回True。此调用测试<ident>跳过2+3并匹配ab2
来自S 05-mass/charsets.t:

is $latin-chars.comb(/<ident>/).join(" "), "ABCDEFGHIJKLMNOPQRSTUVWXYZ _ abcdefghijklmnopqrstuvwxyz ª µ º ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö øùúûüýþÿ", 'ident chars';

is测试它的第一个参数是否与第二个参数匹配。这个调用测试ident规则从由前256个Unicode码点(拉丁语1字符集)组成的字符串中匹配什么。
下面是这个测试的一个变体,它更清楚地显示了发生的匹配:

say ~$_ for $latin-chars ~~ m:g/<ident>/;

打印:

ABCDEFGHIJKLMNOPQRSTUVWXYZ
_
abcdefghijklmnopqrstuvwxyz
ª
µ
º
ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ
ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö
øùúûüýþÿ

但是<ident>将匹配比Latin-1中的100个左右字符多得多的字符。因此,虽然上述测试涵盖了<ident> * 官方指定/测试 * 的匹配,但它们显然没有涵盖全貌。
因此,让我们来看看official speculation,它可能被认为与“规范”有关。
首先,我们注意到顶部的警告:

Note: these documents may be out of date.
For Perl 6 documentation see docs.perl6.org;
for specs, see the official test suite.

本警告中的术语“specs”是“specification”的缩写。正如已经解释过的,* 官方 * 规范测试套件是 roast,而不是任何人类语言的赘言。
(Some人们仍然认为这些历史上的设计文档也是“规格”,并称它们为“规格”,但官方的观点是,“规格”,适用于设计文档,应被视为“投机”的缩写,以强调它们不是完全依赖的东西。
search for ident in design.raku.org显示了几个匹配项。
最有用的匹配位于S 05的 * 预定义子规则 * 部分:
以下是任何文法或正则表达式的一些预定义子规则:

  • ident.匹配一个标识符。

呃...

<ident>、“字符类”和“标识符”文档(更正)

来自官方文档中的预定义字符类

Class                             Description
    <ident>                           Identifier. Also a default rule.

这在三个方面具有误导性:

  • ident不是字符类。字符类匹配该字符类中的 * 单个 * 字符;如果与量词一起使用,它们只匹配一串这样的字符,每个字符可以是该类中的任何字符。相反,<ident>匹配特定的字符模式。它可能是一个字符,但你不能控制它;规则是贪婪的,匹配尽可能多的字符符合模式。如果你应用一个量词,它控制整个规则的重复,而不是规则的单个匹配中包含多少个字符。
    *所有内置规则均为默认规则。我认为默认注解是为了强调如果您不喜欢内置模式,可以编写自己的ident规则。这对所有规则都是正确的,尽管覆盖内置的规则(如<lower><lower>)等规范字符类)通常意义不大。
  • ident不**匹配 * 标识符 *!或者,更准确地说,对于大多数Raku标识符,它本身并不这样做。有关详细信息,请参阅下一节。

ident vs Raku * 标识符 *

my @Identifiers = < $bar %hash Foo Foo::Bar your_ident anothers' my-ident >; 
say (~$/ if m/^<ident>$/ for @Identifiers); # (Foo your_ident)
say (~$/ if m/ <ident> / for @Identifiers); # (bar hash Foo Foo your_ident anothers my)

在NQP的Grammar.nqp中定义的nqp语法中,有:

token identifier { <.ident> [ <[\-']> <.ident> ]* }

在Raku的语法中,Rakudo的Grammar.nqp中定义了一些代码,它们看起来略有不同,但效果完全相同:

token apostrophe { <[ ' \- ]> }
token identifier { <.ident> [ <.apostrophe> <.ident> ]* }

所以<identifier>匹配一个包含一个或多个<ident> s的模式,其中<apostrophe> s介于两者之间。
ident方法在NQPMatchRole中,这意味着它是一个内置的,是用户语法的规则命名空间的一部分。
但是identifier方法不是由Raku或nqp导出的。所以它们不是用户语法的规则命名空间的一部分。
如果我们编写自己的indentifier令牌,我们可以看到它的运行情况:

my token identifier { <.ident> [ <[\-']> <.ident> ]* }
my token sigil { <[$@%&]> }
say (~$/ if m/^ <sigil>? <identifier> $/ for @Identifiers)

显示:

($bar %hash Foo your_ident my-ident)

总结上述和其他一些考虑:

  • <ident>只匹配<identifier>匹配的 * 部分 *(尽管它们对于简单名称是相同的)。以is-prime为例。这是一个Raku标识符,但包含 * 两个 * <ident>匹配项(isprime)。
  • <identifier>只匹配“Raku identifiers”的 * 部分 *(尽管它们对于简单名称是相同的)。以infix:<+>为例。这有时被称为Raku标识符,但需要<identifier>匹配和:<+>匹配。
  • Raku标识符本身只是名称的 * 部分 *(尽管它们对于最简单的名称是相同的)。考虑Foo-Bar::Baz-Qux,它包含 * 两个 * <identifier>匹配(每个匹配又包含两个<ident>匹配)。

脚注

[1]如果您不确定捕获是什么,请参阅捕获、命名捕获和子规则。
[2]Raku的官方规范是一个名为roast的测试套件--RepositoryOfAllSspecificationTests。roast的特定分支的最新版本定义了Raku的特定版本。当我第一次写这个答案的时候,只有两个官方的分支/版本的烤,因此是Raku。第一个是6.c,也就是6.Christmas。这是cut on Christmas day 2015,从那天起就被故意冻结了。第二个是6.c.errata,它保守地添加了对6.c的修正,这些修正被认为足够重要,并且向后兼容,可以包含在(当时)当前的官方推荐版本中。一个“正式兼容的”Raku编译器通过了某个正式的roast分支。Rakudo编译器(然后)通过了6.c.errata。如果您阅读了所有涉及某个特性的测试,比如roast的6.c.errata分支,那么您就已经阅读了该特性在Raku语言的6.c.errata版本中的 * 官方指定 * 含义的完整定义。

tvz2xvvm

tvz2xvvm2#

通常,查找文档的位置是Perl6 documentation。这是正则表达式的一部分,可以在字符类的定义中找到。它匹配Perl6标识符。ident前面的.所做的是抑制捕获。

相关问题