From ec939223aa53ab4d422a136cff154a6b9afab5bb Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 10 Jul 2018 18:00:12 -0700 Subject: 0.5: Support hex/oct/bin integers. cc #224 --- src/de.rs | 69 +++++++++++++++++++++---------------- test-suite/tests/invalid-misc.rs | 4 +++ test-suite/tests/valid/integer.json | 10 +++++- test-suite/tests/valid/integer.toml | 14 ++++++++ 4 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/de.rs b/src/de.rs index e033950..85009ed 100644 --- a/src/de.rs +++ b/src/de.rs @@ -871,7 +871,14 @@ impl<'a> Deserializer<'a> { } fn number(&mut self, Span { start, end}: Span, s: &'a str) -> Result, Error> { - if s.contains('e') || s.contains('E') { + let to_integer = |f| Value { e: E::Integer(f), start: start, end: end }; + if s.starts_with("0x") { + self.integer(&s[2..], 16).map(to_integer) + } else if s.starts_with("0o") { + self.integer(&s[2..], 8).map(to_integer) + } else if s.starts_with("0b") { + self.integer(&s[2..], 2).map(to_integer) + } else if s.contains('e') || s.contains('E') { self.float(s, None).map(|f| Value { e: E::Float(f), start: start, end: end }) } else if self.eat(Token::Period)? { let at = self.tokens.current(); @@ -892,7 +899,7 @@ impl<'a> Deserializer<'a> { } else if s == "-nan" { Ok(Value { e: E::Float(-f64::NAN), start: start, end: end }) } else { - self.integer(s).map(|f| Value { e: E::Integer(f), start: start, end: end }) + self.integer(s, 10).map(to_integer) } } @@ -906,22 +913,25 @@ impl<'a> Deserializer<'a> { } } - fn integer(&self, s: &'a str) -> Result { - let (prefix, suffix) = self.parse_integer(s, true, false)?; + fn integer(&self, s: &'a str, radix: u32) -> Result { + let allow_sign = radix == 10; + let allow_leading_zeros = radix != 10; + let (prefix, suffix) = self.parse_integer(s, allow_sign, allow_leading_zeros, radix)?; let start = self.tokens.substr_offset(s); if suffix != "" { return Err(self.error(start, ErrorKind::NumberInvalid)) } - prefix.replace("_", "").trim_left_matches('+').parse().map_err(|_e| { - self.error(start, ErrorKind::NumberInvalid) - }) + i64::from_str_radix(&prefix.replace("_", "").trim_left_matches('+'), radix) + .map_err(|_e| self.error(start, ErrorKind::NumberInvalid)) } - fn parse_integer(&self, - s: &'a str, - allow_sign: bool, - allow_leading_zeros: bool) - -> Result<(&'a str, &'a str), Error> { + fn parse_integer( + &self, + s: &'a str, + allow_sign: bool, + allow_leading_zeros: bool, + radix: u32, + ) -> Result<(&'a str, &'a str), Error> { let start = self.tokens.substr_offset(s); let mut first = true; @@ -934,21 +944,20 @@ impl<'a> Deserializer<'a> { continue } - match c { - '0' if first => first_zero = true, - '0' ... '9' if !first && first_zero && !allow_leading_zeros => { - return Err(self.error(at, ErrorKind::NumberInvalid)) + if c == '0' && first { + first_zero = true; + } else if c.to_digit(radix).is_some() { + if !first && first_zero && !allow_leading_zeros { + return Err(self.error(at, ErrorKind::NumberInvalid)); } - '0' ... '9' => underscore = false, - '_' if first => { - return Err(self.error(at, ErrorKind::NumberInvalid)) - } - '_' if !underscore => underscore = true, - _ => { - end = i; - break - } - + underscore = false; + } else if c == '_' && first { + return Err(self.error(at, ErrorKind::NumberInvalid)); + } else if c == '_' && !underscore { + underscore = true; + } else { + end = i; + break; } first = false; } @@ -960,7 +969,7 @@ impl<'a> Deserializer<'a> { fn float(&mut self, s: &'a str, after_decimal: Option<&'a str>) -> Result { - let (integral, mut suffix) = self.parse_integer(s, true, false)?; + let (integral, mut suffix) = self.parse_integer(s, true, false, 10)?; let start = self.tokens.substr_offset(integral); let mut fraction = None; @@ -968,7 +977,7 @@ impl<'a> Deserializer<'a> { if suffix != "" { return Err(self.error(start, ErrorKind::NumberInvalid)) } - let (a, b) = self.parse_integer(after, false, true)?; + let (a, b) = self.parse_integer(after, false, true, 10)?; fraction = Some(a); suffix = b; } @@ -979,12 +988,12 @@ impl<'a> Deserializer<'a> { self.eat(Token::Plus)?; match self.next()? { Some((_, Token::Keylike(s))) => { - self.parse_integer(s, false, false)? + self.parse_integer(s, false, false, 10)? } _ => return Err(self.error(start, ErrorKind::NumberInvalid)), } } else { - self.parse_integer(&suffix[1..], true, false)? + self.parse_integer(&suffix[1..], true, false, 10)? }; if b != "" { return Err(self.error(start, ErrorKind::NumberInvalid)) diff --git a/test-suite/tests/invalid-misc.rs b/test-suite/tests/invalid-misc.rs index 218be4f..80421e3 100644 --- a/test-suite/tests/invalid-misc.rs +++ b/test-suite/tests/invalid-misc.rs @@ -11,6 +11,10 @@ fn bad() { bad("a = 1_"); bad("''"); bad("a = 9e99999"); + bad("a = \"\u{7f}\""); bad("a = '\u{7f}'"); + + bad("a = -0x1"); + bad("a = 0x-1"); } diff --git a/test-suite/tests/valid/integer.json b/test-suite/tests/valid/integer.json index 86f779f..77ecb6c 100644 --- a/test-suite/tests/valid/integer.json +++ b/test-suite/tests/valid/integer.json @@ -1,6 +1,14 @@ { "answer": {"type": "integer", "value": "42"}, "neganswer": {"type": "integer", "value": "-42"}, + "neg_zero": {"type": "integer", "value": "0"}, - "pos_zero": {"type": "integer", "value": "0"} + "pos_zero": {"type": "integer", "value": "0"}, + + "hex1": {"type": "integer", "value": "3735928559"}, + "hex2": {"type": "integer", "value": "3735928559"}, + "hex3": {"type": "integer", "value": "3735928559"}, + "oct1": {"type": "integer", "value": "342391"}, + "oct2": {"type": "integer", "value": "493"}, + "bin1": {"type": "integer", "value": "214"} } diff --git a/test-suite/tests/valid/integer.toml b/test-suite/tests/valid/integer.toml index 2bdca34..8362459 100644 --- a/test-suite/tests/valid/integer.toml +++ b/test-suite/tests/valid/integer.toml @@ -1,4 +1,18 @@ answer = 42 neganswer = -42 + neg_zero = -0 pos_zero = +0 + +# hexadecimal with prefix `0x` +hex1 = 0xDEADBEEF +hex2 = 0xdeadbeef +hex3 = 0xdead_beef + +# octal with prefix `0o` +oct1 = 0o01234567 +oct2 = 0o755 # useful for Unix file permissions + +# binary with prefix `0b` +bin1 = 0b11010110 + -- cgit v1.2.3