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.