我正在尝试使用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'
只是另一种类型,例如元组或标识符。
您有解决方案吗?
答案 0 :(得分:3)
在使用箭头语法之前,我想先对您的ws
解析器进行评论。使用|>> (fun _ -> ())
效率不高,因为FParsec必须构造一个结果对象然后立即将其丢弃。内置的spaces
和spaces1
解析器可能更适合您的需求,因为它们不需要构造结果对象。
现在,对于您所遇到的问题,在我看来,您似乎想稍微考虑一下箭头解析器。将其视为由->
分隔的一系列类型,并使用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框中输入即可。我给您的代码示例并非按原样工作,而是让您了解如何进行操作。如果您需要一个按原样工作的示例,我很乐意尝试给您一个示例,但是我现在没有时间这样做。