1

我想编写一个解析器,它产生一些数据结构并通过在其上运行谓词来验证其一致性。如果谓词返回false,解析器应该返回一个自定义Error对象(而不是 a Failure,因为这可以通过 来实现^?)。

我正在寻找可以实现这一目标的解析器上的一些运算符。例如,假设我想解析一个整数列表并检查它们是否不同。我想要这样的东西:

import util.parsing.combinator.RegexParsers

object MyParser extends RegexParsers {
  val number: Parser[Int] = """\d+""".r ^^ {_.toInt }
  val list = repsep(number, ",") ^!(checkDistinct, "numbers have to be unique")

  def checkDistinct(numbers: List[Int]) = (numbers.length == numbers.distinct.length)
}

上面代码中的^!就是我要找的。如何验证解析器输出并在未验证时返回有用的错误消息?

4

4 回答 4

3

实现此目的的一种方法是使用Pimp My Library模式将^!运算符添加到Parser[List[T]](的返回类型repsep)。定义一个隐式 def,然后在需要使用时将其导入作用域:

class ParserWithMyExtras[T](val parser:Parser[List[T]]){
  def ^!(predicate:List[T]=>Boolean, errorMessage:String) = {...}
}

implicit def augmentParser[T](parser:Parser[List[T]]) = 
  new ParserWithMyExtras(parser)
于 2011-09-20T16:19:11.800 回答
2

Parsers.commit transforms Failure to Error. So a first step would be

commit(p ^?(condition, message))

However this would give an error if p gives a failure, which I suppose is not what you want, you want an error only when p succeeds and then the check fails. So you should rather do

p into {result => commit(success(result) ^? (condition,message))}

That may sound rather contrived, you may also implement directly, just copy the implementation of ^? replacing failure with an error.

Finally you should probably do as suggested by Dylan and add the operator. If you want to do it outside of your grammar (Parsers) , I think you will need a mixin:

trait PimpedParsers { self: Parsers => 
   implicit def ...
}

Otherwise you cannot easily refer to (single) Parser.

于 2011-09-20T16:34:46.477 回答
1

这是一个完整的Pimp My Library实现:

implicit def validatingParsers[T](parser: Parser[T]) = new {
  def ^!(predicate: T => Boolean, error: => String) = Parser { in =>
    parser(in) match {
      case s @Success(result, sin) => predicate(result) match {
        case true => s
        case false => Error(error, sin)   // <--
      }
      case e @NoSuccess(_, _) => e
    }
  }
}

new 运算符^!将左侧的解析器转换为应用谓词的新解析器。

One important thing to note is the sin on the line marked with <--. Because the Error that is eventually returned by Scala's parser library is the one in the latest position in the input, it is crucial to pass sin in that line instead of in, as sin represents the point where the inner parser completed its own parsing.

If we passed in instead of sin, the error that would eventually be reported could be the latest failure that happened during the parsing of the inner rule (which we know that eventually succeeded if we got to that line).

于 2011-09-20T16:30:58.463 回答
0

^?接受错误消息生成器,commit将 a 转换FailureError

val list = commit {
  repsep(number, ",") ^? (
    { case numbers if checkDistinct(numbers) => true},
    _ => "numbers have to be unique" )
}
于 2011-09-20T16:13:18.603 回答