【问题标题】:How do I match using :global in Raku grammar?如何在 Raku 语法中使用 :global 进行匹配?
【发布时间】:2021-03-17 03:43:23
【问题描述】:

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

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

有效输入示例:

  • 无语言:easy
  • 一种语言:hard javascript
  • 多种语言:medium javascript python 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。

【问题讨论】:

    标签: raku


    【解决方案1】:

    这里有两个问题。

    要在语法分析中指定子规则,命名参数总是:rule,无论在语法中它是ruletokenmethod 还是@987654326 @。您的前两个测试通过了,因为它们代表有效的完整语法分析(即 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>] + }
    

    在您的情况下,您有&lt;language&gt;+,并且由于language\w+,因此不可能匹配两个(因为第一个将消耗所有\w)。简单的解决方案,只需将&lt;language&gt;+ 更改为&lt;language&gt; +

    为了让&lt;difficulty&gt; 令牌浮动,我想到的第一个解决方案是匹配它并使用&lt;language&gt; 令牌:

    token language { <!difficulty> \w+ }
    

    &lt;!foo&gt; 在那个位置会失败,它可以匹配&lt;foo&gt;。在您获得像“easyFoo”这样的语言之前,这几乎可以完美运行。简单的解决方法是确保难度标记始终出现在单词边界:

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

    &gt;&gt; 在右侧声明一个单词边界。

    【讨论】:

    • 谢谢,我将在早上将这些更改添加到我的程序中。
    • 请参阅 When is white space really important in Raku grammars? 以详细讨论 @user0721090601 解释的所有问题背后的问题。忽略未处理的命名参数这一事实具有实际和战略进化的好处,但其缺点是目前在没有警告的情况下完成。目前,这个缺点只是你需要注意的事情。 Aiui、Raku、Rakudo 和/或 CommaIDE 可能会在未来几年提供缓解。
    猜你喜欢
    • 2022-07-26
    • 2020-05-03
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-06
    • 2020-07-13
    • 1970-01-01
    相关资源
    最近更新 更多