Zatrzymywanie gramatyki Raku w EOS (End of String)

9

W trakcie pisania tłumacza jednego języka muzycznego na inny (od ABC do Alda) jako wymówki do nauki umiejętności Raku DSL zauważyłem, że nie ma sposobu na zakończenie .parse! Oto mój skrócony kod demonstracyjny:

#!/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;

I to ostatnia część wyświetlacza Grammer :: Tracer pokazuje mój problem.

|  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
」

W wierszu od drugiego do ostatniego słowo FAIL mówi mi, że uruchomienie .parse nie ma możliwości wyjścia. Zastanawiam się, czy to jest poprawne? .Say wyświetla wszystko tak, jak powinno być, więc nie jestem pewien, jak prawdziwy jest FAIL? Pozostaje pytanie: „Jak poprawnie napisać gramatykę, która analizuje wiele wierszy bez błędów?”

hsmyers
źródło
Nie chcę ingerować w proces uczenia się, ale na wypadek, gdybyś nie był tego świadomy, istnieje moduł ABC .
raiph
1
Przynajmniej nie wybraliśmy tych samych utworów do przetestowania!
hsmyers,

Odpowiedzi:

10

Kiedy używasz debugera gramatyki, pozwala dokładnie zobaczyć, w jaki sposób silnik analizuje ciąg - awarie są normalne i oczekiwane. Uważane na przykład za pasujące a+b*do łańcucha aab. Powinieneś dostać dwa dopasowania dla „a”, a następnie błąd (ponieważ bnie jesta ), ale wtedy spróbuje ponownie z bpowodzeniem.

Można to łatwiej zobaczyć, jeśli wykonasz naprzemiennie z ||(co wymusza porządek). Jeśli masz

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

i parsujesz zdanie „Mam kiwi”, zobaczysz, że najpierw pasuje do „Mam”, potem dwa kończą się niepowodzeniem z „jabłkiem” i „pomarańczą”, a na końcu pasują do „kiwi”.

Teraz spójrzmy na twoją skrzynkę:

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

Błąd tutaj jest normalny: w pewnym momencie zabraknie <score>tokenów, więc błąd jest nieunikniony. Kiedy tak się dzieje, silnik gramatyki może przejść do wszystkiego, co nastąpi po <score>+gramatyce. Ponieważ nie ma nic, to niepowodzenie powoduje dopasowanie całego łańcucha (ponieważ TOPpasuje do niejawnego/^…$/ ).

Możesz również rozważyć przepisanie gramatyki z regułą, która wstawia <.ws> * automatycznie (chyba że ważne jest, aby była to pojedyncza spacja):

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

Ponadto, IME, możesz chcieć również dodać token proto dla uc / lc, ponieważ kiedy będziesz [ <foo> | <bar> ], zawsze będziesz mieć jeden z nich niezdefiniowany, co może sprawić, że przetwarzanie ich w klasie działań będzie trochę denerwujące. Możesz spróbować:

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

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

$<letter> zawsze będzie zdefiniowane w ten sposób.

użytkownik 0721090601
źródło
To wyjaśnia fakt, że obiekt dopasowania zwrócił ”, więc jest tak samo prawdą nawet w przypadku„ FAIL ”. Myślałem, że może tak być; Wrócę do dodawania niezbędnych tokenów dla prawdziwego projektu;)
hsmyers
Prawdziwa gramatyka nie lubi automatycznego wstawiania <.ws> *; prawdopodobnie z powodu dodatkowych warstw zaangażowanych poza <score>. Twoja sugestia użycia proto wygląda dobrze, gdy tylko mogę owinąć głowę techniką…
hsmyers
Nienawidzę posiadania kodu, którego nie potrzebuję - więcej do debugowania, a potem jest estetyka tego wszystkiego! Rzeczywistym problemem jest to, że ABC nie obchodzi spacji. Istnieją pewne wyjątki, ale zasadniczo mogą one wystąpić prawie wszędzie. Przypadek „use” jest kwestią czytelności, podobnie jak przecinki dużymi cyframi. W razie potrzeby ponownie przejdę do problemu, dopóki go nie zrozumiem i nie ograniczę go do minimum.
hsmyers
1
hsmyers: na szczęście zrozumienie protonie jest zbyt trudne, a kiedy już to zrozumiesz, znacznie ułatwi ci życie.
user0721090601