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

时间:2018-06-01 16:31:37

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

1 个答案:

答案 0 :(得分:4)

发生这种情况,因为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