问题
I am currently learning the FParsec library, but I have come across an issue. When I want to parse an optional string and continue parsing as normal afterwards, FParsec will return a fatal error on the optional parser, rather than returning None
as I expect. The below working code sample illustrates my point:
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
The behavior I would expect is for pOptEntity
to return a Default
entity when an entity is not provided. However, instead, I get the following error:
Failure:
Error in Ln: 1 Col: 8
account:subaccount
^
Expecting: '/'
Shouldn't opt
provide the behavior I am describing and continue to parse the account string as normal or am I approaching this in the incorrect manner? I took a look at attempt
but, then, I wouldn't be able to provide the default entity behavior like I want.
Your help is much appreciated, thank you.
回答1:
The opt combinator follows the same rules as <|>
; if you look at the <|> documentation, it mentions that if the first parser fails without changing the parser state, the second parser is attempted. http://www.quanttec.com/fparsec/users-guide/parsing-alternatives.html goes into more detail.
Here, the .>>? combinator is what you want to use in your pEntity
parser. Replace the .>>
with .>>?
and you'll have a pEntity
parser that, if it is not followed by a /
, will backtrack to the beginning of what it attempted and not consume input. This will allow the opt
combinator to function as designed.
P.S. I tested this, and it worked. Replacing the .>>
with .>>?
in pEntity
and then running your code produced the following output:
Success: Entity "test"
Success: Default
来源:https://stackoverflow.com/questions/46658706/fparsec-failing-on-optional-parser