使用FParsec解析箭头类型

时间:2018-12-06 16:23:07

标签: f# parsec fparsec

我正在尝试使用FParsec解析箭头类型。 也就是说,这是

Int -> Int -> Int -> Float -> Char

例如。

我尝试使用此代码,但仅适用于一种类型的箭头(Int -> Int),而不再适用。我还想避免使用括号,因为我已经有一个使用它们的元组类型,并且我也不希望它在语法方面过于繁琐。

let ws = pspaces >>. many pspaces |>> (fun _ -> ())

let str_ws s = pstring s .>> ws

type Type = ArrowType of Type * Type

let arrowtype' =
    pipe2
        (ws >>. ty')
        (ws >>. str_ws "->" >>. ws >>. ty')
        (fun t1 t2 -> ArrowType(t1, t2))

let arrowtype =
    pipe2
        (ws >>. ty' <|> arrowtype')
        (ws >>. str_ws "->" >>. ws >>. ty' <|> arrowtype')
        (fun t1 t2 -> ArrowType(t1, t2)) <?> "arrow type"

ty'只是另一种类型,例如元组或标识符。

您有解决方案吗?

1 个答案:

答案 0 :(得分:3)

在使用箭头语法之前,我想先对您的ws解析器进行评论。使用|>> (fun _ -> ())效率不高,因为FParsec必须构造一个结果对象然后立即将其丢弃。内置的spacesspaces1解析器可能更适合您的需求,因为它们不需要构造结果对象。

现在,对于您所遇到的问题,在我看来,您似乎想稍微考虑一下箭头解析器。将其视为由->分隔的一系列类型,并使用sepBy系列解析器组合器呢?像这样:

let arrow = spaces1 >>. pstring "->" .>> spaces1
let arrowlist = sepBy1 ty' arrow
let arrowtype = arrowlist |>> (fun types ->
    types |> List.reduce (fun ty1 ty2 -> ArrowType(ty1, ty2))

请注意,arrowlist解析器也将与纯Int匹配,因为sepBy1的定义不是“必须至少有一个列表分隔符”,而是“列表中必须至少有一项”。因此,要区分Int类型和箭头类型,您需要执行以下操作:

let typeAlone = ty' .>> notFollowedBy arrow
let typeOrArrow = attempt typeAlone <|> arrowtype

此处必须使用attempt,以便如果存在箭头,则ty'消耗的字符将被回溯。

有一个复杂的因素我没有解决,因为您提到不希望使用括号。但是,如果您决定要具有箭头类型的箭头类型(即以函数作为输入的函数),则需要解析(Int -> Int) -> (Int -> Float) -> Char之类的类型。这会使sepBy的使用复杂化,而我完全没有解决。如果最终需要包括括号在内的更复杂的分析,则可能需要使用OperatorPrecedenceParser。但是出于您不涉及括号的简单需求,sepBy1似乎是您的最佳选择。

最后,我应该给出一个警告:我根本没有测试过,只需在Stack Overflow框中输入即可。我给您的代码示例并非按原样工作,而是让您了解如何进行操作。如果您需要一个按原样工作的示例,我很乐意尝试给您一个示例,但是我现在没有时间这样做。