mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-19 14:25:18 +01:00
340 lines
10 KiB
Rust
340 lines
10 KiB
Rust
mod app;
|
|
mod hardware_task;
|
|
mod idle;
|
|
mod init;
|
|
mod resource;
|
|
mod software_task;
|
|
mod util;
|
|
|
|
use proc_macro2::TokenStream as TokenStream2;
|
|
use syn::{
|
|
braced,
|
|
parse::{self, Parse, ParseStream, Parser},
|
|
token::Brace,
|
|
Attribute, Ident, Item, LitInt, Meta, Token,
|
|
};
|
|
|
|
use crate::syntax::{
|
|
ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal},
|
|
Either,
|
|
};
|
|
|
|
// Parse the app, both app arguments and body (input)
|
|
pub fn app(args: TokenStream2, input: TokenStream2) -> parse::Result<App> {
|
|
let args = AppArgs::parse(args)?;
|
|
let input: Input = syn::parse2(input)?;
|
|
|
|
App::parse(args, input)
|
|
}
|
|
|
|
pub(crate) struct Input {
|
|
pub attribute_metas: Vec<Meta>,
|
|
_mod_token: Token![mod],
|
|
pub ident: Ident,
|
|
_brace_token: Brace,
|
|
pub items: Vec<Item>,
|
|
}
|
|
|
|
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;
|
|
|
|
let mut attributes = input.call(Attribute::parse_outer)?;
|
|
let _mod_token = input.parse()?;
|
|
let ident = input.parse()?;
|
|
let _brace_token = braced!(content in input);
|
|
let inner_attributes = content.call(Attribute::parse_inner)?;
|
|
let items = content.call(parse_items)?;
|
|
|
|
attributes.extend(inner_attributes);
|
|
let attribute_metas = attributes.into_iter().map(|a| a.meta).collect();
|
|
|
|
Ok(Input {
|
|
attribute_metas,
|
|
_mod_token,
|
|
ident,
|
|
_brace_token,
|
|
items,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn init_args(tokens: TokenStream2) -> parse::Result<InitArgs> {
|
|
(|input: ParseStream<'_>| -> parse::Result<InitArgs> {
|
|
if input.is_empty() {
|
|
return Ok(InitArgs::default());
|
|
}
|
|
|
|
let mut local_resources = None;
|
|
|
|
if !input.is_empty() {
|
|
loop {
|
|
// Parse identifier name
|
|
let ident: Ident = input.parse()?;
|
|
// Handle equal sign
|
|
let _: Token![=] = input.parse()?;
|
|
|
|
match &*ident.to_string() {
|
|
"local" => {
|
|
if local_resources.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
local_resources = Some(util::parse_local_resources(input)?);
|
|
}
|
|
_ => {
|
|
return Err(parse::Error::new(ident.span(), "unexpected argument"));
|
|
}
|
|
}
|
|
|
|
if input.is_empty() {
|
|
break;
|
|
}
|
|
// Handle comma: ,
|
|
let _: Token![,] = input.parse()?;
|
|
}
|
|
}
|
|
|
|
if let Some(locals) = &local_resources {
|
|
for (ident, task_local) in locals {
|
|
if let TaskLocal::External = task_local {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"only declared local resources are allowed in init",
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(InitArgs {
|
|
local_resources: local_resources.unwrap_or_default(),
|
|
})
|
|
})
|
|
.parse2(tokens)
|
|
}
|
|
|
|
fn idle_args(tokens: TokenStream2) -> parse::Result<IdleArgs> {
|
|
(|input: ParseStream<'_>| -> parse::Result<IdleArgs> {
|
|
if input.is_empty() {
|
|
return Ok(IdleArgs::default());
|
|
}
|
|
|
|
let mut shared_resources = None;
|
|
let mut local_resources = None;
|
|
|
|
if !input.is_empty() {
|
|
loop {
|
|
// Parse identifier name
|
|
let ident: Ident = input.parse()?;
|
|
// Handle equal sign
|
|
let _: Token![=] = input.parse()?;
|
|
|
|
match &*ident.to_string() {
|
|
"shared" => {
|
|
if shared_resources.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
shared_resources = Some(util::parse_shared_resources(input)?);
|
|
}
|
|
|
|
"local" => {
|
|
if local_resources.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
local_resources = Some(util::parse_local_resources(input)?);
|
|
}
|
|
|
|
_ => {
|
|
return Err(parse::Error::new(ident.span(), "unexpected argument"));
|
|
}
|
|
}
|
|
if input.is_empty() {
|
|
break;
|
|
}
|
|
|
|
// Handle comma: ,
|
|
let _: Token![,] = input.parse()?;
|
|
}
|
|
}
|
|
|
|
Ok(IdleArgs {
|
|
shared_resources: shared_resources.unwrap_or_default(),
|
|
local_resources: local_resources.unwrap_or_default(),
|
|
})
|
|
})
|
|
.parse2(tokens)
|
|
}
|
|
|
|
fn task_args(tokens: TokenStream2) -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
|
|
(|input: ParseStream<'_>| -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
|
|
if input.is_empty() {
|
|
return Ok(Either::Right(SoftwareTaskArgs::default()));
|
|
}
|
|
|
|
let mut binds = None;
|
|
let mut trampoline = None;
|
|
let mut priority = None;
|
|
let mut shared_resources = None;
|
|
let mut local_resources = None;
|
|
let mut prio_span = None;
|
|
|
|
loop {
|
|
if input.is_empty() {
|
|
break;
|
|
}
|
|
|
|
// Parse identifier name
|
|
let ident: Ident = input.parse()?;
|
|
let ident_s = ident.to_string();
|
|
|
|
// Handle equal sign
|
|
let _: Token![=] = input.parse()?;
|
|
|
|
match &*ident_s {
|
|
"binds" => {
|
|
if binds.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
// Parse identifier name
|
|
let ident = input.parse()?;
|
|
|
|
binds = Some(ident);
|
|
},
|
|
|
|
"trampoline" => {
|
|
if trampoline.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
// Parse identifier name
|
|
let ident: Ident = input.parse()?;
|
|
|
|
trampoline = Some(ident);
|
|
},
|
|
|
|
"priority" => {
|
|
if priority.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
// #lit
|
|
let lit: LitInt = input.parse()?;
|
|
|
|
if !lit.suffix().is_empty() {
|
|
return Err(parse::Error::new(
|
|
lit.span(),
|
|
"this literal must be unsuffixed",
|
|
));
|
|
}
|
|
|
|
let value = lit.base10_parse::<u8>().ok();
|
|
if value.is_none() {
|
|
return Err(parse::Error::new(
|
|
lit.span(),
|
|
"this literal must be in the range 0...255",
|
|
));
|
|
}
|
|
|
|
prio_span = Some(lit.span());
|
|
priority = Some(value.unwrap());
|
|
}
|
|
|
|
"shared" => {
|
|
if shared_resources.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
shared_resources = Some(util::parse_shared_resources(input)?);
|
|
}
|
|
|
|
"local" => {
|
|
if local_resources.is_some() {
|
|
return Err(parse::Error::new(
|
|
ident.span(),
|
|
"argument appears more than once",
|
|
));
|
|
}
|
|
|
|
local_resources = Some(util::parse_local_resources(input)?);
|
|
}
|
|
|
|
a => {
|
|
return Err(parse::Error::new(ident.span(), format!("unexpected argument {}", a)));
|
|
}
|
|
}
|
|
|
|
if input.is_empty() {
|
|
break;
|
|
}
|
|
|
|
// Handle comma: ,
|
|
let _: Token![,] = input.parse()?;
|
|
}
|
|
let shared_resources = shared_resources.unwrap_or_default();
|
|
let local_resources = local_resources.unwrap_or_default();
|
|
|
|
Ok(if let Some(binds) = binds {
|
|
// Hardware tasks can't run at anything lower than 1
|
|
let priority = priority.unwrap_or(1);
|
|
|
|
if priority == 0 {
|
|
return Err(parse::Error::new(
|
|
prio_span.unwrap(),
|
|
"hardware tasks are not allowed to be at priority 0",
|
|
));
|
|
}
|
|
|
|
Either::Left(HardwareTaskArgs {
|
|
binds,
|
|
priority,
|
|
shared_resources,
|
|
local_resources,
|
|
trampoline,
|
|
})
|
|
} else {
|
|
// Software tasks start at idle priority
|
|
let priority = priority.unwrap_or(0);
|
|
|
|
Either::Right(SoftwareTaskArgs {
|
|
priority,
|
|
shared_resources,
|
|
local_resources,
|
|
})
|
|
})
|
|
})
|
|
.parse2(tokens)
|
|
}
|