I'm trying to use nom to parse a text based protocol. This protocol can have floating point values in it of the form:
[-]digit+[.digit+]
Examples of which are:
The nom parser I've built to recognize this is... not pretty. It also doesn't quite typecheck. What I've got so far:
named!(float_prs <&[u8], f64>,
alt!(
take_while!(nom::is_digit) => {|x| FromStr::from_str(std::str::from_utf8(x).unwrap()).unwrap()} |
recognize!(chain!(
take_while!(nom::is_digit) ~
tag!(".") ~
take_while!(nom::is_digit),
|| {})
) => {|x: &[u8]| FromStr::from_str(std::str::from_utf8(x).unwrap()).unwrap() }
)
);
The first alternative parser recognizes digit+
, the second is an attempt to recognize digit+.digit+
but
<nom macros>:5:38: 5:62 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
<nom macros>:5 let index = ( $ i ) . offset ( i ) ; $ crate:: IResult:: Done (
Recognizing -digit
etc is not addressed in the above. There's a significant amount of duplication and the intent is very much obscured. Is there a better way to parse [-]digit+[.digit+]
that I'm not seeing?
Just because nom heavily uses macros to do it's dirty work, don't forget that you can still apply normal programming best practices. Specifically, break down the problem into smaller parts and compose them.
In nom, this can be achieved with the chain!
macro, which allows you to build up component parsers, and named!
, to give them useful names.
I'd recommend creating sub-parsers for the three parts of the number - the optional sign, the required integral part, and the optional decimal part. Note that digit
already pulls in multiple sequential numeric characters.
The main tricky thing with this code was the need to use complete!
to force the decimal
parser to be all-or-nothing.
#[macro_use]
extern crate nom;
use nom::digit;
named!(negative, tag!("-"));
named!(decimal, complete!(do_parse!(
tag!(".") >>
val: digit >>
(val)
)));
named!(floating_point<(Option<&[u8]>, &[u8], Option<&[u8]>)>, tuple!(
opt!(negative), digit, opt!(decimal)
));
fn main() {
println!("{:?}", floating_point(&b"0"[..]));
println!("{:?}", floating_point(&b"0."[..]));
println!("{:?}", floating_point(&b"0.0"[..]));
println!("{:?}", floating_point(&b"-0"[..]));
println!("{:?}", floating_point(&b"-0."[..]));
println!("{:?}", floating_point(&b"-0.0"[..]));
}
I've left off any conversion of the bytes to something interesting, it would clutter up the code and I don't really know how I'd do something useful with it anyway. ^_^
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