regex - Perl6语法中的<.ident>函数/捕获

标签 regex grammar raku

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

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

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

最佳答案

TL; DR 我将从一个准确而相对简洁的答案开始。该答案的其余部分适用于那些希望全面了解内置规则和/或特别要深入研究ident的人员。

<.ident> function/capture



由于.<.ident>仅匹配,因此无法捕获[1]。对于此答案的其余部分,我通常会省略.,因为它除了捕获方面对规则的含义没有影响。

正如您可以在编程语言中在另一个函数的声明中调用(也称为“调用”)一个函数一样,您也可以在其中调用规则/ token /正则表达式/方法(此后我通常只使用术语“规则”)另一条规则的声明。 <foo>是用于调用名为foo的规则的语法;因此<ident>调用了一个名为ident的(方法)。

在撰写本文时,XML::Grammar语法本身并未定义/声明一个名为ident的规则。这意味着该调用最终被调度到具有该名称的内置声明中。

内置的ident规则的作用与声明为:

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

official Predefined character classes doc应该提供<alpha><alnum>的精确定义。或者,相关详细信息也将包含在此答案的后面。

底线是ident与一个或多个“字母数字”字符的字符串匹配,但第一个字符不能为“数字”。

因此,abcdef123都匹配,而123abc不匹配。

这个答案的其余部分

