问题
I'm trying to parse standard simple types (in the sense of lambda calculus) using FParsec, but I've having difficulty going from a Lex/Yacc style to that used in FParsec, specifically with respect to recursive definitions.
Examples of the types I am trying to parse are:
- o
- o -> o
- (o -> o -> o) -> o
And here is my attempt:
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"`
When I load this into the interactive the last line causes a stack overflow (ironically quite hard to search for these days). I can imagine why, given that there are recursive references, but I would have thought the one token lookahead would have prevented following the first (bracketed) choice in stype
. I assume therefore that it must be choosing arr
, which chooses stype
, and so on. But how to prevent this loop?
I'm interested in comments on idiomatic use of the library as well as corrections to my attempted solution.
回答1:
When you work with FParsec you need to parse sequences with the help of the sequence combinators instead of left-recursion. In your case you could for example use the sepBy1
combinator:
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"
回答2:
This runs, but is probably hacked together too much. The type Parser...
stuff is from the FParsec docs to avoid compiler errors.
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
}
来源:https://stackoverflow.com/questions/6763982/parsing-simple-types-in-fparsec