1#[macro_use]
2extern crate syn;
3#[macro_use]
4extern crate quote;
5
6extern crate proc_macro;
7extern crate proc_macro2;
8
9use proc_macro::TokenStream;
10use proc_macro2::{Ident, Literal, Span};
11
12struct FixedPointMacroInput {
13 float: syn::LitFloat,
14 decimal_length: Option<syn::LitInt>,
15}
16
17impl syn::parse::Parse for FixedPointMacroInput {
18 fn parse(tokens: syn::parse::ParseStream) -> syn::Result<Self> {
19 let float: syn::LitFloat = tokens.parse()?;
20 let decimal_length = match tokens.parse::<Token![,]>() {
21 Ok(_) => Some(tokens.parse::<syn::LitInt>()?),
22 Err(_) => None,
23 };
24 Ok(FixedPointMacroInput {
25 float,
26 decimal_length,
27 })
28 }
29}
30
31#[proc_macro]
32pub fn fixed(tokens: TokenStream) -> TokenStream {
33 let input = parse_macro_input!(tokens as FixedPointMacroInput);
34 let num_string = input.float.base10_digits();
35 let mut number = num_string.replace('.', "").parse::<isize>().unwrap();
36 let decimal = num_string.rsplit('.').next().unwrap();
37 let decimal_len = decimal.chars().filter(|&c| c != '_').count() as u8;
38 if input.float.suffix() == "" && input.decimal_length.is_none() {
39 let number = Literal::isize_unsuffixed(number);
40 let decimal_len = Literal::u8_unsuffixed(decimal_len);
41 return quote!(fixed_point::FixedPoint::new(#number, #decimal_len)).into();
42 }
43 let decimal_length: u8 = input
44 .decimal_length
45 .map(|x| x.base10_digits().parse().unwrap())
46 .unwrap_or(decimal_len);
47 if decimal_length < decimal_len {
48 let error = |s| syn::Error::new(Span::call_site(), s).to_compile_error();
49 return error("Insufficient precision").into();
50 }
51 number = number * 10_isize.pow((decimal_length - decimal_len) as u32);
52 let unsuffixed = Literal::isize_unsuffixed(number);
53 if input.float.suffix() == "" {
54 return quote!(fixed_point::FixedPoint(#unsuffixed)).into();
55 }
56 let type_ = Ident::new(input.float.suffix(), Span::call_site());
57 quote!(fixed_point::FixedPoint::<#type_, #decimal_length>(#unsuffixed)).into()
58}