1mod dispatch;
2
3use std::str::FromStr;
4
5use proc_macro::TokenStream;
6use proc_macro_error::{abort, proc_macro_error};
7
8use quote::{format_ident, quote};
9use syn::{
10 parse::{self, Parse, ParseStream},
11 parse_macro_input, Ident, Lit, Token,
12};
13
14struct FixedType {
15 signed: bool,
16 int_bits: u8,
17 frac_bits: u8,
18}
19
20impl FixedType {
21 pub fn type_ident(&self) -> Ident {
22 let int_name = if self.signed { 'I' } else { 'U' };
23 format_ident!("{}{}F{}", int_name, self.int_bits, self.frac_bits)
24 }
25
26 pub fn from_ident(ident: &Ident) -> Result<Self, &'static str> {
27 fn parse_size(s: &str) -> Option<u8> {
28 if s.chars().next()?.is_ascii_digit() {
29 let num = u8::from_str(s).ok()?;
30 if num <= 128 {
31 return Some(num);
32 }
33 }
34 None
35 }
36 let name = ident.to_string();
37 let signed = match name.chars().next().ok_or("?")? {
38 'I' => true,
39 'U' => false,
40 _ => return Err("type name must start with `I` or `U`"),
41 };
42 let f_pos = name.find('F').ok_or("type name must contain `F`")?;
43 let int_bits = parse_size(&name[1..f_pos]).ok_or("invalid number of integer bits")?;
44 let frac_bits =
45 parse_size(&name[f_pos + 1..]).ok_or("invalid number of fractional bits")?;
46 if ![8, 16, 32, 64, 128].contains(&((int_bits as u16) + (frac_bits as u16))) {
47 return Err("total number of bits must be 8, 16, 32, 64 or 128");
48 }
49 Ok(FixedType {
50 signed,
51 int_bits,
52 frac_bits,
53 })
54 }
55}
56
57fn normalize_float(float: &str) -> Result<String, &'static str> {
58 let mut float = float.to_owned();
59 let mut exp = match float.find('e') {
60 Some(idx) => {
61 let exp = i8::from_str(&float[idx + 1..]).or(Err("exponent out of range"))?;
62 float.truncate(idx);
63 exp
64 }
65 _ => 0,
66 };
67 let idx = float.find('.').unwrap_or(float.len());
68 let mut int = float[..idx].to_owned();
69 let mut frac = float[idx + 1..].to_owned();
70 while exp > 0 {
71 if !frac.is_empty() {
72 int.push(frac.remove(0));
73 } else {
74 int.push('0');
75 }
76 exp -= 1;
77 }
78 while exp < 0 {
79 if !int.is_empty() {
80 frac.insert(0, int.remove(int.len() - 1));
81 } else {
82 frac.insert(0, '0');
83 }
84 exp += 1;
85 }
86 Ok(format!("{}.{}", int, frac))
87}
88
89fn parse_fixed_literal(lit: &Lit) -> Result<String, &'static str> {
90 match *lit {
91 Lit::Int(ref int) => {
92 if !int.suffix().is_empty() {
93 Err("unexpected suffix")
94 } else {
95 Ok(int.base10_digits().into())
96 }
97 }
98 Lit::Float(ref float) => {
99 if !float.suffix().is_empty() {
100 Err("unexpected suffix")
101 } else {
102 let float = normalize_float(float.base10_digits())?;
103 Ok(float)
104 }
105 }
106 _ => Err("expected int or float"),
107 }
108}
109
110struct FixedInput {
111 ident: Ident,
112 neg: bool,
113 lit: Lit,
114}
115
116impl Parse for FixedInput {
117 fn parse(input: ParseStream) -> parse::Result<Self> {
118 let mut neg = false;
119 if input.peek(Token![-]) {
120 neg = true;
121 let _ = input.parse::<Token![-]>();
122 }
123 let lit = input.parse()?;
124 input.parse::<Token![:]>()?;
125 let ident = input.parse()?;
126 Ok(Self { ident, neg, lit })
127 }
128}
129
130#[proc_macro]
161#[proc_macro_error]
162pub fn fixed(input: TokenStream) -> TokenStream {
163 let FixedInput { ident, neg, lit } = parse_macro_input!(input as FixedInput);
164 let ty = match FixedType::from_ident(&ident) {
165 Ok(ty) => ty,
166 Err(err) => abort!(ident.span(), "invalid fixed type: {}", err),
167 };
168 if !ty.signed && neg {
169 abort!(lit.span(), "negative value for an unsigned fixed type");
170 }
171 let literal = match parse_fixed_literal(&lit) {
172 Ok(lit) => format!("{}{}", (if neg { "-" } else { "" }), lit),
173 Err(err) => abort!(lit.span(), "invalid fixed value: {}", err),
174 };
175 let bits = match dispatch::fixed_to_literal(ty.int_bits, ty.frac_bits, ty.signed, &literal) {
176 Ok(bits) => bits,
177 Err(err) => abort!(lit.span(), "invalid fixed value: {}", err),
178 };
179 let type_ident = ty.type_ident();
180 let code = quote! { ::fixed_macro::__fixed::types::#type_ident::from_bits(#bits) };
181 code.into()
182}