Aangepaste fouten voor RegexParsers

Kan iemand me helpen het volgende gedrag te begrijpen: parseAll (parseIf, "If bla blablaa) zou moeten resulteren in wordt verwacht . In plaats daarvan krijg ik altijd tekenreeksaanpassing regex 'is \ b' verwacht maar 'b' gevonden . Ik denk dat het iets met whitespaces te maken heeft, omdat "If bla is blablaa" (let op de spaties aan het begin) resulteert in hetzelfde gedrag. Ik probeerde het met StandardTokenParsers en alles werkte prima. Maar STP ondersteunt helaas geen regex. Vervolgvraag: Hoe zou ik RegexParsers moeten veranderen, zodat het een reeks snaren gebruikt in plaats van een reeks tekens? Dat zou het melden van fouten veel eenvoudiger maken.

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 antwoord

Dit probleem is heel raar. Wanneer u | aanroept en beide zijden zijn mislukt, wordt de zijde waar de fout is opgetreden laatste geselecteerd, waarbij de voorkeur wordt gegeven aan de linkerzijde.

Wanneer u rechtstreeks probeert te parseren met giverRole , produceert het het resultaat dat u verwacht. Als u echter een succesvolle overeenkomst vóór de fout toevoegt, produceert deze het resultaat dat u ziet.

De reden is nogal subtiel - ik heb het alleen ontdekt door log -instructies over alle parsers te sprenkelen. Om dit te begrijpen, moet u begrijpen hoe RegexParser spaties overslaat . Spaties worden met name overgeslagen op accepteren . Omdat failure geen accept aanroept, worden spaties niet overgeslagen.

Terwijl het falen van kwIs plaatsvindt op b , als de spatie zoals overgeslagen, gebeurt de fout failure op de spatie achter Als . Hier:

If bla blablaa
   ^ kwIs fails here
  ^ failure fails here

Daarom krijgt de foutmelding op kwIs voorrang boven de regel die ik heb genoemd.

U kunt dit probleem omzeilen door ervoor te zorgen dat de parser de spaties overslaat zonder iets aan te passen. Het is belangrijk dat dit patroon altijd overeenkomt, anders krijg je een nog meer verwarrende foutmelding. Hier is een suggestie waarvan ik denk dat het werkt:

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

Een andere oplossing is om acceptIf of acceptMatch te gebruiken in plaats van de impliciete regex-acceptatie te gebruiken. In dat geval kunt u een aangepast foutbericht opgeven.

0
toegevoegd
@awertos Ik heb eindelijk ontdekt wat het probleem was. De eerste oplossing was meer geschikt dan ik dacht - ik veranderde het gewoon om de fout op de juiste plaats te laten verschijnen, door geen niet-spatie karakters te gebruiken.
toegevoegd de auteur Daniel C. Sobral, de bron
Ik heb een parser geschreven met regex en lexicale vaardigheid en acceptIf gebruikt zoals je suggereert. Maar het is nog steeds vreemd dat het bovenstaande voorbeeld niet werkt zoals verwacht. Bedankt voor je hulp
toegevoegd de auteur awertos, de bron