parse.rs.html -- source
Description: Source to the Rust file `src/libcore/num/dec2flt/parse.rs`.
Keywords: rust, rustlang, rust-lang

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124

    //! Validating and decomposing a decimal string of the form:
    //!
    //! `(digits | digits? '.'? digits?) (('e' | 'E') ('+' | '-')? digits)?`
    //!
    //! In other words, standard floating-point syntax, with two exceptions: No sign, and no
    //! handling of "inf" and "NaN". These are handled by the driver function (super::dec2flt).
    //!
    //! Although recognizing valid inputs is relatively easy, this module also has to reject the
    //! countless invalid variations, never panic, and perform numerous checks that the other
    //! modules rely on to not panic (or overflow) in turn.
    //! To make matters worse, all that happens in a single pass over the input.
    //! So, be careful when modifying anything, and double-check with the other modules.
    use self::ParseResult::{Invalid, ShortcutToInf, ShortcutToZero, Valid};
    use super::num;

    #[derive(Debug)]
    pub enum Sign {
        Positive,
        Negative,
    }

    #[derive(Debug, PartialEq, Eq)]
    /// The interesting parts of a decimal string.
    pub struct Decimal<'a> {
        pub integral: &'a [u8],
        pub fractional: &'a [u8],
        /// The decimal exponent, guaranteed to have fewer than 18 decimal digits.
        pub exp: i64,
    }

    impl<'a> Decimal<'a> {
        pub fn new(integral: &'a [u8], fractional: &'a [u8], exp: i64) -> Decimal<'a> {
            Decimal { integral, fractional, exp }
        }
    }

    #[derive(Debug, PartialEq, Eq)]
    pub enum ParseResult<'a> {
        Valid(Decimal<'a>),
        ShortcutToInf,
        ShortcutToZero,
        Invalid,
    }

    /// Checks if the input string is a valid floating point number and if so, locate the integral
    /// part, the fractional part, and the exponent in it. Does not handle signs.
    pub fn parse_decimal(s: &str) -> ParseResult<'_> {
        if s.is_empty() {
            return Invalid;
        }

        let s = s.as_bytes();
        let (integral, s) = eat_digits(s);

        match s.first() {
            None => Valid(Decimal::new(integral, b"", 0)),
            Some(&b'e') | Some(&b'E') => {
                if integral.is_empty() {
                    return Invalid; // No digits before 'e'
                }

                parse_exp(integral, b"", &s[1..])
            }
            Some(&b'.') => {
                let (fractional, s) = eat_digits(&s[1..]);
                if integral.is_empty() && fractional.is_empty() {
                    // We require at least a single digit before or after the point.
                    return Invalid;
                }

                match s.first() {
                    None => Valid(Decimal::new(integral, fractional, 0)),
                    Some(&b'e') | Some(&b'E') => parse_exp(integral, fractional, &s[1..]),
                    _ => Invalid, // Trailing junk after fractional part
                }
            }
            _ => Invalid, // Trailing junk after first digit string
        }
    }

    /// Carves off decimal digits up to the first non-digit character.
    fn eat_digits(s: &[u8]) -> (&[u8], &[u8]) {
        let mut i = 0;
        while i < s.len() && b'0' <= s[i] && s[i] <= b'9' {
            i += 1;
        }
        (&s[..i], &s[i..])
    }

    /// Exponent extraction and error checking.
    fn parse_exp<'a>(integral: &'a [u8], fractional: &'a [u8], rest: &'a [u8]) -> ParseResult<'a> {
        let (sign, rest) = match rest.first() {
            Some(&b'-') => (Sign::Negative, &rest[1..]),
            Some(&b'+') => (Sign::Positive, &rest[1..]),
            _ => (Sign::Positive, rest),
        };
        let (mut number, trailing) = eat_digits(rest);
        if !trailing.is_empty() {
            return Invalid; // Trailing junk after exponent
        }
        if number.is_empty() {
            return Invalid; // Empty exponent
        }
        // At this point, we certainly have a valid string of digits. It may be too long to put into
        // an `i64`, but if it's that huge, the input is certainly zero or infinity. Since each zero
        // in the decimal digits only adjusts the exponent by +/- 1, at exp = 10^18 the input would
        // have to be 17 exabyte (!) of zeros to get even remotely close to being finite.
        // This is not exactly a use case we need to cater to.
        while number.first() == Some(&b'0') {
            number = &number[1..];
        }
        if number.len() >= 18 {
            return match sign {
                Sign::Positive => ShortcutToInf,
                Sign::Negative => ShortcutToZero,
            };
        }
        let abs_exp = num::from_str_unchecked(number);
        let e = match sign {
            Sign::Positive => abs_exp as i64,
            Sign::Negative => -(abs_exp as i64),
        };
        Valid(Decimal::new(integral, fractional, e))
    }