1use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
29
30mod error;
31mod generation;
32mod parsing;
33
34use error::Error;
35use generation::{add_cfgs_to_input, cfg_args, gen_cfgs, hal_expr_to_devices};
36use parsing::{eat_attribute, eat_eof, eat_group, eat_hal_expr, eat_operator, eat_string_literal};
37
38#[proc_macro_attribute]
46pub fn hal_cfg(args: TokenStream, input: TokenStream) -> TokenStream {
47 hal_cfg_impl(args).map_or_else(
48 |e| e.to_compile_error("hal_cfg"),
49 |cfgs| add_cfgs_to_input(cfgs, input),
50 )
51}
52
53fn hal_cfg_impl(args: TokenStream) -> Result<Group, Error> {
54 let mut args = args.into_iter().peekable();
55 let expr = eat_hal_expr(&mut args)?;
56 if args.peek().is_some() {
57 eat_operator(",", &mut args)?;
58 }
59 eat_eof(&mut args)?;
60 let cfgs = gen_cfgs(&expr)?;
61 Ok(cfgs)
62}
63
64#[proc_macro_attribute]
99pub fn hal_module(args: TokenStream, input: TokenStream) -> TokenStream {
100 hal_module_impl(args, input).unwrap_or_else(|e| e.to_compile_error("hal_module"))
101}
102
103fn hal_module_impl(args: TokenStream, input: TokenStream) -> Result<TokenStream, Error> {
104 let mut args = args.into_iter().peekable();
105 let args = &mut args;
106
107 let input = input
108 .into_iter()
109 .map(|token| {
110 if let TokenTree::Group(g) = &token {
112 if g.delimiter() == Delimiter::Brace && g.stream().into_iter().count() == 0 {
113 return TokenTree::Punct(Punct::new(';', Spacing::Alone));
114 }
115 }
116 token
117 })
118 .collect::<Vec<_>>();
119
120 let mut out = TokenStream::new();
121
122 while args.peek().is_some() {
123 let hal_expr = eat_hal_expr(args)?;
124
125 out.extend([
126 TokenTree::Punct(Punct::new('#', Spacing::Alone)),
127 TokenTree::Group(gen_cfgs(&hal_expr)?),
128 ]);
129
130 if matches!(args.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '=') {
131 eat_operator("=>", args)?;
132 let path = eat_string_literal(args)?;
133 out.extend([
134 TokenTree::Punct(Punct::new('#', Spacing::Alone)),
135 TokenTree::Group(Group::new(
136 Delimiter::Bracket,
137 TokenStream::from_iter([
138 TokenTree::Ident(Ident::new("path", Span::call_site())),
139 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
140 TokenTree::Literal(Literal::string(&path)),
141 ]),
142 )),
143 ]);
144 }
145
146 out.extend(input.iter().cloned());
147
148 if args.peek().is_some() {
149 eat_operator(",", args)?;
150 }
151 }
152 eat_eof(args)?;
153
154 Ok(out)
155}
156
157#[proc_macro_attribute]
180pub fn hal_macro_helper(_args: TokenStream, input: TokenStream) -> TokenStream {
181 hal_macro_helper_impl(input).unwrap_or_else(|e| e.to_compile_error("hal_macro_helper"))
182}
183
184fn hal_macro_helper_impl(input: TokenStream) -> Result<TokenStream, Error> {
185 let input = input.into_iter();
186 let mut out = Vec::with_capacity(input.size_hint().0);
187 let mut last_was_pound = false;
188
189 for mut arg in input {
190 let saved_last_was_pound = std::mem::replace(&mut last_was_pound, false);
191 match &mut arg {
192 TokenTree::Group(group) => {
193 if saved_last_was_pound {
194 replace_inner_macros(group)?;
195 } else {
196 let mut new_group =
197 Group::new(group.delimiter(), hal_macro_helper_impl(group.stream())?);
198 new_group.set_span(group.span());
199 *group = new_group;
200 }
201 }
202 TokenTree::Punct(p) if p.as_char() == '#' && p.spacing() == Spacing::Alone => {
203 last_was_pound = true;
204 }
205 _ => (),
206 }
207 out.push(arg);
208 }
209 Ok(out.into_iter().collect())
210}
211
212fn replace_inner_macros(group: &mut Group) -> Result<(), Error> {
213 let mut tokens = group.stream().into_iter();
214 let Some(TokenTree::Ident(func)) = tokens.next() else {
215 return Ok(());
216 };
217
218 if func.to_string().as_str() != "hal_cfg" {
219 return Ok(());
220 }
221
222 let Some(TokenTree::Group(inner_group)) = tokens.next() else {
223 return Ok(());
224 };
225
226 if inner_group.delimiter() != Delimiter::Parenthesis {
227 return Ok(());
228 }
229
230 if tokens.next().is_some() {
231 return Ok(());
232 }
233
234 let mut new_group = hal_cfg_impl(inner_group.stream())?;
235 new_group.set_span(group.span());
236 *group = new_group;
237
238 Ok(())
239}
240
241#[proc_macro_attribute]
260pub fn hal_docs(args: TokenStream, input: TokenStream) -> TokenStream {
261 hal_docs_impl(args, input).unwrap_or_else(|e| e.to_compile_error("hal_docs"))
262}
263
264fn hal_docs_impl(args: TokenStream, input: TokenStream) -> Result<TokenStream, Error> {
265 let mut args = args.into_iter().peekable();
266 let args = &mut args;
267
268 let mut out = TokenStream::new();
269
270 while args.peek().is_some() {
271 let mut attribute_formatter = None;
272
273 if !matches!(args.peek(), Some(TokenTree::Group(_))) {
274 let expr = eat_hal_expr(args)?;
275 eat_operator("=>", args)?;
276
277 let mut cfg_args = cfg_args(hal_expr_to_devices(&expr)?);
278 cfg_args.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
279 attribute_formatter = Some(move |attribute_body: TokenStream| {
280 TokenStream::from_iter([
281 TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
282 TokenTree::Group(Group::new(
283 Delimiter::Parenthesis,
284 TokenStream::from_iter(cfg_args.iter().cloned().chain(attribute_body)),
285 )),
286 ])
287 });
288 }
289
290 let group = eat_group(Delimiter::Brace, args)?;
291 let mut items = group.stream().into_iter().peekable();
292
293 while items.peek().is_some() {
294 let mut attribute = eat_attribute(&mut items)?;
295 if let Some(attribute_formatter) = &mut attribute_formatter {
296 attribute.inner_stream = attribute_formatter(attribute.inner_stream);
297 }
298 out.extend([
299 attribute.pound,
300 TokenTree::Group(Group::new(Delimiter::Bracket, attribute.inner_stream)),
301 ])
302 }
303 }
304 eat_eof(args)?;
305
306 out.extend(input);
307
308 Ok(out)
309}