mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-30 07:24:33 +01:00
1236 lines
38 KiB
Rust
1236 lines
38 KiB
Rust
|
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
|
||
|
}
|
||
|
}
|