FParsec很多都失败了

Jon*_*ats 5 f# fparsec

我有这个测试程序:

open FParsec

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

let str s = pstring s

let sepPart = skipNewline >>. pstring "-"

let part = manyChars (notFollowedBy sepPart >>. anyChar)

[<EntryPoint>]
let main argv = 
    let s = "AA 12345\nBB 6789\n-----\nCC 9876\nDD 54321\n-----"
    test part s
    test (many part) s

    0 // return an integer exit code
Run Code Online (Sandbox Code Playgroud)

行{test part s}按预期工作,但下一行{test(很多部分)s}失败,我不明白我做错了什么.

编辑:

澄清一下,我要做的是{test(很多部分)s}返回["AA 12345 \nBB 6789"; "CC 9876 \nDD 54321"].换句话说,我所拥有的是一个由"pars"或"chunk"组成的输入字符串,用所有短划线分隔.对于输出,我想要一个数组,其中每个元素是其中一个部分,并且简单地丢弃带有破折号的行.

Ste*_*orf 10

执行示例时,FParsec会抛出异常,并显示以下消息:

附加信息:(Ln:2,Col:8):组合器'many'应用于解析器,该解析器成功而不消耗输入并且不以任何其他方式更改解析器状态.(如果没有引发异常,组合器可能会进入无限循环.)

问题是你的part解析器总是成功,即使它只能解析一个空字符串.您可以通过替换with manyChars的定义来解决该问题.partmany1Chars

如果您搜索例如"应用于成功而不消耗输入的解析器",您会在互联网上找到类似错误的几个讨论,包括FParse的用户指南中的一个:http://www.quanttec.com/fparsec/users- 指南/解析-sequences.html#的,很多解析器

更新: 这是一个简单的解析器定义:

let sepPart = skipNewline 
              >>? (skipMany1SatisfyL ((=) '-') "'-'" 
                    >>. (skipNewline <|> eof))

let part = many1CharsTill anyChar sepPart    
let parser = many part
Run Code Online (Sandbox Code Playgroud)

请注意,我>>?在定义中使用,sepPart如果换行符后面没有换行符,则允许此解析器回溯到开头.或者你也可以使用attempt (skipNewline >>. ...),它也会在初始破折号后回溯错误.many[Chars]Till p endp状态的文档与之等效many (notFollowedBy endp >>. p) .>> endp并不严格,因为many[Chars]Till不会像我们那样回溯notFollowedBy.我将澄清文件.

如果您避免使用many[Chars]TillnotFollowedBy在可能的情况下回溯,那么性能会更好.例如,您还可以解析您的行块,如下所示:

let id = manyMinMaxSatisfyL 2 2 isUpper "id (two capital letters)"

let line = id .>>. (pchar ' ' >>. restOfLine true)

let separator = many1SatisfyL ((=) '-') "dash separator"
                >>. (skipNewline <|> eof)

let chunk = many1 line     
let parser = sepEndBy1 chunk separator
Run Code Online (Sandbox Code Playgroud)

请注意,此实现不要求最后一个块由分隔符结束.如果你想要,你可以使用:

let chunk = many line .>> separator
let parser = many chunk
Run Code Online (Sandbox Code Playgroud)

如果要允许带有sepEndBy定义的空块,可以使用:

let chunk = many1 line <|> (notFollowedByEof >>% [])
let parser = sepEndBy1 chunk separator
Run Code Online (Sandbox Code Playgroud)