rtic_macros/syntax/
parse.rs
1mod app;
2mod hardware_task;
3mod idle;
4mod init;
5mod resource;
6mod software_task;
7mod util;
8
9use proc_macro2::TokenStream as TokenStream2;
10use syn::{
11 braced,
12 parse::{self, Parse, ParseStream, Parser},
13 token::Brace,
14 Ident, Item, LitInt, Token,
15};
16
17use crate::syntax::{
18 ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal},
19 Either,
20};
21
22pub fn app(args: TokenStream2, input: TokenStream2) -> parse::Result<App> {
24 let args = AppArgs::parse(args)?;
25 let input: Input = syn::parse2(input)?;
26
27 App::parse(args, input)
28}
29
30pub(crate) struct Input {
31 _mod_token: Token![mod],
32 pub ident: Ident,
33 _brace_token: Brace,
34 pub items: Vec<Item>,
35}
36
37impl Parse for Input {
38 fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
39 fn parse_items(input: ParseStream<'_>) -> parse::Result<Vec<Item>> {
40 let mut items = vec![];
41
42 while !input.is_empty() {
43 items.push(input.parse()?);
44 }
45
46 Ok(items)
47 }
48
49 let content;
50
51 let _mod_token = input.parse()?;
52 let ident = input.parse()?;
53 let _brace_token = braced!(content in input);
54 let items = content.call(parse_items)?;
55
56 Ok(Input {
57 _mod_token,
58 ident,
59 _brace_token,
60 items,
61 })
62 }
63}
64
65fn init_args(tokens: TokenStream2) -> parse::Result<InitArgs> {
66 (|input: ParseStream<'_>| -> parse::Result<InitArgs> {
67 if input.is_empty() {
68 return Ok(InitArgs::default());
69 }
70
71 let mut local_resources = None;
72
73 if !input.is_empty() {
74 loop {
75 let ident: Ident = input.parse()?;
77 let _: Token![=] = input.parse()?;
79
80 match &*ident.to_string() {
81 "local" => {
82 if local_resources.is_some() {
83 return Err(parse::Error::new(
84 ident.span(),
85 "argument appears more than once",
86 ));
87 }
88
89 local_resources = Some(util::parse_local_resources(input)?);
90 }
91 _ => {
92 return Err(parse::Error::new(ident.span(), "unexpected argument"));
93 }
94 }
95
96 if input.is_empty() {
97 break;
98 }
99 let _: Token![,] = input.parse()?;
101 }
102 }
103
104 if let Some(locals) = &local_resources {
105 for (ident, task_local) in locals {
106 if let TaskLocal::External = task_local {
107 return Err(parse::Error::new(
108 ident.span(),
109 "only declared local resources are allowed in init",
110 ));
111 }
112 }
113 }
114
115 Ok(InitArgs {
116 local_resources: local_resources.unwrap_or_default(),
117 })
118 })
119 .parse2(tokens)
120}
121
122fn idle_args(tokens: TokenStream2) -> parse::Result<IdleArgs> {
123 (|input: ParseStream<'_>| -> parse::Result<IdleArgs> {
124 if input.is_empty() {
125 return Ok(IdleArgs::default());
126 }
127
128 let mut shared_resources = None;
129 let mut local_resources = None;
130
131 if !input.is_empty() {
132 loop {
133 let ident: Ident = input.parse()?;
135 let _: Token![=] = input.parse()?;
137
138 match &*ident.to_string() {
139 "shared" => {
140 if shared_resources.is_some() {
141 return Err(parse::Error::new(
142 ident.span(),
143 "argument appears more than once",
144 ));
145 }
146
147 shared_resources = Some(util::parse_shared_resources(input)?);
148 }
149
150 "local" => {
151 if local_resources.is_some() {
152 return Err(parse::Error::new(
153 ident.span(),
154 "argument appears more than once",
155 ));
156 }
157
158 local_resources = Some(util::parse_local_resources(input)?);
159 }
160
161 _ => {
162 return Err(parse::Error::new(ident.span(), "unexpected argument"));
163 }
164 }
165 if input.is_empty() {
166 break;
167 }
168
169 let _: Token![,] = input.parse()?;
171 }
172 }
173
174 Ok(IdleArgs {
175 shared_resources: shared_resources.unwrap_or_default(),
176 local_resources: local_resources.unwrap_or_default(),
177 })
178 })
179 .parse2(tokens)
180}
181
182fn task_args(tokens: TokenStream2) -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
183 (|input: ParseStream<'_>| -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
184 if input.is_empty() {
185 return Ok(Either::Right(SoftwareTaskArgs::default()));
186 }
187
188 let mut binds = None;
189 let mut priority = None;
190 let mut shared_resources = None;
191 let mut local_resources = None;
192 let mut prio_span = None;
193
194 loop {
195 if input.is_empty() {
196 break;
197 }
198
199 let ident: Ident = input.parse()?;
201 let ident_s = ident.to_string();
202
203 let _: Token![=] = input.parse()?;
205
206 match &*ident_s {
207 "binds" => {
208 if binds.is_some() {
209 return Err(parse::Error::new(
210 ident.span(),
211 "argument appears more than once",
212 ));
213 }
214
215 let ident = input.parse()?;
217
218 binds = Some(ident);
219 }
220
221 "priority" => {
222 if priority.is_some() {
223 return Err(parse::Error::new(
224 ident.span(),
225 "argument appears more than once",
226 ));
227 }
228
229 let lit: LitInt = input.parse()?;
231
232 if !lit.suffix().is_empty() {
233 return Err(parse::Error::new(
234 lit.span(),
235 "this literal must be unsuffixed",
236 ));
237 }
238
239 let value = lit.base10_parse::<u8>().ok();
240 if value.is_none() {
241 return Err(parse::Error::new(
242 lit.span(),
243 "this literal must be in the range 0...255",
244 ));
245 }
246
247 prio_span = Some(lit.span());
248 priority = Some(value.unwrap());
249 }
250
251 "shared" => {
252 if shared_resources.is_some() {
253 return Err(parse::Error::new(
254 ident.span(),
255 "argument appears more than once",
256 ));
257 }
258
259 shared_resources = Some(util::parse_shared_resources(input)?);
260 }
261
262 "local" => {
263 if local_resources.is_some() {
264 return Err(parse::Error::new(
265 ident.span(),
266 "argument appears more than once",
267 ));
268 }
269
270 local_resources = Some(util::parse_local_resources(input)?);
271 }
272
273 _ => {
274 return Err(parse::Error::new(ident.span(), "unexpected argument"));
275 }
276 }
277
278 if input.is_empty() {
279 break;
280 }
281
282 let _: Token![,] = input.parse()?;
284 }
285 let shared_resources = shared_resources.unwrap_or_default();
286 let local_resources = local_resources.unwrap_or_default();
287
288 Ok(if let Some(binds) = binds {
289 let priority = priority.unwrap_or(1);
291
292 if priority == 0 {
293 return Err(parse::Error::new(
294 prio_span.unwrap(),
295 "hardware tasks are not allowed to be at priority 0",
296 ));
297 }
298
299 Either::Left(HardwareTaskArgs {
300 binds,
301 priority,
302 shared_resources,
303 local_resources,
304 })
305 } else {
306 let priority = priority.unwrap_or(0);
308
309 Either::Right(SoftwareTaskArgs {
310 priority,
311 shared_resources,
312 local_resources,
313 })
314 })
315 })
316 .parse2(tokens)
317}