Erori personalizate pentru RegexParsers

Poate cineva să mă ajute să înțeleg următorul comportament: parseAll (parseIf, "Dacă bla blablaa") ar trebui să ducă la este de așteptat . În schimb, întotdeauna obțin codul care corespunde regexului "este \ b ', dar" b "a fost găsit . Cred că are legătură cu spațiile albe, deoarece "Dacă bla este blablaa" (observați spațiile albe de la început) rezultă același comportament. Am încercat-o cu StandardTokenParsers și totul a mers bine. Dar, din păcate, STP nu acceptă regex. Următoarea întrebare: Cum ar trebui să modifice RegexParsers astfel încât să folosească o secvență de șiruri în loc de o secvență de caractere? Acest lucru ar face raportarea erorilor mult mai ușoară.

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 răspunsuri

Această problemă este foarte ciudată. Atunci când apelați | și ambele părți sunt eșecuri, este selectată partea în care sa produs eșecul ultima , legăturile favorizează partea stângă.

Când încercați să parsezi direct cu giverRole , produce rezultatul pe care îl aștepți. Dacă adăugați un meci de succes înainte de eșec, totuși, acesta produce rezultatul pe care îl vedeți.

Motivul este destul de subtil - am găsit-o doar prin stropirea instrucțiunilor log pe toate parserii. Pentru a o înțelege, trebuie să înțelegeți modul în care RegexParser săriți spațiile . În mod specific, spațiile sunt ignorate pe accept . Deoarece failure nu sună accept , nu trece peste spații.

În timp ce eșecul kwIs se întâmplă pe b , deoarece spațiul este omis, eșecul eșec > Dacă . Aici:

If bla blablaa
   ^ kwIs fails here
  ^ failure fails here

Prin urmare, mesajul de eroare de pe kwIs are prioritate de regula pe care am menționat-o.

Puteți rezolva această problemă făcând parserul sări peste spații fără a se potrivi nimic. Este important ca acest model să se potrivească întotdeauna sau veți primi un mesaj de eroare și mai confuz. Iată o sugestie care cred că funcționează:

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

O altă soluție este să utilizați acceptIf sau acceptMatch în loc să utilizați acceptul implicit de regex, caz în care puteți furniza un mesaj de eroare adaptat.

0
adăugat
@awertos Am aflat în sfârșit ce a fost problema. Prima soluție a fost mai potrivită decât am crezut - tocmai am schimbat-o pentru a face eroarea să apară la locul potrivit, fără a consuma caractere non-spațiu.
adăugat autor Daniel C. Sobral, sursa
Am scris un parser cu abilități regex și lexicale și a folosit acceptarea așa cum sugerați. Dar este încă ciudat că exemplul de mai sus nu va funcționa așa cum era de așteptat. Multumesc pentru ajutor
adăugat autor awertos, sursa