UserState 类型通用的 FParsec 解析器

FParsec parsers generic to UserState type

所以。我有一组解析器,我希望它们对用户状态保持通用,因为它们现在不需要这些信息。默认值似乎是 Parser<'a, obj>,这是不明确的,但我想基本上没问题。 但是,在测试中,我希望能够使用 CharParsers.run,它需要 Parser<'a, unit>。 我如何创建一个解析器树,所有这些解析器都在 FParsec 中的 UserState 类型上共享通用性,理想情况下不使每个解析器都成为 <'a> |= unit -> Parser<SomeType, 'a> 类型的函数,但如果这是我需要做的,那就是我需要做。

FParsec 的 CharParsers.run function is just shorthand for calling CharParsers.runParserOnString() 作为用户状态,"" 作为流名称。所以在定义解析器时不要指定用户状态,让 F# 的类型推断为您计算。然后根据需要使用 CharParsers.run 进行测试,F# 将自动推断您的用户状态类型为 unit。然后,一旦您需要引入一些实际的用户状态,只需切换到使用 CharParsers.runParserOnString 并将其传递给您的初始状态即可。

也许一个例子会有所帮助。假设您需要解析一个由空格分隔并用方括号括起来的整数列表。所以你写了你的基本解析器代码:

let pListContents = many (pint32 .>> spaces)
let pList = pchar '[' >>. spaces >>. pListContents .>> pchar ']'

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

test pList "[1 2 3]"

现在您花了一段时间向解析器添加功能,最终发现您想要保留一些用户状态。也许您想存储迄今为止找到的最大数字,或者最长的列表,或者其他东西。所以你改变你的解析器来使用用户状态(我不会给出一个详细的例子,因为听起来你已经知道如何在解析器中使用用户状态),现在你的 test 函数不再编译,因为它期待用户状态为 unit 类型(并且您已将其设为 intint list 或其他类型)。没问题;只需按如下方式更改 test 函数:

let test p initState str =
    match runParserOnString p initState "" str with
    // ... the rest of the test function remains unchanged ...

test pList [] "[1 2 3]"

这就是您要做的全部。只要不指定解析器函数的类型,让 F# 的类型推断为您完成所有工作,您应该没问题。