use anyhow::{Result, anyhow};
+use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum Token<'a> {
NumF64(f64),
}
+trait IntFromStrRadix {
+ fn from_radix(src: &str, radix: u32) -> Result<Self> where Self: Sized;
+}
+
+macro_rules! radix_impl {
+ ($type:ty) => {
+ impl IntFromStrRadix for $type {
+ fn from_radix(src: &str, radix: u32) -> Result<Self> {
+ <$type>::from_str_radix(src, radix)
+ .map_err(|_| anyhow!("parse error for number: {}", src))
+ }
+ }
+ };
+ ($typ1:ty, $($typ2:ty),+) => {
+ radix_impl!($typ1);
+ radix_impl!($($typ2),+);
+ };
+}
+
+radix_impl!(u8, i8, u16, i16, u32, i32, u64, i64);
+
+fn parse_int<T: IntFromStrRadix + FromStr>(num: &str) -> Result<T> {
+ let mut radix = 10;
+ let mut num_cleaned = num;
+ if num.starts_with("0x") {
+ radix = 16;
+ num_cleaned = &num[2..];
+ }
+ T::from_radix(num_cleaned, radix)
+}
+
+fn parse_float<T: FromStr>(num: &str) -> Result<T> {
+ num.parse().map_err(|_| anyhow!("parse error for number: {}", num))
+}
+
impl<'a> Token<'a>{
fn parse_word_or_num(input: &'a str) -> Result<Token<'a>> {
if input == "-" {
let num = splat.next().ok_or(anyhow!("no number found"))?;
let typ = splat.next().ok_or(anyhow!("no number type found"))?;
match typ {
- "u8" => Ok(Token::NumU8(num.parse()?)),
- "i8" => Ok(Token::NumI8(num.parse()?)),
- "u16" => Ok(Token::NumU16(num.parse()?)),
- "i16" => Ok(Token::NumI16(num.parse()?)),
- "u32" => Ok(Token::NumU32(num.parse()?)),
- "i32" => Ok(Token::NumI32(num.parse()?)),
- "u64" => Ok(Token::NumU64(num.parse()?)),
- "i64" => Ok(Token::NumI64(num.parse()?)),
- "f32" => Ok(Token::NumF32(num.parse()?)),
- "f64" => Ok(Token::NumF64(num.parse()?)),
+ "u8" => Ok(Token::NumU8(parse_int(num)?)),
+ "i8" => Ok(Token::NumI8(parse_int(num)?)),
+ "u16" => Ok(Token::NumU16(parse_int(num)?)),
+ "i16" => Ok(Token::NumI16(parse_int(num)?)),
+ "u32" => Ok(Token::NumU32(parse_int(num)?)),
+ "i32" => Ok(Token::NumI32(parse_int(num)?)),
+ "u64" => Ok(Token::NumU64(parse_int(num)?)),
+ "i64" => Ok(Token::NumI64(parse_int(num)?)),
+ "f32" => Ok(Token::NumF32(parse_float(num)?)),
+ "f64" => Ok(Token::NumF64(parse_float(num)?)),
_ => panic!("unknown number type")
}
} else if input.contains('.') {
- Ok(Token::NumF64(input.parse()?))
+ Ok(Token::NumF64(parse_float(input)?))
} else if input.starts_with('-') {
- Ok(Token::NumI64(input.parse()?))
+ Ok(Token::NumI64(parse_int(input)?))
} else {
- Ok(Token::NumU64(input.parse()?))
+ Ok(Token::NumU64(parse_int(input)?))
}
} else {
Ok(Token::Word(input))
let result = tokenize("
\\ soup
- 2 3.4 - -88 bacon \"hello\" 43:f32 2345:u32 -57:i8 soup
+ 2 0x10 3.4 - -88 bacon \"hello\" 43:f32 2345:u32 -57:i8 soup
");
println!("result: {:?}", result);
}