为什么 FParsec 不使用解析列表分隔符的字符?
Why does FParsec not consume characters parsing a list separator?
下面的真实场景是虚构的。问题的目的是为了更多地了解 FParsec 在这里做什么。
我正在解析由一个或多个 space 个字符 ' '
分隔的字符串 (w)
和 (x)
的列表。
我的列表 xs
的解析器使用 sepBy
和分隔符解析器 isSeparator
。
isSeparator
基于 manySatisfy
并且似乎正确地消耗了 spaces。我相信这可以在下面的测试输出中看到,当它解析两个前导 space 字符时它在位置 3.
结束
但是,我在xs
中使用它时失败了,如下图。
为什么会失败?什么是处理可能是一个或多个 spaces 的分隔符的好方法?
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
匹配 零个或多个 个满足给定谓词的字符,关键字是 "zero"。这意味着,在输入的最后,isSeparator
实际上成功了,即使它没有消耗任何字符。由于 isSeparator
成功,sepBy
期望在分隔符之后找到 woz
的另一个实例。但是没有更多的实例,所以 sepBy
returns 一个错误。
要验证这一点,请尝试解析 w
和 z
之间没有空格的输入:test xs "(z)(w)"
。这应该打印 "Success",因为空分隔符是可以的。
要使 isSeparator
始终消耗至少一个字符并在找不到空格时失败,请使用 many1Satisfy
而不是 manySatisfy
:
let isSeparator = many1Satisfy (fun c -> c = ' ')
let xs = sepBy woz isSeparator
下面的真实场景是虚构的。问题的目的是为了更多地了解 FParsec 在这里做什么。
我正在解析由一个或多个 space 个字符 ' '
分隔的字符串 (w)
和 (x)
的列表。
我的列表 xs
的解析器使用 sepBy
和分隔符解析器 isSeparator
。
isSeparator
基于 manySatisfy
并且似乎正确地消耗了 spaces。我相信这可以在下面的测试输出中看到,当它解析两个前导 space 字符时它在位置 3.
但是,我在xs
中使用它时失败了,如下图。
为什么会失败?什么是处理可能是一个或多个 spaces 的分隔符的好方法?
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
匹配 零个或多个 个满足给定谓词的字符,关键字是 "zero"。这意味着,在输入的最后,isSeparator
实际上成功了,即使它没有消耗任何字符。由于 isSeparator
成功,sepBy
期望在分隔符之后找到 woz
的另一个实例。但是没有更多的实例,所以 sepBy
returns 一个错误。
要验证这一点,请尝试解析 w
和 z
之间没有空格的输入:test xs "(z)(w)"
。这应该打印 "Success",因为空分隔符是可以的。
要使 isSeparator
始终消耗至少一个字符并在找不到空格时失败,请使用 many1Satisfy
而不是 manySatisfy
:
let isSeparator = many1Satisfy (fun c -> c = ' ')
let xs = sepBy woz isSeparator