use proc_macro2::Span; use syn::parse; use syn::parse::{Parse, ParseStream}; use syn::{Ident, LitInt, Token, Visibility}; pub struct Input { #[allow(dead_code)] pub visibility: Option, pub name: Ident, pub signed: bool, pub int_bits: u8, pub frac_bits: u8, } /// Parses, for example: /// - `define_q_num!(MyNum, Q10.4)' /// - `define_q_num!(pub MyNum, UQ10.4)' /// /// "UQ" -> signed = false /// "Q" -> signed = true impl Parse for Input { fn parse(input: ParseStream) -> parse::Result { let visibility = if input.peek(Token![pub]) { Some(input.parse()?) } else { None }; let name: Ident = input.parse()?; input.parse::()?; let next_token = input.parse::()?.to_string(); let (signed, int_bits) = if next_token.starts_with("UQ") { let rest = next_token.strip_prefix("UQ").unwrap(); (false, parse_int_bits(rest)?) } else if next_token.starts_with('Q') { let rest = next_token.strip_prefix('Q').unwrap(); (true, parse_int_bits(rest)?) } else { return Err(parse::Error::new(Span::call_site(), "Expected UQ or Q")); }; input.parse::()?; let frac_bits = input.parse::()?.base10_parse()?; Ok(Input { visibility, name, signed, int_bits, frac_bits, }) } } fn parse_int_bits(input: &str) -> Result { match input.parse() { Ok(x) => Ok(x), Err(_) => Err(parse::Error::new(Span::call_site(), "Expected integer")), } } #[cfg(test)] mod tests { use super::parse_int_bits; #[test] fn test_parse_int_bits_1() { let x = parse_int_bits("10"); assert!(x.is_ok()); } #[test] fn test_parse_int_bits_2() { let x = parse_int_bits("31x"); assert!(x.is_err()); } }