Search code examples
f#fparsec

Why are these two FParsec snippets different?


I want to parse a given char twice, but return a string of that character only once.

For example:

aa    ->    a

I have some code that works, but also some code that does not work, and I don't understand why.

Why are these snippets different?

// Works
let parseEscapedQuote (c : char)  =
  let q = string c
  pstring (q + q) >>% q
// Does not work
let parseEscapedQuote (c : char)  =
  let q = string c
  pchar c >>. pchar c >>% q

Solution

  • The second one will successfully parse a repeated character the way you want, but it might not fail the way you expect. If only the first pchar c succeeds, it will leave your parser in an invalid state. To fix this, you can use attempt, which restores the prior state if it fails:

    attempt (pchar c >>. pchar c) >>% q
    

    Here's a complete example that illustrates the difference:

    open FParsec
    
    let parseTwiceBad (c : char) =
        pchar c >>. pchar c >>% string c
    
    let parseTwiceGood (c : char) =
        attempt (pchar c >>. pchar c) >>% string c
    
    let mkParser parseTwice =
        choice [
            parseTwice 'x'
            anyString 3
        ]
    
    let run parser str =
        let result = runParserOnString parser () "" str
        match result with
            | Success (value, _, _) -> printfn "Success: %A" value
            | Failure (msg, _, _) -> printfn "Failure: %s" msg
    
    let test str =
    
        printfn ""
        printfn "Parsing \"%s\" with bad parser:" str
        let parser = mkParser parseTwiceBad
        run parser str
    
        printfn "Parsing \"%s\" with good parser:" str
        let parser = mkParser parseTwiceGood
        run parser str
    
    [<EntryPoint>]
    let main argv =
        test "xx"
        test "xAx"
        0
    

    Output:

    Parsing "xx" with bad parser:
    Success: "x"
    Parsing "xx" with good parser:
    Success: "x"
    
    Parsing "xAx" with bad parser:
    Failure: Error in Ln: 1 Col: 2
    xAx
     ^
    Expecting: 'x'
    
    Parsing "xAx" with good parser:
    Success: "xAx"