如何使Perl 6语法产生多个匹配项(例如:ex和:ov)?

Eug*_*sky 5 raku

我想做grammar这样的事情:

> "abc" ~~ m:ex/^ (\w ** 1..2) (\w ** 1..2) $ {say $0, $1}/
?ab??c?
?a??bc?
Run Code Online (Sandbox Code Playgroud)

或像这样:

> my regex left { \S ** 1..2  }
> my regex right { \S ** 1..2  }
> "abc" ~~ m:ex/^ <left><right> $ {say $<left>, $<right>}/
?ab??c?
?a??bc?
Run Code Online (Sandbox Code Playgroud)

这是我的grammar

grammar LR {
  regex TOP {
    <left> 
    <right>
  }
  regex left {
    \w ** 1..2 
  }
  regex right {
    \w ** 1..2 
  }
}

my $string = "abc";
my $match = LR.parse($string);
say "input: $string";
printf "split: %s|%s\n", ~$match<left>, ~$match<right>;
Run Code Online (Sandbox Code Playgroud)

其输出为:

$ input: abc
$ split: ab|c
Run Code Online (Sandbox Code Playgroud)

因此,<left>只能贪婪而已<right>。如何修改代码以匹配两种可能的变体?

$ input: abc
$ split: a|bc, ab|c
Run Code Online (Sandbox Code Playgroud)

mor*_*itz 5

语法的设计目的是给出零个或一个答案,但不多于此,因此您必须使用一些技巧使它们执行您想要的操作。

由于Grammar.parse仅返回一个Match对象,因此您必须使用另一种方法来获取所有匹配项:

sub callback($match) {
    say $match;
}
grammar LR {
    regex TOP {
        <left> 
        <right>
        $
        { callback($/) }
        # make the match fail, thus forcing backtracking:
        <!>
    }
    regex left {
        \w ** 1..2 
    }
    regex right {
        \w ** 1..2 
    }
}

LR.parse('abc');
Run Code Online (Sandbox Code Playgroud)

通过调用<!>断言使匹配失败(总是失败)将迫使先前的原子回溯,从而找到不同的解决方案。当然,这会使语法的可重用性降低,因为它在语法的常规调用约定之外起作用。

请注意,对于调用者而言,LR.parse似乎总是失败。您将所有匹配项作为对回调函数的调用。

稍微好一点的API(但下面使用相同的方法)是使用gather/ take获得所有匹配项的序列:

grammar LR {
    regex TOP {
        <left> 
        <right>
        $
        { take $/ }
        # make the match fail, thus forcing backtracking:
        <!>
    }
    regex left {
        \w ** 1..2 
    }
    regex right {
        \w ** 1..2 
    }
}

.say for gather LR.parse('abc');
Run Code Online (Sandbox Code Playgroud)