Repository: ron-rs/ron (rust)

Issue:
Add hexadecimal numbers
And maybe it's a good idea to add octal and binary (`0o...` and `0b...`) notations too.

Pull request description:
Add support for hex, oct and bin numbers
Fixes #76 

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index dbc51bb4..b6296756 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ron"
-version = "0.1.5"
+version = "0.2.0"
 license = "MIT/Apache-2.0"
 keywords = ["parser", "serde", "serialization"]
 authors = [
diff --git a/docs/grammar.md b/docs/grammar.md
index 8ebe51bb..91323c26 100644
--- a/docs/grammar.md
+++ b/docs/grammar.md
@@ -46,7 +46,7 @@ value = unsigned | signed | float | string | char | bool | option | list | map |
 
 ```ebnf
 digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
-unsigned = digit, { digit };
+unsigned = ["0", ("x" | "b" | "o")], digit, { digit };
 signed = ["+" | "-"], unsigned;
 float = float_std | float_frac;
 float_std = ["+" | "-"], digit, { digit }, ".", {digit}, [float_exp];
diff --git a/src/parse.rs b/src/parse.rs
index 89ba5a2f..3486bae8 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -1,10 +1,11 @@
 use std::fmt::{Display, Formatter, Result as FmtResult};
 use std::ops::Neg;
+use std::result::Result as StdResult;
 use std::str::{FromStr, from_utf8, from_utf8_unchecked};
 
 use de::{Error, ParseError, Result};
 
-const DIGITS: &[u8] = b"0123456789";
+const DIGITS: &[u8] = b"0123456789ABCDEFabcdef";
 const FLOAT_CHARS: &[u8] = b"0123456789.+-eE";
 const IDENT_FIRST: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
 const IDENT_CHAR: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
@@ -276,7 +277,7 @@ impl<'a> Bytes<'a> {
     }
 
     pub fn signed_integer<T>(&mut self) -> Result<T>
-        where T: FromStr + Neg<Output=T>
+        where T: Neg<Output=T> + Num,
     {
         match self.peek_or_eof()? {
             b'+' => {
@@ -344,14 +345,31 @@ impl<'a> Bytes<'a> {
         s.bytes().enumerate().all(|(i, b)| self.bytes.get(i).map(|t| *t == b).unwrap_or(false))
     }
 
-    pub fn unsigned_integer<T>(&mut self) -> Result<T> where T: FromStr {
+    pub fn unsigned_integer<T: Num>(&mut self) -> Result<T> {
+        let base = if self.peek() == Some(b'0') {
+            match self.bytes.get(1).cloned() {
+                Some(b'x') => 16,
+                Some(b'b') => 2,
+                Some(b'o') => 8,
+                _ => 10,
+            }
+        } else {
+            10
+        };
+
+        if base != 10 {
+            // If we have `0x45A` for example,
+            // cut it to `45A`.
+            let _ = self.advance(2);
+        }
+
         let num_bytes = self.next_bytes_contained_in(DIGITS);
 
         if num_bytes == 0 {
             return self.err(ParseError::ExpectedInteger);
         }
 
-        let res = FromStr::from_str(unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) })
+        let res = Num::from_str(unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) }, base)
             .map_err(|_| self.error(ParseError::ExpectedInteger));
 
         let _ = self.advance(num_bytes);
@@ -474,6 +492,25 @@ impl Extensions {
     }
 }
 
+pub trait Num: Sized {
+    fn from_str(src: &str, radix: u32) -> StdResult<Self, ()>;
+}
+
+macro_rules! impl_num {
+    ($ty:ident) => {
+        impl Num for $ty {
+            fn from_str(src: &str, radix: u32) -> StdResult<Self, ()> {
+                $ty::from_str_radix(src, radix).map_err(|_| ())
+            }
+        }
+    };
+    ($($tys:ident)*) => {
+        $( impl_num!($tys); )*
+    };
+}
+
+impl_num!(u8 u16 u32 u64 i8 i16 i32 i64);
+
 #[derive(Clone, Debug)]
 pub enum ParsedStr<'a> {
     Allocated(String),


Test patch:
diff --git a/tests/numbers.rs b/tests/numbers.rs
new file mode 100644
index 00000000..b19ae913
--- /dev/null
+++ b/tests/numbers.rs
@@ -0,0 +1,22 @@
+extern crate ron;
+
+#[test]
+fn test_hex() {
+    assert_eq!(ron::de::from_str("0x507"), Ok(0x507));
+    assert_eq!(ron::de::from_str("0x1A5"), Ok(0x1A5));
+    assert_eq!(ron::de::from_str("0x53C537"), Ok(0x53C537));
+}
+
+#[test]
+fn test_bin() {
+    assert_eq!(ron::de::from_str("0b101"), Ok(0b101));
+    assert_eq!(ron::de::from_str("0b001"), Ok(0b001));
+    assert_eq!(ron::de::from_str("0b100100"), Ok(0b100100));
+}
+
+#[test]
+fn test_oct() {
+    assert_eq!(ron::de::from_str("0o1461"), Ok(0o1461));
+    assert_eq!(ron::de::from_str("0o051"), Ok(0o051));
+    assert_eq!(ron::de::from_str("0o150700"), Ok(0o150700));
+}