通过函数应用程序解析器组合器发出信号失败

时间:2014-12-16 01:37:20

标签: scala parser-combinators

我需要对解析器匹配执行比标准符号允许的更复杂的语法检查,并且目前正在函数应用程序^^中执行此操作。示例简化方案是检查重复的关键字:

def keywords: Parser[List[String]] = "[" ~ repsep(keyword, ",") ~ "]" ^^ {
  case _ ~ ks ~ _ =>
    ks.groupBy(x => x).filter(_._2.length > 1).keys.toList match {
      case Nil => ks
      case x => throw new DuplicateKeywordsException(x)
    }
}

这是有效的,因为在我的解析器中会抛出一个异常,但我希望将失败捕获为ParseResult.Failure捕获它发生的位置的输入。我无法弄清楚如何在^^块内发出信号,或者使用其他一些构造来达到同样的目的。

1 个答案:

答案 0 :(得分:3)

好的,我按照Erik Meijer的建议跟随类型走下了快乐的道路。看看如何在 Scala编程中定义^^(与实际代码不同),我意识到它基本上只是一个Map函数:

def ˆˆ [U](f: T => U): Parser[U] = new Parser[U] {
  def apply(in: Input) = p(in) match {
    case Success(x, in1) => Success(f(x), in1)
    case failure => failure
  }
}

基本上是Parser[T] => Parser[U]

Parser[T]本身是Input => ParseResult[T]的函数,而^^只是通过提供apply方法来定义新的解析器,该方法在调用时将Success[T]转换为Success[U]或只是传递Failure

为了实现在映射期间注入新Failure的目标,我需要一个新的映射函数,它接受类似f: T => Either[String,U]的函数,这样我就可以发出错误消息或成功映射的信号。我选择了Either with string,因为Failure只接受一个字符串消息。然后通过隐式类将此新映射函数添加到Parser[U]

implicit class RichParser[+T](p: Parser[T]) {
  def ^^? [U](f: T => Either[String,U]): Parser[U] = new Parser[U] {
    def apply(in: Input) = p(in) match {
      case Success(x, in1) => f(x) match {
        case Left(error) => Failure(error,in1)
        case Right(x1) => Success(x1,in1)
      }
      case failure:Failure => failure
      case error:Error => error
    }
  }
}

现在keywords可以定义为:

def keywords: Parser[List[String]] = "[" ~ repsep(keyword, ",") ~ "]" ^^? {
  case _ ~ ks ~ _ =>
    ks.groupBy(x => x).filter(_._2.length > 1).keys.toList match {
      case Nil => Right(ks)
      case x => Left("found duplicate keywords: "+x.reduce[String] { case (a, b) => s"$a, $b"})
    }
}