summaryrefslogtreecommitdiffstats
path: root/third_party/rust/jsparagus-parser/src/numeric_value.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/jsparagus-parser/src/numeric_value.rs')
-rw-r--r--third_party/rust/jsparagus-parser/src/numeric_value.rs160
1 files changed, 160 insertions, 0 deletions
diff --git a/third_party/rust/jsparagus-parser/src/numeric_value.rs b/third_party/rust/jsparagus-parser/src/numeric_value.rs
new file mode 100644
index 0000000000..2fd5cab431
--- /dev/null
+++ b/third_party/rust/jsparagus-parser/src/numeric_value.rs
@@ -0,0 +1,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")
+}