2012-02-22 10 views
5

Considere ejemplo del analizador de esta manera:Combinadores de analizadores: ¿replicar permite retroceder?

object TestParser extends RegexParsers { 
    override protected val whiteSpace = """[ \t]*""".r 

    def eol = """(\r?\n)+""".r 
    def item = "[a-zA-Z][a-zA-Z0-9-]*".r 
    def list = "items:" ~> rep1sep(item,",") 
    def constraints = "exclude:" ~> item 

    def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol)) 
} 

Si trato de analizar esta entrada (sin dos líneas contiene excluir las obras OK):

items: item1, item2, item3, item3, item4 
items: item2, item3, item3, item5, item4  
items: item4, item5, item6, item10  
items: item1, item2, item3 
exclude: item1 
exclude: item2 

consigo siguiente error:

[5.5] failure: `items:' expected but `e' found 

     exclude: item1 

    ^

El problema es obvio esta línea:

def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol)) 

Cuál es la razón por la que no funciona. ¿Tiene algo que ver con retroceder? ¿Qué alternativas tengo para que funcione?

+0

Si alguien tiene una propuesta para obtener un mejor título de la pregunta, hágamelo saber. No estoy seguro si incluso tiene sentido. – PrimosK

Respuesta

5

Se necesitaría un EOL entre sus listas y sus limitaciones

(rep1sep(list, eol) <~ eol) ~ repsep(constraint,eol) 

Completar la respuesta:

Su gramática especifica EOL como separador entre las listas, no un terminador. Aceptaría una entrada donde aparece el primer exclude justo después del último item3 (con un espacio en blanco, pero no una línea nueva).

Después de que su analizador llegue al no deseado eol, busca items, y encuentra excludes en su lugar. Que da el mensaje de error que se muestra. Entonces, el analizador REALMENTE retrocede, a la nueva línea anterior. Considera la posibilidad de que la parte de la lista se detenga allí y busque las exclusiones. Pero si encuentra un eol en su lugar. Entonces otro posible mensaje de error sería "excludes expected, eol found", que en este caso habría sido más útil

Cuando hay una opción en la gramática, y ninguna rama tiene éxito, el analizador regresa el error a la posición más lejana, que normalmente es la estrategia correcta. Supongamos que la gramática permite un "if" o un "for", y la entrada es "if !!!". En la rama if, el error sería algo así como "(" expected, "!" found. En la rama for, el mensaje sería "for expected, if found". Claramente, el mensaje de la rama if, que aparece en el segundo token, es mucho mejor que el mensaje de la rama for, en el primer token y no es relevante en absoluto.

Sobre la cuestión del separador/terminador, puede considerar:

  • separador (; en Pascal): repsep(item, separator)
  • terminador (; en C): rep(item <~ terminator)
  • flexibles: repsep(item, separator) <~ separator?

el último permitiría un solo separador después de ningún elemento. Si esto no es deseable, tal vez (rep1sep(item, separator) <~ separator?)?.

+0

Guau .. ¡Genial! ¿Pero cuál es la razón por la que funciona de esta manera? – PrimosK

+0

Supongo porque los "sep" s están entre la "lista" y no después de cada repetición. Entonces el analizador no puede "salir" del primer 'rep1sep' y espera una" lista "después de cada" eol ". – paradigmatic

+0

¡¡Gran respuesta !! TY – PrimosK

Cuestiones relacionadas