winnow-rs / winnow

Making parsing a breeze
https://docs.rs/winnow
Other
525 stars 40 forks source link

`winnow::ascii::float` doesn't match `+inf` or `-inf` #411

Closed dezyh closed 9 months ago

dezyh commented 9 months ago

Please complete the following tasks

rust version

rustc 1.75.0 (82e1608df 2023-12-21)

winnow version

0.5.31

Minimal reproducible code

Link to playground


#[cfg(test)]
mod test {
    use winnow::error::ContextError;

    #[test]
    fn test_std_parse_inf_camel() {
        assert_eq!(
            "Inf".parse::<f64>().unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_std_parse_pos_inf_camel() {
        assert_eq!(
            "+Inf".parse::<f64>().unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_std_parse_neg_inf_camel() {
        assert_eq!(
            "-Inf".parse::<f64>().unwrap(), 
            f64::NEG_INFINITY,
        );
    }

    #[test]
    fn test_std_parse_inf_lower() {
        assert_eq!(
            "inf".parse::<f64>().unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_std_parse_pos_inf_lower() {
        assert_eq!(
            "+inf".parse::<f64>().unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_std_parse_neg_inf_lower() {
        assert_eq!(
            "-inf".parse::<f64>().unwrap(), 
            f64::NEG_INFINITY,
        );
    }

    #[test]
    fn test_winnow_parse_inf_camel() {
        assert_eq!(
            winnow::ascii::float::<&str, f64, ContextError>(&mut "Inf").unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_winnow_parse_pos_inf_camel() {
        assert_eq!(
            winnow::ascii::float::<&str, f64, ContextError>(&mut "+Inf").unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_winnow_parse_neg_inf_camel() {
        assert_eq!(
            winnow::ascii::float::<&str, f64, ContextError>(&mut "-Inf").unwrap(), 
            f64::NEG_INFINITY,
        );
    }

    #[test]
    fn test_winnow_parse_inf_lower() {
        assert_eq!(
            winnow::ascii::float::<&str, f64, ContextError>(&mut "inf").unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_winnow_parse_pos_inf_lower() {
        assert_eq!(
            winnow::ascii::float::<&str, f64, ContextError>(&mut "+inf").unwrap(), 
            f64::INFINITY,
        );
    }

    #[test]
    fn test_winnow_parse_neg_inf_lower() {
        assert_eq!(
            winnow::ascii::float::<&str, f64, ContextError>(&mut "-inf").unwrap(), 
            f64::NEG_INFINITY,
        );
    }
}

Steps to reproduce the bug with the above code

cargo test or run Test in the playground example

Actual Behaviour

Any of +Inf, +inf, -inf, -Inf do not get matched by winnow::ascii::float (and presumably any variant of infinity too based on the code).

running 12 tests
test test::test_std_parse_inf_lower ... ok
test test::test_std_parse_inf_camel ... ok
test test::test_std_parse_neg_inf_camel ... ok
test test::test_std_parse_neg_inf_lower ... ok
test test::test_std_parse_pos_inf_camel ... ok
test test::test_std_parse_pos_inf_lower ... ok
test test::test_winnow_parse_inf_camel ... ok
test test::test_winnow_parse_inf_lower ... ok
test test::test_winnow_parse_neg_inf_camel ... FAILED
test test::test_winnow_parse_neg_inf_lower ... FAILED
test test::test_winnow_parse_pos_inf_camel ... FAILED
test test::test_winnow_parse_pos_inf_lower ... FAILED

failures:

---- test::test_winnow_parse_neg_inf_camel stdout ----
thread 'test::test_winnow_parse_neg_inf_camel' panicked at src/lib.rs:72:74:
called `Result::unwrap()` on an `Err` value: Backtrack(ContextError { context: [], cause: None })
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- test::test_winnow_parse_neg_inf_lower stdout ----
thread 'test::test_winnow_parse_neg_inf_lower' panicked at src/lib.rs:96:74:
called `Result::unwrap()` on an `Err` value: Backtrack(ContextError { context: [], cause: None })

---- test::test_winnow_parse_pos_inf_camel stdout ----
thread 'test::test_winnow_parse_pos_inf_camel' panicked at src/lib.rs:64:74:
called `Result::unwrap()` on an `Err` value: Backtrack(ContextError { context: [], cause: None })

---- test::test_winnow_parse_pos_inf_lower stdout ----
thread 'test::test_winnow_parse_pos_inf_lower' panicked at src/lib.rs:88:74:
called `Result::unwrap()` on an `Err` value: Backtrack(ContextError { context: [], cause: None })

Expected Behaviour

  1. I think winnow::ascii::float should ideally match whatever parse::<f64>() can parse.
  2. Both +inf and -inf are separate values as per IEEE-754, so it makes sense to be able to parse both.
  3. My personal motivation for the change comes from trying to parse an example from the prometheus metric exposition format examples, in which the +Inf value is currently unable to be parsed.
    something_weird{problem="division by zero"} +Inf -3982045
epage commented 9 months ago

FYI I generally see float, dec_int, etc as conveniences for prototyping and the expected syntax is not too clear. However, I am fine moving forward with this.