parsingf#fparsec

How to parse a fixed string with FParsec


I'm trying to parse fixed strings with FParsec. For example parsing null from the documentation:

open FParsec

type Json = JNull
let jnull : Parser<_>  = stringReturn "null" JNull

then running jnull on "null" gives the expected result

> run jnull "null";;
val it : ParserResult<Json,unit> = Success: JNull

But if I run it on "nulls" it also succeeds

> run jnull "nulls";;
val it : ParserResult<Json,unit> = Success: JNull

Then I tried to add the requirement that null should be followed by a space:

let jnull : Parser<_>  = stringReturn "null" JNull >>. spaces

However, this gives me the same result as before.

I also tried to use manyMinMaxSatisfyL:

let jnull: Parser<_> =
        manyMinMaxSatisfyL 4 4 isLower "should be null"
        >>. pstring "null"
        >>. spaces

This one fails on "nulls" as it should, but is also fails on "null":

> run jnull "nulls";;
val it : ParserResult<unit,unit> =
  Failure:
Error in Ln: 1 Col: 5
nulls
    ^
Expecting: 'null'

> run jnull "null";; 
val it : ParserResult<unit,unit> =
  Failure:
Error in Ln: 1 Col: 5
null
    ^
Note: The error occurred at the end of the input stream.
Expecting: 'null'

What am I doing wrong here? Or did I completely misunderstand something about parsing?


Solution

  • In general, parsers always consume some input, produce a result and leave the rest of the input for later processing. This makes it possible to compose them, but it means that it's tricky to write a parser that consumes the whole input.

    One way to do what you want is to use notFollowedBy anyChar like this:

    let jnull : Parser<_,unit> = 
      (stringReturn "null" JNull) >>. notFollowedBy anyChar 
    
    run jnull "null"  // Success
    run jnull "nulls" // Error
    

    The notFollowedBy parser succeeds if the rest of the input cannot be parsed using the parser given as argument. Here, this means that notFollowedBy anyChar succeeds only if the rest cannot be parsed using anyChar, i.e. it is empty.