4

私は自分が抱えているこの問題を盲目的に見つめてきましたが、これはおそらく本当にばかげた質問になると思います. しかし、私は自分のプライドを飲み込まなければなりません。

思ったようにバックトラックしないこのコンビネーターパーサーがあります。コンテキストを完全に削除することなく、小さな例に減らしてきました。「foobar」のように感じます-例は読みにくいだけです. ここに行きます:

@RunWith(classOf[JUnitRunner])
class ParserBacktrackTest extends RegexParsers with Spec with ShouldMatchers {
  override def skipWhitespace = false

  lazy val optSpace = opt(whiteSpace)
  lazy val number = """\d+([\.]\d+)?""".r
  lazy val numWithOptSpace = number <~ optSpace

  private def litre = numWithOptSpace <~ ("litre" | "l")
  def volume = litre ^^ { case _ => "volume" }

  private def namedPieces = numWithOptSpace <~ ("pcs") ^^ { case _ => "explPcs" }
  private def implicitPieces = number ^^ { case _ => "implPcs" }
  protected def unitAmount = namedPieces | implicitPieces

  def nameOfIngredient = ".*".r

  def amount = volume | unitAmount
//  def amount = unitAmount
  protected def ingredient = (amount <~ whiteSpace) ~ nameOfIngredient

  describe("IngredientParser") {
    it("should parse volume") {
      shouldParse("1 litre lime")
    }
    it("should parse explicit pieces") {
      shouldParse("1 pcs lime")
    }
    it("should parse implicit pieces") {
      shouldParse("1 lime")
    }
  }

  def shouldParse(row: String) = {
    val result = parseAll(ingredient, row)
    result match {
      case Success(value, _) => println(value)
      case x => println(x)
    }
    result.successful should be(true)
  }
}

したがって、3 番目のテストは失敗します。

(volume~lime)
(explPcs~lime)
[1.4] failure: string matching regex `\s+' expected but `i' found

1 lime
   ^

そのため、 l が消費されたようで、litre-parserスペースが見つからなかったときに失敗しました。しかし、その時は後戻りして、次の生産ルールを試すだろうと思っていたでしょう。前のボリューム パーサーを削除 (コメントを削除) すると成功するため、明らかにimplicitPiecesパーサーはこの行を解析します。

(implPcs~litre lime)
(explPcs~lime)
(implPcs~lime)

なぜamount後戻りしないのですか?私は何を誤解していますか?

4

2 に答える 2

4

私の誤解を示す最小限の例を投稿したいだけです。私はこれが成功すると思った:

  def foo = "foo" | "fo"
  def obar = "obar"

  def foobar = foo ~ obar

  describe("foobar-parser") {
    it("should parse it") {
      shouldParseWith(foobar, "foobar")
    }
  }

しかし、バックトラックはそのように|は機能しません。論理和パーサーは「foo」を消費し、それを返すことはありません。

正規化する必要があるため、選言は最上位に移動します。

def workingFooBar = ("foo" ~ obar) | ("fo" ~ obar)
于 2012-02-08T16:42:54.860 に答える
2

のためにバックトラックしません1 lime

  • ingredientで始まるamount
  • amountで始まるvolume
  • volumeで始まりlitre
  • litre正常に消費1 lします1 lime

だからlitrevolumeそしてamountすべて成功しました!ingredientこれが、全体がの2番目の部分である。に続く理由whiteSpaceです。

HTH!

于 2012-02-08T11:52:16.360 に答える