2012-02-08 5 views
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 
^

だから、litre-parserはリットルを消費し、それがどのスペースを見つけることができなかったとき、それが失敗したようです。しかし、私はそれが逆戻りして次の生産ルールを試みると思っていたでしょう。明らかにimplicitPiecesパーサは、この行を解析し、私は(コメントを削除する)前のボリュームパーサーを削除した場合、それは

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

が成功するためになぜamount後戻りはないでしょうか?私は何を誤解していますか?

答えて

2

それはvolume

  • volumelitreで始まり、
  • litreが正常に1 lime
  • 1 lを消費して amount
  • amount開始で始まり1 lime

    • ingredientためなぜなら後戻りしません

    したがってlitrevolumeおよびamountはすべて成功しました。これが理由の1つで、ingredientの2番目の部分、つまりwhiteSpaceで続きます。

    HTH!

  • +0

    私はちょうどそれを得ることはありませんか?私はそれが失敗したときに後退することができなければならないということです。非バックトラッキングCFGパーサーはあまり価値がないでしょう。私はこれが動作することを意味します: '{def foo =" foo "; def bar = "bar"; def baz = "baz"; def foobarbaz = foo〜bar〜baz; def foobarfoo = foo〜bar〜foo; def p = foobarbaz | foob​​arfoo; ( "foobarに-パーサ")記述foobarbaz'が失敗この例では、{ それ( "それを解析する必要があり"){ shouldParseWith(P、 "foobarfoo") } }} ' –

    +1

    @hedefalk'ので、 '| '代替案を試みる。あなたの質問では、 'volume'は成功しているので、' | 'は代替を試みません。 –

    +0

    はい、これは私が表示しようとしていたものです。 @hedefalk:文法の細かい部分だけを書き直すだけで済むようにすることが重要です。ダニエルが言ったように、 'a |右手側は、左手側が失敗した場合にのみ評価され、これはあなたの手術ではそうではありません。 1つ以上のルールを並べ替えることができるかどうかを確認して、必要な優先順位を取得します。 – fotNelton

    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) 
    
    +0

    自動的に変換が行われるスカラーにパーサライブラリはありますか? – HRJ

    +1

    @HRJこれは有望そうだと思う:https://github.com/Anastassija/Meerkat –

    関連する問題