I'm trying to parse a data using combinator parsers to return a Parser[java.util.Date]
I do that in two phases, first, I parse a year using a simpleYear Parser, then I tried to plug in the result of this simple parser into andThen, I would then manipulate this input to have as output of this andThen a ParseResult[Date]:
Sadly I get the following error from the compiler at the line of declaration:
type mismatch; found :
parser.DateParser.Input ⇒ parser.DateParser.ParseResult[java.util.Date] (which
expands to)
scala.util.parsing.input.Reader[Char] ⇒ parser.DateParser.ParseResult[java.util. required: parser.DateParser.Parser[java.util.Date]
here is the code:
object DateParser extends RegexParsers {
val formatter: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd")
def year = """\d{4}""".r
def month: Parser[String] =
def day = """[0-2]\d""".r | """3[01]""".r
def month = """0\d""".r | """1[0-2]""".r
def simpleDate: Parser[String] =
(year ~ "-" ~ month ~ "-" ~ day) ^^
{ case y ~ "-" ~ m ~ "-" ~ d => y + "-" + m + "-" + d }
def date: Parser[Date] = simpleDate andThen {
case Success(s, in) =>
val x: ParseResult[Date] = try {
Success(formatter.parse(s), in)
} catch {
case pe: ParseException => Failure("date format invalid", in)
}
x
case f: Failure => f
}
}
It seems that the scala compiler can't do an implicit conversion itself of the type of date into a Parser[Date](maybe because of the try/catch?)
Is there some other way to do what I want to do?
Parser[T]
is a subclass of a function Input => ParseResult[T]
and the method andThen
you are using comes from the function. You are passing to it a function ParseResult[String] => ParseResult[Date]
, so you get back Input => ParseResult[Date]
, which doesn't match the type Parser[Date]
, and that's why you get this error.
But you can simply wrap a function of type Input => ParseResult[T]
in a Parser
method to get a Parser[T]
. So you can define date
like this:
def date: Parser[Date] = Parser(simpleDate andThen {
// Cleaned up `Success` case a bit
case Success(s, in) =>
try Success(formatter.parse(s), in)
catch {
case pe: ParseException => Failure("date format invalid", in)
}
// It's better to use `NoSuccess` instead of `Failure`,
// to cover the `Error` case as well.
case f: NoSuccess => f
})
That said, it's not the best/cleanest method. As you want to call a function on the parser result to modify it somehow, you can use the Parser
's methods map
and flatMap
or their equivalents (map
equivalent to ^^
, flatMap
equivalent to into
and >>
). Those have the same idea as map
and flatMap
of other Scala classes such as Try
or Future
.
In this case you have to account for the possibility of failure, so you have to use flatMap
. A definition of date
using flatMap
may look like this:
def date: Parser[Date] = simpleDate >> (s =>
try success(formatter.parse(s))
catch { case pe: ParseException => failure("date format invalid") })
Also, you may want (if you hadn't already done it yourself) to set formatter
to be non-lenient: formatter.setLenient(false)
. Otherwise it will do things like parsing 2000-02-31
as the 2nd of March!