Arn*_*sen 5 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)
}
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为在我的解析器中会抛出异常,但我希望将失败捕获为ParseResult.Failure捕获它发生的位置的输入.我无法弄清楚如何从一个^^块内发出信号或使用其他一些构造来达到同样的目的.
好吧,我听从了 Erik Meijer 的建议,跟随 Type 走上幸福的道路。看看《Scala 编程》中^^的定义方式(与实际代码不同),我意识到它基本上只是一个函数:Map
def \xcb\x86\xcb\x86 [U](f: T => U): Parser[U] = new Parser[U] {\n def apply(in: Input) = p(in) match {\n case Success(x, in1) => Success(f(x), in1)\n case failure => failure\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n基本上就是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]) {\n def ^^? [U](f: T => Either[String,U]): Parser[U] = new Parser[U] {\n def apply(in: Input) = p(in) match {\n case Success(x, in1) => f(x) match {\n case Left(error) => Failure(error,in1)\n case Right(x1) => Success(x1,in1)\n }\n case failure:Failure => failure\n case error:Error => error\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n现在keywords可以定义为:
def keywords: Parser[List[String]] = "[" ~ repsep(keyword, ",") ~ "]" ^^? {\n case _ ~ ks ~ _ =>\n ks.groupBy(x => x).filter(_._2.length > 1).keys.toList match {\n case Nil => Right(ks)\n case x => Left("found duplicate keywords: "+x.reduce[String] { case (a, b) => s"$a, $b"})\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n