Припинення граматики Раку в EOS (кінець рядка)


9

Під час написання перекладача однієї мови музики на іншу (ABC до Alda) як привід вивчити DSL-здатність Raku, я помітив, що, здається, не існує способу припинити використання .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 відображає все так, як має бути, тож мені не ясно, наскільки справжній FAIL? Залишається питання: "Як правильно написати граматику, яка без помилок розбирає кілька рядків?"


Я не хочу втручатися у ваш процес навчання, але про всяк випадок, коли ви цього не знали, є модуль ABC .
raiph

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> завжди визначатиметься таким чином.


Це пояснює той факт, що об'єкт відповідності повернувся "так, що справжній навіть з" FAIL ". Я думав, що це може бути так; Я перейду до додавання необхідних жетонів для реального проекту;)
hsmyers

Справжня граматика не схожа на автоматичне вставлення <.ws> *; ймовірно, через додаткові шари, що беруть участь за межами <score>. Ваша пропозиція використовувати прото виглядає добре, як тільки я зможу обернути голову навколо техніки…
hsmyers


Я ненавиджу код, який мені не потрібен - більше для налагодження, і тут є естетика всього цього! Актуальною проблемою є те, що ABC не проклятає про простори. Є деякі винятки, але за великим рахунком вони можуть траплятися майже де завгодно. Випадок "використання" - це розбірливість, дещо схожа на коми у великорядних рядках. Я перегляну проблему за необхідності, поки не зрозумію проблему і не зменшу її до мінімуму.
hsmyers

1
hsmyers: на щастя, розуміння protoне надто важке, і як тільки ви отримаєте повісити це, це робить ваше життя набагато простішим.
user0721090601
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.