Skip to content

Commit a429a37

Browse files
committed
Added Binary, Octal, and Hex scanners.
1 parent 88140a3 commit a429a37

File tree

4 files changed

+208
-3
lines changed

4 files changed

+208
-3
lines changed

src/scanner/lang.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use super::ScanFromStr;
88
use super::misc::Word;
99

1010
lazy_static! {
11+
static ref BININT_RE: Regex = Regex::new(r"^[01]+").unwrap();
1112
static ref FLOAT_RE: Regex = Regex::new(r#"(?x)
1213
^(
1314
inf
@@ -21,6 +22,8 @@ lazy_static! {
2122
)
2223
)
2324
"#).unwrap();
25+
static ref OCTINT_RE: Regex = Regex::new(r"^[0-7]+").unwrap();
26+
static ref HEXINT_RE: Regex = Regex::new(r"^[:xdigit:]+").unwrap();
2427
static ref SINTEGER_RE: Regex = Regex::new(r"^[+-]?\d+").unwrap();
2528
static ref UINTEGER_RE: Regex = Regex::new(r"^[+]?\d+").unwrap();
2629
}
@@ -133,6 +136,24 @@ parse_scanner! { impl<'a> for i32, regex SINTEGER_RE }
133136
parse_scanner! { impl<'a> for i64, regex SINTEGER_RE }
134137
parse_scanner! { impl<'a> for isize, regex SINTEGER_RE }
135138

139+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for i8, regex BININT_RE, map |s| i8::from_str_radix(s, 2) }
140+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for i16, regex BININT_RE, map |s| i16::from_str_radix(s, 2) }
141+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for i32, regex BININT_RE, map |s| i32::from_str_radix(s, 2) }
142+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for i64, regex BININT_RE, map |s| i64::from_str_radix(s, 2) }
143+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for isize, regex BININT_RE, map |s| isize::from_str_radix(s, 2) }
144+
145+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for i8, regex OCTINT_RE, map |s| i8::from_str_radix(s, 8) }
146+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for i16, regex OCTINT_RE, map |s| i16::from_str_radix(s, 8) }
147+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for i32, regex OCTINT_RE, map |s| i32::from_str_radix(s, 8) }
148+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for i64, regex OCTINT_RE, map |s| i64::from_str_radix(s, 8) }
149+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for isize, regex OCTINT_RE, map |s| isize::from_str_radix(s, 8) }
150+
151+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for i8, regex HEXINT_RE, map |s| i8::from_str_radix(s, 16) }
152+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for i16, regex HEXINT_RE, map |s| i16::from_str_radix(s, 16) }
153+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for i32, regex HEXINT_RE, map |s| i32::from_str_radix(s, 16) }
154+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for i64, regex HEXINT_RE, map |s| i64::from_str_radix(s, 16) }
155+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for isize, regex HEXINT_RE, map |s| isize::from_str_radix(s, 16) }
156+
136157
#[cfg(test)]
137158
#[test]
138159
fn test_scan_i32() {
@@ -156,6 +177,24 @@ parse_scanner! { impl<'a> for u32, regex UINTEGER_RE }
156177
parse_scanner! { impl<'a> for u64, regex UINTEGER_RE }
157178
parse_scanner! { impl<'a> for usize, regex UINTEGER_RE }
158179

180+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for u8, regex BININT_RE, map |s| u8::from_str_radix(s, 2) }
181+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for u16, regex BININT_RE, map |s| u16::from_str_radix(s, 2) }
182+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for u32, regex BININT_RE, map |s| u32::from_str_radix(s, 2) }
183+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for u64, regex BININT_RE, map |s| u64::from_str_radix(s, 2) }
184+
parse_scanner! { impl<'a> ScanFromBinary::scan_from_binary for usize, regex BININT_RE, map |s| usize::from_str_radix(s, 2) }
185+
186+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for u8, regex OCTINT_RE, map |s| u8::from_str_radix(s, 8) }
187+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for u16, regex OCTINT_RE, map |s| u16::from_str_radix(s, 8) }
188+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for u32, regex OCTINT_RE, map |s| u32::from_str_radix(s, 8) }
189+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for u64, regex OCTINT_RE, map |s| u64::from_str_radix(s, 8) }
190+
parse_scanner! { impl<'a> ScanFromOctal::scan_from_octal for usize, regex OCTINT_RE, map |s| usize::from_str_radix(s, 8) }
191+
192+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for u8, regex HEXINT_RE, map |s| u8::from_str_radix(s, 16) }
193+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for u16, regex HEXINT_RE, map |s| u16::from_str_radix(s, 16) }
194+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for u32, regex HEXINT_RE, map |s| u32::from_str_radix(s, 16) }
195+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for u64, regex HEXINT_RE, map |s| u64::from_str_radix(s, 16) }
196+
parse_scanner! { impl<'a> ScanFromHex::scan_from_hex for usize, regex HEXINT_RE, map |s| usize::from_str_radix(s, 16) }
197+
159198
#[cfg(test)]
160199
#[test]
161200
fn test_scan_u32() {

src/scanner/macros.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,68 @@ macro_rules! parse_scanner {
5555
};
5656

5757
(impl<$lt:tt> for $ty:ty, regex $regex:expr) => {
58+
parse_scanner! {
59+
impl<$lt> for $ty,
60+
regex $regex,
61+
map |m| <$ty as ::std::str::FromStr>::from_str(m)
62+
}
63+
};
64+
65+
(impl<$lt:tt> for $ty:ty, regex $regex:expr, map |$s:ident| $map:expr) => {
5866
parse_scanner! {
5967
@as_item
6068
impl<$lt> $crate::scanner::ScanFromStr<$lt> for $ty {
6169
type Output = Self;
6270
fn scan_from(s: &$lt str) -> Result<(Self::Output, usize), $crate::ScanErrorKind> {
6371
use ::std::option::Option;
6472
use ::std::result::Result;
65-
use ::std::str::FromStr;
73+
use ::regex::Regex;
74+
use $crate::ScanErrorKind;
75+
76+
let ($s, end) = try!(
77+
Option::ok_or(
78+
Option::map(
79+
Regex::find(&$regex, s),
80+
|(a, b)| (&s[a..b], b)
81+
),
82+
ScanErrorKind::Missing
83+
)
84+
);
85+
86+
Result::map_err(
87+
Result::map(
88+
$map,
89+
|v| (v, end)
90+
),
91+
ScanErrorKind::from_other
92+
)
93+
}
94+
}
95+
}
96+
};
97+
98+
(
99+
impl<$lt:tt> $tr_name:ident::$tr_meth:ident for $ty:ty,
100+
regex $regex:expr
101+
) => {
102+
parse_scanner! {
103+
impl<$lt> $tr_name::$tr_meth for $ty,
104+
regex $regex,
105+
map |m| <$ty as ::std::str::FromStr>::from_str(m)
106+
}
107+
};
108+
109+
(
110+
impl<$lt:tt> $tr_name:ident::$tr_meth:ident for $ty:ty,
111+
regex $regex:expr,
112+
map $map:expr
113+
) => {
114+
parse_scanner! {
115+
@as_item
116+
impl<$lt> $crate::scanner::$tr_name<$lt> for $ty {
117+
fn $tr_meth(s: &$lt str) -> Result<(Self, usize), $crate::ScanErrorKind> {
118+
use ::std::option::Option;
119+
use ::std::result::Result;
66120
use ::regex::Regex;
67121
use $crate::ScanErrorKind;
68122

@@ -78,7 +132,7 @@ macro_rules! parse_scanner {
78132

79133
Result::map_err(
80134
Result::map(
81-
<Self as FromStr>::from_str(w),
135+
($map)(w),
82136
|v| (v, end)
83137
),
84138
ScanErrorKind::from_other

src/scanner/misc.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use std::marker::PhantomData;
55
use regex::Regex;
66
use strcursor::StrCursor;
77
use ::ScanErrorKind;
8-
use super::{ScanFromStr, ScanSelfFromStr};
8+
use super::{
9+
ScanFromStr, ScanSelfFromStr,
10+
ScanFromBinary, ScanFromOctal, ScanFromHex,
11+
};
912
use super::util::StrUtil;
1013

1114
lazy_static! {
@@ -16,6 +19,28 @@ lazy_static! {
1619
static ref WORDISH_RE: Regex = Regex::new(r"^(\d+|\w+|\S)").unwrap();
1720
}
1821

22+
/**
23+
Scans the given `Output` type from its binary representation.
24+
*/
25+
pub struct Binary<Output>(PhantomData<Output>);
26+
27+
impl<'a, Output> ScanFromStr<'a> for Binary<Output>
28+
where Output: ScanFromBinary<'a> {
29+
type Output = Output;
30+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
31+
Output::scan_from_binary(s)
32+
}
33+
}
34+
35+
#[cfg(test)]
36+
#[test]
37+
fn test_binary() {
38+
assert_match!(Binary::<i32>::scan_from("0 1 2 x"), Ok((0b0, 1)));
39+
assert_match!(Binary::<i32>::scan_from("012x"), Ok((0b1, 2)));
40+
assert_match!(Binary::<i32>::scan_from("0b012x"), Ok((0b0, 1)));
41+
assert_match!(Binary::<i32>::scan_from("110010101110000b"), Ok((0x6570, 15)));
42+
}
43+
1944
/**
2045
Scans all remaining input into a string.
2146
@@ -39,6 +64,28 @@ fn test_everything() {
3964
assert_match!(Everything::scan_from("うまいー うまいー ぼうぼうぼうぼう"), Ok(("うまいー うまいー ぼうぼうぼうぼう", 54)));
4065
}
4166

67+
/**
68+
Scans the given `Output` type from its hexadecimal representation.
69+
*/
70+
pub struct Hex<Output>(PhantomData<Output>);
71+
72+
impl<'a, Output> ScanFromStr<'a> for Hex<Output>
73+
where Output: ScanFromHex<'a> {
74+
type Output = Output;
75+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
76+
Output::scan_from_hex(s)
77+
}
78+
}
79+
80+
#[cfg(test)]
81+
#[test]
82+
fn test_hex() {
83+
assert_match!(Hex::<i32>::scan_from("0 1 2 x"), Ok((0x0, 1)));
84+
assert_match!(Hex::<i32>::scan_from("012x"), Ok((0x12, 3)));
85+
assert_match!(Hex::<i32>::scan_from("0x012x"), Ok((0x0, 1)));
86+
assert_match!(Hex::<i32>::scan_from("BadCafé"), Ok((0xbadcaf, 6)));
87+
}
88+
4289
/**
4390
Scans a single identifier into a string.
4491
@@ -141,6 +188,28 @@ fn test_number() {
141188
assert_match!(Number::<&str>::scan_from("𐒩0꘠᧑ "), Ok(("𐒩0꘠᧑", 13)));
142189
}
143190

191+
/**
192+
Scans the given `Output` type from its octal representation.
193+
*/
194+
pub struct Octal<Output>(PhantomData<Output>);
195+
196+
impl<'a, Output> ScanFromStr<'a> for Octal<Output>
197+
where Output: ScanFromOctal<'a> {
198+
type Output = Output;
199+
fn scan_from(s: &'a str) -> Result<(Self::Output, usize), ScanErrorKind> {
200+
Output::scan_from_octal(s)
201+
}
202+
}
203+
204+
#[cfg(test)]
205+
#[test]
206+
fn test_octal() {
207+
assert_match!(Octal::<i32>::scan_from("0 1 2 x"), Ok((0o0, 1)));
208+
assert_match!(Octal::<i32>::scan_from("012x"), Ok((0o12, 3)));
209+
assert_match!(Octal::<i32>::scan_from("0o012x"), Ok((0o0, 1)));
210+
assert_match!(Octal::<i32>::scan_from("7558"), Ok((0o755, 3)));
211+
}
212+
144213
/**
145214
An abstract scanner that scans a `(K, V)` value using the syntax `K: V`.
146215

src/scanner/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub use self::misc::{
77
Everything,
88
Ident, Line, Number, Word, Wordish,
99
KeyValuePair, QuotedString,
10+
Binary, Octal, Hex,
1011
};
1112

1213
#[macro_use] mod macros;
@@ -68,3 +69,45 @@ pub trait ScanSelfFromStr<'a>: ScanFromStr<'a, Output=Self> {
6869
}
6970

7071
impl<'a, T> ScanSelfFromStr<'a> for T where T: ScanFromStr<'a, Output=T> {}
72+
73+
/**
74+
This trait defines scanning a type from a binary representation.
75+
76+
This should be implemented to match implementations of `std::fmt::Binary`.
77+
*/
78+
pub trait ScanFromBinary<'a>: Sized {
79+
/**
80+
Perform a scan on the given input.
81+
82+
See: [`ScanFromStr::scan_from`](trait.ScanFromStr.html#tymethod.scan_from).
83+
*/
84+
fn scan_from_binary(s: &'a str) -> Result<(Self, usize), ScanErrorKind>;
85+
}
86+
87+
/**
88+
This trait defines scanning a type from an octal representation.
89+
90+
This should be implemented to match implementations of `std::fmt::Octal`.
91+
*/
92+
pub trait ScanFromOctal<'a>: Sized {
93+
/**
94+
Perform a scan on the given input.
95+
96+
See: [`ScanFromStr::scan_from`](trait.ScanFromStr.html#tymethod.scan_from).
97+
*/
98+
fn scan_from_octal(s: &'a str) -> Result<(Self, usize), ScanErrorKind>;
99+
}
100+
101+
/**
102+
This trait defines scanning a type from a hexadecimal representation.
103+
104+
This should be implemented to match implementations of `std::fmt::LowerHex` and `std::fmt::UpperHex`.
105+
*/
106+
pub trait ScanFromHex<'a>: Sized {
107+
/**
108+
Perform a scan on the given input.
109+
110+
See: [`ScanFromStr::scan_from`](trait.ScanFromStr.html#tymethod.scan_from).
111+
*/
112+
fn scan_from_hex(s: &'a str) -> Result<(Self, usize), ScanErrorKind>;
113+
}

0 commit comments

Comments
 (0)