首页 > 解决方案 > 为什么 FParsec 不使用解析列表分隔符的字符?

问题描述

下面的实际场景是虚构的。该问题的目的是更多地了解 FParsec 在这里所做的事情。

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

我的列表的解析器与分隔符解析器一起xs使用。sepByisSeparator

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)

标签: f#fparsec

解决方案


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

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

isSeparator始终使用至少一个字符并在找不到空格时失败,请使用many1Satisfy代替manySatisfy

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

推荐阅读