rtic_macros/syntax/parse/
app.rs
1use std::collections::HashSet;
2
3use proc_macro2::TokenStream as TokenStream2;
5use syn::{
6 parse::{self, ParseStream, Parser},
7 spanned::Spanned,
8 Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Visibility,
9};
10
11use crate::syntax::{
12 ast::{
13 App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs,
14 LocalResource, SharedResource, SoftwareTask,
15 },
16 backend::BackendArgs,
17 parse::{self as syntax_parse, util},
18 Either, Map, Set,
19};
20
21use super::Input;
22
23impl AppArgs {
24 pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> {
25 (|input: ParseStream<'_>| -> parse::Result<Self> {
26 let mut custom = Set::new();
27 let mut device = None;
28 let mut core = true;
29 let mut peripherals = true;
30 let mut dispatchers = Dispatchers::new();
31 let mut backend = None;
32
33 loop {
34 if input.is_empty() {
35 break;
36 }
37
38 let ident: Ident = input.parse()?;
40 let _eq_token: Token![=] = input.parse()?;
41
42 if custom.contains(&ident) {
43 return Err(parse::Error::new(
44 ident.span(),
45 "argument appears more than once",
46 ));
47 }
48
49 custom.insert(ident.clone());
50
51 let ks = ident.to_string();
52
53 match &*ks {
54 "device" => {
55 if let Ok(p) = input.parse::<Path>() {
56 device = Some(p);
57 } else {
58 return Err(parse::Error::new(
59 ident.span(),
60 "unexpected argument value; this should be a path",
61 ));
62 }
63 }
64
65 "core" => {
66 if let Ok(p) = input.parse::<LitBool>() {
67 core = p.value;
68 } else {
69 return Err(parse::Error::new(
70 ident.span(),
71 "unexpected argument value; this should be a boolean",
72 ));
73 }
74 }
75
76 "peripherals" => {
77 if let Ok(p) = input.parse::<LitBool>() {
78 peripherals = p.value;
79 } else {
80 return Err(parse::Error::new(
81 ident.span(),
82 "unexpected argument value; this should be a boolean",
83 ));
84 }
85 }
86
87 "dispatchers" => {
88 if let Ok(p) = input.parse::<ExprArray>() {
89 for e in p.elems {
90 match e {
91 Expr::Path(ep) => {
92 let path = ep.path;
93 let ident = if path.leading_colon.is_some()
94 || path.segments.len() != 1
95 {
96 return Err(parse::Error::new(
97 path.span(),
98 "interrupt must be an identifier, not a path",
99 ));
100 } else {
101 path.segments[0].ident.clone()
102 };
103 let span = ident.span();
104 if dispatchers.contains_key(&ident) {
105 return Err(parse::Error::new(
106 span,
107 "this extern interrupt is listed more than once",
108 ));
109 } else {
110 dispatchers
111 .insert(ident, Dispatcher { attrs: ep.attrs });
112 }
113 }
114 _ => {
115 return Err(parse::Error::new(
116 e.span(),
117 "interrupt must be an identifier",
118 ));
119 }
120 }
121 }
122 } else {
123 return Err(parse::Error::new(
124 ident.span(),
125 "unexpected argument value; expected an array",
127 ));
128 }
129 }
130
131 "backend" => {
132 if let Ok(p) = input.parse::<BackendArgs>() {
133 backend = Some(p);
134 } else {
135 return Err(parse::Error::new(
136 ident.span(),
137 "unable to parse backend configuration",
138 ));
139 }
140 }
141
142 _ => {
143 return Err(parse::Error::new(ident.span(), "unexpected argument"));
144 }
145 }
146
147 if input.is_empty() {
148 break;
149 }
150
151 let _: Token![,] = input.parse()?;
153 }
154
155 let device = if let Some(device) = device {
156 device
157 } else {
158 return Err(parse::Error::new(input.span(), "missing `device = ...`"));
159 };
160
161 Ok(AppArgs {
162 device,
163 core,
164 peripherals,
165 dispatchers,
166 backend,
167 })
168 })
169 .parse2(tokens)
170 }
171}
172
173impl App {
174 pub(crate) fn parse(args: AppArgs, input: Input) -> parse::Result<Self> {
175 let mut init = None;
176 let mut idle = None;
177
178 let mut shared_resources_ident = None;
179 let mut shared_resources_vis = Visibility::Inherited;
180 let mut shared_resources = Map::new();
181
182 let mut local_resources_ident = None;
183 let mut local_resources_vis = Visibility::Inherited;
184 let mut local_resources = Map::new();
185
186 let mut hardware_tasks = Map::new();
187 let mut software_tasks = Map::new();
188 let mut user_imports = vec![];
189 let mut user_code = vec![];
190
191 let mut seen_idents = HashSet::<Ident>::new();
192 let mut bindings = HashSet::<Ident>::new();
193
194 let mut check_binding = |ident: &Ident| {
195 if bindings.contains(ident) {
196 return Err(parse::Error::new(
197 ident.span(),
198 "this interrupt is already bound",
199 ));
200 } else {
201 bindings.insert(ident.clone());
202 }
203
204 Ok(())
205 };
206
207 let mut check_ident = |ident: &Ident| {
208 if seen_idents.contains(ident) {
209 return Err(parse::Error::new(
210 ident.span(),
211 "this identifier has already been used",
212 ));
213 } else {
214 seen_idents.insert(ident.clone());
215 }
216
217 Ok(())
218 };
219
220 for mut item in input.items {
221 match item {
222 Item::Fn(mut item) => {
223 let span = item.sig.ident.span();
224 if let Some(pos) = item
225 .attrs
226 .iter()
227 .position(|attr| util::attr_eq(attr, "init"))
228 {
229 let args = InitArgs::parse(
230 item.attrs.remove(pos).parse_args().unwrap_or_default(),
231 )?;
232
233 if init.is_some() {
235 return Err(parse::Error::new(
236 span,
237 "`#[init]` function must appear at most once",
238 ));
239 }
240
241 check_ident(&item.sig.ident)?;
242
243 init = Some(Init::parse(args, item)?);
244 } else if let Some(pos) = item
245 .attrs
246 .iter()
247 .position(|attr| util::attr_eq(attr, "idle"))
248 {
249 let args = IdleArgs::parse(
250 item.attrs.remove(pos).parse_args().unwrap_or_default(),
251 )?;
252
253 if idle.is_some() {
255 return Err(parse::Error::new(
256 span,
257 "`#[idle]` function must appear at most once",
258 ));
259 }
260
261 check_ident(&item.sig.ident)?;
262
263 idle = Some(Idle::parse(args, item)?);
264 } else if let Some(pos) = item
265 .attrs
266 .iter()
267 .position(|attr| util::attr_eq(attr, "task"))
268 {
269 if hardware_tasks.contains_key(&item.sig.ident)
270 || software_tasks.contains_key(&item.sig.ident)
271 {
272 return Err(parse::Error::new(
273 span,
274 "this task is defined multiple times",
275 ));
276 }
277
278 match syntax_parse::task_args(
279 item.attrs.remove(pos).parse_args().unwrap_or_default(),
280 )? {
281 Either::Left(args) => {
282 check_binding(&args.binds)?;
283 check_ident(&item.sig.ident)?;
284
285 hardware_tasks.insert(
286 item.sig.ident.clone(),
287 HardwareTask::parse(args, item)?,
288 );
289 }
290
291 Either::Right(args) => {
292 check_ident(&item.sig.ident)?;
293
294 software_tasks.insert(
295 item.sig.ident.clone(),
296 SoftwareTask::parse(args, item)?,
297 );
298 }
299 }
300 } else {
301 user_code.push(Item::Fn(item.clone()));
303 }
304 }
305
306 Item::Struct(ref mut struct_item) => {
307 if let Some(_pos) = struct_item
310 .attrs
311 .iter()
312 .position(|attr| util::attr_eq(attr, "shared"))
313 {
314 let span = struct_item.ident.span();
315
316 shared_resources_ident = Some(struct_item.ident.clone());
317
318 if !shared_resources.is_empty() {
319 return Err(parse::Error::new(
320 span,
321 "`#[shared]` struct must appear at most once",
322 ));
323 }
324
325 shared_resources_vis = struct_item.vis.clone();
326
327 if let Fields::Named(fields) = &mut struct_item.fields {
328 for field in &mut fields.named {
329 let ident = field.ident.as_ref().expect("UNREACHABLE");
330
331 if shared_resources.contains_key(ident) {
332 return Err(parse::Error::new(
333 ident.span(),
334 "this resource is listed more than once",
335 ));
336 }
337
338 shared_resources
339 .insert(ident.clone(), SharedResource::parse(field)?);
340 }
341 } else {
342 return Err(parse::Error::new(
343 struct_item.span(),
344 "this `struct` must have named fields",
345 ));
346 }
347 } else if let Some(_pos) = struct_item
348 .attrs
349 .iter()
350 .position(|attr| util::attr_eq(attr, "local"))
351 {
352 let span = struct_item.ident.span();
353
354 local_resources_ident = Some(struct_item.ident.clone());
355
356 if !local_resources.is_empty() {
357 return Err(parse::Error::new(
358 span,
359 "`#[local]` struct must appear at most once",
360 ));
361 }
362
363 local_resources_vis = struct_item.vis.clone();
364
365 if let Fields::Named(fields) = &mut struct_item.fields {
366 for field in &mut fields.named {
367 let ident = field.ident.as_ref().expect("UNREACHABLE");
368
369 if local_resources.contains_key(ident) {
370 return Err(parse::Error::new(
371 ident.span(),
372 "this resource is listed more than once",
373 ));
374 }
375
376 local_resources.insert(ident.clone(), LocalResource::parse(field)?);
377 }
378 } else {
379 return Err(parse::Error::new(
380 struct_item.span(),
381 "this `struct` must have named fields",
382 ));
383 }
384 } else {
385 user_code.push(item.clone());
387 }
388 }
389
390 Item::ForeignMod(mod_) => {
391 if !util::abi_is_rust(&mod_.abi) {
392 return Err(parse::Error::new(
393 mod_.abi.extern_token.span(),
394 "this `extern` block must use the \"Rust\" ABI",
395 ));
396 }
397
398 for item in mod_.items {
399 if let ForeignItem::Fn(mut item) = item {
400 let span = item.sig.ident.span();
401 if let Some(pos) = item
402 .attrs
403 .iter()
404 .position(|attr| util::attr_eq(attr, "init"))
405 {
406 let args = InitArgs::parse(
407 item.attrs.remove(pos).parse_args().unwrap_or_default(),
408 )?;
409 if init.is_some() {
411 return Err(parse::Error::new(
412 span,
413 "`#[init]` function must appear at most once",
414 ));
415 }
416
417 check_ident(&item.sig.ident)?;
418
419 init = Some(Init::parse_foreign(args, item)?);
420 } else if let Some(pos) = item
421 .attrs
422 .iter()
423 .position(|attr| util::attr_eq(attr, "idle"))
424 {
425 let args = IdleArgs::parse(
426 item.attrs.remove(pos).parse_args().unwrap_or_default(),
427 )?;
428
429 if idle.is_some() {
431 return Err(parse::Error::new(
432 span,
433 "`#[idle]` function must appear at most once",
434 ));
435 }
436
437 check_ident(&item.sig.ident)?;
438
439 idle = Some(Idle::parse_foreign(args, item)?);
440 } else if let Some(pos) = item
441 .attrs
442 .iter()
443 .position(|attr| util::attr_eq(attr, "task"))
444 {
445 if hardware_tasks.contains_key(&item.sig.ident)
446 || software_tasks.contains_key(&item.sig.ident)
447 {
448 return Err(parse::Error::new(
449 span,
450 "this task is defined multiple times",
451 ));
452 }
453
454 if item.attrs.len() != 1 {
455 return Err(parse::Error::new(
456 span,
457 "`extern` task required `#[task(..)]` attribute",
458 ));
459 }
460
461 match syntax_parse::task_args(
462 item.attrs.remove(pos).parse_args().unwrap_or_default(),
463 )? {
464 Either::Left(args) => {
465 check_binding(&args.binds)?;
466 check_ident(&item.sig.ident)?;
467
468 hardware_tasks.insert(
469 item.sig.ident.clone(),
470 HardwareTask::parse_foreign(args, item)?,
471 );
472 }
473
474 Either::Right(args) => {
475 check_ident(&item.sig.ident)?;
476
477 software_tasks.insert(
478 item.sig.ident.clone(),
479 SoftwareTask::parse_foreign(args, item)?,
480 );
481 }
482 }
483 } else {
484 return Err(parse::Error::new(
485 span,
486 "`extern` task, init or idle must have either `#[task(..)]`,
487 `#[init(..)]` or `#[idle(..)]` attribute",
488 ));
489 }
490 } else {
491 return Err(parse::Error::new(
492 item.span(),
493 "this item must live outside the `#[app]` module",
494 ));
495 }
496 }
497 }
498 Item::Use(itemuse_) => {
499 user_imports.push(itemuse_.clone());
501 }
502 _ => {
503 user_code.push(item.clone());
505 }
506 }
507 }
508
509 let shared_resources_ident =
510 shared_resources_ident.expect("No `#[shared]` resource struct defined");
511 let local_resources_ident =
512 local_resources_ident.expect("No `#[local]` resource struct defined");
513 let init = init.expect("No `#[init]` function defined");
514
515 if shared_resources_ident != init.user_shared_struct {
516 return Err(parse::Error::new(
517 init.user_shared_struct.span(),
518 format!(
519 "This name and the one defined on `#[shared]` are not the same. Should this be `{shared_resources_ident}`?"
520 ),
521 ));
522 }
523
524 if local_resources_ident != init.user_local_struct {
525 return Err(parse::Error::new(
526 init.user_local_struct.span(),
527 format!(
528 "This name and the one defined on `#[local]` are not the same. Should this be `{local_resources_ident}`?"
529 ),
530 ));
531 }
532
533 Ok(App {
534 args,
535 name: input.ident,
536 init,
537 idle,
538 shared_resources,
539 shared_resources_vis,
540 local_resources,
541 local_resources_vis,
542 user_imports,
543 user_code,
544 hardware_tasks,
545 software_tasks,
546 })
547 }
548}