如何在 FParsec 中添加解析数字必须满足的条件?

How to add a condition that a parsed number must satisfy in FParsec?

我正在尝试使用 FParsec 解析 int32,但有一个额外的限制,即数字必须小于某个最大值。他们是在不编写我自己的自定义解析器(如下)的情况下执行此操作的方法吗?and/or 是我的自定义解析器(如下)实现要求的适当方法。

我问是因为大多数内置库函数似乎围绕 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

更新

下面是根据下面评论中给出的方向尝试更合适的 FParsec 解决方案:

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

这是正确的做法吗?

您进行了出色的研究并且几乎回答了您自己的问题。

一般有两种做法:

  1. 无条件地解析出一个int并让进一步的代码检查它的有效性;
  2. 使用绑定到解析器的保护规则。在这种情况下 (>>=) 是正确的工具;

为了做出好的选择,问问自己未能通过保护规则的整数是否必须通过触发另一个"give another chance"解析器?

这就是我的意思。通常,在实际项目中,解析器组合在一些链中。如果一个解析器失败,则尝试下一个。例如,在this question中,一些编程语言被解析,所以它需要这样的东西:

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

理论上,您的 DSL 可能需要将 "small int value" 与另一种类型区分开来:

/// 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

以这种方式构建,pCombined 将 return:

  • "42 ABC" 被解析为 [ SmallValue 42 ; Comment "ABC" ]
  • "1234567 ABC" 被解析为 [ LargeValueAndString(1234567, "ABC") ]

正如我们所见,保护规则会影响解析器的应用方式,因此保护规则必须在解析过程中。

但是,如果您不需要这样的复杂化(例如,int 无条件地 解析),您的第一个片段就可以了。