使用解析结果

Using ParserResult

下面的示例代码似乎运行良好:

open FParsec
let capitalized : Parser<unit,unit> =(asciiUpper >>. many asciiLower >>. eof)
let inverted : Parser<unit,unit> =(asciiLower >>. many asciiUpper >>. eof)
let capsOrInvert =choice [capitalized;inverted]

然后你可以这样做:

run capsOrInvert "Dog";;
run capsOrInvert "dOG";;

并获得成功或:

run capsOrInvert "dog";;

失败了。

现在我有了一个 ParserResult,我该如何处理它?例如,向后打印字符串?

ParserResult是有区别的工会。您只需匹配 SuccessFailure 案例。

let r = run capsOrInvert "Dog"

match r with
| Success(result, _, _)   -> printfn "Success: %A" result
| Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg

但这可能不是您对您的情况感到棘手的地方。

关于您的 Parser<unit, unit> 类型的事情是解析值是 unit 类型(Parser 的第一个类型参数)。这意味着这个解析器实际上并没有产生任何可供您使用的合理输出——它只能告诉你它是否可以解析一个字符串(在这种情况下你会得到一个 Success ((), _, _) - 携带单个值输入 unit) 或不输入。

您希望从这个解析器中得到什么?

编辑:这听起来很接近你想要的,或者至少你应该能够从中得到一些提示。 capitalized 接受大写字符串,inverted 接受已反转的大写字符串并将它们反转为解析器逻辑的一部分。

let reverse (s: string) = 
    System.String(Array.rev (Array.ofSeq s))

let capitalized : Parser<string,unit> = 
    (asciiUpper .>>. manyChars asciiLower)  
    |>> fun (upper, lower) -> string upper + lower

let inverted : Parser<string,unit> = 
    (manyChars asciiLower .>>. asciiUpper)
    |>> fun (lower, upper) -> reverse (lower + string upper)

let capsOrInvert = choice [capitalized;inverted]

run capsOrInvert "Dog"
run capsOrInvert "doG"
run capsOrInvert "dog"

您的代码有几个值得注意的问题。

首先,如@scrwtp 的回答所述,你的解析器 returns unit. 原因如下:operator (>>.) return 只是 return 由 right 内部解析器编辑的结果。另一方面,(.>>) 会 return left 解析器的结果,而 (.>>.) 会 return 两个

所以,parser1 >>. parser2 >>. eof 本质上是 (parser1 >>. parser2) >>. eof
parens 中的代码完全忽略 parser1 的结果,而第二个 (>>.) 然后忽略 parens 中解析器的整个结果。最后,eof returns unit,这个值正在 returned.

您可能需要一些 有意义的数据 returned,例如解析后的字符串。最简单的方法是:

let capitalized = (asciiUpper .>>. many asciiLower .>> eof)

注意操作员。
inverted 的代码可以类似的方式完成。


此解析器属于 Parser<(char * char list), unit> 类型,第一个字符和所有其余字符的元组,因此您可能需要 将它们合并回来 。有几种方法可以做到这一点,这里是一种方法:

let mymerge (c1: char, cs: char list) = c1 :: cs // a simple cons
let pCapitalized = capitalized >>= mymerge

这段代码的美妙之处在于你的 mymerge 是一个普通函数 ,与普通的 char 一起工作,它对解析器或所以。它只处理数据,其余的由 (>>=) 运算符完成。

注意,pCapitalized 也是一个解析器,但它 return 是一个单独的 char list


没有什么能阻止您应用进一步的转换。正如您提到的向后打印字符串:

let pCapitalizedAndReversed =
    capitalized
    >>= mymerge
    >>= List.rev

我特意这样写代码的。在不同的行中,您会看到 域数据的逐渐过渡 仍在解析器的范例中 。这是一个重要的考虑因素,因为任何后续转换都可能 "decide" 数据由于某种原因而损坏并引发解析异常,例如。或者,也可以将它与其他解析器合并。

一旦您的域数据(一个解析出的词)完成,您就可以提取另一个答案中提到的结果。


小记。 choice 仅对两个解析器而言是多余的。请改用 (<|>)。根据经验,仔细选择解析器组合器很重要,因为核心解析器逻辑深处的错误选择很容易使您的解析器显着变慢。
有关详细信息,请参阅 FParsec Primitives