Parse a number of a certain size in nom

644 views Asked by At

I can parse a number like this fine:

map_res(digit1, |s: &str| s.parse::<u16>())

but how can I parse a number only if it is within a certain range?

3

There are 3 answers

2
user4815162342 On BEST ANSWER

You could check that the parsed number fits in the range and return an error if not:

map_res(digit1, |s: &str| {
    // uses std::io::Error for brevity, you'd define your own error
    match s.parse::<u16>() {
        Ok(n) if n < MIN || n > MAX => Err(io::Error::new(io::ErrorKind::Other, "out of range")),
        Ok(n) => Ok(n),
        Err(e) => Err(io::Error::new(io::ErrorKind::Other, e.to_string())),
    }
})

The match can also be expressed with the and_then and map_err combinators:

map_res(digit1, |s: &str| {
    s.parse::<u16>()
        .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
        .and_then(|n| {
            if n < MIN || n > MAX {
                Err(io::Error::new(io::ErrorKind::Other, "out of range"))
            } else {
                Ok(n)
            }
        })
})
0
MeetTitan On

You can use the verify convenience combinator. Like so:

fn parse_u16_clamped(i: &str, min: u16, max: u16) -> IResult<&str, u16> {
    let number = map_res(digit1, |s: &str| s.parse::<u16>());
    verify(number, |n| n < max && n > min)(i)
}
0
sshine On

Since ErrorKind::Other does not occur in Nom 7, you can also add context() to a VerboseError:

fn parse_range<T: FromStr + PartialOrd>(
    s: &str,
    r: Range<T>,
) -> IResult<&str, T, VerboseError<&str>> {
    let (s2, n) = map_res(digit1, |digits: &str| digits.parse::<T>())(s)?;
    if !r.contains(&n) {
        context("out of range", fail)(s)
    } else {
        Ok((s2, n))
    }
}