4

障害/エラー処理をよりよく理解する必要があることに進む前に、Scala パーサーの組み合わせを使い始めます (注: Scala についても同様です)。

"a = b, c = d" のような文字列を解析してタプルのリストに入れたいが、ぶら下がっているコンマが見つかったときにユーザーにフラグを立てたい。

コンマ区切りのプロパティ割り当てを照合するときの照合の失敗 ("a = b, ") についての考え:

 def commaList[T](inner: Parser[T]): Parser[List[T]] =
   rep1sep(inner, ",") | rep1sep(inner, ",") ~> opt(",") ~> failure("Dangling comma")

 def propertyAssignment: Parser[(String, String)] = ident ~ "=" ~ ident ^^ {
   case id ~ "=" ~ prop => (id, prop)
 }

そして、パーサーを次のように呼び出します。

 p.parseAll(p.commaList(p.propertyAssignment), "name = John , ")

これは失敗に終わりますが、驚くことではありませんが、次のようになります。

 string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but end of source found

commList 関数は、最初のプロパティの割り当てで成功し、コンマを指定して繰り返しを開始しますが、次の文字がソース データの末尾であるという事実により、次の "ident" は失敗します。commList の 2 番目の選択肢が一致することをキャッチできると思いました:

 rep1sep(inner, ",") ~> opt(",") ~> failure("Dangling comma")

ニックス。アイデア?

4

2 に答える 2

4

Scalaz が助けてくれます :-)

警告を処理している場合、パーサーをエラーで終了するのは得策ではありません。パーサーを Scalaz ライター モナドと簡単に組み合わせることができます。このモナドを使用すると、パーサーの実行中に部分的な結果にメッセージを追加できます。これらのメッセージは、情報、警告、またはエラーである可能性があります。パーサーが終了したら、結果が使用可能かどうか、または重大な問題が含まれているかどうかを検証できます。このような個別の検証ステップを使用すると、通常よりもはるかに優れたエラー メッセージが表示されます。たとえば、文字列の末尾に任意の文字を受け入れることができますが、それらが見つかった場合はエラーを発行します (たとえば、「最後のステートメントの後にガベージが見つかりました」)。エラー メッセージは、以下の例で得られる不可解なデフォルト メッセージよりも、ユーザーにとってはるかに役立つ場合があります ("string matching regex `\z' expected [...]")。

質問のコードに基づく例を次に示します。

    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    import util.parsing.combinator.RegexParsers
    import scalaz._, Scalaz._

    object DemoParser extends RegexParsers {
      type Warning = String
      case class Equation(left : String, right : String)
      type PWriter = Writer[Vector[Warning], List[Equation]]
      val emptyList : List[Equation] = Nil

      def rep1sep2[T](p : => Parser[T], q : => Parser[Any]): Parser[List[T]] =
        p ~ rep(q ~> p) ^^ {case x~y => x::y}


      def name : Parser[String] = """\w+""".r
      def equation : Parser[Equation] = name ~ "=" ~ name ^^ { case n ~ _ ~ v => Equation(n,v) }
      def commaList : Parser[PWriter] = rep1sep(equation, ",") ^^ (_.set(Vector()))
      def danglingComma  : Parser[PWriter] = opt(",") ^^ (
         _ map (_ => emptyList.set(Vector("Warning: Dangling comma")))
                 getOrElse(emptyList.set(Vector("getOrElse(emptyList.set(Vector(""))))
      def danglingList : Parser[PWriter] =  commaList ~ danglingComma ^^ {
        case l1 ~ l2 => (l1.over ++ l2.over).set(l1.written ++ l2.written) }

      def apply(input: String): PWriter = parseAll(danglingList, input) match {
        case Success(result, _) => result
        case failure : NoSuccess => emptyList.set(Vector(failure.msg))
      }
    }

    // Exiting paste mode, now interpreting.

    import util.parsing.combinator.RegexParsers
    import scalaz._
    import Scalaz._
    defined module DemoParser

    scala> DemoParser("a=1, b=2")
    res2: DemoParser.PWriter = (Vector(),List(Equation(a,1), Equation(b,2)))

    scala> DemoParser("a=1, b=2,")
    res3: DemoParser.PWriter = (Vector(Warning: Dangling comma),List(Equation(a,1), Equation(b,2)))

    scala> DemoParser("a=1, b=2, ")
    res4: DemoParser.PWriter = (Vector(Warning: Dangling comma),List(Equation(a,1), Equation(b,2)))

    scala> DemoParser("a=1, b=2, ;")
    res5: DemoParser.PWriter = (Vector(string matching regex `\z' expected but `;' found),List())

    scala>

ご覧のとおり、エラー ケースを適切に処理します。例を拡張する場合は、さまざまな種類のエラーのケース クラスを追加し、現在のパーサー位置をメッセージに含めます。

ところで。空白の問題はRegexParsersクラスによって処理されます。空白の扱いを変更したい場合は、フィールドをオーバーライドしてwhiteSpaceください。

于 2013-08-15T17:19:18.550 に答える