如何在 Raku 语法中使用 :global 进行匹配?

How do I match using :global in Raku grammar?

我正在尝试编写一个 Raku 语法,它可以解析要求编程难题的命令。

这是针对我的问题的简化版本,但这些命令结合了难度级别和可选的语言列表。

有效输入示例:

我可以让它匹配一种语言,但不能匹配多种语言。我不确定在哪里添加 :g.

这是我目前所拥有的示例:

grammar Command {
    rule TOP { <difficulty> <languages>? }

    token difficulty { 'easy' | 'medium' | 'hard' }

    rule languages { <language>+ }
    token language { \w+ }
}

multi sub MAIN(Bool :$test) {
    use Test;
    plan 5;

    # These first 3 pass.
    ok Command.parse('hard', :token<difficulty>), '<difficulty> can parse a difficulty';

    nok Command.parse('no', :token<difficulty>), '<difficulty> should not parse random words';

    # Why does this parse <languages>, but <language> fails below?
    ok Command.parse('js', :rule<languages>), '<languages> can parse a language';

    # These last 2 fail.
    ok Command.parse('js', :token<language>), '<language> can parse a language';

    # Why does this not match both words? Can I use :g somewhere?
    ok Command.parse('js python', :rule<languages>), '<languages> can parse multiple languages';
}

即使我的测试 #4 失败了,这仍然有效:

my token wrd { \w+ }
'js' ~~ &wrd;  #=> 「js」

提取多种语言与使用此语法的正则表达式一起工作,但我不确定如何在语法中使用它:

'js python' ~~ m:g/ \w+ /;  #=> (「js」 「python」)

此外,是否有一种理想的方法可以使顺序不重要,以便 difficulty 可以出现在字符串中的任何位置?示例:

rule TOP { <languages>* <difficulty> <languages>? }

理想情况下,我希望任何不是 difficulty 的内容都被解读为 language。示例:raku python medium js 应将 medium 读作 difficulty,其余读作 languages。

这里有两个问题。

要在语法分析中指定子规则,命名参数总是:rule,无论在语法中它是否是ruletokenmethodregex。您的前两个测试通过了,因为它们代表了有效的全语法分析(即 TOP),因为 :token 命名参数因为未知而被忽略。

这让我们:

ok  Command.parse('hard',      :rule<difficulty>), '<difficulty> can parse a difficulty';
nok Command.parse('no',        :rule<difficulty>), '<difficulty> should not parse random words';
ok  Command.parse('js',        :rule<languages> ), '<languages> can parse a language';
ok  Command.parse('js',        :rule<language>  ), '<language> can parse a language';
ok  Command.parse('js python', :rule<languages> ), '<languages> can parse multiple languages';

# Output
ok 1 - <difficulty> can parse a difficulty
ok 2 - <difficulty> should not parse random words
ok 3 - <languages> can parse a language
ok 4 - <language> can parse a language
not ok 5 - <languages> can parse multiple languages

第二个问题是如何在 rule 中处理隐含的空格。在token中,以下是等价的:

token foo { <alpha>+  }
token bar { <alpha> + }

但在 rule 中,它们会有所不同。比较以下规则的令牌等价物:

rule  foo { <alpha>+       } 
token foo { <alpha>+ <.ws> }

rule  bar { <alpha> +         }
token bar { [<alpha> <.ws>] + }

在你的情况下,你有 <language>+,并且由于 language\w+,所以不可能匹配两个(因为第一个会消耗所有 \w ).简单的解决方案,只需将 <language>+ 更改为 <language> +.

为了让 <difficulty> 令牌四处浮动,我想到的第一个解决方案是匹配它并保释 <language> 令牌:

token language { <!difficulty> \w+ }

<!foo> 如果在那个位置可以匹配 <foo> 将失败。在你得到像 'easyFoo' 这样的语言之前,这几乎可以完美地工作。简单的解决方法是确保难度标记始终出现在单词边界处:

token difficulty {
   [
   | easy
   | medium
   | hard
   ]
   >> 
}

其中 >> 断言右侧的单词边界。