aboutsummaryrefslogtreecommitdiff
path: root/macros/src/syntax.rs
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/syntax.rs')
-rw-r--r--macros/src/syntax.rs1235
1 files changed, 1235 insertions, 0 deletions
diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs
new file mode 100644
index 00000000..24586dcf
--- /dev/null
+++ b/macros/src/syntax.rs
@@ -0,0 +1,1235 @@
+use std::{
+ collections::{HashMap, HashSet},
+ iter, u8,
+};
+
+use proc_macro2::Span;
+use syn::{
+ braced, bracketed, parenthesized,
+ parse::{self, Parse, ParseStream},
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token::Brace,
+ ArgCaptured, AttrStyle, Attribute, Expr, FnArg, ForeignItem, Ident, IntSuffix, Item, ItemFn,
+ ItemForeignMod, ItemStatic, LitInt, Path, PathArguments, PathSegment, ReturnType, Stmt, Token,
+ Type, TypeTuple, Visibility,
+};
+
+pub struct AppArgs {
+ pub device: Path,
+}
+
+impl Parse for AppArgs {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ let mut device = None;
+ loop {
+ if input.is_empty() {
+ break;
+ }
+
+ // #ident = ..
+ let ident: Ident = input.parse()?;
+ let _eq_token: Token![=] = input.parse()?;
+
+ let ident_s = ident.to_string();
+ match &*ident_s {
+ "device" => {
+ if device.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ device = Some(input.parse()?);
+ }
+ _ => {
+ return Err(parse::Error::new(
+ ident.span(),
+ "expected `device`; other keys are not accepted",
+ ))
+ }
+ }
+
+ if input.is_empty() {
+ break;
+ }
+
+ // ,
+ let _: Token![,] = input.parse()?;
+ }
+
+ Ok(AppArgs {
+ device: device.ok_or(parse::Error::new(
+ Span::call_site(),
+ "`device` argument is required",
+ ))?,
+ })
+ }
+}
+
+pub struct Input {
+ _const_token: Token![const],
+ _ident: Ident,
+ _colon_token: Token![:],
+ _ty: TypeTuple,
+ _eq_token: Token![=],
+ _brace_token: Brace,
+ pub items: Vec<Item>,
+ _semi_token: Token![;],
+}
+
+impl Parse for Input {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ fn parse_items(input: ParseStream) -> parse::Result<Vec<Item>> {
+ let mut items = vec![];
+
+ while !input.is_empty() {
+ items.push(input.parse()?);
+ }
+
+ Ok(items)
+ }
+
+ let content;
+ Ok(Input {
+ _const_token: input.parse()?,
+ _ident: input.parse()?,
+ _colon_token: input.parse()?,
+ _ty: input.parse()?,
+ _eq_token: input.parse()?,
+ _brace_token: braced!(content in input),
+ items: content.call(parse_items)?,
+ _semi_token: input.parse()?,
+ })
+ }
+}
+
+pub struct App {
+ pub args: AppArgs,
+ pub idle: Option<Idle>,
+ pub init: Init,
+ pub exceptions: Exceptions,
+ pub interrupts: Interrupts,
+ pub resources: Resources,
+ pub tasks: Tasks,
+ pub free_interrupts: FreeInterrupts,
+}
+
+impl App {
+ pub fn parse(items: Vec<Item>, args: AppArgs) -> parse::Result<Self> {
+ let mut idle = None;
+ let mut init = None;
+ let mut exceptions = HashMap::new();
+ let mut interrupts = HashMap::new();
+ let mut resources = HashMap::new();
+ let mut tasks = HashMap::new();
+ let mut free_interrupts = None;
+
+ for item in items {
+ match item {
+ Item::Fn(mut item) => {
+ if let Some(pos) = item.attrs.iter().position(|attr| eq(attr, "idle")) {
+ if idle.is_some() {
+ return Err(parse::Error::new(
+ item.span(),
+ "`#[idle]` function must appear at most once",
+ ));
+ }
+
+ let args = syn::parse2(item.attrs.swap_remove(pos).tts)?;
+
+ idle = Some(Idle::check(args, item)?);
+ } else if let Some(pos) = item.attrs.iter().position(|attr| eq(attr, "init")) {
+ if init.is_some() {
+ return Err(parse::Error::new(
+ item.span(),
+ "`#[init]` function must appear exactly once",
+ ));
+ }
+
+ let args = syn::parse2(item.attrs.swap_remove(pos).tts)?;
+
+ init = Some(Init::check(args, item)?);
+ } else if let Some(pos) =
+ item.attrs.iter().position(|attr| eq(attr, "exception"))
+ {
+ if exceptions.contains_key(&item.ident)
+ || interrupts.contains_key(&item.ident)
+ || tasks.contains_key(&item.ident)
+ {
+ return Err(parse::Error::new(
+ item.ident.span(),
+ "this task is defined multiple times",
+ ));
+ }
+
+ let args = syn::parse2(item.attrs.swap_remove(pos).tts)?;
+
+ exceptions.insert(item.ident.clone(), Exception::check(args, item)?);
+ } else if let Some(pos) =
+ item.attrs.iter().position(|attr| eq(attr, "interrupt"))
+ {
+ if exceptions.contains_key(&item.ident)
+ || interrupts.contains_key(&item.ident)
+ || tasks.contains_key(&item.ident)
+ {
+ return Err(parse::Error::new(
+ item.ident.span(),
+ "this task is defined multiple times",
+ ));
+ }
+
+ let args = syn::parse2(item.attrs.swap_remove(pos).tts)?;
+
+ interrupts.insert(item.ident.clone(), Interrupt::check(args, item)?);
+ } else if let Some(pos) = item.attrs.iter().position(|attr| eq(attr, "task")) {
+ if exceptions.contains_key(&item.ident)
+ || interrupts.contains_key(&item.ident)
+ || tasks.contains_key(&item.ident)
+ {
+ return Err(parse::Error::new(
+ item.ident.span(),
+ "this task is defined multiple times",
+ ));
+ }
+
+ let args = syn::parse2(item.attrs.swap_remove(pos).tts)?;
+
+ tasks.insert(item.ident.clone(), Task::check(args, item)?);
+ } else {
+ return Err(parse::Error::new(
+ item.span(),
+ "this item must live outside the `#[app]` module",
+ ));
+ }
+ }
+ Item::Static(item) => {
+ if resources.contains_key(&item.ident) {
+ return Err(parse::Error::new(
+ item.ident.span(),
+ "this resource is listed twice",
+ ));
+ }
+
+ resources.insert(item.ident.clone(), Resource::check(item)?);
+ }
+ Item::ForeignMod(item) => {
+ if free_interrupts.is_some() {
+ return Err(parse::Error::new(
+ item.abi.extern_token.span(),
+ "`extern` block can only appear at most once",
+ ));
+ }
+
+ free_interrupts = Some(FreeInterrupt::parse(item)?);
+ }
+ _ => {
+ return Err(parse::Error::new(
+ item.span(),
+ "this item must live outside the `#[app]` module",
+ ))
+ }
+ }
+ }
+
+ Ok(App {
+ args,
+ idle,
+ init: init.ok_or_else(|| {
+ parse::Error::new(Span::call_site(), "`#[init]` function is missing")
+ })?,
+ exceptions,
+ interrupts,
+ resources,
+ tasks,
+ free_interrupts: free_interrupts.unwrap_or_else(|| FreeInterrupts::new()),
+ })
+ }
+
+ /// Returns an iterator over all resource accesses.
+ ///
+ /// Each resource access include the priority it's accessed at (`u8`) and the name of the
+ /// resource (`Ident`). A resource may appear more than once in this iterator
+ pub fn resource_accesses(&self) -> impl Iterator<Item = (u8, &Ident)> {
+ self.idle
+ .as_ref()
+ .map(|idle| -> Box<Iterator<Item = _>> {
+ Box::new(idle.args.resources.iter().map(|res| (0, res)))
+ })
+ .unwrap_or_else(|| Box::new(iter::empty()))
+ .chain(self.exceptions.values().flat_map(|e| {
+ e.args
+ .resources
+ .iter()
+ .map(move |res| (e.args.priority, res))
+ }))
+ .chain(self.interrupts.values().flat_map(|i| {
+ i.args
+ .resources
+ .iter()
+ .map(move |res| (i.args.priority, res))
+ }))
+ .chain(self.tasks.values().flat_map(|t| {
+ t.args
+ .resources
+ .iter()
+ .map(move |res| (t.args.priority, res))
+ }))
+ }
+
+ /// Returns an iterator over all `spawn` calls
+ ///
+ /// Each spawn call includes the priority of the task from which it's issued and the name of the
+ /// task that's spawned. A task may appear more that once in this iterator.
+ ///
+ /// A priority of `None` means that this being called from `init`
+ pub fn spawn_calls(&self) -> impl Iterator<Item = (Option<u8>, &Ident)> {
+ self.init
+ .args
+ .spawn
+ .iter()
+ .map(|s| (None, s))
+ .chain(
+ self.idle
+ .as_ref()
+ .map(|idle| -> Box<Iterator<Item = _>> {
+ Box::new(idle.args.spawn.iter().map(|s| (Some(0), s)))
+ })
+ .unwrap_or_else(|| Box::new(iter::empty())),
+ )
+ .chain(
+ self.exceptions
+ .values()
+ .flat_map(|e| e.args.spawn.iter().map(move |s| (Some(e.args.priority), s))),
+ )
+ .chain(
+ self.interrupts
+ .values()
+ .flat_map(|i| i.args.spawn.iter().map(move |s| (Some(i.args.priority), s))),
+ )
+ .chain(
+ self.tasks
+ .values()
+ .flat_map(|t| t.args.spawn.iter().map(move |s| (Some(t.args.priority), s))),
+ )
+ }
+
+ /// Returns an iterator over all `schedule` calls
+ ///
+ /// Each spawn call includes the priority of the task from which it's issued and the name of the
+ /// task that's spawned. A task may appear more that once in this iterator.
+ #[allow(dead_code)]
+ pub fn schedule_calls(&self) -> impl Iterator<Item = (Option<u8>, &Ident)> {
+ self.init
+ .args
+ .schedule
+ .iter()
+ .map(|s| (None, s))
+ .chain(
+ self.idle
+ .as_ref()
+ .map(|idle| -> Box<Iterator<Item = _>> {
+ Box::new(idle.args.schedule.iter().map(|s| (Some(0), s)))
+ })
+ .unwrap_or_else(|| Box::new(iter::empty())),
+ )
+ .chain(self.exceptions.values().flat_map(|e| {
+ e.args
+ .schedule
+ .iter()
+ .map(move |s| (Some(e.args.priority), s))
+ }))
+ .chain(self.interrupts.values().flat_map(|i| {
+ i.args
+ .schedule
+ .iter()
+ .map(move |s| (Some(i.args.priority), s))
+ }))
+ .chain(self.tasks.values().flat_map(|t| {
+ t.args
+ .schedule
+ .iter()
+ .map(move |s| (Some(t.args.priority), s))
+ }))
+ }
+
+ #[allow(dead_code)]
+ pub fn schedule_callers(&self) -> impl Iterator<Item = (Ident, &Idents)> {
+ self.idle
+ .as_ref()
+ .map(|idle| -> Box<Iterator<Item = _>> {
+ Box::new(iter::once((
+ Ident::new("idle", Span::call_site()),
+ &idle.args.schedule,
+ )))
+ })
+ .unwrap_or_else(|| Box::new(iter::empty()))
+ .chain(iter::once((
+ Ident::new("init", Span::call_site()),
+ &self.init.args.schedule,
+ )))
+ .chain(
+ self.exceptions
+ .iter()
+ .map(|(name, exception)| (name.clone(), &exception.args.schedule)),
+ )
+ .chain(
+ self.interrupts
+ .iter()
+ .map(|(name, interrupt)| (name.clone(), &interrupt.args.schedule)),
+ )
+ .chain(
+ self.tasks
+ .iter()
+ .map(|(name, task)| (name.clone(), &task.args.schedule)),
+ )
+ }
+
+ pub fn spawn_callers(&self) -> impl Iterator<Item = (Ident, &Idents)> {
+ self.idle
+ .as_ref()
+ .map(|idle| -> Box<Iterator<Item = _>> {
+ Box::new(iter::once((
+ Ident::new("idle", Span::call_site()),
+ &idle.args.spawn,
+ )))
+ })
+ .unwrap_or_else(|| Box::new(iter::empty()))
+ .chain(iter::once((
+ Ident::new("init", Span::call_site()),
+ &self.init.args.spawn,
+ )))
+ .chain(
+ self.exceptions
+ .iter()
+ .map(|(name, exception)| (name.clone(), &exception.args.spawn)),
+ )
+ .chain(
+ self.interrupts
+ .iter()
+ .map(|(name, interrupt)| (name.clone(), &interrupt.args.spawn)),
+ )
+ .chain(
+ self.tasks
+ .iter()
+ .map(|(name, task)| (name.clone(), &task.args.spawn)),
+ )
+ }
+}
+
+pub type Idents = HashSet<Ident>;
+
+pub type Exceptions = HashMap<Ident, Exception>;
+
+pub type Interrupts = HashMap<Ident, Interrupt>;
+
+pub type Resources = HashMap<Ident, Resource>;
+
+pub type Statics = Vec<ItemStatic>;
+
+pub type Tasks = HashMap<Ident, Task>;
+
+pub type FreeInterrupts = HashMap<Ident, FreeInterrupt>;
+
+pub struct Idle {
+ pub args: IdleArgs,
+ pub attrs: Vec<Attribute>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub statics: HashMap<Ident, Static>,
+ pub stmts: Vec<Stmt>,
+}
+
+pub type IdleArgs = InitArgs;
+
+impl Idle {
+ fn check(args: IdleArgs, item: ItemFn) -> parse::Result<Self> {
+ let valid_signature = item.vis == Visibility::Inherited
+ && item.constness.is_none()
+ && item.asyncness.is_none()
+ && item.abi.is_none()
+ && item.decl.generics.params.is_empty()
+ && item.decl.generics.where_clause.is_none()
+ && item.decl.inputs.is_empty()
+ && item.decl.variadic.is_none()
+ && is_bottom(&item.decl.output);
+
+ let span = item.span();
+
+ if !valid_signature {
+ return Err(parse::Error::new(
+ span,
+ "`idle` must have type signature `[unsafe] fn() -> !`",
+ ));
+ }
+
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ Ok(Idle {
+ args,
+ attrs: item.attrs,
+ unsafety: item.unsafety,
+ statics: Static::parse(statics)?,
+ stmts,
+ })
+ }
+}
+
+pub struct InitArgs {
+ pub resources: Idents,
+ pub schedule: Idents,
+ pub spawn: Idents,
+}
+
+impl Default for InitArgs {
+ fn default() -> Self {
+ InitArgs {
+ resources: Idents::new(),
+ schedule: Idents::new(),
+ spawn: Idents::new(),
+ }
+ }
+}
+
+impl Parse for InitArgs {
+ fn parse(input: ParseStream) -> parse::Result<InitArgs> {
+ if input.is_empty() {
+ return Ok(InitArgs::default());
+ }
+
+ let mut resources = None;
+ let mut schedule = None;
+ let mut spawn = None;
+
+ let content;
+ parenthesized!(content in input);
+ loop {
+ if content.is_empty() {
+ break;
+ }
+
+ // #ident = ..
+ let ident: Ident = content.parse()?;
+ let _: Token![=] = content.parse()?;
+
+ let ident_s = ident.to_string();
+ match &*ident_s {
+ "schedule" if cfg!(not(feature = "timer-queue")) => {
+ return Err(parse::Error::new(
+ ident.span(),
+ "The `schedule` API requires that the `timer-queue` feature is \
+ enabled in the `cortex-m-rtfm` crate",
+ ));
+ }
+ "resources" | "schedule" | "spawn" => {} // OK
+ _ => {
+ return Err(parse::Error::new(
+ ident.span(),
+ "expected one of: resources, schedule or spawn",
+ ))
+ }
+ }
+
+ // .. [#(#idents)*]
+ let inner;
+ bracketed!(inner in content);
+ let mut idents = Idents::new();
+ for ident in inner.call(Punctuated::<_, Token![,]>::parse_terminated)? {
+ if idents.contains(&ident) {
+ return Err(parse::Error::new(
+ ident.span(),
+ "element appears more than once in list",
+ ));
+ }
+
+ idents.insert(ident);
+ }
+
+ let ident_s = ident.to_string();
+ match &*ident_s {
+ "resources" => {
+ if resources.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ resources = Some(idents);
+ }
+ "schedule" => {
+ if schedule.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ schedule = Some(idents);
+ }
+ "spawn" => {
+ if spawn.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ spawn = Some(idents);
+ }
+ _ => unreachable!(),
+ }
+
+ if content.is_empty() {
+ break;
+ }
+
+ // ,
+ let _: Token![,] = content.parse()?;
+ }
+
+ Ok(InitArgs {
+ resources: resources.unwrap_or(Idents::new()),
+ schedule: schedule.unwrap_or(Idents::new()),
+ spawn: spawn.unwrap_or(Idents::new()),
+ })
+ }
+}
+
+pub struct Assign {
+ pub left: Ident,
+ pub right: Box<Expr>,
+}
+
+pub struct Init {
+ pub args: InitArgs,
+ pub attrs: Vec<Attribute>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub statics: HashMap<Ident, Static>,
+ pub stmts: Vec<Stmt>,
+ pub assigns: Vec<Assign>,
+}
+
+impl Init {
+ fn check(args: InitArgs, item: ItemFn) -> parse::Result<Self> {
+ let valid_signature = item.vis == Visibility::Inherited
+ && item.constness.is_none()
+ && item.asyncness.is_none()
+ && item.abi.is_none()
+ && item.decl.generics.params.is_empty()
+ && item.decl.generics.where_clause.is_none()
+ && item.decl.inputs.is_empty()
+ && item.decl.variadic.is_none()
+ && is_unit(&item.decl.output);
+
+ let span = item.span();
+
+ if !valid_signature {
+ return Err(parse::Error::new(
+ span,
+ "`init` must have type signature `[unsafe] fn()`",
+ ));
+ }
+
+ let (statics, stmts) = extract_statics(item.block.stmts);
+ let (stmts, assigns) = extract_assignments(stmts);
+
+ Ok(Init {
+ args,
+ attrs: item.attrs,
+ unsafety: item.unsafety,
+ statics: Static::parse(statics)?,
+ stmts,
+ assigns,
+ })
+ }
+}
+
+pub struct Exception {
+ pub args: ExceptionArgs,
+ pub attrs: Vec<Attribute>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub statics: Statics,
+ pub stmts: Vec<Stmt>,
+}
+
+pub struct ExceptionArgs {
+ pub priority: u8,
+ pub resources: Idents,
+ pub schedule: Idents,
+ pub spawn: Idents,
+}
+
+impl Parse for ExceptionArgs {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ parse_args(input, false).map(
+ |TaskArgs {
+ priority,
+ resources,
+ schedule,
+ spawn,
+ ..
+ }| {
+ ExceptionArgs {
+ priority,
+ resources,
+ schedule,
+ spawn,
+ }
+ },
+ )
+ }
+}
+
+impl Exception {
+ fn check(args: ExceptionArgs, item: ItemFn) -> parse::Result<Self> {
+ let valid_signature = item.vis == Visibility::Inherited
+ && item.constness.is_none()
+ && item.asyncness.is_none()
+ && item.abi.is_none()
+ && item.decl.generics.params.is_empty()
+ && item.decl.generics.where_clause.is_none()
+ && item.decl.inputs.is_empty()
+ && item.decl.variadic.is_none()
+ && is_unit(&item.decl.output);
+
+ if !valid_signature {
+ return Err(parse::Error::new(
+ item.span(),
+ "`exception` handlers must have type signature `[unsafe] fn()`",
+ ));
+ }
+
+ let span = item.ident.span();
+ match &*item.ident.to_string() {
+ "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
+ | "DebugMonitor" | "PendSV" => {} // OK
+ "SysTick" => {
+ if cfg!(feature = "timer-queue") {
+ return Err(parse::Error::new(
+ span,
+ "the `SysTick` exception can't be used because it's used by \
+ the runtime when the `timer-queue` feature is enabled",
+ ));
+ }
+ }
+ _ => {
+ return Err(parse::Error::new(
+ span,
+ "only exceptions with configurable priority can be used as hardware tasks",
+ ));
+ }
+ }
+
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ Ok(Exception {
+ args,
+ attrs: item.attrs,
+ unsafety: item.unsafety,
+ statics,
+ stmts,
+ })
+ }
+}
+
+pub struct Interrupt {
+ pub args: InterruptArgs,
+ pub attrs: Vec<Attribute>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub statics: Statics,
+ pub stmts: Vec<Stmt>,
+}
+
+pub type InterruptArgs = ExceptionArgs;
+
+impl Interrupt {
+ fn check(args: InterruptArgs, item: ItemFn) -> parse::Result<Self> {
+ let valid_signature = item.vis == Visibility::Inherited
+ && item.constness.is_none()
+ && item.asyncness.is_none()
+ && item.abi.is_none()
+ && item.decl.generics.params.is_empty()
+ && item.decl.generics.where_clause.is_none()
+ && item.decl.inputs.is_empty()
+ && item.decl.variadic.is_none()
+ && is_unit(&item.decl.output);
+
+ let span = item.span();
+
+ if !valid_signature {
+ return Err(parse::Error::new(
+ span,
+ "`interrupt` handlers must have type signature `[unsafe] fn()`",
+ ));
+ }
+
+ match &*item.ident.to_string() {
+ "init" | "idle" | "resources" => {
+ return Err(parse::Error::new(
+ span,
+ "`interrupt` handlers can NOT be named `idle`, `init` or `resources`",
+ ));
+ }
+ _ => {}
+ }
+
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ Ok(Interrupt {
+ args,
+ attrs: item.attrs,
+ unsafety: item.unsafety,
+ statics,
+ stmts,
+ })
+ }
+}
+
+pub struct Resource {
+ pub singleton: bool,
+ pub attrs: Vec<Attribute>,
+ pub mutability: Option<Token![mut]>,
+ pub ty: Box<Type>,
+ pub expr: Option<Box<Expr>>,
+}
+
+impl Resource {
+ fn check(mut item: ItemStatic) -> parse::Result<Resource> {
+ if item.vis != Visibility::Inherited {
+ return Err(parse::Error::new(
+ item.span(),
+ "resources must have inherited / private visibility",
+ ));
+ }
+
+ let uninitialized = match *item.expr {
+ Expr::Tuple(ref tuple) => tuple.elems.is_empty(),
+ _ => false,
+ };
+
+ let pos = item.attrs.iter().position(|attr| eq(attr, "Singleton"));
+
+ if let Some(pos) = pos {
+ item.attrs[pos].path.segments.insert(
+ 0,
+ PathSegment::from(Ident::new("owned_singleton", Span::call_site())),
+ );
+ }
+
+ Ok(Resource {
+ singleton: pos.is_some(),
+ attrs: item.attrs,
+ mutability: item.mutability,
+ ty: item.ty,
+ expr: if uninitialized { None } else { Some(item.expr) },
+ })
+ }
+}
+
+pub struct TaskArgs {
+ pub capacity: Option<u8>,
+ pub priority: u8,
+ pub resources: Idents,
+ pub spawn: Idents,
+ pub schedule: Idents,
+}
+
+impl Default for TaskArgs {
+ fn default() -> Self {
+ TaskArgs {
+ capacity: None,
+ priority: 1,
+ resources: Idents::new(),
+ schedule: Idents::new(),
+ spawn: Idents::new(),
+ }
+ }
+}
+
+impl Parse for TaskArgs {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ parse_args(input, true)
+ }
+}
+
+// Parser shared by TaskArgs and ExceptionArgs / InterruptArgs
+fn parse_args(input: ParseStream, accept_capacity: bool) -> parse::Result<TaskArgs> {
+ if input.is_empty() {
+ return Ok(TaskArgs::default());
+ }
+
+ let mut capacity = None;
+ let mut priority = None;
+ let mut resources = None;
+ let mut schedule = None;
+ let mut spawn = None;
+
+ let content;
+ parenthesized!(content in input);
+ loop {
+ if content.is_empty() {
+ break;
+ }
+
+ // #ident = ..
+ let ident: Ident = content.parse()?;
+ let _: Token![=] = content.parse()?;
+
+ let ident_s = ident.to_string();
+ match &*ident_s {
+ "capacity" if accept_capacity => {
+ // #lit
+ let lit: LitInt = content.parse()?;
+
+ if lit.suffix() != IntSuffix::None {
+ return Err(parse::Error::new(
+ lit.span(),
+ "this literal must be unsuffixed",
+ ));
+ }
+
+ let value = lit.value();
+ if value > u64::from(u8::MAX) || value == 0 {
+ return Err(parse::Error::new(
+ lit.span(),
+ "this literal must be in the range 1...255",
+ ));
+ }
+
+ capacity = Some(value as u8);
+ }
+ "priority" => {
+ // #lit
+ let lit: LitInt = content.parse()?;
+
+ if lit.suffix() != IntSuffix::None {
+ return Err(parse::Error::new(
+ lit.span(),
+ "this literal must be unsuffixed",
+ ));
+ }
+
+ let value = lit.value();
+ if value > u64::from(u8::MAX) {
+ return Err(parse::Error::new(
+ lit.span(),
+ "this literal must be in the range 0...255",
+ ));
+ }
+
+ priority = Some(value as u8);
+ }
+ "schedule" if cfg!(not(feature = "timer-queue")) => {
+ return Err(parse::Error::new(
+ ident.span(),
+ "The `schedule` API requires that the `timer-queue` feature is \
+ enabled in the `cortex-m-rtfm` crate",
+ ));
+ }
+ "resources" | "schedule" | "spawn" => {
+ // .. [#(#idents)*]
+ let inner;
+ bracketed!(inner in content);
+ let mut idents = Idents::new();
+ for ident in inner.call(Punctuated::<_, Token![,]>::parse_terminated)? {
+ if idents.contains(&ident) {
+ return Err(parse::Error::new(
+ ident.span(),
+ "element appears more than once in list",
+ ));
+ }
+
+ idents.insert(ident);
+ }
+
+ match &*ident_s {
+ "resources" => {
+ if resources.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ resources = Some(idents);
+ }
+ "schedule" => {
+ if schedule.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ schedule = Some(idents);
+ }
+ "spawn" => {
+ if spawn.is_some() {
+ return Err(parse::Error::new(
+ ident.span(),
+ "argument appears more than once",
+ ));
+ }
+
+ spawn = Some(idents);
+ }
+ _ => unreachable!(),
+ }
+ }
+ _ => {
+ return Err(parse::Error::new(
+ ident.span(),
+ "expected one of: priority, resources, schedule or spawn",
+ ))
+ }
+ }
+
+ if content.is_empty() {
+ break;
+ }
+
+ // ,
+ let _: Token![,] = content.parse()?;
+ }
+
+ Ok(TaskArgs {
+ capacity,
+ priority: priority.unwrap_or(1),
+ resources: resources.unwrap_or(Idents::new()),
+ schedule: schedule.unwrap_or(Idents::new()),
+ spawn: spawn.unwrap_or(Idents::new()),
+ })
+}
+
+pub struct Static {
+ pub attrs: Vec<Attribute>,
+ pub ty: Box<Type>,
+ pub expr: Box<Expr>,
+}
+
+impl Static {
+ fn parse(items: Vec<ItemStatic>) -> parse::Result<HashMap<Ident, Static>> {
+ let mut statics = HashMap::new();
+
+ for item in items {
+ if statics.contains_key(&item.ident) {
+ return Err(parse::Error::new(
+ item.ident.span(),
+ "this `static` is listed twice",
+ ));
+ }
+
+ statics.insert(
+ item.ident,
+ Static {
+ attrs: item.attrs,
+ ty: item.ty,
+ expr: item.expr,
+ },
+ );
+ }
+
+ Ok(statics)
+ }
+}
+
+pub struct Task {
+ pub args: TaskArgs,
+ pub attrs: Vec<Attribute>,
+ pub unsafety: Option<Token![unsafe]>,
+ pub inputs: Vec<ArgCaptured>,
+ pub statics: HashMap<Ident, Static>,
+ pub stmts: Vec<Stmt>,
+}
+
+impl Task {
+ fn check(args: TaskArgs, item: ItemFn) -> parse::Result<Self> {
+ let valid_signature = item.vis == Visibility::Inherited
+ && item.constness.is_none()
+ && item.asyncness.is_none()
+ && item.abi.is_none()
+ && item.decl.generics.params.is_empty()
+ && item.decl.generics.where_clause.is_none()
+ && item.decl.variadic.is_none()
+ && is_unit(&item.decl.output);
+
+ let span = item.span();
+
+ if !valid_signature {
+ return Err(parse::Error::new(
+ span,
+ "`task` handlers must have type signature `[unsafe] fn(..)`",
+ ));
+ }
+
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ let mut inputs = vec![];
+ for input in item.decl.inputs {
+ if let FnArg::Captured(capture) = input {
+ inputs.push(capture);
+ } else {
+ return Err(parse::Error::new(
+ span,
+ "inputs must be named arguments (e.f. `foo: u32`) and not include `self`",
+ ));
+ }
+ }
+
+ match &*item.ident.to_string() {
+ "init" | "idle" | "resources" => {
+ return Err(parse::Error::new(
+ span,
+ "`task` handlers can NOT be named `idle`, `init` or `resources`",
+ ));
+ }
+ _ => {}
+ }
+
+ Ok(Task {
+ args,
+ attrs: item.attrs,
+ unsafety: item.unsafety,
+ inputs,
+ statics: Static::parse(statics)?,
+ stmts,
+ })
+ }
+}
+
+pub struct FreeInterrupt {
+ pub attrs: Vec<Attribute>,
+}
+
+impl FreeInterrupt {
+ fn parse(mod_: ItemForeignMod) -> parse::Result<FreeInterrupts> {
+ let mut free_interrupts = FreeInterrupts::new();
+
+ for item in mod_.items {
+ if let ForeignItem::Fn(f) = item {
+ let valid_signature = f.vis == Visibility::Inherited
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.inputs.is_empty()
+ && f.decl.variadic.is_none()
+ && is_unit(&f.decl.output);
+
+ if !valid_signature {
+ return Err(parse::Error::new(
+ f.span(),
+ "free interrupts must have type signature `fn()`",
+ ));
+ }
+
+ if free_interrupts.contains_key(&f.ident) {
+ return Err(parse::Error::new(
+ f.ident.span(),
+ "this interrupt appears twice",
+ ));
+ }
+
+ free_interrupts.insert(f.ident, FreeInterrupt { attrs: f.attrs });
+ } else {
+ return Err(parse::Error::new(
+ mod_.abi.extern_token.span(),
+ "`extern` block should only contains functions",
+ ));
+ }
+ }
+
+ Ok(free_interrupts)
+ }
+}
+
+fn eq(attr: &Attribute, name: &str) -> bool {
+ attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && {
+ let pair = attr.path.segments.first().unwrap();
+ let segment = pair.value();
+ segment.arguments == PathArguments::None && segment.ident.to_string() == name
+ }
+}
+
+/// Extracts `static mut` vars from the beginning of the given statements
+fn extract_statics(stmts: Vec<Stmt>) -> (Statics, Vec<Stmt>) {
+ let mut istmts = stmts.into_iter();
+
+ let mut statics = Statics::new();
+ let mut stmts = vec![];
+ while let Some(stmt) = istmts.next() {
+ match stmt {
+ Stmt::Item(Item::Static(var)) => {
+ if var.mutability.is_some() {
+ statics.push(var);
+ } else {
+ stmts.push(Stmt::Item(Item::Static(var)));
+ break;
+ }
+ }
+ _ => {
+ stmts.push(stmt);
+ break;
+ }
+ }
+ }
+
+ stmts.extend(istmts);
+
+ (statics, stmts)
+}
+
+fn extract_assignments(stmts: Vec<Stmt>) -> (Vec<Stmt>, Vec<Assign>) {
+ let mut istmts = stmts.into_iter().rev();
+
+ let mut assigns = vec![];
+ let mut stmts = vec![];
+ while let Some(stmt) = istmts.next() {
+ match stmt {
+ Stmt::Semi(Expr::Assign(assign), semi) => {
+ if let Expr::Path(ref expr) = *assign.left {
+ if expr.path.segments.len() == 1 {
+ assigns.push(Assign {
+ left: expr.path.segments[0].ident.clone(),
+ right: assign.right,
+ });
+ continue;
+ }
+ }
+
+ stmts.push(Stmt::Semi(Expr::Assign(assign), semi));
+ }
+ _ => {
+ stmts.push(stmt);
+ break;
+ }
+ }
+ }
+
+ stmts.extend(istmts);
+
+ (stmts.into_iter().rev().collect(), assigns)
+}
+
+fn is_bottom(ty: &ReturnType) -> bool {
+ if let ReturnType::Type(_, ty) = ty {
+ if let Type::Never(_) = **ty {
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+}
+
+fn is_unit(ty: &ReturnType) -> bool {
+ if let ReturnType::Type(_, ty) = ty {
+ if let Type::Tuple(ref tuple) = **ty {
+ tuple.elems.is_empty()
+ } else {
+ false
+ }
+ } else {
+ true
+ }
+}