5

我有这个测试程序:

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 (many part) s} 失败,我不明白我做错了什么。

编辑:

为了澄清,我想做的是让 {test (many part) s} return ["AA 12345\nBB 6789"; “CC 9876\nDD 54321”]。换句话说,我所拥有的是一个由“pars”或“chunks”组成的输入字符串,这些字符串由带有所有破折号的行分隔。对于输出,我想要一个数组,其中每个元素都是部分之一,并且带有破折号的行被简单地丢弃。

4

1 回答 1

11

当您执行您的示例时,FParsec 会引发异常并显示以下消息:

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

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

如果您搜索例如“应用于不消耗输入就成功的解析器”,您会在互联网上找到几个类似错误的讨论,包括 FParse 的用户指南中的一个:http ://www.quanttec.com/fparsec/users-指南/解析-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与 that 等价的状态的文档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
于 2013-05-19T21:13:24.083 回答