Parsing 如何在FParsec中解析同构列表?

Parsing 如何在FParsec中解析同构列表?,parsing,f#,fparsec,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

我在尝试解析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
如果在使用
运算符时交换
p\u INT\u VALUE
p\u BOOL\u VALUE
的顺序,则
[true,false]
解析成功,但
[1,2,3]
失败并出现类似错误。所以基本上,我首先使用的解析器就是它试图使用的

我知道如果LHS改变了用户状态,
操作符不会尝试RHS解析器,但我看不出这是怎么发生的。P_BOOL_值和P_INT_值没有任何共同的起始字符,因此当试图解析错误的数据类型时,这两个值应该会立即失败。整数从不以“false”或“true”开头,布尔从不以数字开头


我做错了什么?

啊,我已经明白了。错误消息中的提示是
或']'
。问题是,
sebby
在空输入时成功,因此当它点击
t
时,它成功返回一个空列表,然后控制返回到
之间的
,它尝试找到终止的
]

解决方案是将空列表从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
让P_列出值=
让commaDelimitedList ptype=sepBy1(ptype.>>P_空格)(P_逗号.>>P_空格)
设delimitedList=(commaDelimitedList P_INT_值)(commaDelimitedList P_BOOL_值)预turn[]
设enclosedList=在P_L_SBRACE P_R_SBRACE delimitedList之间
随附列表|>>CList
请注意,使用了
sepBy1
而不是
sepBy
,并添加了
preturn[]
,以便在
分隔列表中仅处理一次空大小写


顺便说一句,我不知道您的确切应用程序,但在解析器中强制键入通常不是一个好主意;实现这一点的一种更常见的方法是只解析
commaDelimitedList(P_INT_VALUE P_BOOL_VALUE)
(使用原始的
commaDelimitedList
),然后在后续的分析阶段检查键入情况。

我还没有测试过你的代码,但是一个快速提示是将两个解析器包装成
trunt
trunt
应该是不必要的,正如我刚才测试的那样,它并不能解决问题。我真的不明白为什么这不起作用。成功!真管用!如果我理解正确,一旦解析器点击导致P_INT_值失败的't'。接下来,sebby假设它得到的是一个空输入并成功。然后在尝试解析']'和失败之间?实际上,我考虑过在解析器中接受一个异构列表,然后在分析阶段对其进行验证,如您所说。出于某种原因,我认为在解析器中处理它更好。走这条路线的缺点是什么?
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