1

データ構造を生成し、述語を実行してその一貫性を検証するパーサーを作成したいと考えています。述語が返される場合false、パーサーはカスタムErrorオブジェクトを返す必要があります (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

これを実現する 1 つの方法は、Pimp My Libraryパターンを使用して^!演算子をParser[List[T]](の戻り値の型repsep) に追加することです。暗黙的な定義を定義し、それを使用する必要があるときにスコープにインポートします。

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失敗をエラーに変換します。したがって、最初のステップは

commit(p ^?(condition, message))

ただし、 p が失敗した場合、これはエラーになります。これはあなたが望むものではないと思います.pが成功し、チェックが失敗した場合にのみエラーが必要です。だからあなたはむしろそうすべきです

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

かなり不自然に聞こえるかもしれませんが、直接実装することもできます。^? の実装をコピーするだけです。失敗をエラーに置き換えます。

最後に、Dylan の提案に従って、演算子を追加する必要があります。文法 ( ) の外でそれを行いたい場合はParsers、ミックスインが必要になると思います。

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

そうしないと、 (single) を簡単に参照できませんParser

于 2011-09-20T16:34:46.477 に答える
1

これが完全なPimpMyLibraryの実装です。

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演算子^!は、左側のパーサーを、述語を適用する新しいパーサーに変換します。

注意すべき重要なことの1つは、sinでマークされた行にあり<--ます。Scalaのパーサーライブラリによって最終的に返されるエラーは入力の最新の位置にあるエラーであるため、内部パーサーが独自の解析を完了したポイントを表すように、sinの代わりにその行を渡すことが重要です。insin

inの代わりに合格した場合sin、最終的に報告されるエラーは、内部ルールの解析中に発生した最新の失敗である可能性があります(その行に到達した場合、最終的に成功したことがわかります)。

于 2011-09-20T16:30:58.463 に答える
0

^?エラー メッセージ ジェネレータを受け入れ、 a を に変換しcommitます。FailureError

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