使用andThen组合Parser来创建另一个不同类型的Parser

时间:2016-03-19 14:39:09

标签: scala parser-combinators

我正在尝试使用组合器解析器解析数据以返回a 解析器[java.util.Date]

我分两个阶段完成,首先,我使用simpleYear解析一年 解析器,然后我试图插入这个简单的解析器的结果 然后,我会操纵这个输入以获得此输出然后 一个ParseResult [日期]:

遗憾的是,我在声明的编译器中收到了以下错误:

       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]

这是代码:

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
        }
}

似乎scala编译器本身不能进行隐式转换 解析器[日期]中的日期类型(可能是因为try / catch?)

还有其他方法可以做我想做的事吗?

1 个答案:

答案 0 :(得分:1)

Parser[T]是函数Input => ParseResult[T]的子类,您使用的方法andThen来自函数。您传递给函数ParseResult[String] => ParseResult[Date],因此您返回Input => ParseResult[Date],这与Parser[Date]类型不匹配,这就是您收到此错误的原因。

但您只需在Input => ParseResult[T]方法中包含Parser类型的函数即可获得Parser[T]。因此,您可以像这样定义date

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
})

那就是说,这不是最好/最干净的方法。由于您希望在解析器结果上调用函数以某种方式对其进行修改,因此您可以使用Parser的方法mapflatMap或其等价物(map等效于^^flatMap相当于into>>)。这些与mapflatMap等其他Scala类的TryFuture具有相同的想法。

在这种情况下,您必须考虑失败的可能性,因此您必须使用flatMap。使用date的{​​{1}}定义可能如下所示:

flatMap

另外,您可能希望(如果您还没有自己完成)将def date: Parser[Date] = simpleDate >> (s => try success(formatter.parse(s)) catch { case pe: ParseException => failure("date format invalid") }) 设置为非宽松:formatter。否则它将执行解析formatter.setLenient(false)作为3月2日的事情!