对于那些有兴趣详细了解的人,我写了以下部分:
  • Raku(标准语言和类(class)详细信息)
  • Rakudo(高级实现)
  • NQP(中级实现)
  • MoarVM(低级实现)
  • ident的规范和“规范”
  • <ident>,“字符类”和“标识符”的
  • 文档的更正。
  • ident与Raku标识符

  • Raku(标准语言和类(class)详细信息)
    XML::Grammar是用户定义的Raku语法。 Raku语法是一门课。 ("Grammars are really just slightly specialized classes"。)

    Raku rule是正则表达式是一种方法:

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

    通常,由于在语法上调用了 <ident> 或类似命令,因此会调用规则调用(如.parse)。 .parse调用根据语法规则匹配输入字符串。

    在比赛期间评估<ident>XML::Grammar的出现时,结果是对ident实例的XML::Grammar方法(规则)调用(如果.parse调用只是一个类型对象,则将创建其invocant的实例)。

    由于XML::Grammar本身并未定义该名称的规则/方法,因此ident调用而是根据标准方法解析规则来分派(dispatch)的。 (我在这里使用的是非Raku的一般意义上的“规则”一词。啊,语言。)

    在Raku中,使用grammar foo { ... }形式的声明创建的任何类都会自动继承自Grammar类,而Match类又继承自ident类:

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

    在内置的Match类中可以找到Match

    Rakudo(高级实现)

    在Rakudo编译器中,the does class NQPMatchRolerole NQPMatchRole

    identNQPMatchRole的最高级别实现的位置。

    NQP(中级实现)
    ident用nqp语言编写,它是Raku的一个子集,用于引导整个Raku,它是编译器工具包NQP的核心。

    仅从the _ declaration中摘录并重新格式化最突出的代码,第一个字符的匹配可归结为:

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

    如果第一个字符是95(CCLASS_ALPHABETIC是下划线的ASCII代码/Unicode代码点)或与NQP中定义的称为CCLASS_WORD的字符类匹配的字符,则此匹配。

    显着代码的另一位是:

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

    这与字符类CCLASS_ALPHABETIC中的零个或多个后续字符匹配。

    search of NQP for CCLASS_WORD 显示多个匹配项。最有用的似乎是an NQP test file。尽管此文件清楚表明CCLASS_ALPHABETICCCLASS的超集,但并不清楚这些类实际上匹配什么。

    NQP针对多个“后端”或具体的虚拟机。鉴于Rakudo/NQP doc/测试相对缺乏这些规则和字符类的实际匹配性,因此必须查看其后端之一以验证什么。

    MoarVM(低级别实现)

    MoarVM是唯一受官方支持的后端。

    search of MoarVM for switch (cclass) 显示多个匹配项。

    重要的一个似乎是ops.c,而includes a MVM_CCLASS_ALPHABETIC statement则包括MVM_CCLASS_WORDCCLASS_ALPHABETIC的情况,这些情况与NQP的类似命名常量相对应。

    根据代码的注释:
    <:L>当前与完全Raku或NQP <alpha> 规则完全相同的字符匹配,即Unicode字符已分类为“字母”。

    我认为这意味着CCLASS_ALPHABETIC等于_CCLASS_WORD的并集(下划线)。
    <:Nd><alnum>相同,即十进制数字(以任何人类语言,而不仅仅是英语)。

    我认为这意味着Raku/NQP CCLASS_WORD规则等效于ident
    ident的规范和“规范”

    Raku的官方规范体现在烤[2]中。

    search of roast for <ident> 显示多个匹配项。

    大多数测试只是偶然地使用<ident>,作为测试其他内容的一部分。规范要求它们按所示方式工作,但是通过查看附带用法,您将无法理解<ident>应该做什么。

    三个测试清楚地测试了6.c本身。其中之一本质上是多余的,剩下两个。我发现这两个匹配项的6.c.errataok版本之间没有任何变化:

    S05-mass/rx.t:

    ok ('2+3 ab2' ~~ /<ident>/) && matchcheck($/, q/mob<ident>: <ab2 @ 4>/), 'capturing builtin <ident>';
    
    True测试其第一个参数是否返回<ident>。此调用测试2+3跳过ab2并匹配is

    S05-mass/charsets.t:

    is $latin-chars.comb(/<ident>/).join(" "), "ABCDEFGHIJKLMNOPQRSTUVWXYZ _ abcdefghijklmnopqrstuvwxyz ª µ º ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö øùúûüýþÿ", 'ident chars';
    
    ident测试其第一个参数与第二个参数匹配。此调用从由前256个Unicode代码点(拉丁1字符集)组成的字符串中测试<ident>规则与什么匹配。

    这是此测试的一种变体,可以更清楚地显示发生的匹配:

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

    打印:

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

    但是<ident>匹配的内容不仅仅来自Latin-1中的一百多个字符。因此,尽管以上测试涵盖了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.
    

    该警告中的术语“规范”是“规范”的缩写。如前所述,官方规范测试套件是烤制的,不是任何人类语言的措辞。

    (有些人仍然将这些历史设计文档也视为“规范”,并将它们称为“规范”,但官方认为,适用于设计文档的“规范”应被视为“规范”的简称)。投机”以强调它们不是完全值得依赖的东西。)

    search for <ident> in design.raku.org显示多个匹配项。

    最有用的匹配项是the Predefined Subrules section of S05:

    These are some of the predefined subrules for any grammar or regex:


  • ident ...匹配标识符。

  • 喔...

    (更正)ident,“字符类”和“标识符”的文档

    Predefined character classes in the official doc:

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

    这在三种方式上具有误导性:
  • <ident>而不是 a character class。字符类与该字符类中的单个字符匹配;如果与量词一起使用,它们仅匹配一串这样的字符,每个字符都可以是该类中的任何字符。相反,ident与特定的字符模式匹配。它可能是一个字符,但您无法控制。规则是贪婪的,匹配符合模式的字符数。如果应用量词,则它控制整个规则的重复,而不是规则的单个匹配中包含多少个字符。
  • 所有内置规则都是默认规则。我认为默认注释是为了强调,如果您不喜欢内置模式,则可以编写自己的<lower>规则。这对于所有规则都是正确的,尽管通常覆盖诸如ident(小写)之类的规范字符类之类的内置插件的意义要小得多。
  • ident是否而不是匹配标识符!或者,更准确地说,对于大多数Raku标识符而言,它并不是单独这样做的。有关详细信息,请参见下一部分。
  • <identifier>与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> ]* }
    

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

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

    因此,<ident>与包含一个或多个<apostrophe>以及介于两者之间的ident的模式匹配。
    NQPMatchRole方法位于identifier中,这意味着它是内置的,属于用户语法的规则 namespace 。

    但是indentifier方法是akut或nqp导出的而不是。因此,它们不是用户语法的规则命名空间的而不是的一部分。

    如果我们编写自己的<ident> token ,我们可以看到它的作用:

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

    显示:

    ($bar %hash Foo your_ident my-ident)
    

    总结以上内容和其他一些注意事项:
  • <identifier>仅匹配is-prime匹配的部分内容(尽管简单名称相同)。考虑<ident>。这是Raku标识符,但包含两个is匹配项(prime<identifier>)。
  • infix:<+>仅匹配“Raku标识符”的一部分(尽管简单名称相同)。考虑<identifier>。有时将其称为Raku标识符,但需要:<+>匹配和Foo-Bar::Baz-Qux匹配。
  • Raku标识符本身只是名称的一部分(尽管对于最简单的名称它们是相同的)。考虑包含两个<identifier>匹配项的<ident>(每个依次包含两个6.c匹配项)。

  • 脚注

    [1]如果不确定是什么捕获,请参阅CapturingNamed capturesSubrules

    [2] Raku的官方规范是一个称为roast的测试套件- R 知识库 O f ll S 规范化 T ests。 roast特定分支的最新版本定义了Raku的特定版本。当我第一次写这个答案时,只有两个正式的分支机构/版本的烤肉,因此也只有Raku。第一个是 6.Christmas aka 6.c.errata。这是cut on Christmas day 2015,自那天以来一直故意卡住。第二个是6.c,它保守地添加了对6.c.errata的修正,该修正被认为足够重要并且向后兼容,可以包含在(当时)当前的官方推荐版本的Raku中。一个“官方兼容的” Raku编译器通过了一些正式的烘烤程序。 Rakudo编译器(然后)传递了6.c.errata。如果您阅读了例如roast的6.c.errata分支中涉及某个功能的所有测试,那么您将已经阅读了Raku语言的ojit_code版本对该功能的正式指定含义的完整定义。

    关于regex - Perl6语法中的<.ident>函数/捕获,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50676007/

    相关文章:

    标记的 ANTLR 语法表达式求值,可以使用 0 次或 1 次

    grammar - 有 Perl6 规范形式吗?

    raku - 包名前的关键字 'unit' 有什么作用?

    javascript - 从 C# 到 javascript 的等效正则表达式模式

    Java 正则表达式不会返回捕获组

    c# - 正则表达式 C# 获取捕获组的子组

    javascript - 从正则表达式模式过滤结果

    java - 在简单的整数列表语法中使用 AntLR4 中的访问者

    parsing - 如何确定一种语言是否为 LL(1) LR(0) SLR(1)

    raku - 有没有类似于Python中的Counter的内置方法?