How to provide an explicit error/failure message in the Scala fastparse library?

164 views Asked by At

I'm using Li Haoyi's FastParse library. I have several situations where I'd like to provide explicit failure messages.

For example:

  def courseRE[p: P]: P[Regex] =
    P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.|*+[]()-^$").repX(1).!).map { re =>
      try { re.r }
      catch { case e => failure(s"Ill-formed regular expression: ${re}.") }
    }

But there is (apparently) no failure function.

Any suggested work-arounds? Simply throwing an exception doesn't give any context information.

2

There are 2 answers

0
bwbecker On

I haven't yet found a good solution. I don't like the solution proposed by @user2963757 because it loses all the information from the parser about what it was looking for, where it was looking, etc.

This is raised a number of times in the FastParse issues list on GitHub. See issue 213, issue 187, issue 243, and pull request 244. There are a few vague suggestions but as far as I can tell the pull request hasn't been acted on (as of 2023-02-09).

The best I've found so far is defining this in an accessible location:

  // Fail with a message.  See https://github.com/com-lihaoyi/fastparse/issues/213
  // The message shows up as "Expected ..."; phrase it appropriately.
  private def Fail[T](expected: String)(implicit ctx: P[_]): P[T] = {
    val res = ctx.freshFailure()
    if (ctx.verboseFailures) ctx.setMsg(ctx.index, () => expected)
    res
  }

To use it:

    P(CharIn("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.|*+[]()-^$").repX(1).!).flatMap(re =>
      Try(re.r)
        .map(Pass(_))
        .getOrElse(Fail("<Well-formed regular expression>"))
    )

Trying to parse "^CS1[1345" yields

Expected <Well-formed regular expression>:1:10, found ""

Notice that the failure message has to be stated in terms of what was expected, not the actual problem. The actual error message thrown by the exception usually doesn't work well in this situation. I'm also not getting the fragment that it found.

Unfortunately, even this message is usually unavailable. For example, parsing a larger piece of my input results in

Expected (courseSpecDef | minUnits | xOf | courseSpec):1:14, found "^CS1[1345 "

I'd like to be able to surface the more exact error of "Unclosed character class" but seemingly can't.

By the way, I looked in the documentation, source code, and the sample parsers (PythonParse and ScalaParse) for examples of the use of the Fail parser. Can't find any. The only one is the one shown in the documentation, which doesn't compose with another parser.

If anyone has a better solution, I'd still love to hear it.

0
winitzki On

As of FastParse 2 there is an official Fail constructor as well as a Pass constructor. See https://github.com/com-lihaoyi/fastparse/issues/187