Errors and failures in Scala Parser Combinators
Asked Answered
B

2

6

I would like to implement a parser for some defined language using Scala Parser Combinators. However, the software that will compile the language does not implements all the language's feature, so I would like to fail if these features are used. I tried to forge a small example below :

object TestFail extends JavaTokenParsers {
  def test: Parser[String] =
    "hello" ~ "world" ^^ { case _ => ??? } |
    "hello" ~ ident ^^ { case "hello" ~ id => s"hi, $id" }
}

I.e., the parser succeeds on "hello" + some identifier, but fails if the identifier is "world". I see that there exist fail() and err() parsers in the Parsers class, but I cannot figure out how to use them, as they return Parser[Nothing] instead of a String. The documentation does not seem to cover this use case…

Birdhouse answered 3/7, 2013 at 12:33 Comment(0)
C
8

In this case you want err, not failure, since if the first parser in a disjunction fails you'll just move on to the second, which isn't what you want.

The other issue is that ^^ is the equivalent of map, but you want flatMap, since err("whatever") is a Parser[Nothing], not a Nothing. You could use the flatMap method on Parser, but in this context it's more idiomatic to use the (completely equivalent) >> operator:

object TestFail extends JavaTokenParsers {
  def test: Parser[String] =
    "hello" ~> "world" >> (x => err(s"Can't say hello to the $x!")) |
    "hello" ~ ident ^^ { case "hello" ~ id => s"hi, $id" }
}

Or, a little more simply:

object TestFail extends JavaTokenParsers {
  def test: Parser[String] =
    "hello" ~ "world" ~> err(s"Can't say hello to the world!") |
    "hello" ~ ident ^^ { case "hello" ~ id => s"hi, $id" }
}

Either approach should do what you want.

Captor answered 3/7, 2013 at 12:53 Comment(4)
It's exactly what I was looking for. Are the >>, ~> (and <~) operators documented somewhere (outside of the Scaladoc which was not detailed enough for me) ?Birdhouse
@scand1sk: See Parsers#Parser class for documentation.Discobolus
I guess "hello" ~ "world" >> is a typo, there should be "hello" ~> "world" >> to use $x.Discobolus
@senia: Thanks—you're right, my original version will compile and work but the error message won't be as clear as it should be.Captor
D
3

You could use ^? method:

object TestFail extends JavaTokenParsers {
  def test: Parser[String] =
    "hello" ~> ident ^? (
      { case id if id != "world" => s"hi, $id" },
      s => s"Should not use '$s' here."
  )
}
Discobolus answered 3/7, 2013 at 12:54 Comment(2)
Unfortunately, this solution would be too much cumbersome in my global parser project…Birdhouse
@scand1sk: >> is the best solution in your case. But ^? allows you to use an addition method like this: case _ ~ id if isValidId(id) =>.Discobolus

© 2022 - 2024 — McMap. All rights reserved.