Errores personalizados para RegexParsers

Que alguien me ayude a entender el siguiente comportamiento: parseAll (parseIf, "If bla blablaa") debe dar como resultado que se espera . En cambio, siempre obtengo cadena que coincide con expresiones regulares 'es \ b' esperado pero 'b' encontrado . Supongo que tiene algo que ver con los espacios en blanco porque "Si bla es blablaa" (observe que los espacios en blanco al principio) produce el mismo comportamiento. Lo probé con StandardTokenParsers y todo funcionó bien. Pero, lamentablemente, STP no admite expresiones regulares. Pregunta de seguimiento: ¿Cómo tendría que alterar RegexParsers para que use una secuencia de cadenas en lugar de una secuencia de caracteres? Eso haría que el informe de errores sea mucho más fácil.

lazy val parseIf = roleGiverIf ~ giverRole

lazy val roleGiverIf =
  kwIf ~> identifier | failure("""A rule must begin with if""")
lazy val giverRole =
  kwIs ~> identifier | failure("""is expected""")

lazy val keyword =
  kwIf | kwAnd | kwThen | kwOf | kwIs | kwFrom | kwTo

lazy val identifier =
  not(keyword) ~ roleEntityLiteral
// ...

def roleEntityLiteral: Parser[String] =
  """([^"\p{Cntrl}\\]|\\[\\/bfnrt]|\\u[a-fA-F0-9]{4})\S*""".r 
def kwIf: Parser[String] = "If\\b".r
def kwIs: Parser[String] = "is\\b".r

// ...

parseAll(parseIf, "If bla blablaa") match {
  case Success(parseIf, _) => println(parseIf)
  case Failure(msg, _) => println("Failure: " + msg)
  case Error(msg, _) => println("Error: " + msg)
1

1 Respuestas

Este problema es muy raro. Cuando llama a | y ambos lados son fallos, se selecciona el lado donde ocurrió el fallo último , los vínculos favorecen al lado izquierdo.

Cuando intenta analizar directamente con giverRole , produce el resultado que espera. Sin embargo, si agrega una coincidencia exitosa antes de la falla, produce el resultado que está viendo.

El motivo es bastante sutil: solo lo descubrí mediante la aspersión de las declaraciones de log en todos los analizadores. Para entenderlo, debe entender cómo RegexParser salta espacios . Específicamente, los espacios se omiten en accept . Como failure no llama a accept , no omite espacios.

Mientras que la falla de kwIs ocurre en b , como el espacio omitido, la falla de falla ocurre en el espacio después de Si . Aquí:

If bla blablaa
   ^ kwIs fails here
  ^ failure fails here

Por lo tanto, el mensaje de error en kwIs tiene prioridad por la regla que mencioné.

Puede solucionar este problema haciendo que el analizador omita los espacios sin hacer coincidir nada. Es importante que este patrón siempre coincida, o recibirás un mensaje de error aún más confuso. Aquí hay una sugerencia que creo que funciona:

"\\b|$".r ~ failure("is expected")

Otra solución es usar acceptIf o acceptMatch en lugar de usar la expresión implícita de aceptación de expresiones regulares, en cuyo caso puede proporcionar un mensaje de error personalizado.

0
agregado
@awertos finalmente descubrí cuál era el problema. La primera solución fue más apropiada de lo que pensé: simplemente la cambié para hacer que el error aparezca en el lugar adecuado, al no consumir ningún carácter que no sea espacio.
agregado el autor Daniel C. Sobral, fuente
He escrito un analizador con expresiones regulares y habilidades léxicas y he usado acceptIf como usted sugiere. Pero aún es extraño que el ejemplo anterior no funcione como se esperaba. Gracias por tu ayuda
agregado el autor awertos, fuente