FParsec 解析器泛型于 UserState 类型



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

FParsec 的CharParsers.run函数只是调用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# 的类型推断为您完成所有工作,您应该没问题。