f# - 为什么 FParsec 不使用解析列表分隔符的字符?

标签 f# fparsec

下面的实际场景是虚构的。这个问题的目的是更多地了解 FParsec 在这里做什么。

我正在解析由一个或多个空格字符分隔的字符串 (w)(x) 的列表 ' ' .

我的列表 xs 的解析器使用 sepBy使用分隔符解析器 isSeparator

isSeparator 基于 manySatisfy并且似乎正确地消耗了空间。我相信这可以在下面的测试输出中看到,当它解析两个在位置 3 结束的前导空格字符时。

但是,当我在xs中使用它时,它失败了,如下所示。

为什么会失败?什么是处理可能是一个或多个空格的分隔符的好方法?

open FParsec

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

let str s = pstringCI s

let w = str "(w)"
let z = str "(z)"

let woz = w <|> z

let isSeparator = manySatisfy (fun c -> c = ' ')
let xs = sepBy woz isSeparator

test isSeparator "  (w)" // Success: "  " position = (Ln: 1, Col: 3)
test xs "(z) (w)"        // Failure: Error in Ln: 1 Col: 8
                         // (z) (w)
                         //        ^
                         // Note: The error occurred at the end of the input stream.                         
                         // Expecting: '(w)' (case-insensitive) or '(z)' (case-insensitive)

最佳答案

发生这种情况是因为 manySatisfy 匹配零个或多个 满足给定谓词的字符,关键字为“零”。这意味着,在输入的最后,isSeparator 实际上成功了,即使它不消耗任何字符。由于 isSeparator 成功,sepBy 期望在分隔符之后找到另一个 woz 实例。但是没有更多的实例,所以 sepBy 返回一个错误。

要验证这一点,请尝试解析 wz 之间没有空格的输入:test xs "(z)(w)"。这应该打印“成功”,因为空分隔符是可以的。

要使 isSeparator 始终消耗至少一个字符并在找不到空格时失败,请使用 many1Satisfy 而不是 manySatisfy:

let isSeparator = many1Satisfy (fun c -> c = ' ')
let xs = sepBy woz isSeparator

关于f# - 为什么 FParsec 不使用解析列表分隔符的字符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50647674/

相关文章:

c# - 对于有经验的 C# 3.0 开发人员来说,学习 F# 有多难?

parsing - 递归解析语法消耗输入并且无法解析序列

f# - 有人可以举一个在 FParsec 中使用 chainl1 的例子吗?

f# - F#中面向铁路的程序如何从错误轨道切换回成功轨道?

f# - CsvProvider 抛出 OutOfMemoryException

f# - 将表达式树解析为嵌套列表

f# - Visual Studio 2013 预览版、Google Test Runner 无法加载文件或程序集 F#

f# - 如何使用 FParsec 解析标记列表

f# - 解析 if/else/if 语句

f# - FParsec 的缩进、表达式、语句和 StackOverflowException - 错误