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

标签 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 对于常规数组来说工作得很好,但是嵌套数组却让它崩溃了。

可能是什么原因造成的?

最佳答案

这仍然不是一个完整的答案,但要扩展我之前的评论:考虑 pArrayRef 如何解析字符串前缀 [ [。它一路向下遍历 pStringArraypIntegerArraypFloatArray 等,所有这些都会在第二个 [ 并回溯到第一个 [。然后最后你点击了递归调用attempt pArray。此时,解析器尚未消耗任何内容(所有这些尝试都回溯到第一个[之前),因此您对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
    ]

我目前没有时间对此进行测试,但我相信这应该适合您。

关于parsing - 为什么我的递归 FParsec 解析器在解析嵌套数组时会抛出异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51924167/

相关文章:

f# - ".NET"和 "OCaml"签名格式

f# - fparsec 解析字符串序列

parsing - 如何在 Go 中将持续时间字符串解析为时间或持续时间对象?

python - 使用python从文本文件中提取细节

.net - 什么是 RequireQualifiedAccess 属性?

visual-studio-2005 - 在 Visual Studio 2005 中设置 F#

parsing - 使用 FParsec 进行分块解析

c++ - boost::未排序模式的精神语法

java - 在 Parse 数据浏览器中安全存储用户的余额