使用 FParsec 解析箭头类型
Parsing the arrow type with 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'
只是另一种类型,例如元组或标识符。
你有解决办法吗?
在进入箭头语法之前,我想对您的 ws
解析器发表评论。使用 |>> (fun _ -> ())
有点低效,因为 FParsec 必须构造一个结果对象,然后立即将其丢弃。内置的 spaces
and 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
的定义不是 "there must be at least one list separator",而是 "there must be at least one item in the list"。因此,要区分 Int
类型和箭头类型,您需要执行以下操作:
let typeAlone = ty' .>> notFollowedBy arrow
let typeOrArrow = attempt typeAlone <|> arrowtype
此处必须使用 attempt
,这样如果出现箭头,ty'
消耗的字符将被回溯。
有一个复杂的因素我根本没有解决,因为你提到不需要括号。但是,如果您决定希望能够拥有箭头类型 of arrow types (即,将函数作为输入的函数),则需要解析类型 (Int -> Int) -> (Int -> Float) -> Char
.这会使 sepBy
的使用复杂化,我根本没有解决它。如果您最终需要更复杂的解析,包括括号,那么您可能想要使用 OperatorPrecedenceParser
。但是对于不涉及括号的简单需求,sepBy1
看起来是最好的选择。
最后,我应该给出一个警告:我根本没有测试过这个,只是把它输入到 Stack Overflow 框中。我给你的代码示例并不是为了按原样工作,而是为了让你了解如何继续。如果您需要一个按原样工作的示例,我很乐意为您提供一个,但我现在没有时间这样做。
我正在尝试使用 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'
只是另一种类型,例如元组或标识符。
你有解决办法吗?
在进入箭头语法之前,我想对您的 ws
解析器发表评论。使用 |>> (fun _ -> ())
有点低效,因为 FParsec 必须构造一个结果对象,然后立即将其丢弃。内置的 spaces
and 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
的定义不是 "there must be at least one list separator",而是 "there must be at least one item in the list"。因此,要区分 Int
类型和箭头类型,您需要执行以下操作:
let typeAlone = ty' .>> notFollowedBy arrow
let typeOrArrow = attempt typeAlone <|> arrowtype
此处必须使用 attempt
,这样如果出现箭头,ty'
消耗的字符将被回溯。
有一个复杂的因素我根本没有解决,因为你提到不需要括号。但是,如果您决定希望能够拥有箭头类型 of arrow types (即,将函数作为输入的函数),则需要解析类型 (Int -> Int) -> (Int -> Float) -> Char
.这会使 sepBy
的使用复杂化,我根本没有解决它。如果您最终需要更复杂的解析,包括括号,那么您可能想要使用 OperatorPrecedenceParser
。但是对于不涉及括号的简单需求,sepBy1
看起来是最好的选择。
最后,我应该给出一个警告:我根本没有测试过这个,只是把它输入到 Stack Overflow 框中。我给你的代码示例并不是为了按原样工作,而是为了让你了解如何继续。如果您需要一个按原样工作的示例,我很乐意为您提供一个,但我现在没有时间这样做。