如何在perl6中访问语法的可选部分?

bli*_*ppy 6 perl6 raku

作为语法的一部分,我有:

        rule EX1        { <EX2> ( '/' <EX2>)*  }
Run Code Online (Sandbox Code Playgroud)

在我的动作课中,我写了:

    method EX1($/) {
            my @ex2s = map *.made,  $/.<EX2>;
            my $ex1 = @ex2s.join('|');
            #say "EX1 making $ex1";
            $/.make($ex1);
    }
Run Code Online (Sandbox Code Playgroud)

所以基本上我只是试图将所有的连接EX2在一起,并'|'在它们之间而不是一个'/'。但是我的代码有些不对劲,因为它只会拾取第一个EX2而不是随后的代码。我如何找出可选的是什么?

rai*_*iph 10

TL; DR如果您rule创建了操作方法所期望的数据结构,则该操作方法将起作用。因此,我们将修复rule并保留该方法。

主要问题

假设EX1规则被放入一个有效的语法中;字符串已成功解析;子字符串ex2/ex2/ex2符合EX1规则;并且我们已经显示了解析树的相应部分(仅say通过.parse查看使用语法的结果即可):

EX1 => ?ex2/ex2/ex2?
 EX2 => ?ex2?
 0 => ?/ex2?
  EX2 => ?ex2?
 0 => ?/ex2?
  EX2 => ?ex2?
Run Code Online (Sandbox Code Playgroud)

请注意无关的0 =>捕获,以及第二个和第三个EX2s在它们下面的缩进以及相对于第一个缩进的方式EX2。相对于方法的假设,这是错误的嵌套结构。

布拉德对主要问题的解决方案

正如Brad ++在回应此答案的第一个版本的评论中指出的那样,您可以简单地从既分组又捕获((...))的结构切换到仅分组([...])的结构。

    rule EX1        { <EX2> [ '/' <EX2>]*  }
Run Code Online (Sandbox Code Playgroud)

现在,与上述相同的输入字符串的对应解析树片段为:

EX1 => ?ex2/ex2/ex2?
 EX2 => ?ex2?
 EX2 => ?ex2?
 EX2 => ?ex2?
Run Code Online (Sandbox Code Playgroud)

0捕获的消失,EX2小号,现在都是兄弟姐妹。有关何时以及为什么P6 嵌套捕获其方式的进一步讨论,请参阅jnthn对“ 为什么/如何...捕获组”的回答

您的操作方法现在应该可以工作-对于某些输入...

Håkon解决另一个可能的问题的方法

如果布拉德的解决方案适用于一些输入你期望它的工作,但不是全部,问题的部分原因可能是你怎么rule之间的匹配<EX2>/个性。

正如Håkon++在他们的答案中指出的那样,您的rule空格可能无法满足您的要求。

如果您不希望模式中的间距很大,则不要使用rule。在模式中的一个tokenregex 所有空格中(例如,在字符串内部忽略' ')只是为了使您的模式更具可读性,并且相对于匹配的任何输入字符串都没有意义。如有疑问,请使用token(或regex)而不是rule

token EX1 { <EX2> ( '/' <EX2>)* }
                           
Run Code Online (Sandbox Code Playgroud)

所指示的间距并不重要。您可以忽略它或对其进行扩展,这对规则与输入的匹配方式没有影响。仅出于可读性。

相反,rule构造的要点是模式中每个原子和每个量词之后的空白很重要。这样的间隔在输入中的相应子字符串之后隐式应用了(用户可覆盖的)边界匹配规则(默认情况下,该规则允许空格和/或“单词”和非“单词”字符之间的过渡)。

在您的EX1规则中,为确保清晰起见,我在下面重复使用夸张的空格来重复该规则,其中一些空格并不重要,就像它不在token或中一样regex

     rule EX1        {  <EX2>   (  '/'  <EX2>)*   }
                                                 
Run Code Online (Sandbox Code Playgroud)

如前所述,间距并不重要-您可以省略或扩展它,并且不会有任何区别。要记住的是,模式(或子模式)开始处的空格只是为了提高可读性。(使用经验表明,如果将任何间距视为有效间距,效果会更好。)

但是原子或量词后面的间距或缺少间距重要:

This spacing is significant: ?      ?        ?
     rule EX1        { <EX2>   ( '/'  <EX2>)*   }
This LACK of spacing is significant:      ??
Run Code Online (Sandbox Code Playgroud)

通过按rule原样编写,就告诉P6仅将输入与边界匹配(默认情况下允许空白)匹配:

  • 一个之后 <EX2>(因此一个 之前/);

  • 之间 /和随后的<EX2>比赛;

  • 之后最后一个 <EX2>比赛。

因此,您的规则告诉P6在a /<EX2>match 之间按该顺序出现时留出空格- /,然后<EX2>

但它也告诉P6到允许的空间周围的其他方法-一种之间<EX2>的比赛和/在比赛命令!除了第一<EX2> '/'对!P6可以让您声明任意复杂度(包括空格)的匹配模式,但是我怀疑这是您的意思或想要的。

有关“在原子之后”的含义的完整列表(即,当rules中的空格很重要时),请参见何时在Perl6语法中空格真正重要?

此重要的间距特征是:

  • 经典Perl DWIMery旨在使生活更轻松;

  • 惯用语-在大多数语法中使用,因为它确实确实使生活更轻松;

  • rule声明符存在的唯一原因(这个重要的空白是a 和a 之间的唯一区别);ruletoken

  • 完全可选,因为您可以使用a token代替。

如果某人认为不愿使用此重要的空间功能,则可以使用tokens代替。(这反过来可能会导致他们了解为什么rule存在作为选项,然后,或者也许以后,了解为什么它以它的方式工作,并重新欣赏其DWIMery。:))

您要匹配的模式的内置构造

最后,这是编写您要匹配的模式的惯用方式:

rule EX1        { <EX2> + % '/' }
Run Code Online (Sandbox Code Playgroud)

这告诉P6匹配一个或多个<EX2>/字符分隔的。见修改量词:%%%这个漂亮的建造物的解释。

这仍然是一个rule很大的间隔。对于此结构,何时出现和不出现的确切细节显然是最复杂的,因为它最多包含三个有效的分隔符,而没有一个:

NOT significant:  ?                 ?
     rule EX1   {   <EX2>    +    %    '/'   }
Significant:              ?    ?          ?
Run Code Online (Sandbox Code Playgroud)

既包括前间隔+是多余的:

     rule EX1   {   <EX2>    +    %    '/'   }
     rule EX1   {   <EX2>    +%        '/'   } # same match result
     rule EX1   {   <EX2>+        %    '/'   } # same match result
Run Code Online (Sandbox Code Playgroud)