1mod app;
2mod hardware_task;
3mod idle;
4mod init;
5mod monotonic;
6mod resource;
7mod software_task;
8mod util;
9
10use proc_macro2::TokenStream as TokenStream2;
11use syn::{
12 braced, parenthesized,
13 parse::{self, Parse, ParseStream, Parser},
14 token::{self, Brace},
15 Ident, Item, LitBool, LitInt, Path, Token,
16};
17
18use crate::{
19 ast::{
20 App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs,
21 TaskLocal,
22 },
23 Either, Settings,
24};
25
26pub fn app(args: TokenStream2, input: TokenStream2, settings: &Settings) -> parse::Result<App> {
28 let args = AppArgs::parse(args)?;
29 let input: Input = syn::parse2(input)?;
30
31 App::parse(args, input, settings)
32}
33
34pub(crate) struct Input {
35 _mod_token: Token![mod],
36 pub ident: Ident,
37 _brace_token: Brace,
38 pub items: Vec<Item>,
39}
40
41impl Parse for Input {
42 fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
43 fn parse_items(input: ParseStream<'_>) -> parse::Result<Vec<Item>> {
44 let mut items = vec![];
45
46 while !input.is_empty() {
47 items.push(input.parse()?);
48 }
49
50 Ok(items)
51 }
52
53 let content;
54
55 let _mod_token = input.parse()?;
56 let ident = input.parse()?;
57 let _brace_token = braced!(content in input);
58 let items = content.call(parse_items)?;
59
60 Ok(Input {
61 _mod_token,
62 ident,
63 _brace_token,
64 items,
65 })
66 }
67}
68
69fn init_args(tokens: TokenStream2) -> parse::Result<InitArgs> {
70 (|input: ParseStream<'_>| -> parse::Result<InitArgs> {
71 if input.is_empty() {
72 return Ok(InitArgs::default());
73 }
74
75 let mut local_resources = None;
76
77 let content;
78 parenthesized!(content in input);
79
80 if !content.is_empty() {
81 loop {
82 let ident: Ident = content.parse()?;
84 let _: Token![=] = content.parse()?;
86
87 match &*ident.to_string() {
88 "local" => {
89 if local_resources.is_some() {
90 return Err(parse::Error::new(
91 ident.span(),
92 "argument appears more than once",
93 ));
94 }
95
96 local_resources = Some(util::parse_local_resources(&content)?);
97 }
98 _ => {
99 return Err(parse::Error::new(ident.span(), "unexpected argument"));
100 }
101 }
102
103 if content.is_empty() {
104 break;
105 }
106 let _: Token![,] = content.parse()?;
108 }
109 }
110
111 if let Some(locals) = &local_resources {
112 for (ident, task_local) in locals {
113 if let TaskLocal::External = task_local {
114 return Err(parse::Error::new(
115 ident.span(),
116 "only declared local resources are allowed in init",
117 ));
118 }
119 }
120 }
121
122 Ok(InitArgs {
123 local_resources: local_resources.unwrap_or_default(),
124 })
125 })
126 .parse2(tokens)
127}
128
129fn idle_args(tokens: TokenStream2) -> parse::Result<IdleArgs> {
130 (|input: ParseStream<'_>| -> parse::Result<IdleArgs> {
131 if input.is_empty() {
132 return Ok(IdleArgs::default());
133 }
134
135 let mut shared_resources = None;
136 let mut local_resources = None;
137
138 let content;
139 parenthesized!(content in input);
140 if !content.is_empty() {
141 loop {
142 let ident: Ident = content.parse()?;
144 let _: Token![=] = content.parse()?;
146
147 match &*ident.to_string() {
148 "shared" => {
149 if shared_resources.is_some() {
150 return Err(parse::Error::new(
151 ident.span(),
152 "argument appears more than once",
153 ));
154 }
155
156 shared_resources = Some(util::parse_shared_resources(&content)?);
157 }
158
159 "local" => {
160 if local_resources.is_some() {
161 return Err(parse::Error::new(
162 ident.span(),
163 "argument appears more than once",
164 ));
165 }
166
167 local_resources = Some(util::parse_local_resources(&content)?);
168 }
169
170 _ => {
171 return Err(parse::Error::new(ident.span(), "unexpected argument"));
172 }
173 }
174 if content.is_empty() {
175 break;
176 }
177
178 let _: Token![,] = content.parse()?;
180 }
181 }
182
183 Ok(IdleArgs {
184 shared_resources: shared_resources.unwrap_or_default(),
185 local_resources: local_resources.unwrap_or_default(),
186 })
187 })
188 .parse2(tokens)
189}
190
191fn task_args(
192 tokens: TokenStream2,
193 settings: &Settings,
194) -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
195 (|input: ParseStream<'_>| -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
196 if input.is_empty() {
197 return Ok(Either::Right(SoftwareTaskArgs::default()));
198 }
199
200 let mut binds = None;
201 let mut capacity = None;
202 let mut priority = None;
203 let mut shared_resources = None;
204 let mut local_resources = None;
205
206
207 let content;
208 parenthesized!(content in input);
209 loop {
210 if content.is_empty() {
211 break;
212 }
213
214 let ident: Ident = content.parse()?;
216 let _: Token![=] = content.parse()?;
218
219 let ident_s = ident.to_string();
220 match &*ident_s {
221 "binds" if !settings.parse_binds => {
222 return Err(parse::Error::new(
223 ident.span(),
224 "Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set.",
225 ));
226 }
227
228 "binds" if settings.parse_binds => {
229 if binds.is_some() {
230 return Err(parse::Error::new(
231 ident.span(),
232 "argument appears more than once",
233 ));
234 }
235
236 if capacity.is_some() {
237 return Err(parse::Error::new(
238 ident.span(),
239 "hardware tasks can't use the `capacity` argument",
240 ));
241 }
242
243 let ident = content.parse()?;
245
246 binds = Some(ident);
247 }
248
249 "capacity" => {
250 if capacity.is_some() {
251 return Err(parse::Error::new(
252 ident.span(),
253 "argument appears more than once",
254 ));
255 }
256
257 if binds.is_some() {
258 return Err(parse::Error::new(
259 ident.span(),
260 "hardware tasks can't use the `capacity` argument",
261 ));
262 }
263
264 let lit: LitInt = content.parse()?;
266
267 if !lit.suffix().is_empty() {
268 return Err(parse::Error::new(
269 lit.span(),
270 "this literal must be unsuffixed",
271 ));
272 }
273
274 let value = lit.base10_parse::<u8>().ok();
275 if value.is_none() || value == Some(0) {
276 return Err(parse::Error::new(
277 lit.span(),
278 "this literal must be in the range 1...255",
279 ));
280 }
281
282 capacity = Some(value.unwrap());
283 }
284
285 "priority" => {
286 if priority.is_some() {
287 return Err(parse::Error::new(
288 ident.span(),
289 "argument appears more than once",
290 ));
291 }
292
293 let lit: LitInt = content.parse()?;
295
296 if !lit.suffix().is_empty() {
297 return Err(parse::Error::new(
298 lit.span(),
299 "this literal must be unsuffixed",
300 ));
301 }
302
303 let value = lit.base10_parse::<u8>().ok();
304 if value.is_none() || value == Some(0) {
305 return Err(parse::Error::new(
306 lit.span(),
307 "this literal must be in the range 1...255",
308 ));
309 }
310
311 priority = Some(value.unwrap());
312 }
313
314 "shared" => {
315 if shared_resources.is_some() {
316 return Err(parse::Error::new(
317 ident.span(),
318 "argument appears more than once",
319 ));
320 }
321
322 shared_resources = Some(util::parse_shared_resources(&content)?);
323 }
324
325 "local" => {
326 if local_resources.is_some() {
327 return Err(parse::Error::new(
328 ident.span(),
329 "argument appears more than once",
330 ));
331 }
332
333 local_resources = Some(util::parse_local_resources(&content)?);
334 }
335
336 _ => {
337 return Err(parse::Error::new(ident.span(), "unexpected argument"));
338 }
339 }
340
341 if content.is_empty() {
342 break;
343 }
344
345 let _: Token![,] = content.parse()?;
347 }
348 let priority = priority.unwrap_or(1);
349 let shared_resources = shared_resources.unwrap_or_default();
350 let local_resources = local_resources.unwrap_or_default();
351
352 Ok(if let Some(binds) = binds {
353 Either::Left(HardwareTaskArgs {
354 binds,
355 priority,
356 shared_resources,
357 local_resources,
358 })
359 } else {
360 Either::Right(SoftwareTaskArgs {
361 capacity: capacity.unwrap_or(1),
362 priority,
363 shared_resources,
364 local_resources,
365 })
366 })
367 })
368 .parse2(tokens)
369}
370
371fn monotonic_args(path: Path, tokens: TokenStream2) -> parse::Result<MonotonicArgs> {
372 (|input: ParseStream<'_>| -> parse::Result<MonotonicArgs> {
373 let mut binds = None;
374 let mut priority = None;
375 let mut default = None;
376
377 if !input.peek(token::Paren) {
378 return Err(parse::Error::new(
379 path.segments.first().unwrap().ident.span(),
380 "expected opening ( in #[monotonic( ... )]",
381 ));
382 }
383
384 let content;
385 parenthesized!(content in input);
386
387 if !content.is_empty() {
388 loop {
389 let ident: Ident = content.parse()?;
391 let _: Token![=] = content.parse()?;
393
394 match &*ident.to_string() {
395 "binds" => {
396 if binds.is_some() {
397 return Err(parse::Error::new(
398 ident.span(),
399 "argument appears more than once",
400 ));
401 }
402 let ident = content.parse()?;
404
405 binds = Some(ident);
406 }
407
408 "priority" => {
409 if priority.is_some() {
410 return Err(parse::Error::new(
411 ident.span(),
412 "argument appears more than once",
413 ));
414 }
415
416 let lit: LitInt = content.parse()?;
418
419 if !lit.suffix().is_empty() {
420 return Err(parse::Error::new(
421 lit.span(),
422 "this literal must be unsuffixed",
423 ));
424 }
425
426 let value = lit.base10_parse::<u8>().ok();
427 if value.is_none() || value == Some(0) {
428 return Err(parse::Error::new(
429 lit.span(),
430 "this literal must be in the range 1...255",
431 ));
432 }
433
434 priority = Some(value.unwrap());
435 }
436
437 "default" => {
438 if default.is_some() {
439 return Err(parse::Error::new(
440 ident.span(),
441 "argument appears more than once",
442 ));
443 }
444
445 let lit: LitBool = content.parse()?;
446 default = Some(lit.value);
447 }
448
449 _ => {
450 return Err(parse::Error::new(ident.span(), "unexpected argument"));
451 }
452 }
453 if content.is_empty() {
454 break;
455 }
456
457 let _: Token![,] = content.parse()?;
459 }
460 }
461
462 let binds = if let Some(r) = binds {
463 r
464 } else {
465 return Err(parse::Error::new(
466 content.span(),
467 "`binds = ...` is missing",
468 ));
469 };
470 let default = default.unwrap_or(false);
471
472 Ok(MonotonicArgs {
473 binds,
474 priority,
475 default,
476 })
477 })
478 .parse2(tokens)
479}