作为一项学习练习,我试图使用功能解析器库fparsec(The DOT language)来为graphviz点语言(FParsec)提供解析器。该语言描述了图表。
查看语言定义我不得不写下以下定义:
let rec pstmt_list = opt(pstmt .>> opt(pchar ';') >>. opt pstmt_list)
pstmt
和pchar ';'
是解析器,.>>
和>>.
将左解析器的出现与右解析器的出现相结合,opt
解析器将其参数解析器的可选出现作为选项值。然而,这个定义不起作用抱怨“......结果类型将是无限的...”。
通过查看上面链接的DOT语言,可能最容易理解这个例子。
我知道以下看似相关的问题:
但是我的F#知识可能还不足以翻译它们,如果它们在这里适用的话。
答案 0 :(得分:4)
FParsec为解析序列提供了特殊的组合子。通常你应该更喜欢这些组合器用递归函数重新实现它们。您可以在此处找到用于解析序列的可用组合子的概述:http://www.quanttec.com/fparsec/reference/parser-overview.html#parsing-sequences
在此示例中,pstmt_list
是一系列语句,分隔并可选地以分号结束,因此您可以轻松地将解析器定义为
let pstmt_list = sepEndBy pstmt (pstring ";")
答案 1 :(得分:2)
问题是你的pstmt_list
解析器产生了某些类型的值,但是当你在定义中使用它时,你用另外的option
类型包装这个类型的值(使用{{ 1}}组合者)。
F#编译器认为解析器返回的值的类型,例如opt
应与包装类型'a
相同(当然,这是不可能的)。
无论如何,我认为这不是你需要做的事情 - option 'a
组合器创建一个返回第二个参数结果的解析器,这意味着你将忽略所有结果到目前为止解析了.>>
。
我想你可能需要这样的东西:
pstmt
额外使用let rec pstmt_list : Parser<int list, unit> =
parse.Delay(fun () ->
opt(pstmt .>> pchar ';') .>>. opt pstmt_list
|>> (function Some(prev), Some(rest) -> prev::rest
| Some(prev), _ -> [prev]
| _, Some(rest) -> rest
| _ -> [] ))
是为了避免声明一个直接引用自身的值。