代码之家  ›  专栏  ›  技术社区  ›  Sam

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

  •  4
  • Sam  · 技术社区  · 10 年前

    我试图用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")
    

    这是正确的做法吗?

    1 回复  |  直到 10 年前
        1
  •  6
  •   Community Mohan Dere    8 年前

    你做了一项出色的研究,几乎回答了自己的问题。

    通常有两种方法:

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

    为了做出一个好的选择,问问自己 失败 要通过警戒规则,必须通过触发“再给一次机会” 另一个解析器 ?

    这就是我的意思。通常,在现实生活中的项目中,解析器被组合在一些链中。如果一个解析器失败,则尝试执行下一个解析器。例如,在 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") ]

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

    然而,如果您不需要这种复杂性(例如 整数 已解析 无条件地 ),您的第一段代码很好。