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

时间:2016-09-12 23:56:59

标签: parsing f# fparsec

我在尝试解析FParsec中类似json的同类数组时遇到了问题。我已经将问题分解为一个重现它的简短例子。

#r @"..\packages\FParsec.1.0.2\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec.1.0.2\lib\net40-client\FParsec.dll"

open System
open FParsec

let test p str =
        match run p str with
        | Success(result, _, _)   -> printfn "Success: %A" result
        | Failure(errormsg, _, _) -> printfn "Failure: %s" errormsg


type CValue = CInt of int64
            | CBool of bool
            | CList of CValue list

let P_WHITESPACE = spaces
let P_COMMA = pstring ","
let P_L_SBRACE = pstring "[" .>> P_WHITESPACE
let P_R_SBRACE = P_WHITESPACE >>. pstring "]"

let P_INT_VALUE = pint64 |>> CInt

let P_TRUE = stringReturn "true" (CBool true)
let P_FALSE = stringReturn "false" (CBool false)
let P_BOOL_VALUE = P_TRUE <|> P_FALSE


let P_LIST_VALUE =
    let commaDelimitedList ptype = sepBy (ptype .>> P_WHITESPACE) (P_COMMA .>> P_WHITESPACE)
    let delimitedList = (commaDelimitedList P_INT_VALUE) <|> (commaDelimitedList P_BOOL_VALUE)
    let enclosedList = between P_L_SBRACE P_R_SBRACE delimitedList
    enclosedList |>> CList

当我使用test函数试用时,我得到以下结果:

test P_LIST_VALUE "[1,2,3]"
Success: CList [CInt 1L; CInt 2L; CInt 3L]

test P_LIST_VALUE "[true,false]"
Failure: Error in Ln: 1 Col: 2
[true,false]
 ^
Expecting: integer number (64-bit, signed) or ']'

如果我在使用P_INT_VALUE运算符时交换了P_BOOL_VALUE<|>的顺序,则[true,false]成功解析但[1,2,3]失败并出现类似错误。所以基本上,我首先使用的解析器是它试图使用的东西。

据我所知,如果LHS改变用户状态,<|>运算符将不会尝试RHS解析器 - 但我看不出它是如何发生的。 P_BOOL_VALUE和P_INT_VALUE没有任何共同的起始字符,因此在尝试解析错误的数据类型时,两者都应该立即失败。 Ints永远不会以'false'或'true'开头,bool永远不会以数字开头。

我做错了什么?

1 个答案:

答案 0 :(得分:2)

啊,我已经弄明白了。错误消息中的提示是or ']'。问题是sepBy在空输入上成功,所以当它到达t时,它会成功返回一个空列表,然后控制权传递回between,它会尝试找不到终止]

解决方案是将空列表案例移出int / bool特定的解析器,如下所示:

let P_LIST_VALUE =
    let commaDelimitedList ptype = sepBy1 (ptype .>> P_WHITESPACE) (P_COMMA .>> P_WHITESPACE)
    let delimitedList = (commaDelimitedList P_INT_VALUE) <|> (commaDelimitedList P_BOOL_VALUE) <|> preturn []
    let enclosedList = between P_L_SBRACE P_R_SBRACE delimitedList
    enclosedList |>> CList

请注意使用sepBy1代替sepBy,并添加<|> preturn []仅在delimitedList中处理空案例。

作为旁注,我不知道您的确切应用,但在解析器中强制键入通常不是一个好主意;实现这一目标的更常见方法是仅解析commaDelimitedList (P_INT_VALUE <|> P_BOOL_VALUE)(使用原始commaDelimitedList),然后在后续分析阶段检查输入。