为什么我的递归 FParsec 解析器在解析嵌套数组时抛出异常?
Why is my recursive FParsec parser throwing exception when it parses a nested array?
我正在尝试使用 FParsec 来解析 TOML 数组。我已经为 0.5 规范的各个部分(包括数组)研究了多个解析器。但是,当我尝试支持嵌套数组时,我 运行 陷入了一点麻烦。这是我得到的:
let pArrayOf<'a> (parser:Parser<'a,_>) : Parser<'a list, unit> =
pchar '[' >>. (sepBy parser (spaces >>. pchar ',' .>> spaces)) .>> pchar ']'
let pBasicStringArray = pArrayOf pBasicString
let pLiteralStringArray = pArrayOf pLiteralString
let pMultilineLiteralStringArray = pArrayOf pMultilineLiteralString
let pMultilineStringArray = pArrayOf pMultilineString
let pIntegerArray = pArrayOf pInteger
let pFloatArray = pArrayOf pFloat
let pBoolArray = pArrayOf pBool
let pOffsetDateTimeArray = pArrayOf pOffsetDateTime
let pLocalDateTimeArray = pArrayOf pLocalDateTime
let pLocalDateArray = pArrayOf pDate
let pLocalTimeArray = pArrayOf pTime
let pStringArray = (attempt pBasicStringArray) <|> (attempt pLiteralStringArray) <|> (attempt pMultilineLiteralStringArray) <|> (attempt pMultilineStringArray)
let mapObj (l:'a list) = List.map box l
let pArray,pArrayRef = createParserForwardedToRef()
pArrayRef :=
choice [
attempt pStringArray |>> mapObj;
attempt pIntegerArray |>> mapObj;
attempt pFloatArray |>> mapObj;
attempt pBoolArray |>> mapObj;
attempt pOffsetDateTimeArray |>> mapObj;
attempt pLocalDateTimeArray |>> mapObj;
attempt pLocalDateArray |>> mapObj;
attempt pLocalTimeArray |>> mapObj;
attempt pArray
]
显然这里还有更多代码没有显示;特别是,值解析器(pBasicString
、pInteger
等)未显示。我假设它们工作正常,但任何人都可以在这里查看它们:https://github.com/aggieben/FPConfig/blob/d4dc081dcefcee57fc1b45da69ac2178a1e10b2a/src/FPConfig.Toml/Parsers.fsx
当我尝试使用 createParserForwardedToRef
技术时,问题出现了。当我测试这个解析器时,我得到一个错误:
> test pArray "[1,2,3]";;
Ok: [1; 2; 3] <null> (Ln: 1, Col: 8) val it : unit = ()
> test pArray "[ [1,2], [3,4] ]";;
error FS0193: internal error: Object reference not set to an instance
of an object
>
如您所见,pArray
对于常规数组工作正常,但嵌套数组会破坏它。
可能是什么原因造成的?
这仍然不是一个完整的答案,但可以扩展我之前的评论:考虑 pArrayRef
如何解析字符串前缀 [ [
。它一直向下 pStringArray
、pIntegerArray
、pFloatArray
等,所有这些都会在第二个 [
失败并回溯到第一个 [
.然后在最后你点击了递归调用 attempt pArray
。此时解析器还没有消耗任何东西(所有那些 attempt
s 回溯到第一个 [
之前),所以你递归调用 pArrayRef
(通过 pArray
) 并再次开始循环。一次又一次……你在这里写的是一个无限递归循环。失败并出现空引用错误而不是堆栈溢出错误的事实可能是由于 FParsec 内部实现的一些细节。
我认为你需要做的是:
let pArray,pArrayRef = createParserForwardedToRef()
let pNestedArray = pArrayOf pArray
pArrayRef :=
choice [
attempt pStringArray |>> mapObj;
attempt pIntegerArray |>> mapObj;
attempt pFloatArray |>> mapObj;
attempt pBoolArray |>> mapObj;
attempt pOffsetDateTimeArray |>> mapObj;
attempt pLocalDateTimeArray |>> mapObj;
attempt pLocalDateArray |>> mapObj;
attempt pLocalTimeArray |>> mapObj;
attempt pNestedArray |>> mapObj
]
我现在没有时间测试这个,但我相信这对你有用。
我正在尝试使用 FParsec 来解析 TOML 数组。我已经为 0.5 规范的各个部分(包括数组)研究了多个解析器。但是,当我尝试支持嵌套数组时,我 运行 陷入了一点麻烦。这是我得到的:
let pArrayOf<'a> (parser:Parser<'a,_>) : Parser<'a list, unit> =
pchar '[' >>. (sepBy parser (spaces >>. pchar ',' .>> spaces)) .>> pchar ']'
let pBasicStringArray = pArrayOf pBasicString
let pLiteralStringArray = pArrayOf pLiteralString
let pMultilineLiteralStringArray = pArrayOf pMultilineLiteralString
let pMultilineStringArray = pArrayOf pMultilineString
let pIntegerArray = pArrayOf pInteger
let pFloatArray = pArrayOf pFloat
let pBoolArray = pArrayOf pBool
let pOffsetDateTimeArray = pArrayOf pOffsetDateTime
let pLocalDateTimeArray = pArrayOf pLocalDateTime
let pLocalDateArray = pArrayOf pDate
let pLocalTimeArray = pArrayOf pTime
let pStringArray = (attempt pBasicStringArray) <|> (attempt pLiteralStringArray) <|> (attempt pMultilineLiteralStringArray) <|> (attempt pMultilineStringArray)
let mapObj (l:'a list) = List.map box l
let pArray,pArrayRef = createParserForwardedToRef()
pArrayRef :=
choice [
attempt pStringArray |>> mapObj;
attempt pIntegerArray |>> mapObj;
attempt pFloatArray |>> mapObj;
attempt pBoolArray |>> mapObj;
attempt pOffsetDateTimeArray |>> mapObj;
attempt pLocalDateTimeArray |>> mapObj;
attempt pLocalDateArray |>> mapObj;
attempt pLocalTimeArray |>> mapObj;
attempt pArray
]
显然这里还有更多代码没有显示;特别是,值解析器(pBasicString
、pInteger
等)未显示。我假设它们工作正常,但任何人都可以在这里查看它们:https://github.com/aggieben/FPConfig/blob/d4dc081dcefcee57fc1b45da69ac2178a1e10b2a/src/FPConfig.Toml/Parsers.fsx
当我尝试使用 createParserForwardedToRef
技术时,问题出现了。当我测试这个解析器时,我得到一个错误:
> test pArray "[1,2,3]";;
Ok: [1; 2; 3] <null> (Ln: 1, Col: 8) val it : unit = ()
> test pArray "[ [1,2], [3,4] ]";;
error FS0193: internal error: Object reference not set to an instance
of an object
>
如您所见,pArray
对于常规数组工作正常,但嵌套数组会破坏它。
可能是什么原因造成的?
这仍然不是一个完整的答案,但可以扩展我之前的评论:考虑 pArrayRef
如何解析字符串前缀 [ [
。它一直向下 pStringArray
、pIntegerArray
、pFloatArray
等,所有这些都会在第二个 [
失败并回溯到第一个 [
.然后在最后你点击了递归调用 attempt pArray
。此时解析器还没有消耗任何东西(所有那些 attempt
s 回溯到第一个 [
之前),所以你递归调用 pArrayRef
(通过 pArray
) 并再次开始循环。一次又一次……你在这里写的是一个无限递归循环。失败并出现空引用错误而不是堆栈溢出错误的事实可能是由于 FParsec 内部实现的一些细节。
我认为你需要做的是:
let pArray,pArrayRef = createParserForwardedToRef()
let pNestedArray = pArrayOf pArray
pArrayRef :=
choice [
attempt pStringArray |>> mapObj;
attempt pIntegerArray |>> mapObj;
attempt pFloatArray |>> mapObj;
attempt pBoolArray |>> mapObj;
attempt pOffsetDateTimeArray |>> mapObj;
attempt pLocalDateTimeArray |>> mapObj;
attempt pLocalDateArray |>> mapObj;
attempt pLocalTimeArray |>> mapObj;
attempt pNestedArray |>> mapObj
]
我现在没有时间测试这个,但我相信这对你有用。