1mod logic;
4
5use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
6
7#[proc_macro]
8pub fn osc(ts: TokenStream) -> TokenStream {
9 let FnDecl {
10 attributes,
11 fn_name,
12 generics,
13 mut arg_stream,
14 body,
15 } = parse_function_declaration(ts);
16 let hierarchy: logic::Hierarchy = logic::stratify(body);
17 let parser = hierarchy.parser();
18
19 let mut acc: TokenStream = TokenStream::from_iter(attributes);
20 let () = acc.extend([
21 TokenTree::Ident(Ident::new("async", Span::call_site())),
22 TokenTree::Ident(Ident::new("fn", Span::call_site())),
23 TokenTree::Ident(fn_name),
24 TokenTree::Punct(Punct::new('<', Spacing::Alone)),
25 ]);
26 if !generics.is_empty() {
27 let () = acc.extend(generics);
28 let () = acc.extend(core::iter::once(TokenTree::Punct(Punct::new(
29 ',',
30 Spacing::Alone,
31 ))));
32 }
33 let () = acc.extend([
34 TokenTree::Ident(Ident::new("AsyncRestart", Span::call_site())),
35 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
36 TokenTree::Ident(Ident::new("Future", Span::call_site())),
37 TokenTree::Punct(Punct::new('<', Spacing::Alone)),
38 TokenTree::Ident(Ident::new("Output", Span::call_site())),
39 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
40 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
41 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
42 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
43 TokenTree::Ident(Ident::new("AsyncRecvByte", Span::call_site())),
44 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
45 TokenTree::Ident(Ident::new("Future", Span::call_site())),
46 TokenTree::Punct(Punct::new('<', Spacing::Alone)),
47 TokenTree::Ident(Ident::new("Output", Span::call_site())),
48 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
49 TokenTree::Ident(Ident::new("u8", Span::call_site())),
50 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
51 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
52 TokenTree::Ident(Ident::new("AsyncSendByte", Span::call_site())),
53 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
54 TokenTree::Ident(Ident::new("Future", Span::call_site())),
55 TokenTree::Punct(Punct::new('<', Spacing::Alone)),
56 TokenTree::Ident(Ident::new("Output", Span::call_site())),
57 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
58 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
59 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
60 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
61 TokenTree::Ident(Ident::new("AsyncError", Span::call_site())),
62 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
63 TokenTree::Ident(Ident::new("Future", Span::call_site())),
64 TokenTree::Punct(Punct::new('<', Spacing::Alone)),
65 TokenTree::Ident(Ident::new("Output", Span::call_site())),
66 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
67 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
68 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
69 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
70 TokenTree::Ident(Ident::new("Restart", Span::call_site())),
71 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
72 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
73 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
74 TokenTree::Ident(Ident::new("core", Span::call_site())),
75 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
76 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
77 TokenTree::Ident(Ident::new("ops", Span::call_site())),
78 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
79 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
80 TokenTree::Ident(Ident::new("FnMut", Span::call_site())),
81 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
82 TokenTree::Punct(Punct::new('-', Spacing::Joint)),
83 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
84 TokenTree::Ident(Ident::new("AsyncRestart", Span::call_site())),
85 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
86 TokenTree::Ident(Ident::new("RecvByte", Span::call_site())),
87 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
88 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
89 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
90 TokenTree::Ident(Ident::new("core", Span::call_site())),
91 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
92 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
93 TokenTree::Ident(Ident::new("ops", Span::call_site())),
94 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
95 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
96 TokenTree::Ident(Ident::new("FnMut", Span::call_site())),
97 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
98 TokenTree::Punct(Punct::new('-', Spacing::Joint)),
99 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
100 TokenTree::Ident(Ident::new("AsyncRecvByte", Span::call_site())),
101 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
102 TokenTree::Ident(Ident::new("SendByte", Span::call_site())),
103 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
104 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
105 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
106 TokenTree::Ident(Ident::new("core", Span::call_site())),
107 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
108 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
109 TokenTree::Ident(Ident::new("ops", Span::call_site())),
110 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
111 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
112 TokenTree::Ident(Ident::new("FnMut", Span::call_site())),
113 TokenTree::Group(Group::new(
114 Delimiter::Parenthesis,
115 TokenStream::from_iter(core::iter::once(TokenTree::Ident(Ident::new(
116 "u8",
117 Span::call_site(),
118 )))),
119 )),
120 TokenTree::Punct(Punct::new('-', Spacing::Joint)),
121 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
122 TokenTree::Ident(Ident::new("AsyncSendByte", Span::call_site())),
123 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
124 TokenTree::Ident(Ident::new("Error", Span::call_site())),
125 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
126 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
127 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
128 TokenTree::Ident(Ident::new("core", Span::call_site())),
129 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
130 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
131 TokenTree::Ident(Ident::new("ops", Span::call_site())),
132 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
133 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
134 TokenTree::Ident(Ident::new("FnMut", Span::call_site())),
135 TokenTree::Group(Group::new(
136 Delimiter::Parenthesis,
137 TokenStream::from_iter([
138 TokenTree::Punct(Punct::new('&', Spacing::Alone)),
139 TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
140 TokenTree::Ident(Ident::new("static", Span::call_site())),
141 TokenTree::Ident(Ident::new("str", Span::call_site())),
142 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
143 TokenTree::Ident(Ident::new("u8", Span::call_site())),
144 ]),
145 )),
146 TokenTree::Punct(Punct::new('-', Spacing::Joint)),
147 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
148 TokenTree::Ident(Ident::new("AsyncError", Span::call_site())),
149 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
150 TokenTree::Group(Group::new(Delimiter::Parenthesis, {
151 if !arg_stream.is_empty() {
152 let () = arg_stream.extend(core::iter::once(TokenTree::Punct(Punct::new(
153 ',',
154 Spacing::Alone,
155 ))));
156 }
157 let () = arg_stream.extend([
158 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
159 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
160 TokenTree::Ident(Ident::new("osc_router_traits", Span::call_site())),
161 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
162 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
163 TokenTree::Ident(Ident::new("Driver", Span::call_site())),
164 TokenTree::Group(Group::new(
165 Delimiter::Brace,
166 TokenStream::from_iter([
167 TokenTree::Ident(Ident::new("mut", Span::call_site())),
168 TokenTree::Ident(Ident::new("restart", Span::call_site())),
169 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
170 TokenTree::Ident(Ident::new("mut", Span::call_site())),
171 TokenTree::Ident(Ident::new("recv_byte", Span::call_site())),
172 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
173 TokenTree::Ident(Ident::new("mut", Span::call_site())),
174 TokenTree::Ident(Ident::new("send_byte", Span::call_site())),
175 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
176 TokenTree::Ident(Ident::new("mut", Span::call_site())),
177 TokenTree::Ident(Ident::new("error", Span::call_site())),
178 ]),
179 )),
180 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
181 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
182 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
183 TokenTree::Ident(Ident::new("osc_router_traits", Span::call_site())),
184 TokenTree::Punct(Punct::new(':', Spacing::Joint)),
185 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
186 TokenTree::Ident(Ident::new("Driver", Span::call_site())),
187 TokenTree::Punct(Punct::new('<', Spacing::Alone)),
188 TokenTree::Ident(Ident::new("AsyncRestart", Span::call_site())),
189 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
190 TokenTree::Ident(Ident::new("AsyncRecvByte", Span::call_site())),
191 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
192 TokenTree::Ident(Ident::new("AsyncSendByte", Span::call_site())),
193 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
194 TokenTree::Ident(Ident::new("AsyncError", Span::call_site())),
195 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
196 TokenTree::Ident(Ident::new("Restart", Span::call_site())),
197 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
198 TokenTree::Ident(Ident::new("RecvByte", Span::call_site())),
199 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
200 TokenTree::Ident(Ident::new("SendByte", Span::call_site())),
201 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
202 TokenTree::Ident(Ident::new("Error", Span::call_site())),
203 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
204 ]);
205 arg_stream
206 })),
207 TokenTree::Punct(Punct::new('-', Spacing::Joint)),
208 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
209 TokenTree::Punct(Punct::new('!', Spacing::Alone)),
210 TokenTree::Group(Group::new(
211 Delimiter::Brace,
212 TokenStream::from_iter([
213 TokenTree::Ident(Ident::new("loop", Span::call_site())),
214 TokenTree::Group(Group::new(
215 Delimiter::Brace,
216 TokenStream::from_iter(
217 [
218 TokenTree::Ident(Ident::new("let", Span::call_site())),
219 TokenTree::Group(Group::new(
220 Delimiter::Parenthesis,
221 TokenStream::new(),
222 )),
223 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
224 TokenTree::Ident(Ident::new("restart", Span::call_site())),
225 TokenTree::Group(Group::new(
226 Delimiter::Parenthesis,
227 TokenStream::new(),
228 )),
229 TokenTree::Punct(Punct::new('.', Spacing::Alone)),
230 TokenTree::Ident(Ident::new("await", Span::call_site())),
231 TokenTree::Punct(Punct::new(';', Spacing::Alone)),
232 ]
233 .into_iter()
234 .chain(parser.into_tokens()),
235 ),
236 )),
237 ]),
238 )),
239 ]);
240 acc
241}
242
243#[derive(Debug)]
244struct FnDecl {
245 pub attributes: Vec<TokenTree>,
246 pub fn_name: Ident,
247 pub generics: Vec<TokenTree>,
248 pub arg_stream: TokenStream,
249 pub body: TokenStream,
250}
251
252#[inline]
253fn parse_function_declaration(iter: impl IntoIterator<Item = TokenTree>) -> FnDecl {
254 let mut iter = iter.into_iter();
255
256 let mut attributes = vec![];
257
258 'attributes: loop {
260 match iter.next() {
261 None => panic!(
262 "Expected a function declaration in an OSC router macro, but the macro body ended"
263 ),
264 Some(TokenTree::Punct(punct)) => match punct.as_char() {
265 '#' => match iter.next() {
266 None => panic!(
267 "Expected a function declaration in an OSC router macro, but the macro body ended after a hashtag"
268 ),
269 Some(TokenTree::Group(group)) => match group.delimiter() {
270 Delimiter::Bracket => {
271 let () = attributes.push(TokenTree::Punct(punct));
272 let () = attributes.push(TokenTree::Group(group));
273 }
274 other => panic!(
275 "Expected an attribute body (`[...]`) after a hashtag in an OSC router macro but found a body enclosed in {other:#?} tokens"
276 ),
277 },
278 Some(other) => panic!(
279 "Expected an attribute body (`[...]`) after a hashtag in an OSC router macro but found {other:#?}"
280 ),
281 },
282 c => panic!("Unrecognized punctuation in an OSC router macro: {c:#?}"),
283 },
284 Some(TokenTree::Ident(ident)) => match format!("{ident}").as_str() {
285 "pub" => {
286 let () = attributes.push(TokenTree::Ident(ident));
287 }
288 "async" => break 'attributes,
289 _ => panic!(
290 "Expected `pub` or `async` to begin the function declaration in an OSC router macro but found {ident:#?}"
291 ),
292 },
293 Some(other) => {
294 panic!(
295 "Expected a function declaration in an OSC router macro but found {other:#?}"
296 )
297 }
298 }
299 }
300
301 {
302 let Some(tree) = iter.next() else {
304 panic!("Expected `fn` after `async` in an OSC router macro but the macro body ended")
305 };
306 let TokenTree::Ident(ident) = tree else {
307 panic!("Expected `fn` after `async` in an OSC router macro but found {tree:#?}")
308 };
309 if !matches!(format!("{ident}").as_str(), "fn") {
310 panic!("Expected `fn` after `async` in an OSC router macro but found {ident:#?}")
311 }
312 }
313
314 let fn_name = {
315 let Some(tree) = iter.next() else {
317 panic!(
318 "Expected a function name after `fn` in an OSC router macro but the macro body ended"
319 )
320 };
321 let TokenTree::Ident(ident) = tree else {
322 panic!("Expected a function name after `fn` in an OSC router macro but found {tree:#?}")
323 };
324 ident
325 };
326
327 let mut generics = vec![];
328 let arg_stream = {
329 let Some(mut tree) = iter.next() else {
331 panic!(
332 "Expected arguments after the function name in an OSC router macro but the macro body ended"
333 )
334 };
335 if let TokenTree::Punct(ref punct) = tree {
336 if !matches!(punct.as_char(), '<') {
337 panic!(
338 "Expected arguments after the function name in an OSC router macro but found {tree:#?}"
339 )
340 }
341 let mut angle_bracket_inception: usize = 1;
342 'generics: loop {
343 tree = iter.next().expect(
344 "Function generics in an OSC router macro missing a closing `>` (maybe off-by-one error?)"
345 );
346 if let TokenTree::Punct(ref punct) = tree {
347 match punct.as_char() {
348 '<' => angle_bracket_inception += 1,
349 '>' => {
350 angle_bracket_inception -= 1;
351 if angle_bracket_inception == 0 {
352 tree = iter.next().expect(
353 "Function generics in an OSC router macro missing a closing `>` (maybe off-by-one error?)"
354 );
355 break 'generics;
356 }
357 }
358 _ => {}
359 }
360 }
361 let () = generics.push(tree);
362 }
363 }
364 let TokenTree::Group(group) = tree else {
365 panic!(
366 "Expected arguments after the function name in an OSC router macro but found {tree:#?}"
367 )
368 };
369 group.stream()
370 };
371
372 {
373 let Some(tree) = iter.next() else {
374 panic!(
375 "Expected `->` after function arguments in an OSC router macro but the macro body ended"
376 )
377 };
378 let TokenTree::Punct(punct) = tree else {
379 panic!(
380 "Expected `->` after function arguments in an OSC router macro but found {tree:#?}"
381 )
382 };
383 if !matches!(punct.as_char(), '-') {
384 panic!(
385 "Expected `->` after function arguments in an OSC router macro but found {punct:#?}"
386 )
387 }
388 if !matches!(punct.spacing(), Spacing::Joint) {
389 panic!(
390 "Expected `->` after function arguments in an OSC router macro but found {punct:#?}"
391 )
392 }
393 }
394
395 {
396 let Some(tree) = iter.next() else {
397 panic!(
398 "Expected `->` after function arguments in an OSC router macro but the macro body ended"
399 )
400 };
401 let TokenTree::Punct(punct) = tree else {
402 panic!(
403 "Expected `->` after function arguments in an OSC router macro but found `-` and then {tree:#?}"
404 )
405 };
406 if !matches!(punct.as_char(), '>') {
407 panic!(
408 "Expected `->` after function arguments in an OSC router macro but found `-` and then {punct:#?}"
409 )
410 }
411 if !matches!(punct.spacing(), Spacing::Alone) {
412 panic!(
413 "Expected `->` after function arguments in an OSC router macro but found `-` and then {punct:#?}"
414 )
415 }
416 }
417
418 {
419 let Some(tree) = iter.next() else {
420 panic!(
421 "Expected `!` as the return type of the function in an OSC router macro but the macro body ended"
422 )
423 };
424 let TokenTree::Punct(punct) = tree else {
425 panic!(
426 "Expected `!` as the return type of the function in an OSC router macro but found {tree:#?}"
427 )
428 };
429 if !matches!(punct.as_char(), '!') {
430 panic!(
431 "Expected `!` as the return type of the function in an OSC router macro but found {punct:#?}"
432 )
433 }
434 if !matches!(punct.spacing(), Spacing::Alone) {
435 panic!(
436 "Expected `!` as the return type of the function in an OSC router macro but found {punct:#?}"
437 )
438 }
439 }
440
441 let body = {
442 let Some(tree) = iter.next() else {
443 panic!(
444 "Expected a function body after `-> !` in an OSC router macro but the macro body ended"
445 )
446 };
447 let TokenTree::Group(group) = tree else {
448 panic!(
449 "Expected a function body after `-> !` in an OSC router macro but found {tree:#?}"
450 )
451 };
452 if !matches!(group.delimiter(), Delimiter::Brace) {
453 panic!(
454 "Expected a function body after `-> !` in an OSC router macro but found {group:#?}"
455 )
456 }
457 group.stream()
458 };
459
460 if let Some(extra) = iter.next() {
461 panic!("Expected the OSC router macro to end after a single function but found {extra:#?}");
462 }
463
464 FnDecl {
465 attributes,
466 fn_name,
467 generics,
468 arg_stream,
469 body,
470 }
471}