Stoppen der Raku-Grammatik bei EOS (End of String)

9

Beim Schreiben eines Übersetzers einer Musiksprache in eine andere (ABC an Alda) als Ausrede für das Erlernen der Raku-DSL-Fähigkeit fiel mir auf, dass es anscheinend keine Möglichkeit gibt, a zu beenden .parse! Hier ist mein verkürzter Demo-Code:

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

Und es ist der letzte Teil des Grammer :: Tracer-Displays, der mein Problem demonstriert.

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

In der vorletzten Zeile sagt mir das Wort FAIL, dass der .parse-Lauf nicht beendet werden kann. Ich frage mich, ob das richtig ist? Das .say zeigt alles so an, wie es sein sollte, also bin ich mir nicht sicher, wie real der FAIL ist? Die Frage bleibt: "Wie schreibe ich eine Grammatik, die mehrere Zeilen fehlerfrei analysiert, richtig?"

hsmyers
quelle
Ich möchte mich nicht in Ihren Lernprozess einmischen, aber für den Fall, dass Sie es nicht wissen, gibt es ein ABC-Modul .
Raiph
1
Zumindest haben wir nicht die gleichen Songs zum Testen ausgewählt!
Hsmyers

Antworten:

10

Wenn Sie den Grammatik-Debugger verwenden, können Sie genau sehen, wie die Engine die Zeichenfolge analysiert. Fehler sind normal und werden erwartet. Wird beispielsweise als Übereinstimmung a+b*mit der Zeichenfolge betrachtet aab. Sie sollten zwei Übereinstimmungen für 'a' erhalten, gefolgt von einem Fehler (weil dies bnicht der Fall ist a), aber dann wird es erneut versucht bund erfolgreich abgeglichen.

Dies ist möglicherweise leichter zu erkennen, wenn Sie einen Wechsel mit ||(der die Reihenfolge erzwingt) durchführen. Wenn Sie haben

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

und wenn Sie den Satz "Ich habe eine Kiwi" analysieren, sehen Sie, dass er zuerst mit "Ich habe eine" übereinstimmt, gefolgt von zwei Fehlern mit "Apfel" und "Orange" und schließlich mit "Kiwi".

Schauen wir uns nun Ihren Fall an:

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

Der Fehler hier ist normal: Irgendwann werden uns die <score>Token ausgehen, sodass ein Fehler unvermeidlich ist. In diesem Fall kann die Grammatik-Engine zu dem wechseln, was nach dem <score>+in Ihrer Grammatik steht. Da es nichts gibt, führt dieser Fehler tatsächlich zu einer Übereinstimmung der gesamten Zeichenfolge (da TOPÜbereinstimmungen mit impliziten Zeichenfolgen vorliegen /^…$/).

Sie können Ihre Grammatik auch mit einer Regel umschreiben, die <.ws> * automatisch einfügt (es sei denn, es ist wichtig, dass es sich nur um ein einzelnes Leerzeichen handelt):

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

Außerdem, IME, möchten Sie möglicherweise auch ein Prototoken für das uc / lc hinzufügen, da bei Ihnen [ <foo> | <bar> ]immer eines davon undefiniert ist, was die Verarbeitung in einer Aktionsklasse etwas ärgerlich machen kann. Du könntest es versuchen:

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

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

$<letter> wird immer so definiert.

user0721090601
quelle
Dies erklärt die Tatsache, dass das zurückgegebene Übereinstimmungsobjekt auch mit 'FAIL' als wahr ausgegeben wurde. Ich dachte, das könnte der Fall sein; Ich werde wieder die notwendigen Token für das eigentliche Projekt hinzufügen;)
hsmyers
Die eigentliche Grammatik scheint es nicht zu mögen, <.ws> * automatisch einzufügen. wahrscheinlich aufgrund zusätzlicher Ebenen, die über <score> hinausgehen. Ihr Vorschlag, Proto zu verwenden, sieht gut aus, sobald ich in der Lage bin, meinen Kopf um die Technik zu wickeln…
hsmyers
Ich hasse es, Code zu haben, den ich nicht brauche - mehr zum Debuggen, und dann ist da noch die Ästhetik von allem! Das eigentliche Problem ist, dass ABC sich nicht um Leerzeichen kümmert. Es gibt einige Ausnahmen, aber im Großen und Ganzen können sie fast überall auftreten. Der Anwendungsfall ist eine Frage der Lesbarkeit, ähnlich wie Kommas in großen Ziffernfolgen. Ich werde das Problem nach Bedarf erneut prüfen, bis ich das Problem verstanden und auf ein Minimum reduziert habe.
Hsmyers
1
hsmyers: Zum Glück ist das Verstehen protonicht allzu schwer, und wenn Sie erst einmal den Dreh raus haben, wird Ihr Leben so viel einfacher.
user0721090601