我有这个测试程序:
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”组成的输入字符串,用所有短划线分隔。对于输出,我想要一个数组,其中每个元素是其中一个部分,带有破折号的行只是被丢弃。
答案 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]Till
或notFollowedBy
进行回溯,那么效果会更好。例如,您还可以按如下方式解析行块:
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