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

标签 f# fparsec

我正在尝试使用 FParsec 解析 int32,但有一个额外的限制,即该数字必须小于某个最大值。他们是一种无需编写我自己的自定义解析器(如下所示)即可执行此操作的方法和/或我的自定义解析器(如下所示)是满足要求的适当方式。

我问是因为大多数内置库函数似乎都围绕着 字符 满足某些谓词而不是任何其他类型。

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")

这是正确的方法吗?

最佳答案

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

一般来说,有两种方法:

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

  • 为了做出一个好的选择,问问自己一个未能通过保护规则的整数是否必须通过触发另一个解析器 来“再给一次机会”?

    这就是我的意思。通常,在现实生活中的项目中,解析器组合在一些链中。如果一个解析器失败,则尝试下一个解析器。例如,在 this question 中,一些编程语言被解析,所以它需要类似:
    let pContent =
        pLineComment <|> pOperator <|> pNumeral <|> pKeyword <|> pIdentifier
    

    从理论上讲,您的 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
    

    以这种方式构建,pCombined 将返回:
  • "42 ABC" 被解析为 [ SmallValue 42 ; Comment "ABC" ]
  • "1234567 ABC" 被解析为 [ LargeValueAndString(1234567, "ABC") ]

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

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

    关于f# - 如何在 FParsec 中添加解析后的数字必须满足的条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31939198/

    相关文章:

    .net - F# STA 线程异步

    .net - F# 版本的 Haskell 列表解构

    f# - F#获取随机数列表

    json - 如何序列化以在 F# 上使用 JSON.NET 进行流式传输?

    parsing - 使用 FParsec,如何在解析器之间使用 manyCharsTill 而不会在结束字符串上失败?

    parsing - 如何解析 FParsec 中的同类列表?

    f# - List.map会不会每次都分配一个新的List?

    f# - 我应该使用 Workflow 还是 fsYacc?

    f# - 解析为复杂类型

    f# - 解析括号表达式