I'm trying to add error recovery to an FParsec parser that consumes a comma-separated sequence in parentheses, e.g. "(a,b,c)".
I came across the following problem: Given the two inputs "()" and "(a,b,)", the first is correct in my syntax, but the second is not (since it does not end the sequence properly). While I managed to recover in the second use case and produce appropriate diagnostics there, my error recovery mechanism still produced a false positive diagnostic in the first case which I wanted to avoid.
To distinguish these two cases, my idea was to "simply check" if my sequence was actually empty when I parse the closing ")".
To accomplish this, I tried to add a mutable counter and increment it while parsing the sequence.
I came across a very strange behavior: The incrementing works, but checking if the value is zero or greater than zero does not.
After hours of debugging and trying to understand what was going wrong, I wrote a simple program to reproduce the strange behavior.
open FParsec
type MyClass() =
let mutable contextCounter = 0
member this.Increment() =
contextCounter <- contextCounter + 1
member this.Reset() =
contextCounter <- 0
member this.GetCounter() = contextCounter
member this.Print(s:string) =
printf "\ncurrent value is %i, %s after checking if equals 0" contextCounter s
let x = pchar 'x'
let y = pchar 'y'
let z = pchar 'z'
let c = skipChar ','
let ad = MyClass()
let choicexyz (ad:MyClass) =
if ad.GetCounter()=0 then
choice [x;y;z] >>= fun result ->
ad.Print("in if block")
ad.Increment()
preturn result
else
z >>= fun result ->
ad.Print("in else block")
ad.Increment()
preturn result
let parserSepBy (ad:MyClass) =
ad.Reset()
sepBy (choicexyz ad) c
let inputSepBy = "x,x,y,y,z,z"
let resultSepBy = run (parserSepBy ad) inputSepBy
printf "\n%O" resultSepBy
This program leaves out all the unnecessary stuff like the opening and closing parentheses, error recovery, diagnostics, etc. But it reproduces what I want to demonstrate. The output is
current value is 0, in if block after checking if equals 0
current value is 1, in if block after checking if equals 0
current value is 2, in if block after checking if equals 0
current value is 3, in if block after checking if equals 0
current value is 4, in if block after checking if equals 0
current value is 5, in if block after checking if equals 0
Success: ['x'; 'x'; 'y'; 'y'; 'z'; 'z']
Why does the increment work, but checking if the value is equal to 0 doesn't?
When you call choicexyz
in sepBy (choicexyz ad) c
, you create a parser function. At that point the counter is evaluated and still zero (as you just reset it). So the parser function is what you define in the if
branch. When then later that parser function is applied by run
, it will always be that same function and print in if block
.
I think you have to work with user state in fparsec to do what you want to do.