parse.rs.html -- source
Description: Source to the Rust file `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
    125
    126
    127
    128
    129
    130
    131
    132
    133

    // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
    // file at the top-level directory of this distribution and at
    // http://rust-lang.org/COPYRIGHT.
    //
    // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
    // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
    // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
    // option. This file may not be copied, modified, or distributed
    // except according to those terms.

    //! 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 super::num;
    use self::ParseResult::{Valid, ShortcutToInf, ShortcutToZero, Invalid};

    #[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: integral, fractional: fractional, exp: exp }
        }
    }

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

    /// Check 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() && s.is_empty() {
                    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
        }
    }

    /// Carve 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))
    }

# Help

## Keyboard Shortcuts

?
  Show this help dialog
S
  Focus the search field
↑
  Move up in search results
↓
  Move down in search results
↹
  Switch tab
⏎
  Go to active search result
+ / -
  Collapse/expand all sections

## Search Tricks

Prefix searches with a type followed by a colon (e.g. `fn:`) to restrict the search to a given type.

Accepted types are: `fn`, `mod`, `struct`, `enum`, `trait`, `type`, `macro`, and `const`.

Search functions by type signature (e.g. `vec -> usize` or `* -> vec`)