Arrêt de la grammaire Raku sur EOS (fin de chaîne)

9

En train d'écrire un traducteur d'une langue musicale à une autre (ABC à Alda) comme excuse pour apprendre la capacité Raku DSL, j'ai remarqué qu'il ne semblait pas y avoir de moyen de terminer un .parse! Voici mon code de démonstration abrégé:

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

Et c'est la dernière partie de l'affichage Grammer :: Tracer qui illustre mon problème.

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

Sur l'avant-dernière ligne, le mot FAIL m'indique que le cycle .parse n'a aucun moyen de quitter. Je me demande si c'est correct? Le .say affiche tout comme il se doit, donc je ne sais pas à quel point le FAIL est réel? La question demeure: "Comment écrire correctement une grammaire qui analyse plusieurs lignes sans erreur?"

hsmyers
la source
Je ne veux pas interférer dans votre processus d'apprentissage, mais juste au cas où vous ne seriez pas au courant, il y a un module ABC .
raiph
1
Eh bien, au moins, nous n'avons pas choisi les mêmes morceaux pour tester!
hsmyers

Réponses:

10

Lorsque vous utilisez le débogueur de grammaire, il vous permet de voir exactement comment le moteur analyse la chaîne - les échecs sont normaux et attendus. Considéré, par exemple, la correspondance a+b*avec la chaîne aab. Vous devriez obtenir deux correspondances pour 'a', suivies d'un échec (car ce bn'est pas le cas a), mais il réessayera avec bet correspondra avec succès.

Cela pourrait être plus facile à voir si vous faites une alternance avec ||(qui applique l'ordre). Si tu as

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

et vous analysez la phrase "J'ai un kiwi", vous le verrez d'abord correspondre "J'ai un", suivi de deux échecs avec "pomme" et "orange", et enfin un match avec "kiwi".

Voyons maintenant votre cas:

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

L'échec ici est normal: à un moment donné, nous manquerons de <score>jetons, donc un échec est inévitable. Lorsque cela se produit, le moteur de grammaire peut passer à tout ce qui vient après <score>+dans votre grammaire. Puisqu'il n'y a rien, cet échec entraîne en fait une correspondance de la chaîne entière (car TOPcorrespond à implicite /^…$/).

En outre, vous pourriez envisager de réécrire votre grammaire avec une règle qui insère <.ws> * automatiquement (sauf s'il est important que ce soit un seul espace):

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

De plus, IME, vous voudrez peut-être également ajouter un jeton de proto pour l'uc / lc, car lorsque vous en [ <foo> | <bar> ]aurez, l'un d'eux sera toujours indéfini, ce qui peut rendre le traitement dans une classe d'actions un peu ennuyeux. Tu pourrais essayer:

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

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

$<letter> sera toujours défini de cette façon.

user0721090601
la source
Cela explique le fait que l'objet de correspondance soit retourné 'so's out as true même avec le' FAIL '. J'ai pensé que cela pourrait être le cas; Je reviendrai sur l'ajout des jetons nécessaires pour le vrai projet;)
hsmyers
La vraie grammaire ne semble pas aimer insérer automatiquement <.ws> *; probablement en raison de couches supplémentaires impliquées au-delà de <score>. Votre suggestion d'utiliser le proto semble bonne dès que je serai capable de
comprendre
Je déteste avoir du code dont je n'ai pas besoin - plus à déboguer, et puis il y a l'esthétique de tout cela! Le vrai problème est que ABC ne se soucie pas des espaces. Il existe quelques exceptions, mais dans l'ensemble, elles peuvent se produire presque partout. Le cas d'utilisation est une question de lisibilité un peu comme les virgules dans les chaînes à gros chiffres. Je reverrai le problème au besoin jusqu'à ce que je comprenne le problème et que je le réduise au minimum.
hsmyers
1
hsmyers: heureusement, la compréhension proton'est pas trop difficile et une fois que vous avez compris, cela vous facilite la vie.
user0721090601