Skip to content

Commit cf56d76

Browse files
actor API syntax
see rtic-rs/rfcs#52 for details includes: core proposal and first `#[init]` extension Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
1 parent 9ba39eb commit cf56d76

File tree

8 files changed

+452
-19
lines changed

8 files changed

+452
-19
lines changed

src/analyze.rs

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ use crate::{
1212
Set,
1313
};
1414

15+
type TaskName = String;
16+
1517
pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
1618
// Collect all tasks into a vector
17-
type TaskName = String;
18-
type Priority = u8;
1919

2020
// The task list is a Tuple (Name, Shared Resources, Local Resources, Priority)
2121
let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> =
@@ -259,23 +259,51 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
259259
let spawnee_prio = spawnee.args.priority;
260260

261261
let channel = channels.entry(spawnee_prio).or_default();
262-
channel.tasks.insert(name.clone());
262+
channel
263+
.spawnees
264+
.insert(Spawnee::Task { name: name.clone() });
263265

264266
// All inputs are now send as we do not know from where they may be spawned.
265267
spawnee.inputs.iter().for_each(|input| {
266268
send_types.insert(input.ty.clone());
267269
});
268270
}
269271

272+
for (name, actor) in &app.actors {
273+
let spawnee_prio = actor.priority;
274+
275+
if !actor.subscriptions.is_empty() {
276+
for (index, subscription) in actor.subscriptions.iter().enumerate() {
277+
let channel = channels.entry(spawnee_prio).or_default();
278+
channel.spawnees.insert(Spawnee::Actor {
279+
name: name.clone(),
280+
subscription_index: index,
281+
});
282+
283+
// All inputs are now send as we do not know from where they may be spawned.
284+
send_types.insert(Box::new(subscription.ty.clone()));
285+
}
286+
}
287+
}
288+
270289
// No channel should ever be empty
271-
debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty()));
290+
debug_assert!(channels
291+
.values()
292+
.all(|channel| !channel.spawnees.is_empty()));
272293

273294
// Compute channel capacities
274295
for channel in channels.values_mut() {
275296
channel.capacity = channel
276-
.tasks
297+
.spawnees
277298
.iter()
278-
.map(|name| app.software_tasks[name].args.capacity)
299+
.map(|spawnee| match spawnee {
300+
Spawnee::Task { name } => app.software_tasks[name].args.capacity,
301+
Spawnee::Actor {
302+
name,
303+
subscription_index,
304+
..
305+
} => app.actors[name].subscriptions[*subscription_index].capacity,
306+
})
279307
.sum();
280308
}
281309

@@ -359,8 +387,25 @@ pub struct Channel {
359387
/// The channel capacity
360388
pub capacity: u8,
361389

362-
/// Tasks that can be spawned on this channel
363-
pub tasks: BTreeSet<Task>,
390+
/// Tasks / AO that can be spawned on this channel
391+
pub spawnees: BTreeSet<Spawnee>,
392+
}
393+
394+
/// What can be spawned
395+
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
396+
pub enum Spawnee {
397+
/// A software task
398+
Task {
399+
/// The name of the task
400+
name: Task,
401+
},
402+
/// An actor
403+
Actor {
404+
/// The name of the actor
405+
name: Ident,
406+
/// Index into the actor's `subscriptions` field
407+
subscription_index: usize,
408+
},
364409
}
365410

366411
/// Resource ownership

