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.rs1382
1 files changed, 0 insertions, 1382 deletions
diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs
deleted file mode 100644
index c6814d5f..00000000
--- a/macros/src/syntax.rs
+++ /dev/null
@@ -1,1382 +0,0 @@
-use std::{
- collections::{BTreeMap, BTreeSet},
- 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, Pat, Path, PathArguments, 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],
- pub 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 = BTreeMap::new();
- let mut interrupts = BTreeMap::new();
- let mut resources = BTreeMap::new();
- let mut tasks = BTreeMap::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<dyn 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<dyn 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<dyn 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<dyn 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<dyn 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 = BTreeSet<Ident>;
-
-pub type Exceptions = BTreeMap<Ident, Exception>;
-
-pub type Interrupts = BTreeMap<Ident, Interrupt>;
-
-pub type Resources = BTreeMap<Ident, Resource>;
-
-pub type Statics = Vec<ItemStatic>;
-
-pub type Tasks = BTreeMap<Ident, Task>;
-
-pub type FreeInterrupts = BTreeMap<Ident, FreeInterrupt>;
-
-pub struct Idle {
- pub args: IdleArgs,
- pub attrs: Vec<Attribute>,
- pub context: Pat,
- pub statics: BTreeMap<Ident, Static>,
- pub stmts: Vec<Stmt>,
-}
-
-pub type IdleArgs = InitArgs;
-
-impl Idle {
- fn check(args: IdleArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature =
- check_signature(&item) && item.decl.inputs.len() == 1 && is_bottom(&item.decl.output);
-
- let span = item.span();
-
- if valid_signature {
- if let Some((context, _)) = check_inputs(item.decl.inputs, "idle") {
- let (statics, stmts) = extract_statics(item.block.stmts);
-
- return Ok(Idle {
- args,
- attrs: item.attrs,
- context,
- statics: Static::parse(statics)?,
- stmts,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- "`idle` must have type signature `fn(idle::Context) -> !`",
- ))
- }
-}
-
-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 Init {
- pub args: InitArgs,
- pub attrs: Vec<Attribute>,
- pub statics: BTreeMap<Ident, Static>,
- pub context: Pat,
- pub stmts: Vec<Stmt>,
- pub returns_late_resources: bool,
- pub span: Span,
-}
-
-impl Init {
- fn check(args: InitArgs, item: ItemFn) -> parse::Result<Self> {
- let mut valid_signature = check_signature(&item) && item.decl.inputs.len() == 1;
-
- const DONT_CARE: bool = false;
-
- let returns_late_resources = match &item.decl.output {
- ReturnType::Default => false,
- ReturnType::Type(_, ty) => {
- match &**ty {
- Type::Tuple(t) => {
- if t.elems.is_empty() {
- // -> ()
- true
- } else {
- valid_signature = false;
-
- DONT_CARE
- }
- }
-
- Type::Path(_) => {
- if is_path(ty, &["init", "LateResources"]) {
- // -> init::LateResources
- true
- } else {
- valid_signature = false;
-
- DONT_CARE
- }
- }
-
- _ => {
- valid_signature = false;
-
- DONT_CARE
- }
- }
- }
- };
-
- let span = item.span();
-
- if valid_signature {
- if let Some((context, _)) = check_inputs(item.decl.inputs, "init") {
- let (statics, stmts) = extract_statics(item.block.stmts);
-
- return Ok(Init {
- args,
- attrs: item.attrs,
- statics: Static::parse(statics)?,
- context,
- stmts,
- returns_late_resources,
- span,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- "`init` must have type signature `fn(init::Context) [-> init::LateResources]`",
- ))
- }
-}
-
-/// Union of `TaskArgs`, `ExceptionArgs` and `InterruptArgs`
-pub struct Args {
- pub binds: Option<Ident>,
- pub capacity: Option<u8>,
- pub priority: u8,
- pub resources: Idents,
- pub schedule: Idents,
- pub spawn: Idents,
-}
-
-impl Default for Args {
- fn default() -> Self {
- Args {
- binds: None,
- capacity: None,
- priority: 1,
- resources: Idents::new(),
- schedule: Idents::new(),
- spawn: Idents::new(),
- }
- }
-}
-
-pub struct Exception {
- pub args: ExceptionArgs,
- pub attrs: Vec<Attribute>,
- pub statics: BTreeMap<Ident, Static>,
- pub context: Pat,
- pub stmts: Vec<Stmt>,
-}
-
-pub struct ExceptionArgs {
- binds: Option<Ident>,
- pub priority: u8,
- pub resources: Idents,
- pub schedule: Idents,
- pub spawn: Idents,
-}
-
-impl ExceptionArgs {
- /// Returns the name of the exception / interrupt this handler binds to
- pub fn binds<'a>(&'a self, handler: &'a Ident) -> &'a Ident {
- self.binds.as_ref().unwrap_or(handler)
- }
-}
-
-impl Parse for ExceptionArgs {
- fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
- parse_args(input, /* binds */ true, /* capacity */ false).map(
- |Args {
- binds,
- priority,
- resources,
- schedule,
- spawn,
- ..
- }| {
- ExceptionArgs {
- binds,
- priority,
- resources,
- schedule,
- spawn,
- }
- },
- )
- }
-}
-
-impl Exception {
- fn check(args: ExceptionArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature =
- check_signature(&item) && item.decl.inputs.len() == 1 && is_unit(&item.decl.output);
-
- let span = item.span();
-
- let name = item.ident.to_string();
- if valid_signature {
- if let Some((context, _)) = check_inputs(item.decl.inputs, &name) {
- let span = item.ident.span();
- match &*args
- .binds
- .as_ref()
- .map(|ident| ident.to_string())
- .unwrap_or(name)
- {
- "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);
-
- return Ok(Exception {
- args,
- attrs: item.attrs,
- statics: Static::parse(statics)?,
- context,
- stmts,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- &format!(
- "this `exception` handler must have type signature `fn({}::Context)`",
- name
- ),
- ))
- }
-}
-
-pub struct Interrupt {
- pub args: InterruptArgs,
- pub attrs: Vec<Attribute>,
- pub statics: BTreeMap<Ident, Static>,
- pub context: Pat,
- pub stmts: Vec<Stmt>,
-}
-
-pub type InterruptArgs = ExceptionArgs;
-
-impl Interrupt {
- fn check(args: InterruptArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature =
- check_signature(&item) && item.decl.inputs.len() == 1 && is_unit(&item.decl.output);
-
- let span = item.span();
-
- let name = item.ident.to_string();
- if valid_signature {
- if let Some((context, _)) = check_inputs(item.decl.inputs, &name) {
- match &*name {
- "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);
-
- return Ok(Interrupt {
- args,
- attrs: item.attrs,
- statics: Static::parse(statics)?,
- context,
- stmts,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- format!(
- "this `interrupt` handler must have type signature `fn({}::Context)`",
- name
- ),
- ))
- }
-}
-
-pub struct Resource {
- pub cfgs: Vec<Attribute>,
- pub attrs: Vec<Attribute>,
- pub mutability: Option<Token![mut]>,
- pub ty: Box<Type>,
- pub expr: Option<Box<Expr>>,
-}
-
-impl Resource {
- fn check(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 (cfgs, attrs) = extract_cfgs(item.attrs);
-
- Ok(Resource {
- cfgs,
- 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 Parse for TaskArgs {
- fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
- parse_args(input, /* binds */ false, /* capacity */ true).map(
- |Args {
- capacity,
- priority,
- resources,
- schedule,
- spawn,
- ..
- }| {
- TaskArgs {
- capacity,
- priority,
- resources,
- schedule,
- spawn,
- }
- },
- )
- }
-}
-
-// Parser shared by ExceptionArgs, InterruptArgs and TaskArgs
-fn parse_args(
- input: ParseStream<'_>,
- accepts_binds: bool,
- accepts_capacity: bool,
-) -> parse::Result<Args> {
- if input.is_empty() {
- return Ok(Args::default());
- }
-
- let mut binds = None;
- 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 {
- "binds" if accepts_binds => {
- if binds.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- // #ident
- let ident = content.parse()?;
-
- binds = Some(ident);
- }
- "capacity" if accepts_capacity => {
- if capacity.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- // #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" => {
- if priority.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- // #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",
- ));
- }
-
- 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(),
- format!(
- "expected one of: {}{}priority, resources, schedule or spawn",
- if accepts_binds { "binds, " } else { "" },
- if accepts_capacity { "capacity, " } else { "" },
- ),
- ));
- }
- }
-
- if content.is_empty() {
- break;
- }
-
- // ,
- let _: Token![,] = content.parse()?;
- }
-
- Ok(Args {
- binds,
- 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 {
- /// `#[cfg]` attributes
- pub cfgs: Vec<Attribute>,
- /// Attributes that are not `#[cfg]`
- pub attrs: Vec<Attribute>,
- pub ty: Box<Type>,
- pub expr: Box<Expr>,
-}
-
-impl Static {
- fn parse(items: Vec<ItemStatic>) -> parse::Result<BTreeMap<Ident, Static>> {
- let mut statics = BTreeMap::new();
-
- for item in items {
- if statics.contains_key(&item.ident) {
- return Err(parse::Error::new(
- item.ident.span(),
- "this `static` is listed twice",
- ));
- }
-
- let (cfgs, attrs) = extract_cfgs(item.attrs);
-
- statics.insert(
- item.ident,
- Static {
- cfgs,
- attrs,
- ty: item.ty,
- expr: item.expr,
- },
- );
- }
-
- Ok(statics)
- }
-}
-
-pub struct Task {
- pub args: TaskArgs,
- pub cfgs: Vec<Attribute>,
- pub attrs: Vec<Attribute>,
- pub inputs: Vec<ArgCaptured>,
- pub context: Pat,
- pub statics: BTreeMap<Ident, Static>,
- pub stmts: Vec<Stmt>,
-}
-
-impl Task {
- fn check(args: TaskArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature =
- check_signature(&item) && !item.decl.inputs.is_empty() && is_unit(&item.decl.output);
-
- let span = item.span();
-
- let name = item.ident.to_string();
- if valid_signature {
- if let Some((context, rest)) = check_inputs(item.decl.inputs, &name) {
- let (statics, stmts) = extract_statics(item.block.stmts);
-
- let inputs = rest.map_err(|arg| {
- parse::Error::new(
- arg.span(),
- "inputs must be named arguments (e.f. `foo: u32`) and not include `self`",
- )
- })?;
-
- match &*name {
- "init" | "idle" | "resources" => {
- return Err(parse::Error::new(
- span,
- "`task` handlers can NOT be named `idle`, `init` or `resources`",
- ));
- }
- _ => {}
- }
-
- let (cfgs, attrs) = extract_cfgs(item.attrs);
- return Ok(Task {
- args,
- cfgs,
- attrs,
- inputs,
- context,
- statics: Static::parse(statics)?,
- stmts,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- &format!(
- "this `task` handler must have type signature `fn({}::Context, ..)`",
- name
- ),
- ))
- }
-}
-
-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
- }
-}
-
-fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
- let mut cfgs = vec![];
- let mut not_cfgs = vec![];
-
- for attr in attrs {
- if eq(&attr, "cfg") {
- cfgs.push(attr);
- } else {
- not_cfgs.push(attr);
- }
- }
-
- (cfgs, not_cfgs)
-}
-
-/// 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)
-}
-
-// checks that the list of arguments has the form `#pat: #name::Context, (..)`
-//
-// if the check succeeds it returns `#pat` plus the remaining arguments
-fn check_inputs(
- inputs: Punctuated<FnArg, Token![,]>,
- name: &str,
-) -> Option<(Pat, Result<Vec<ArgCaptured>, FnArg>)> {
- let mut inputs = inputs.into_iter();
-
- match inputs.next() {
- Some(FnArg::Captured(first)) => {
- if is_path(&first.ty, &[name, "Context"]) {
- let rest = inputs
- .map(|arg| match arg {
- FnArg::Captured(arg) => Ok(arg),
- _ => Err(arg),
- })
- .collect::<Result<Vec<_>, _>>();
-
- Some((first.pat, rest))
- } else {
- None
- }
- }
-
- _ => None,
- }
-}
-
-/// checks that a function signature
-///
-/// - has no bounds (like where clauses)
-/// - is not `async`
-/// - is not `const`
-/// - is not `unsafe`
-/// - is not generic (has no type parametrs)
-/// - is not variadic
-/// - uses the Rust ABI (and not e.g. "C")
-fn check_signature(item: &ItemFn) -> bool {
- item.vis == Visibility::Inherited
- && item.constness.is_none()
- && item.asyncness.is_none()
- && item.abi.is_none()
- && item.unsafety.is_none()
- && item.decl.generics.params.is_empty()
- && item.decl.generics.where_clause.is_none()
- && item.decl.variadic.is_none()
-}
-
-fn is_path(ty: &Type, segments: &[&str]) -> bool {
- match ty {
- Type::Path(tpath) if tpath.qself.is_none() => {
- tpath.path.segments.len() == segments.len()
- && tpath
- .path
- .segments
- .iter()
- .zip(segments)
- .all(|(lhs, rhs)| lhs.ident == **rhs)
- }
-
- _ => false,
- }
-}
-
-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
- }
-}