devolver mensajes de error significativos de un programa de análisis escrito con Scala Analizador combinadores
-
08-10-2019 - |
Pregunta
Trato de escribir un analizador de Scala utilizando Analizador combinadores. Si Emparejo de forma recursiva,
def body: Parser[Body] =
("begin" ~> statementList ) ^^ {
case s => { new Body(s); }
}
def statementList : Parser[List[Statement]] =
("end" ^^ { _ => List() } )|
(statement ~ statementList ^^ { case statement ~ statementList => statement :: statementList })
Entonces consigo buenas ErrorMessages cada vez que hay un fallo en un comunicado. Sin embargo, se trata de código largo feo. Así que me gustaría que escribir esto:
def body: Parser[Body] =
("begin" ~> statementList <~ "end" ) ^^ {
case s => { new Body(s); }
}
def statementList : Parser[List[Statement]] =
rep(statement)
Este código funciona, pero sólo imprime mensajes significativos si hay un error en el primer estado de cuenta. Si se trata de una declaración posterior, el mensaje se vuelve dolorosamente inservible, porque el analizador quiere ver toda la declaración errónea reemplazado por el "fin" token:
Exception in thread "main" java.lang.RuntimeException: [4.2] error: "end" expected but "let" found
let b : string = x(3,b,"WHAT???",!ERRORHERE!,7 )
^
Mi pregunta: ¿hay una manera de conseguir rep y repsep que trabaja en combinación con los mensajes de error significativos, ese lugar el cursor en el lugar correcto en lugar de en el inicio del fragmento repitiendo?
Solución 2
Ah, encontró la solución! Resulta que es necesario utilizar la frase de la función en el analizador principal de devolver un nuevo analizador que está menos inclinado a realizar un seguimiento posterior. (Me pregunto qué significa exactamente, tal vez que si encuentra un salto de línea no va a rastrear?) seguimiento de la última posición en wich se produjo un fallo.
cambiado:
def parseCode(code: String): Program = {
program(new lexical.Scanner(code)) match {
case Success(program, _) => program
case x: Failure => throw new RuntimeException(x.toString())
case x: Error => throw new RuntimeException(x.toString())
}
}
def program : Parser[Program] ...
en
def parseCode(code: String): Program = {
phrase(program)(new lexical.Scanner(code)) match {
case Success(program, _) => program
case x: Failure => throw new RuntimeException(x.toString())
case x: Error => throw new RuntimeException(x.toString())
}
}
def program : Parser[Program] ...
Otros consejos
Puede hacerlo mediante la combinación de un "hecho en casa" método rep
con no dar marcha atrás dentro de sentencias. Por ejemplo:
scala> object X extends RegexParsers {
| def myrep[T](p: => Parser[T]): Parser[List[T]] = p ~! myrep(p) ^^ { case x ~ xs => x :: xs } | success(List())
| def t1 = "this" ~ "is" ~ "war"
| def t2 = "this" ~! "is" ~ "war"
| def t3 = "begin" ~ rep(t1) ~ "end"
| def t4 = "begin" ~ myrep(t2) ~ "end"
| }
defined module X
scala> X.parse(X.t4, "begin this is war this is hell end")
res13: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] =
[1.27] error: `war' expected but ` ' found
begin this is war this is hell end
^
scala> X.parse(X.t3, "begin this is war this is hell end")
res14: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] =
[1.19] failure: `end' expected but ` ' found
begin this is war this is hell end
^