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