Detener la gramática de Raku en EOS (Fin de cadena)

9

En el proceso de escribir un traductor de un lenguaje musical a otro (ABC a Alda) como una excusa para aprender la habilidad Raku DSL, ¡me di cuenta de que no parece haber una manera de terminar a .parse! Aquí está mi código de demostración acortado:

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

Y es la última parte de la pantalla Grammer :: Tracer la que demuestra mi problema.

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

En la penúltima línea, la palabra FAIL me dice que la ejecución .parse no tiene forma de salir. Me pregunto si esto es correcto? El .say muestra todo como debe ser, así que no tengo claro qué tan real es el FALLO. La pregunta sigue siendo: "¿Cómo escribo correctamente una gramática que analiza múltiples líneas sin error?"

hsmyers
fuente
No quiero interferir en su proceso de aprendizaje, pero en caso de que no lo supiera, hay un módulo ABC .
raiph
1
¡Bueno, al menos no elegimos las mismas canciones para probar!
hsmyers

Respuestas:

10

Cuando utiliza el depurador gramatical, le permite ver exactamente cómo el motor analiza la cadena: los errores son normales y esperados. Considerado, por ejemplo, coincidir a+b*con la cadena aab. Debería obtener dos coincidencias para 'a', seguido de una falla (porque bno lo es a), pero luego volverá a intentarlo by coincidirá con éxito.

Esto podría verse más fácilmente si realiza una alternancia con ||(que impone el orden). Si usted tiene

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

y analiza la frase "Tengo un kiwi", verá que primero coincide con "Tengo un", seguido de dos fallas con "manzana" y "naranja", y finalmente una coincidencia con "kiwi".

Ahora veamos su caso:

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

El fallo aquí es normal: en algún momento nos quedaremos sin <score>tokens, por lo que un fallo es inevitable. Cuando eso sucede, el motor de gramática puede pasar a lo que viene después de la <score>+gramática. Como no hay nada, ese fallo en realidad da como resultado una coincidencia de toda la cadena (porque TOPcoincide con implícito /^…$/).

Además, puede considerar reescribir su gramática con una regla que inserte <.ws> * automáticamente (a menos que sea importante que sea solo un espacio):

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

Además, IME, es posible que también desee agregar un token de proto para el uc / lc, porque cuando lo tenga [ <foo> | <bar> ], siempre tendrá uno de ellos indefinido, lo que puede hacer que procesarlos en una clase de acciones sea un poco molesto. Tu podrías intentar:

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

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

$<letter> siempre se definirá de esta manera.

usuario0721090601
fuente
Esto explica el hecho de que el objeto de coincidencia devolvió 'también es cierto incluso con' FALLO '. Pensé que ese podría ser el caso; Volveré a agregar tokens necesarios para el proyecto real;)
hsmyers
Parece que a la gramática real no le gusta insertar <.ws> * automáticamente; probablemente debido a capas adicionales involucradas más allá de <punto>. Su sugerencia de utilizar proto ve bien, tan pronto como soy capaz de hacerme a la técnica ...
hsmyers
Odio tener un código que no necesito, ¡más para depurar, y luego está la estética de todo! El problema real es que a ABC no le importan los espacios. Hay algunas excepciones, pero en general, pueden ocurrir en casi cualquier lugar. El caso de "uso" es una cuestión de legibilidad algo así como comas en cadenas de dígitos grandes. Revisaré el problema según sea necesario hasta que lo entienda y lo haya reducido al mínimo.
hsmyers
1
hsmyers: afortunadamente, comprender protono es demasiado difícil y una vez que te acostumbras te hace la vida mucho más fácil.
user0721090601