rtic_syntax/parse/
app.rs

1use std::collections::HashSet;
2
3// use indexmap::map::Entry;
4use 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                // #ident = ..
35                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                                // increasing the length of the error message will break rustfmt
111                                "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                // ,
125                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 an init function already exists, error
208                        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 an idle function already exists, error
226                        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                        // Forward normal functions
272                        user_code.push(Item::Fn(item.clone()));
273                    }
274                }
275
276                Item::Struct(ref mut struct_item) => {
277                    // Match structures with the attribute #[shared], name of structure is not
278                    // important
279                    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                        // Structure without the #[resources] attribute should just be passed along
371                        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                    // Store the user provided use-statements
446                    user_imports.push(itemuse_.clone());
447                }
448                Item::Type(ref mut type_item) => {
449                    // Match types with the attribute #[monotonic]
450                    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                    // All types are passed on
484                    user_code.push(item.clone());
485                }
486                _ => {
487                    // Anything else within the module should not make any difference
488                    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}