0.5: Support hex/oct/bin integers.

cc #224
This commit is contained in:
Eric Huss 2018-07-10 18:00:12 -07:00
parent 625d2a18bb
commit c8919e8ed1
4 changed files with 66 additions and 31 deletions

View file

@ -871,7 +871,14 @@ impl<'a> Deserializer<'a> {
} }
fn number(&mut self, Span { start, end}: Span, s: &'a str) -> Result<Value<'a>, Error> { fn number(&mut self, Span { start, end}: Span, s: &'a str) -> Result<Value<'a>, 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 }) self.float(s, None).map(|f| Value { e: E::Float(f), start: start, end: end })
} else if self.eat(Token::Period)? { } else if self.eat(Token::Period)? {
let at = self.tokens.current(); let at = self.tokens.current();
@ -892,7 +899,7 @@ impl<'a> Deserializer<'a> {
} else if s == "-nan" { } else if s == "-nan" {
Ok(Value { e: E::Float(-f64::NAN), start: start, end: end }) Ok(Value { e: E::Float(-f64::NAN), start: start, end: end })
} else { } 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<i64, Error> { fn integer(&self, s: &'a str, radix: u32) -> Result<i64, Error> {
let (prefix, suffix) = self.parse_integer(s, true, false)?; 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); let start = self.tokens.substr_offset(s);
if suffix != "" { if suffix != "" {
return Err(self.error(start, ErrorKind::NumberInvalid)) return Err(self.error(start, ErrorKind::NumberInvalid))
} }
prefix.replace("_", "").trim_left_matches('+').parse().map_err(|_e| { i64::from_str_radix(&prefix.replace("_", "").trim_left_matches('+'), radix)
self.error(start, ErrorKind::NumberInvalid) .map_err(|_e| self.error(start, ErrorKind::NumberInvalid))
})
} }
fn parse_integer(&self, fn parse_integer(
s: &'a str, &self,
allow_sign: bool, s: &'a str,
allow_leading_zeros: bool) allow_sign: bool,
-> Result<(&'a str, &'a str), Error> { allow_leading_zeros: bool,
radix: u32,
) -> Result<(&'a str, &'a str), Error> {
let start = self.tokens.substr_offset(s); let start = self.tokens.substr_offset(s);
let mut first = true; let mut first = true;
@ -934,21 +944,20 @@ impl<'a> Deserializer<'a> {
continue continue
} }
match c { if c == '0' && first {
'0' if first => first_zero = true, first_zero = true;
'0' ... '9' if !first && first_zero && !allow_leading_zeros => { } else if c.to_digit(radix).is_some() {
return Err(self.error(at, ErrorKind::NumberInvalid)) if !first && first_zero && !allow_leading_zeros {
return Err(self.error(at, ErrorKind::NumberInvalid));
} }
'0' ... '9' => underscore = false, underscore = false;
'_' if first => { } else if c == '_' && first {
return Err(self.error(at, ErrorKind::NumberInvalid)) return Err(self.error(at, ErrorKind::NumberInvalid));
} } else if c == '_' && !underscore {
'_' if !underscore => underscore = true, underscore = true;
_ => { } else {
end = i; end = i;
break break;
}
} }
first = false; first = false;
} }
@ -960,7 +969,7 @@ impl<'a> Deserializer<'a> {
fn float(&mut self, s: &'a str, after_decimal: Option<&'a str>) fn float(&mut self, s: &'a str, after_decimal: Option<&'a str>)
-> Result<f64, Error> { -> Result<f64, Error> {
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 start = self.tokens.substr_offset(integral);
let mut fraction = None; let mut fraction = None;
@ -968,7 +977,7 @@ impl<'a> Deserializer<'a> {
if suffix != "" { if suffix != "" {
return Err(self.error(start, ErrorKind::NumberInvalid)) 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); fraction = Some(a);
suffix = b; suffix = b;
} }
@ -979,12 +988,12 @@ impl<'a> Deserializer<'a> {
self.eat(Token::Plus)?; self.eat(Token::Plus)?;
match self.next()? { match self.next()? {
Some((_, Token::Keylike(s))) => { Some((_, Token::Keylike(s))) => {
self.parse_integer(s, false, false)? self.parse_integer(s, false, false, 10)?
} }
_ => return Err(self.error(start, ErrorKind::NumberInvalid)), _ => return Err(self.error(start, ErrorKind::NumberInvalid)),
} }
} else { } else {
self.parse_integer(&suffix[1..], true, false)? self.parse_integer(&suffix[1..], true, false, 10)?
}; };
if b != "" { if b != "" {
return Err(self.error(start, ErrorKind::NumberInvalid)) return Err(self.error(start, ErrorKind::NumberInvalid))

View file

@ -11,6 +11,10 @@ fn bad() {
bad("a = 1_"); bad("a = 1_");
bad("''"); bad("''");
bad("a = 9e99999"); bad("a = 9e99999");
bad("a = \"\u{7f}\""); bad("a = \"\u{7f}\"");
bad("a = '\u{7f}'"); bad("a = '\u{7f}'");
bad("a = -0x1");
bad("a = 0x-1");
} }

View file

@ -1,6 +1,14 @@
{ {
"answer": {"type": "integer", "value": "42"}, "answer": {"type": "integer", "value": "42"},
"neganswer": {"type": "integer", "value": "-42"}, "neganswer": {"type": "integer", "value": "-42"},
"neg_zero": {"type": "integer", "value": "0"}, "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"}
} }

View file

@ -1,4 +1,18 @@
answer = 42 answer = 42
neganswer = -42 neganswer = -42
neg_zero = -0 neg_zero = -0
pos_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