为什么我的递归FParsec解析器解析嵌套数组时会抛出异常?

时间:2018-08-20 04:28:32

标签: parsing f# fparsec

我正在尝试使用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
    ]

显然,这里还有更多未显示的代码;特别是,没有显示值解析器(pBasicStringpInteger等)。我假设它们可以正常工作,但是任何人都可以在这里查看它们: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对于常规数组可以很好地工作,但是嵌套数组会炸毁它。

可能是什么原因造成的?

1 个答案:

答案 0 :(得分:3)

这仍然不是一个完整的答案,而是在我之前的评论中进行扩展:考虑pArrayRef如何解析字符串前缀[ [。它会一直沿pStringArraypIntegerArraypFloatArray等行进,所有这些都将在第二个[上失败并回溯到第一个[ 。然后最后您打了递归调用attempt pArray。此时解析器还没有消耗任何东西(所有attempt都回溯到第一个[之前),因此您可以通过{{1 }}),然后重新开始循环。一遍又一遍...在这里写的是一个无限递归循环。失败的原因是使用空引用错误而不是堆栈溢出错误,这可能是由于FParsec内部实现的某些细节所致。

我认为您需要执行以下操作:

pArrayRef

我目前没有时间对此进行测试,但我认为这应该对您有用。