在 raku 的语法中使用“after”作为后视

Mor*_*ayJ 8 regex grammar raku

我正在尝试用 raku 语法进行匹配,但以“after”失败。我已将我的问题归结为以下代码段:

grammar MyGrammar {

    token TOP {
        <character>
    }

    token character {
        <?after \n\n>LUKE
    }
}

say MyGrammar.subparse("\n\nLUKE");
Run Code Online (Sandbox Code Playgroud)

这将#<failed match>作为 MyGrammar.subparse 和NilMyGrammar.parse返回。

但是如果我在 REPL 中运行匹配:

"\n\nLUKE" ~~ /<?after \n\n>LUKE/

我得到比赛 ?LUKE?

所以有些事情我不明白,我不确定是什么。任何指针?

Jon*_*ton 10

当我们使用语法解析字符串时,匹配被锚定到字符串的开头。解析输入parse需要我们消耗所有的字符串。还有一个subparse,它允许我们不消耗所有输入,但这仍然锚定在字符串的开头。

相比之下,正则表达式 like/<?after \n\n>LUKE/扫描字符串,尝试匹配字符串中每个位置的模式,直到找到匹配的位置(或到达字符串的末尾并放弃)。这就是它起作用的原因。但是请注意,如果您的目标是不捕获\n\n,那么您可以将正则表达式编写为/\n\n <( LUKE/,其中<(指示从何处开始捕获。至少在目前的 Rakudo 编译器实现上,这种方式效率更高。

在没有更多上下文的情况下建议如何编写语法并不容易(我猜这是从一个更大的问题中提取的)。例如,您可以在语法开头使用空格:

grammar MyGrammar {

    token TOP {
        \s+ <character>
    }

    token character {
        <?after \n\n>LUKE
    }
}

say MyGrammar.subparse("\n\nLUKE");
Run Code Online (Sandbox Code Playgroud)

或者使用\n\nin 字符,但将其从与 的匹配中排除<(,如前所述。


rai*_*iph 6

<?after ...> 不前进匹配光标

这里至关重要的一点<?after \n\n>“零宽度”断言

如果匹配光标位于"\n\n"正在匹配的字符串的右侧,则匹配,但它不会推进匹配光标。

为什么~~ / ... /版本匹配

正则表达式/语法引擎会自动为您推进匹配光标。

一个普通的正则表达式风格的匹配就像传统的正则表达式一样工作。特别是,它应该匹配正在匹配的字符串中的任何地方,除非您明确添加锚点,例如^(字符串开头)和/或$(字符串结尾)。

更明确地说,匹配引擎将首先尝试在要匹配的字符串的第一个字符位置进行匹配。然后,如果失败,它会自动向前移动字符串中的一个字符,然后再次尝试从正则表达式模式的开头进行匹配。

所以所有这些也将匹配并给出相同的结果:

"\n\nLUKE" ~~ /LUKE/;                     # ?LUKE?
"\n\nLUKE" ~~ /LUKE $/;                   # ?LUKE?
"LUKE"     ~~ /^ LUKE $/;                 # ?LUKE?
"\n\nLUKE" ~~ / <?after \n\n>LUKE $/;     # ?LUKE?
Run Code Online (Sandbox Code Playgroud)

为什么语法版本不匹配

语法应该从输入字符串的开头开始匹配。否则失败。

更明确地说,在解析的开始和结束处.parse具有隐式^$锚点,并在开始处.subparse具有隐式^

如果匹配光标未能通过第一个字符,则解析失败。您的语法不会使匹配光标超过第一个字符,因此它失败了。

<?after \n\n>如果匹配,不仅无法推进游标,它甚至从不匹配 - 因为在字符串的开头,匹配游标只是在之后什么都没有。如果你写了<?after ''>,那么它总是会成功,但仍然不会推进光标,因此如果这是您所做的唯一更改,语法仍然会失败。)