我正在尝试使用FParsec解析标准的简单类型(在lambda演算的意义上),但是我很难从Lex / Yacc样式转到FParsec中使用的样式,特别是在递归定义方面。 / p>
我尝试解析的类型的示例是:
这是我的尝试:
type SType =
| Atom
| Arrow of SType * SType
let ws = spaces
let stype, styperef = createParserForwardedToRef()
let atom = pchar 'o' .>> ws |>> (fun _ -> Atom)
let arrow = pipe2 (stype .>> (pstring "->" .>> ws))
stype
(fun t1 t2 -> Arrow (t1,t2))
let arr = parse {
let! t1 = stype
do! ws
let! _ = pstring "->"
let! t2 = stype
do! ws
return Arrow (t1,t2)
}
styperef := choice [ pchar '(' >>. stype .>> pchar ')';
arr;
atom ]
let _ = run stype "o -> o"`
当我将它加载到交互式中时,最后一行导致堆栈溢出(具有讽刺意味的是,这些天很难搜索)。我可以想象为什么,鉴于有递归引用,但我认为一个令牌前瞻将阻止stype
中的第一个(括号内)选择。因此,我认为必须选择arr
,选择stype
,依此类推。但是如何防止这种循环?
我对使用该库的惯用法以及对我尝试的解决方案的更正感兴趣。
答案 0 :(得分:4)
使用FParsec时,需要在sequence combinators而不是左递归的帮助下解析序列。在您的情况下,您可以使用sepBy1
组合器:
open FParsec
type SType =
| Atom
| Arrow of SType * SType
let ws = spaces : Parser<unit, unit>
let str_ws s = pstring s >>. ws
let stype, stypeRef = createParserForwardedToRef()
let atom = str_ws "o" >>% Atom
let elem = atom <|> between (str_ws "(") (str_ws ")") stype
do stypeRef:= sepBy1 elem (str_ws "->")
|>> List.reduceBack (fun t1 t2 -> Arrow(t1, t2))
let _ = run stype "o -> o"
答案 1 :(得分:0)
这样运行,但可能太多了。 type Parser...
内容来自FParsec文档,以避免编译错误。
type SType =
| Atom
| Arrow of SType * SType
type UserState = unit
type Parser<'t> = Parser<'t, UserState>
let ws = spaces
let atom : Parser<_> = pchar 'o' .>> ws |>> (fun _ -> Atom)
let rec term =
parse {
// Force something to come before another term. Either
// an atom, or a term in parens.
let! first = choice [atom;
(pstring "(" .>> ws) >>. term .>>
(pstring ")" .>> ws)]
// Check if this is an arrow. If not, just return first.
let! res = choice [((pstring "->") .>> ws) >>. term |>> (fun x ->
Arrow (first, x));
preturn first]
return res
}