FParsec很多都失败了

时间:2013-05-19 19:58:03

标签: 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

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

编辑:

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

1 个答案:

答案 0 :(得分:9)

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

  

附加信息:(Ln:2,Col:8):组合'很多'是   应用于成功的解析器而不消耗输入和没有   以任何其他方式更改解析器状态。 (如果没有例外   提升后,组合器可能会进入无限循环。)

问题是你的part解析器总是成功,即使它只能解析一个空字符串。您可以使用manyChars替换part定义中的many1Chars来解决该问题。

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

更新的 这是一个直截了当的解析器定义:

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

let part = many1CharsTill anyChar sepPart    
let parser = many part

请注意,我在>>?的定义中使用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

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

let chunk = many line .>> separator
let parser = many chunk

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

let chunk = many1 line <|> (notFollowedByEof >>% [])
let parser = sepEndBy1 chunk separator