2010-03-04 10 views
16

Estoy creando un DSL y utilizando la biblioteca del combinador del analizador de Scala para analizar el DSL. El DSL sigue una sintaxis simple similar a Ruby. Una fuente puede contener una serie de bloques que se ven así:¿Cómo puedo crear un combinador de analizador en el que las terminaciones de línea son importantes?

create_model do 
    at 0,0,0 
end 

Los finales de línea son significativos en el DSL, ya que se utilizan efectivamente como terminadores comunicado.

me escribió un analizador Scala que tiene este aspecto:

class ML3D extends JavaTokenParsers { 
    override val whiteSpace = """[ \t]+""".r 

    def model: Parser[Any] = commandList 
    def commandList: Parser[Any] = rep(commandBlock) 
    def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end" 
    def eol: Parser[Any] = """(\r?\n)+""".r 
    def command: Parser[Any] = commandName~opt(commandLabel) 
    def commandName: Parser[Any] = ident 
    def commandLabel: Parser[Any] = stringLiteral 
    def statementList: Parser[Any] = rep(statement) 
    def statement: Parser[Any] = functionName~argumentList~eol 
    def functionName: Parser[Any] = ident 
    def argumentList: Parser[Any] = repsep(argument, ",") 
    def argument: Parser[Any] = stringLiteral | constant 
    def constant: Parser[Any] = wholeNumber | floatingPointNumber 
} 

Desde finales de línea de asunto, me hicieron caso omiso whiteSpace de modo que sólo va a tratar espacios y tabulaciones como espacios en blanco (en lugar de tratar nuevas líneas como espacios en blanco, y así ignorarlos).

Esto funciona, a excepción de la declaración "final" para commandBlock. Como mi archivo de origen contiene una línea nueva, el analizador se queja de que esperaba solo un end pero obtuvo una nueva línea después de la palabra clave end.

Así que cambió la definición commandBlock 's a esto:

def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol) 

(Es decir, he añadido una nueva línea opcional después de "final").

Pero ahora, al analizar el archivo de origen, me sale el siguiente error:

[4.1] failure: `end' expected but `' found 

I que esto se debe a que, después de que chupa la nueva línea de salida, el analizador está encontrando una cadena vacía que piensa que no es válido, pero no estoy seguro de por qué lo está haciendo.

¿Algún consejo sobre cómo solucionar esto? Podría extender el analizador incorrecto desde la biblioteca del combinador de analizadores de Scala, por lo que cualquier sugerencia sobre cómo crear una definición de lenguaje con nuevos caracteres de línea significativos también es bienvenida.

Respuesta

9

Me sale el mismo error en ambos sentidos, pero creo que lo malinterpretas. Lo que está diciendo es que está esperando un end, pero ya ha llegado al final de la entrada.

Y la razón por la que está sucediendo es que end se está leyendo como una declaración. Ahora, estoy seguro de que hay una buena manera de resolver esto, pero no tengo suficiente experiencia con los analizadores de Scala. Parece que el camino a seguir sería utilizar analizadores de tokens con una parte de escaneo, pero no pude encontrar la manera de hacer que el analizador de tokens estándar no trate las nuevas líneas como espacios en blanco.

tanto, aquí hay una alternativa:

import scala.util.parsing.combinator.JavaTokenParsers 

class ML3D extends JavaTokenParsers { 
    override val whiteSpace = """[ \t]+""".r 
    def keywords: Parser[Any] = "do" | "end" 
    def identifier: Parser[Any] = not(keywords)~ident 

    def model: Parser[Any] = commandList 
    def commandList: Parser[Any] = rep(commandBlock) 
    def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol) 
    def eol: Parser[Any] = """(\r?\n)+""".r 
    def command: Parser[Any] = commandName~opt(commandLabel) 
    def commandName: Parser[Any] = identifier 
    def commandLabel: Parser[Any] = stringLiteral 
    def statementList: Parser[Any] = rep(statement) 
    def statement: Parser[Any] = functionName~argumentList~eol 
    def functionName: Parser[Any] = identifier 
    def argumentList: Parser[Any] = repsep(argument, ",") 
    def argument: Parser[Any] = stringLiteral | constant 
    def constant: Parser[Any] = wholeNumber | floatingPointNumber 
} 
+0

Me gusta su interpretación del mensaje de error. Me pregunto si hay una manera de hacer que el analizador imprima lo que intenta igualar a medida que avanza. Eso facilitaría la resolución de problemas. – huynhjl

+4

Puede ajustar cualquier referencia a una producción que aparezca en el lado derecho de otra producción en 'log (...)' y obtendrá un resultado de rastreo siempre que el análisis intente hacer coincidir ese no terminal. Por ejemplo, para registrar un intento particular de emparejar 'modelo', reemplace esa referencia no terminal en una regla con' log (modelo) '. –

+0

Ah, sí, veo el problema ahora - 'end' se leía bajo' functionName', ya que * era * un nombre de función válido. Implementé tus cambios y funciona bien ahora, muchas gracias. – mipadi

0

Puede override la protected val whiteSpace (una expresión regular) cuyo valor predeterminado es """\s+""".r o override el método protected def handleWhiteSpace(...) si necesita más control que se logra fácilmente con una expresión regular. Ambos miembros se organizan en RegexParsers, que es la clase base para JavaTokenParsers.

+0

estoy preponderantes con 'whiteSpace' (ver el código anterior), pero que todavía da lugar a un error. – mipadi

+0

Sí, ya veo. Intenta cambiar ese 'opt (eol)' a 'eol *' (o, igualmente, 'rep (eol)'). –

+0

No funcionó. Resultó en el mismo error. – mipadi

Cuestiones relacionadas