在EOS停止Raku语法(字符串结尾)


9

在学习一种音乐语言的翻译者(从ABC到Alda)作为学习Raku DSL能力的借口的过程中,我注意到似乎没有一种方法可以终止.parse!这是我缩短的演示代码:

#!/home/hsmyers/rakudo741/bin/perl6
use v6d;

# use Grammar::Debugger;
use Grammar::Tracer;

my $test-n01 = q:to/EOS/;
a b c d e f g
A B C D E F G
EOS

grammar test {
  token TOP { <score>+ }
  token score {
      <.ws>?
      [
          | <uc>
          | <lc>
      ]+
      <.ws>?
  }
  token uc { <[A..G]> }
  token lc { <[a..g]> }
}

test.parse($test-n01).say;

这是Grammer :: Tracer显示的最后一部分,展示了我的问题。

|  score
|  |  uc
|  |  * MATCH "G"
|  * MATCH "G\n"
|  score
|  * FAIL
* MATCH "a b c d e f g\nA B C D E F G\n"
「a b c d e f g
A B C D E F G
」

在倒数第二行,FAIL字告诉我.parse运行无法退出。我想知道这是否正确吗?.say会显示应有的所有内容,因此我不清楚FAIL有多真实?问题仍然存在,“我如何正确编写语法分析多行而没有错误?”


我不想干扰您的学习过程,但是以防万一您不知道,这里有一个ABC模块
raiph

1
好吧,至少我们没有选择相同的曲调进行测试!
hsmyers

Answers:


10

使用语法调试器时,它可以让您确切地看到引擎如何解析字符串-失败是正常的,也是预期的。例如,考虑a+b*与字符串匹配aab。您应该为“ a”获得两个匹配项,然后是失败(因为b不是a),但是它将重试b并成功匹配。

如果您与||(执行顺序)进行交替,则可能更容易看出这一点。如果你有

token TOP   { I have a <fruit> }
token fruit { apple || orange || kiwi }

然后解析句子“我有一个猕猴桃”,您会看到它首先与“我有一个猕猴桃”匹配,然后是两个失败的“苹果”和“橙色”,最后是与“猕猴桃”的匹配。

现在让我们看看您的情况:

TOP                  # Trying to match top (need >1 match of score)
|  score             #   Trying to match score (need >1 match of lc/uc)
|  |  lc             #     Trying to match lc
|  |  * MATCH "a"    #     lc had a successful match! ("a")
|  * MATCH "a "      #   and as a result so did score! ("a ")
|  score             #   Trying to match score again (because <score>+)
|  |  lc             #     Trying to match lc 
|  |  * MATCH "b"    #     lc had a successful match! ("b")
|  * MATCH "b "      #   and as a result so did score! ("b ")
……………                #     …so forth and so on until…
|  score             #   Trying to match score again (because <score>+)
|  |  uc             #     Trying to match uc
|  |  * MATCH "G"    #     uc had a successful match! ("G")
|  * MATCH "G\n"     #   and as a result, so did score! ("G\n")
|  score             #   Trying to match *score* again (because <score>+)
|  * FAIL            #   failed to match score, because no lc/uc.
|
|  # <--------------   At this point, the question is, did TOP match?
|  #                     Remember, TOP is <score>+, so we match TOP if there 
|  #                     was at least one <score> token that matched, there was so...
|
* MATCH "a b c d e f g\nA B C D E F G\n" # this is the TOP match

失败是正常的:在某些时候,我们会用完<score>令牌,因此失败是不可避免的。发生这种情况时,语法引擎可以继续进行<score>+语法之后的处理。由于没有任何内容,因此失败实际上会导致整个字符串都TOP匹配(因为与hidden匹配/^…$/)。

此外,您可能会考虑使用自动插入<.ws> *的规则来重写语法(除非仅将一个空格用作空格很重要):

grammar test {
  rule TOP { <score>+ }
  token score {
      [
          | <uc>
          | <lc>
      ]+
  }
  token uc { <[A..G]> }
  token lc { <[a..g]> }
}

此外,IME,您可能还想为uc / lc添加一个原型令牌,因为当您拥有uc / lc时,[ <foo> | <bar> ]总是会有一个未定义的令牌,这会使在动作类中处理它们变得有些烦人。您可以尝试:

grammar test {
  rule  TOP   { <score>  + }
  token score { <letter> + }

  proto token letter    {     *    }
        token letter:uc { <[A..G]> }
        token letter:lc { <[a..g]> }
}

$<letter> 将始终以这种方式定义。


这解释了一个事实,即即使使用“ FAIL”,匹配对象仍返回“ so”为真。我认为可能是这样。我将回到为实际项目添加必要的令牌;)
hsmyers

真正的语法似乎不喜欢自动插入<.ws> *。可能是由于<score>之外涉及的其他层。一旦我能够绕开技术,您对使用原型的建议就很好了……
hsmyers


我讨厌拥有不需要的代码-进行更多调试,然后就拥有了这一切的美感!实际的问题是,ABC不会给空格一个诅咒。有一些例外,但总的来说,它们几乎可以发生在任何地方。“用例”是一个易读性问题,有点像大数字字符串中的逗号。我将根据需要重新研究该问题,直到我理解该问题并将其减少到最低限度。
hsmyers

1
hsmyers:值得庆幸的是,理解proto并不难,一旦掌握了它,就可以使您的生活变得更加轻松。
user0721090601 '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.