我试图用FParsec解析一个int32,但有一个额外的限制,该数字必须小于某个最大值.他们是一种方法来执行此操作而无需编写我自己的自定义解析器(如下所示)和/或是我的自定义解析器(下面)是实现这些要求的适当方法.
我问,因为大多数内置库函数似乎围绕满足某些谓词的char而不是任何其他类型.
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
Run Code Online (Sandbox Code Playgroud)
下面是根据下面评论中给出的方向尝试更合适的FParsec解决方案:
let pRow2: Parser<int> =
pint32 >>= (fun x -> if x <= 1048576 then (preturn x) else fail "int parsed larger than maxRows")
Run Code Online (Sandbox Code Playgroud)
这是正确的方法吗?
你做了很好的研究,几乎回答了你自己的问题.
通常,有两种方法:
int
并让进一步的代码检查它的有效性;(>>=)
是正确的工具;为了做出一个好的选择,问问自己一个未能通过保护规则的整数是否必须通过触发另一个解析器来"给予另一次机会" ?
这就是我的意思.通常,在现实生活中的项目中,解析器组合在一些链中.如果一个解析器失败,则尝试以下解析器.例如,在这个问题中,一些编程语言被解析,所以它需要类似的东西:
let pContent =
pLineComment <|> pOperator <|> pNumeral <|> pKeyword <|> pIdentifier
Run Code Online (Sandbox Code Playgroud)
从理论上讲,您的DSL可能需要将"小int值"与其他类型区分开来:
/// 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
Run Code Online (Sandbox Code Playgroud)
以这种方式建造,pCombined
将返回:
"42 ABC"
被解析为 [ SmallValue 42 ; Comment "ABC" ]
"1234567 ABC"
被解析为 [ LargeValueAndString(1234567, "ABC") ]
正如我们所看到的,保护规则会影响解析器的应用方式,因此保护规则必须在解析过程中.
但是,如果您不需要这样的复杂化(例如,无条件地int
解析),您的第一个片段就可以了.