我需要对解析器匹配执行比标准符号允许的更复杂的语法检查,并且目前正在函数应用程序^^
中执行此操作。示例简化方案是检查重复的关键字:
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捕获它发生的位置的输入。我无法弄清楚如何在^^
块内发出信号,或者使用其他一些构造来达到同样的目的。
答案 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"})
}
}