Остановка грамматики Раку в EOS (конец строки)

9

В процессе написания переводчика одного музыкального языка на другой (ABC для Alda) в качестве предлога для изучения DSL-способности Raku, я заметил, что, похоже, нет способа прекратить a .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 run не имеет возможности выйти. Интересно, правильно ли это? .Say отображает все так, как должно быть, так что я не понимаю, насколько реальна неудача? Остается вопрос: «Как правильно написать грамматику, которая анализирует несколько строк без ошибок?»

hsmyers
источник
Я не хочу вмешиваться в ваш учебный процесс, но на случай, если вы не знали, есть модуль ABC .
Раиф
1
Ну, по крайней мере, мы не выбрали одинаковые мелодии для тестирования!
hsmyers

Ответы:

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совпадает с неявным /^…$/).

Кроме того, вы можете рассмотреть возможность перезаписи вашей грамматики с помощью правила, которое автоматически вставляет <.ws> * (если не важно, чтобы это был только один пробел):

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

Кроме того, IME, вы можете захотеть добавить протокен для 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> всегда будет определяться таким образом.

user0721090601
источник
Это объясняет тот факт, что объект сопоставления возвратил «так что истина, даже с« FAIL ». Я думал, что это может быть так; Я вернусь к добавлению необходимых токенов для реального проекта;)
hsmyers
Настоящая грамматика не любит вставлять <.ws> * автоматически; вероятно, из-за дополнительных слоев, вовлеченных за <оценка>. Ваше предложение использовать Proto выглядит хорошо, как только я смогу обернуть голову вокруг техники ...
hsmyers
Я ненавижу иметь код, который мне не нужен - больше для отладки, а потом есть эстетика всего этого! Фактическая проблема заключается в том, что ABC не заботится о пробелах. Есть некоторые исключения, но по большому счету они могут встречаться практически везде. Вариант использования - это вопрос разборчивости, похожий на запятые в строках с большими цифрами. Я буду пересматривать проблему по мере необходимости, пока не пойму проблему и не уменьшу ее до минимума.
hsmyers
1
hsmyers: к счастью, понимание protoне слишком сложно, и как только вы освоите его, это сделает вашу жизнь намного проще.
user0721090601