src/ast.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub struct App {
2929
/// Task local resources defined in `#[local]`
3030
pub local_resources: Map<LocalResource>,
3131

32+
/// Actors defined in the `#[actors]` struct
33+
pub actors: Map<Actor>,
34+
3235
/// User imports
3336
pub user_imports: Vec<ItemUse>,
3437

@@ -90,6 +93,9 @@ pub struct Init {
9093

9194
/// The name of the user provided local resources struct
9295
pub user_local_struct: Ident,
96+
97+
/// The name of the user provided actors struct
98+
pub user_actors_struct: Option<Ident>,
9399
}
94100

95101
/// `init` context metadata
@@ -186,6 +192,31 @@ pub struct LocalResource {
186192
pub ty: Box<Type>,
187193
}
188194

195+
/// An actor defined in the `#[actors]` struct.
196+
#[derive(Debug)]
197+
#[non_exhaustive]
198+
pub struct Actor {
199+
/// The priority of this actor
200+
pub priority: u8,
201+
/// The expression used to initialized this actor. If absent, uses late/runtime
202+
/// initialization.
203+
pub init: Option<Box<Expr>>,
204+
/// Type of the actor.
205+
pub ty: Box<Type>,
206+
/// #[subscribe] attributes
207+
pub subscriptions: Vec<Subscription>,
208+
}
209+
210+
/// The `#[subscribe]` attribute of an actor
211+
#[derive(Debug)]
212+
#[non_exhaustive]
213+
pub struct Subscription {
214+
/// Capacity of this channel
215+
pub capacity: u8,
216+
/// Message type
217+
pub ty: Type,
218+
}
219+
189220
/// Monotonic
190221
#[derive(Debug)]
191222
#[non_exhaustive]

src/parse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod actor;
12
mod app;
23
mod hardware_task;
34
mod idle;

src/parse/actor.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use proc_macro2::{Ident, Span};
2+
use syn::{
3+
parenthesized,
4+
parse::{self, Parse},
5+
spanned::Spanned,
6+
Field, LitInt, Token, Type, Visibility,
7+
};
8+
9+
use crate::ast::{Actor, Subscription};
10+
11+
use super::util;
12+
13+
impl Actor {
14+
pub(crate) fn parse(item: &Field, span: Span) -> parse::Result<Self> {
15+
if item.vis != Visibility::Inherited {
16+
return Err(parse::Error::new(
17+
span,
18+
"this field must have inherited / private visibility",
19+
));
20+
}
21+
22+
let (cfgs, attrs) = util::extract_cfgs(item.attrs.clone());
23+
24+
if !cfgs.is_empty() {
25+
return Err(parse::Error::new(span, "`#[cfg]` is not allowed on actors"));
26+
}
27+
28+
let mut priority = None;
29+
let mut init = None;
30+
let mut subscriptions = Vec::new();
31+
32+
for attr in attrs {
33+
match attr.path.get_ident() {
34+
Some(name) => {
35+
match &*name.to_string() {
36+
"priority" => {
37+
if priority.is_some() {
38+
return Err(parse::Error::new(
39+
attr.span(),
40+
"only one `#[priority]` attribute is allowed on an actor",
41+
));
42+
}
43+
44+
let prio: EqPriority = syn::parse2(attr.tokens)?;
45+
priority = Some(prio.priority);
46+
}
47+
"init" => {
48+
if init.is_some() {
49+
return Err(parse::Error::new(
50+
attr.span(),
51+
"only one `#[init]` attribute is allowed on an actor",
52+
));
53+
}
54+
55+
// `#[init(expr)]` can be parsed via `ExprParen`
56+
let paren: syn::ExprParen = syn::parse2(attr.tokens)?;
57+
58+
init = Some(paren.expr);
59+
}
60+
"subscribe" => {
61+
let subscribe: Subscribe = syn::parse2(attr.tokens)?;
62+
let capacity = subscribe
63+
.capacity
64+
.map(|lit| {
65+
lit.base10_digits().parse::<u8>().map_err(|_| {
66+
parse::Error::new(lit.span(), "not a `u8` value")
67+
})
68+
})
69+
.transpose()?;
70+
71+
subscriptions.push(Subscription {
72+
ty: subscribe.ty,
73+
capacity: capacity.unwrap_or(1),
74+
});
75+
}
76+
_ => {
77+
return Err(parse::Error::new(
78+
name.span(),
79+
"this attribute is not supported on actor declarations",
80+
));
81+
}
82+
}
83+
}
84+
None => {
85+
return Err(parse::Error::new(
86+
attr.path.span(),
87+
"this attribute is not supported on actor declarations",
88+
));
89+
}
90+
}
91+
}
92+
93+
Ok(Actor {
94+
ty: Box::new(item.ty.clone()),
95+
priority: priority.unwrap_or(1),
96+
init,
97+
subscriptions,
98+
})
99+
}
100+
}
101+
102+
struct EqPriority {
103+
priority: u8,
104+
}
105+
106+
impl parse::Parse for EqPriority {
107+
fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> {
108+
let _eq: Token![=] = input.parse()?;
109+
let lit: syn::LitInt = input.parse()?;
110+
111+
if !lit.suffix().is_empty() {
112+
return Err(parse::Error::new(
113+
lit.span(),
114+
"this literal must be unsuffixed",
115+
));
116+
}
117+
118+
let value = lit.base10_parse::<u8>().ok();
119+
match value {
120+
None | Some(0) => Err(parse::Error::new(
121+
lit.span(),
122+
"this literal must be in the range 1...255",
123+
)),
124+
Some(priority) => Ok(Self { priority }),
125+
}
126+
}
127+
}
128+
129+
struct Subscribe {
130+
ty: Type,
131+
capacity: Option<LitInt>,
132+
}
133+
134+
impl Parse for Subscribe {
135+
fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> {
136+
let content;
137+
parenthesized!(content in input);
138+
139+
let ty = content.parse()?;
140+
141+
let capacity = if content.is_empty() {
142+
None
143+
} else {
144+
let _: Token![,] = content.parse()?;
145+
let ident: Ident = content.parse()?;
146+
147+
if ident.to_string() == "capacity" {
148+
let _: Token![=] = content.parse()?;
149+
Some(content.parse()?)
150+
} else {
151+
return Err(parse::Error::new(
152+
ident.span(),
153+
format!("expected `capacity`, found `{}`", ident),
154+
));
155+
}
156+
};
157+
158+
Ok(Self { ty, capacity })
159+
}
160+
}

0 commit comments

Comments
 (0)