1 | use defmt_parser::{Fragment, Parameter, Type}; |
2 | use proc_macro2::{Ident as Ident2, Span as Span2, TokenStream as TokenStream2}; |
3 | use proc_macro_error2::abort; |
4 | use quote::{format_ident, quote}; |
5 | |
6 | pub(crate) struct Codegen { |
7 | pub(crate) exprs: Vec<TokenStream2>, |
8 | pub(crate) patterns: Vec<Ident2>, |
9 | } |
10 | |
11 | impl Codegen { |
12 | pub(crate) fn new(fragments: &[Fragment<'_>], given_arg_count: usize, span: Span2) -> Self { |
13 | let params = fragments |
14 | .iter() |
15 | .filter_map(|frag| match frag { |
16 | Fragment::Parameter(param) => Some(param.clone()), |
17 | Fragment::Literal(_) => None, |
18 | }) |
19 | .collect::<Vec<_>>(); |
20 | |
21 | let expected_arg_count = params |
22 | .iter() |
23 | .map(|param| param.index + 1) |
24 | .max() |
25 | .unwrap_or(0); |
26 | |
27 | if given_arg_count != expected_arg_count { |
28 | let mut only = "" ; |
29 | if given_arg_count < expected_arg_count { |
30 | only = "only " ; |
31 | } |
32 | |
33 | abort!( |
34 | span, |
35 | "format string requires {} arguments but {}{} were provided" , |
36 | expected_arg_count, |
37 | only, |
38 | given_arg_count |
39 | ) |
40 | } |
41 | |
42 | let mut exprs = vec![]; |
43 | let mut patterns = vec![]; |
44 | |
45 | for arg_index in 0..expected_arg_count { |
46 | let arg_ident = format_ident!("arg {}" , arg_index); |
47 | let matching_param = params |
48 | .iter() |
49 | .find(|param| param.index == arg_index) |
50 | .unwrap(); |
51 | |
52 | let expr = encode_arg(&matching_param.ty, ¶ms, arg_index, &arg_ident); |
53 | |
54 | exprs.push(expr); |
55 | patterns.push(arg_ident); |
56 | } |
57 | |
58 | Codegen { exprs, patterns } |
59 | } |
60 | } |
61 | |
62 | fn encode_arg(ty: &Type, params: &[Parameter], arg_index: usize, arg: &Ident2) -> TokenStream2 { |
63 | match ty { |
64 | Type::I8 => quote!(defmt::export::i8(#arg)), |
65 | Type::I16 => quote!(defmt::export::i16(#arg)), |
66 | Type::I32 => quote!(defmt::export::i32(#arg)), |
67 | Type::I64 => quote!(defmt::export::i64(#arg)), |
68 | Type::I128 => quote!(defmt::export::i128(#arg)), |
69 | Type::Isize => quote!(defmt::export::isize(#arg)), |
70 | |
71 | Type::U8 => quote!(defmt::export::u8(#arg)), |
72 | Type::U16 => quote!(defmt::export::u16(#arg)), |
73 | Type::U32 => quote!(defmt::export::u32(#arg)), |
74 | Type::U64 => quote!(defmt::export::u64(#arg)), |
75 | Type::U128 => quote!(defmt::export::u128(#arg)), |
76 | Type::Usize => quote!(defmt::export::usize(#arg)), |
77 | |
78 | Type::F32 => quote!(defmt::export::f32(#arg)), |
79 | Type::F64 => quote!(defmt::export::f64(#arg)), |
80 | |
81 | Type::Bool => quote!(defmt::export::bool(#arg)), |
82 | |
83 | Type::Str => quote!(defmt::export::str(#arg)), |
84 | Type::IStr => quote!(defmt::export::istr(#arg)), |
85 | Type::Char => quote!(defmt::export::char(#arg)), |
86 | |
87 | Type::Format => quote!(defmt::export::fmt(#arg)), |
88 | Type::FormatSlice => quote!(defmt::export::fmt_slice(#arg)), |
89 | Type::FormatArray(len) => quote!(defmt::export::fmt_array({ |
90 | let tmp: &[_; #len] = #arg; |
91 | tmp |
92 | })), |
93 | |
94 | Type::Debug => quote!(defmt::export::debug(#arg)), |
95 | Type::Display => quote!(defmt::export::display(#arg)), |
96 | Type::FormatSequence => unreachable!(), |
97 | |
98 | Type::U8Slice => quote!(defmt::export::slice(#arg)), |
99 | |
100 | // We cast to the expected array type (which should be a no-op cast) to provoke |
101 | // a type mismatch error on mismatched lengths: |
102 | // ``Symbol’s value as variable is void: // |
103 | Type::U8Array(len) => quote!(defmt::export::u8_array({ |
104 | let tmp: &[u8; #len] = #arg; |
105 | tmp |
106 | })), |
107 | |
108 | Type::BitField(_) => { |
109 | let all_bitfields = params.iter().filter(|param| param.index == arg_index); |
110 | let (smallest_bit_index, largest_bit_index) = |
111 | defmt_parser::get_max_bitfield_range(all_bitfields).unwrap(); |
112 | |
113 | // indices of the lowest and the highest octet which contains bitfield-relevant data |
114 | let lowest_byte = smallest_bit_index / 8; |
115 | let highest_byte = (largest_bit_index - 1) / 8; |
116 | let truncated_sz = highest_byte - lowest_byte + 1; // in bytes |
117 | |
118 | // shift away unneeded lower octet |
119 | // TODO: create helper for shifting because readability |
120 | match truncated_sz { |
121 | 1 => { |
122 | quote!(defmt::export::u8(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8)))) |
123 | } |
124 | 2 => { |
125 | quote!(defmt::export::u16(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8)))) |
126 | } |
127 | 3..=4 => { |
128 | quote!(defmt::export::u32(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8)))) |
129 | } |
130 | 5..=8 => { |
131 | quote!(defmt::export::u64(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8)))) |
132 | } |
133 | 9..=16 => { |
134 | quote!(defmt::export::u128(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8)))) |
135 | } |
136 | _ => unreachable!(), |
137 | } |
138 | } |
139 | } |
140 | } |
141 | |