parsing - How can I parse homogeneous lists in FParsec? -
i'm having issue trying parse homogeneous json-like array in fparsec. i've decomposed problem short example reproduces it.
#r @"..\packages\fparsec.1.0.2\lib\net40-client\fparseccs.dll" #r @"..\packages\fparsec.1.0.2\lib\net40-client\fparsec.dll" open system open fparsec let test p str = match run p str | success(result, _, _) -> printfn "success: %a" result | failure(errormsg, _, _) -> printfn "failure: %s" errormsg type cvalue = cint of int64 | cbool of bool | clist of cvalue list let p_whitespace = spaces let p_comma = pstring "," let p_l_sbrace = pstring "[" .>> p_whitespace let p_r_sbrace = p_whitespace >>. pstring "]" let p_int_value = pint64 |>> cint let p_true = stringreturn "true" (cbool true) let p_false = stringreturn "false" (cbool false) let p_bool_value = p_true <|> p_false let p_list_value = let commadelimitedlist ptype = sepby (ptype .>> p_whitespace) (p_comma .>> p_whitespace) let delimitedlist = (commadelimitedlist p_int_value) <|> (commadelimitedlist p_bool_value) let enclosedlist = between p_l_sbrace p_r_sbrace delimitedlist enclosedlist |>> clist
when use test
function try out, following results:
test p_list_value "[1,2,3]" success: clist [cint 1l; cint 2l; cint 3l] test p_list_value "[true,false]" failure: error in ln: 1 col: 2 [true,false] ^ expecting: integer number (64-bit, signed) or ']'
if swap order of p_int_value
, p_bool_value
when using <|>
operator, [true,false]
parses [1,2,3]
fails similar error. basically, ever parser use first tries use.
i understand <|>
operator won't attempt rhs parser if lhs mutates user state - can't see how happening. p_bool_value , p_int_value don't have starting characters in common, both should failing when trying parse wrong data type. ints never start 'false' or 'true' , bools never start numeric digits.
what doing wrong?
ah, i've figured out. hint in error message or ']'
. problem sepby
succeeds on empty input, when hits t
, returns empty list, , control passes between
tries , fails find terminating ]
.
the solution move empty list case out of int/bool-specific parsers, this:
let p_list_value = let commadelimitedlist ptype = sepby1 (ptype .>> p_whitespace) (p_comma .>> p_whitespace) let delimitedlist = (commadelimitedlist p_int_value) <|> (commadelimitedlist p_bool_value) <|> preturn [] let enclosedlist = between p_l_sbrace p_r_sbrace delimitedlist enclosedlist |>> clist
note use of sepby1
instead of sepby
, , addition of <|> preturn []
handle empty case once in delimitedlist
.
as side-note, don't know exact application, not such idea enforce typing in parser; more common way implement parse commadelimitedlist (p_int_value <|> p_bool_value)
(with original commadelimitedlist
) , check typing in subsequent analysis phase.
Comments
Post a Comment