I have the following subexpression to parse 'quotes' which have the following format
"5.75 @ 5.95"
I therefore have this parsec expression to parse it
let pquote x = (sepBy (pfloat) ((spaces .>> (pchar '/' <|> pchar '@' )>>. spaces))) x
It works fine.. except when there is a trailing space in my input, as the separator expression starts to consume content.So I wrapped it around an attempt, which works and seems, from what I understand, more or less what this was meant to be.
let pquote x = (sepBy (pfloat) (attempt (spaces .>> (pchar '/' <|> pchar '@' )>>. spaces))) x
As I dont know fparsec so well, I wonder if there are any better way to write this. it seems a bit heavy (while still being very manageable of course)
Implementing parsers with FParsec typically means combining higher‐level parsers from lower‐level ones. You start with the parser primitives provided by the library and then successively combine these into higher‐level parsers until you finally have a single parser for the complete input.
FParsec is an incredibly robust framework for building parsers with the combinatorial approach. With some careful fine-tuning, parsers written with this library may even outperform traditional hand-rolled parsers. If you’re using .NET and want to build a text processor, compiler, or a DSL interpreter-FParsec is likely a no-brainer.
To instruct FParsec to backtrack in such cases, we can wrap the individual parsers in attempt which will reset the state after the underlying parser fails:
The traditional way of dealing with insignificant whitespace involves writing a separate lexer component, which parses raw characters into so-called tokens. It can be done with FParsec as well and it provides many benefits, but for the sake of simplicity we’ll be writing a scanner-less parser this time.
let s1 = "5.75 @ 5.95 "
let s2 = "5.75/5.95 "
let pquote: Parser<_> =
pfloat
.>> spaces .>> skipAnyOf ['@'; '/'] .>> spaces
.>>. pfloat
.>> spaces
Notes:
spaces
optional everywherespaces
skips any sequence of zero or more whitespaces, so there's no need to use opt
- thanks @Daniel;type Parser<'t> = Parser<'t, UserState>
- I define it this way in order to avoid "value restriction" error; you may remove it;System.Threading.Thread.CurrentThread.CurrentCulture <- Globalization.CultureInfo.GetCultureInfo "en-US"
sepBy
unless I have a value list of unknown size.'@'
characters), it is recommended to use skip*
functions instead p*
for performance considerations.UPD added slash as separator
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With