Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use FParsec to parse float or int*float

Tags:

f#

fparsec

I've just started out playing around with FParsec, and I'm now trying to parse strings on the following format

10*0.5 0.25 0.75 3*0.1 0.9

I want 3*0.1, for example, to be expanded into 0.1 0.1 0.1

What I have so far is the following

type UserState = unit
type Parser<'t> = Parser<'t, UserState>

let str s : Parser<_> = pstring s

let float_ws : Parser<_> = pfloat .>> spaces

let product = pipe2 pint32 (str "*" >>. float_ws) (fun x y -> List.init x (fun i -> y)) 

The product parser correctly parsers entries on the format int*float and expands it into a list of floats. However, I'm having trouble coming up with a solution that allows me to parse either int*float or just a float. I would like to do something like

many (product <|> float_ws)

This will of course not work since the return types of the parsers differ. Any ideas on how to make this work? Is it possible to wrap of modify float_ws such that it returns a list with only one float?

like image 223
larsjr Avatar asked Feb 02 '16 11:02

larsjr


1 Answers

You can make float_ws return a float list by simply adding a |>> List.singleton

let float_ws : Parser<_> = pfloat .>> spaces |>> List.singleton

|>> is just the map function, where you apply some function to the result of one parser and receive a new parser of some new type:

val (|>>): Parser<'a,'u> -> ('a -> 'b) -> Parser<'b,'u>

See: http://www.quanttec.com/fparsec/reference/primitives.html#members.:124::62::62:


Also, since product parser includes an int parser, it will successfully parse a character from the wrong case, this means the parser state will be changed. That means you cannot use the <|> operator on the first parser directly, you must also add attempt so FParsec can return to the original parser state.

let combined = many (attempt product <|> float_ws)
like image 170
TheInnerLight Avatar answered Sep 20 '22 12:09

TheInnerLight