How to add a condition that needs to be analyzed in FParsec?

I am trying to parse int32 with FParsec, but have an additional restriction so that the number is less than some maximum value. Their way of doing this without writing their own parser of their own (as shown below) and / or my own parser (below) is the appropriate way to achieve the requirements.

I ask because most of the library's built-in functions seem to revolve around char , satisfying certain predicates, and not any other type.

let pRow: Parser<int> = let error = messageError ("int parsed larger than maxRows") let mutable res = Reply(Error, error) fun stream -> let reply = pint32 stream if reply.Status = Ok && reply.Result <= 1000000 then res <- reply res 

UPDATE

The following is an attempt at a more suitable FParsec solution based on the direction indicated in the comment below:

 let pRow2: Parser<int> = pint32 >>= (fun x -> if x <= 1048576 then (preturn x) else fail "int parsed larger than maxRows") 

Is this being done right?

+4
source share
1 answer

You did an excellent research and almost answered your question.

Typically, there are two approaches:

  • Unconditionally parse int and give further code to check its validity;
  • Use the guard rule associated with the parser. In this case (>>=) is the right tool;

To make the right choice, ask yourself, should an integer that does not pass the protection rule β€œgive another chance” by calling another parser ?

Here is what I mean. Usually in real projects, parsers are combined in some chains. If one parser fails, the following is true. For example, in this question , some programming language understands, so it needs something like:

 let pContent = pLineComment <|> pOperator <|> pNumeral <|> pKeyword <|> pIdentifier 

Theoretically, your DSL might need to differentiate the value of the "small int value" from another type:

 /// The resulting type, or DSL type Output = | SmallValue of int | LargeValueAndString of int * string | Comment of string let pSmallValue = pint32 >>= (fun x -> if x <= 1048576 then (preturn x) else fail "int parsed larger than maxRows") |>> SmallValue let pLargeValueAndString = pint32 .>> ws .>>. (manyTill ws) |>> LargeValueAndString let pComment = manyTill ws |>> Comment let pCombined = [ pSmallValue; pLargeValueAndString; pComment] |> List.map attempt // each parser is optional |> choice // on each iteration, one of the parsers must succeed |> many // a loop 

Constructed in this way, pCombined will return:

  • "42 ABC" parsed as [ SmallValue 42 ; Comment "ABC" ] [ SmallValue 42 ; Comment "ABC" ]
  • "1234567 ABC" parsed as [ LargeValueAndString(1234567, "ABC") ]

As we can see, the security rule affects how parsers are applied, so the security rule should be in the process of parsing.

If, however, you do not need this complication (for example, int parsed unconditionally ), your first snippet is just fine.

+6
source

All Articles