How to put a sub inside a regex in Perl 6?

2019-02-24 14:32发布

That's what I'm trying to do.

>  my sub nplus1($n) {$n +1}
> my regex nnplus1 { ^ (\d+) &nplus1($0) $ }
> "123" ~~ &nnplus1
P6opaque: no such attribute '$!pos' in type Match...

标签: perl6
3条回答
劳资没心,怎么记你
2楼-- · 2019-02-24 15:02

Keep in mind that regexes are subs. So don't call your matcher a sub—be more specific and call it a regex. Yes, you can pass arguments to regex/token/rule. It's really important to do this when you match languages that change their state as you parse. For example, in YAML, you can parse "data[0]: 17". After that, the next line can start with "data[1]" but not "data[2]". So passing extra info as parameters is useful.

Also note that when you convert this to a regex, some things change. $n+1 will take on a new meaning (which is wrong). However, simple variables are still interpolated, so if you declare it as a new variable within the regex body with :my $npp = .... But even then, you'll find it still doesn't work. When you add a helper statement like {say "n is $n"}, you'll see you're not getting passed a valid parameter. This is because in code-like contexts without braces (when you use an expression as an argument to another matcher), rakudo does not update the match variable. When you add braces, the current match variable is recomputed or re-cached. This hack looks like a typo, so I suggest you add a comment that explains the empty braces. The final code is this:

my regex nplus1($n) {
 :my $npp=$n+1;
 $npp
}
my regex nnplus1 { (\d+) {} <nplus1($0)> }
say "123124" ~~ &nnplus1;

In this case (which is basically recursion), I like to keep things neater by changing data in the arguments instead of changing data in the function body: <nplus1($0+1)> instead of defining :my $npp = $n+1;.

查看更多
神经病院院长
3楼-- · 2019-02-24 15:04

Based on the Regex interpolation docs as well as on piojo's answer and Håkon Hægland's comment, it seems I've managed to do what I wanted:

my sub nplus1($n) {
 $n+1;
}
my regex nnplus1 { (\d+) {} <nplus1=$(nplus1($0))> }
say "123124" ~~ &nnplus1;

Output:

「123124」
 0 => 「123」
 nplus1 => 「124」

Or we can move the {} to enclose the interpolated sub:

my sub nplus1($n) {
 $n+1;
}
my regex nnplus1 { (\d+)  <nplus1={nplus1($0)}> }
say "123124" ~~ &nnplus1;

(the output will be the same)

查看更多
成全新的幸福
4楼-- · 2019-02-24 15:18

The <{...}> construct runs Perl 6 code inside a regex, and evaluates the result as a regex:

my sub nplus1($n) {$n +1} my regex nnplus1 { ^ (\d+) <{ nplus1($0) }> $ } say so '23' ~~ &nnplus1; # Output: True say so '22' ~~ &nnplus1; # Output: False

查看更多
登录 后发表回答