我有这个测试程序:
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]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
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)