2017-10-10 81 views
2

我目前正在學習FParsec庫,但遇到了一個問題。當我想要解析一個可選字符串並繼續正常解析時,FParsec將在可選解析器上返回一個致命錯誤,而不是像我期望的那樣返回None。下面的工作代碼示例說明了我的觀點:FParsec在可選解析器上失敗

open System 
open FParsec 

type AccountEntity = 
    | Default 
    | Entity of string 

let pEntity = 
    let isEntityFirstChar c = isLetter c 
    let isEntityChar c = isLetter c || isDigit c 
    (many1Satisfy2L isEntityFirstChar isEntityChar "entity") .>> skipString "/" 

let pOptEntity = 
    opt pEntity 
    |>> (fun optEntity -> 
       match optEntity with 
       | Some entity -> Entity entity 
       | None -> Default) 

[<EntryPoint>] 
let main argv = 
    printfn "%A" (run pOptEntity "test/account:subaccount") //works 
    printfn "%A" (run pOptEntity "account:subaccount") //crashes 
    Console.ReadLine() |> ignore 
    0 // return an integer exit code 

我期望的行爲是在不設置實體pOptEntity返回一個Default實體。但是,相反,我得到以下錯誤:

Failure: 
Error in Ln: 1 Col: 8 
account:subaccount 
    ^
Expecting: '/' 

不應該opt提供我所描述的,繼續解析帳戶字符串爲正常還是我不正確的方式處理這個問題?我看了一下attempt,但是,我不能像我想要的那樣提供默認的實體行爲。

非常感謝您的幫助,謝謝。

回答

2

opt combinator遵循與<|>相同的規則;如果您查看<|> documentation,它會提到如果第一個解析器在未更改解析器狀態時失敗,則嘗試第二個解析器。 http://www.quanttec.com/fparsec/users-guide/parsing-alternatives.html進入更多細節。

在這裏,.>>? combinator是你想在你的pEntity解析器中使用的。將.>>替換爲.>>?,您將擁有一個pEntity解析器,如果它後面沒有跟隨/,將回溯到它嘗試的內容的開頭並且不會消耗輸入。這將允許opt組合器按設計運行。

P.S.我測試了這個,它工作。更換.>>.>>?pEntity,然後運行代碼產生了以下的輸出:

Success: Entity "test" 
Success: Default 
+0

所以它只會嘗試下位,如果解析器沒有任何消耗輸入其已經被指示要走回頭路? –

+0

我不清楚你在那個問題中說「它」時的意思。如果你的意思是'<|>'combinator,那麼刪除短語「它已被指示回溯」,答案將是「是」。 '<|>'combinator只會嘗試下一位,如果第一個解析器失敗*沒有消費輸入*。無論是第一個解析器立即失敗,還是嘗試了某種東西然後回溯,與組合器的觀點無關。所有'<|>'知道的是「我嘗試了第一個解析器,並且流仍然處於相同的狀態」。我會在下一條評論中提到'。>>?'。 – rmunn

+0

如果您的問題中的「it」一詞表示'。>>?'combinator,那麼答案是「否」。 '。>>?'組合器將首先嚐試下一位,如果下一位失敗,它將在第一位嘗試*之前回溯到解析器狀態*。以'?'結尾的其他組合變體都遵循類似的規則:它們都嘗試第二位,如果失敗,則返回到第一位嘗試之前的狀態。 – rmunn