summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-parser/src/numeric_value.rs
blob: 2fd5cab431c15b0dee281345f5812e7a32894dfb (plain)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Parse NumericLiteral.

pub type ParseNumberResult = Result<f64, &'static str>;

#[derive(Debug)]
pub enum NumericLiteralBase {
    Decimal,
    Binary,
    Octal,
    Hex,
}

// The number of digits in 2**53 - 1 (integer part of f64).
// 9007199254740991
const F64_INT_DIGITS_MAX_LEN: usize = 16;
// 11111111111111111111111111111111111111111111111111111
const F64_INT_BIN_DIGITS_MAX_LEN: usize = 53;
// 377777777777777777
const F64_INT_OCT_DIGITS_MAX_LEN: usize = 18;
// 1fffffffffffff
const F64_INT_HEX_DIGITS_MAX_LEN: usize = 14;

// To avoid allocating extra buffer when '_' is present, integer cases are
// handled without Rust standard `parse` function, as long as the value
// won't overflow the integer part of f64.
fn parse_decimal_int(s: &str) -> ParseNumberResult {
    debug_assert!(!s.is_empty());

    // NOTE: Maximum length cannot be handled.
    if s.len() >= F64_INT_DIGITS_MAX_LEN {
        // Fallback to float function that can handle all cases.
        return parse_float(s);
    }

    let src = s.as_bytes();

    let mut result = 0.0;
    for &c in src {
        match c {
            b'0'..=b'9' => {
                let n = c - b'0';
                result = result * 10.0 + n as f64;
            }
            b'_' => {}
            _ => panic!("invalid syntax"),
        }
    }
    Ok(result)
}

fn parse_binary(s: &str) -> ParseNumberResult {
    debug_assert!(!s.is_empty());

    // NOTE: Maximum length can be handled.
    if s.len() > F64_INT_BIN_DIGITS_MAX_LEN {
        return Err("too long binary literal");
    }

    let src = s.as_bytes();

    let mut result = 0.0;
    for &c in src {
        match c {
            b'0'..=b'1' => {
                let n = c - b'0';
                result = result * 2.0 + n as f64;
            }
            b'_' => {}
            _ => panic!("invalid syntax"),
        }
    }
    Ok(result)
}

fn parse_octal(s: &str) -> ParseNumberResult {
    debug_assert!(!s.is_empty());

    // NOTE: Maximum length cannot be handled.
    if s.len() >= F64_INT_OCT_DIGITS_MAX_LEN {
        return Err("too long octal literal");
    }

    let src = s.as_bytes();

    let mut result = 0.0;
    for &c in src {
        match c {
            b'0'..=b'7' => {
                let n = c - b'0';
                result = result * 8.0 + n as f64;
            }
            b'_' => {}
            _ => panic!("invalid syntax"),
        }
    }
    Ok(result)
}

fn parse_hex(s: &str) -> ParseNumberResult {
    debug_assert!(!s.is_empty());

    // NOTE: Maximum length cannot be handled.
    if s.len() >= F64_INT_HEX_DIGITS_MAX_LEN {
        return Err("too long hex literal");
    }

    let src = s.as_bytes();

    let mut result = 0.0;
    for &c in src {
        match c {
            b'0'..=b'9' => {
                let n = c - b'0';
                result = result * 16.0 + n as f64;
            }
            b'A'..=b'F' => {
                let n = c - b'A' + 10;
                result = result * 16.0 + n as f64;
            }
            b'a'..=b'f' => {
                let n = c - b'a' + 10;
                result = result * 16.0 + n as f64;
            }
            b'_' => {}
            _ => panic!("invalid syntax"),
        }
    }
    Ok(result)
}

/// Parse integer NumericLiteral.
///
/// NonDecimalIntegerLiteral should contain the leading '0x' etc.
///
/// FIXME: LegacyOctalIntegerLiteral is not supported.
pub fn parse_int<'alloc>(s: &str, kind: NumericLiteralBase) -> ParseNumberResult {
    match kind {
        NumericLiteralBase::Decimal => parse_decimal_int(s),
        NumericLiteralBase::Binary => parse_binary(&s[2..]),
        NumericLiteralBase::Octal => parse_octal(&s[2..]),
        NumericLiteralBase::Hex => parse_hex(&s[2..]),
    }
}

fn parse_float_with_underscore(s: &str) -> ParseNumberResult {
    let filtered: String = s.chars().filter(|c| *c != '_').collect();

    filtered
        .parse::<f64>()
        .map_err(|_| "too long decimal literal")
}

/// Parse non-integer NumericLiteral.
pub fn parse_float(s: &str) -> ParseNumberResult {
    if s.contains('_') {
        return parse_float_with_underscore(s);
    }

    s.parse::<f64>().map_err(|_| "too long decimal literal")
}