1use core::ops::RangeInclusive;
10use proc_macro2::{Span, TokenStream};
11use quote::{quote, ToTokens};
12use syn::{punctuated::Punctuated, spanned::Spanned};
13
14const START_CHAR: u8 = b'A';
15const END_CHAR: u8 = b'A' + 7; const TO_LOWERCASE: u8 = b'a' - b'A';
17
18#[proc_macro]
19pub fn implement_flatten(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
20 flatten_fallible(ts.into())
21 .unwrap_or_else(syn::Error::into_compile_error)
22 .into()
23}
24
25#[proc_macro]
26pub fn implement(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
27 fallible(ts.into())
28 .unwrap_or_else(syn::Error::into_compile_error)
29 .into()
30}
31
32#[inline]
33fn flatten_fallible(ts: TokenStream) -> syn::Result<TokenStream> {
34 if !ts.is_empty() {
35 return Err(syn::Error::new(ts.span(), "This macro takes no arguments"));
36 }
37 let mut out = TokenStream::new();
38 for endc in START_CHAR..=END_CHAR {
39 let chars = START_CHAR..=endc;
40 let mut a_good_start: syn::ItemImpl = syn::parse2(quote! {
41 impl<TODO> crate::Flatten for TODO {}
42 })?;
43 a_good_start.generics.params = chars
44 .clone()
45 .map(|ref c| {
46 syn::GenericParam::Type(syn::TypeParam {
47 attrs: vec![],
48 ident: cr2i(c),
49 colon_token: None,
50 bounds: Punctuated::new(),
51 eq_token: None,
52 default: None,
53 })
54 })
55 .collect();
56 a_good_start.self_ty = Box::new(huge_nested_tuple(chars.clone())?);
57 a_good_start.items = vec![
58 type_flattened_equals(chars.clone())?,
59 fn_flatten(chars.clone())?,
60 ];
61 a_good_start.to_tokens(&mut out);
62 }
63 Ok(out)
64}
65
66#[inline]
67fn fallible(ts: TokenStream) -> syn::Result<TokenStream> {
68 if !ts.is_empty() {
69 return Err(syn::Error::new(ts.span(), "This macro takes no arguments"));
70 }
71 let mut out = TokenStream::new();
72 for endc in START_CHAR..=END_CHAR {
73 let chars = START_CHAR..=endc;
74 let mut a_good_start: syn::ItemImpl = syn::parse2(quote! {
75 impl<TODO> BreadthFirstZip<TODO> for TODO {}
76 })?;
77 let span = a_good_start.span();
78 a_good_start.generics.params = impl_generics(chars.clone())?;
79 a_good_start
80 .trait_
81 .as_mut()
82 .ok_or(syn::Error::new(span, "Internal error"))?
83 .1
84 .segments
85 .first_mut()
86 .ok_or(syn::Error::new(span, "Internal error"))?
87 .arguments = {
88 syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
89 colon2_token: None,
90 lt_token: syn::token::Lt {
91 spans: [Span::call_site()],
92 },
93 args: [syn::GenericArgument::Lifetime(syn::Lifetime {
94 apostrophe: Span::call_site(),
95 ident: syn::Ident::new("item", Span::call_site()),
96 })]
97 .into_iter()
98 .collect(),
99 gt_token: syn::token::Gt {
100 spans: [Span::call_site()],
101 },
102 })
103 };
104 a_good_start.self_ty = Box::new(flat_tuple_type(chars.clone())?);
105 a_good_start.generics.where_clause = Some(where_clause(chars.clone())?);
106 a_good_start.items = vec![
107 type_nested_equals(chars.clone())?,
108 fn_breadth_first()?,
109 fn_unflatten(chars)?,
110 ];
111 a_good_start.to_tokens(&mut out);
112 }
113 Ok(out)
114}
115
116#[inline]
117fn cr2s(c: &u8) -> &str {
118 core::str::from_utf8(core::slice::from_ref(c)).unwrap()
119}
120#[inline]
121fn cr2i(c: &u8) -> syn::Ident {
122 syn::Ident::new(cr2s(c), Span::call_site())
123}
124
125#[inline]
126fn impl_generics(
127 chars: RangeInclusive<u8>,
128) -> syn::Result<Punctuated<syn::GenericParam, syn::token::Comma>> {
129 Ok([syn::GenericParam::Lifetime(syn::LifetimeParam {
130 attrs: vec![],
131 lifetime: syn::Lifetime {
132 apostrophe: Span::call_site(),
133 ident: syn::Ident::new("item", Span::call_site()),
134 },
135 colon_token: None,
136 bounds: Punctuated::new(),
137 })]
138 .into_iter()
139 .chain(chars.map(|ref c| {
140 syn::GenericParam::Type(syn::TypeParam {
141 attrs: vec![],
142 ident: cr2i(c),
143 colon_token: Some(syn::token::Colon {
144 spans: [Span::call_site()],
145 }),
146 bounds: {
147 let iterator = syn::TypeParamBound::Trait(syn::TraitBound {
148 paren_token: None,
149 modifier: syn::TraitBoundModifier::None,
150 lifetimes: None,
151 path: syn::Path {
152 leading_colon: Some(syn::token::PathSep {
153 spans: [Span::call_site(), Span::call_site()],
154 }),
155 segments: [
156 syn::PathSegment {
157 ident: syn::Ident::new("core", Span::call_site()),
158 arguments: syn::PathArguments::None,
159 },
160 syn::PathSegment {
161 ident: syn::Ident::new("iter", Span::call_site()),
162 arguments: syn::PathArguments::None,
163 },
164 syn::PathSegment {
165 ident: syn::Ident::new("Iterator", Span::call_site()),
166 arguments: syn::PathArguments::None,
167 },
168 ]
169 .into_iter()
170 .collect(),
171 },
172 });
173 [iterator].into_iter().collect()
174 },
175 eq_token: None,
176 default: None,
177 })
178 }))
179 .collect())
180}
181
182#[inline]
183fn where_clause(chars: RangeInclusive<u8>) -> syn::Result<syn::WhereClause> {
184 Ok(syn::WhereClause {
185 where_token: syn::parse2(quote!(where))?,
186 predicates: chars
187 .map(|ref c| {
188 syn::WherePredicate::Type(syn::PredicateType {
189 lifetimes: None,
190 bounded_ty: syn::Type::Path(syn::TypePath {
191 qself: None,
192 path: syn::Path {
193 leading_colon: None,
194 segments: [
195 syn::PathSegment {
196 ident: cr2i(c),
197 arguments: syn::PathArguments::None,
198 },
199 syn::PathSegment {
200 ident: syn::Ident::new("Item", Span::call_site()),
201 arguments: syn::PathArguments::None,
202 },
203 ]
204 .into_iter()
205 .collect(),
206 },
207 }),
208 colon_token: syn::token::Colon {
209 spans: [Span::call_site()],
210 },
211 bounds: [syn::TypeParamBound::Lifetime(syn::Lifetime {
212 apostrophe: Span::call_site(),
213 ident: syn::Ident::new("item", Span::call_site()),
214 })]
215 .into_iter()
216 .collect(),
217 })
218 })
219 .collect(),
220 })
221}
222
223#[inline]
224fn flat_tuple_type(chars: RangeInclusive<u8>) -> syn::Result<syn::Type> {
225 Ok(syn::Type::Tuple(syn::TypeTuple {
226 paren_token: paren_token(),
227 elems: chars
228 .map(|ref c| {
229 syn::Type::Path(syn::TypePath {
230 qself: None,
231 path: syn::Path {
232 leading_colon: None,
233 segments: [syn::PathSegment {
234 ident: cr2i(c),
235 arguments: syn::PathArguments::None,
236 }]
237 .into_iter()
238 .collect(),
239 },
240 })
241 })
242 .collect(),
243 }))
244}
245
246#[inline]
247fn paren_token() -> syn::token::Paren {
248 syn::token::Paren {
249 span: proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, TokenStream::new())
250 .delim_span(),
251 }
252}
253
254#[inline]
255fn type_nested_equals(chars: RangeInclusive<u8>) -> syn::Result<syn::ImplItem> {
256 Ok(syn::ImplItem::Type(syn::ImplItemType {
257 attrs: vec![],
258 vis: syn::Visibility::Inherited,
259 defaultness: None,
260 type_token: syn::parse2(quote!(type))?,
261 ident: syn::Ident::new("Nested", Span::call_site()),
262 generics: syn::Generics {
263 lt_token: None,
264 params: Punctuated::new(),
265 gt_token: None,
266 where_clause: None,
267 },
268 eq_token: syn::parse2(quote!(=))?,
269 ty: huge_nested_type(chars)?,
270 semi_token: syn::parse2(quote!(;))?,
271 }))
272}
273
274#[inline]
275fn huge_nested_type(chars: RangeInclusive<u8>) -> syn::Result<syn::Type> {
276 Ok(
277 chars.rfold(syn::parse2(quote!(crate::BaseCase))?, |acc, ref c| {
278 syn::Type::Path(syn::TypePath {
279 qself: None,
280 path: syn::Path {
281 leading_colon: None,
282 segments: [syn::PathSegment {
283 ident: syn::Ident::new("BreadthFirstZipped", Span::call_site()),
284 arguments: syn::PathArguments::AngleBracketed(
285 syn::AngleBracketedGenericArguments {
286 colon2_token: None,
287 lt_token: syn::token::Lt {
288 spans: [Span::call_site()],
289 },
290 args: [
291 syn::GenericArgument::Lifetime(syn::Lifetime {
292 apostrophe: Span::call_site(),
293 ident: syn::Ident::new("item", Span::call_site()),
294 }),
295 syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
296 qself: None,
297 path: syn::Path {
298 leading_colon: None,
299 segments: [syn::PathSegment {
300 ident: cr2i(c),
301 arguments: syn::PathArguments::None,
302 }]
303 .into_iter()
304 .collect(),
305 },
306 })),
307 syn::GenericArgument::Type(acc),
308 ]
309 .into_iter()
310 .collect(),
311 gt_token: syn::token::Gt {
312 spans: [Span::call_site()],
313 },
314 },
315 ),
316 }]
317 .into_iter()
318 .collect(),
319 },
320 })
321 }),
322 )
323}
324
325#[inline]
326fn huge_nested_tuple(chars: RangeInclusive<u8>) -> syn::Result<syn::Type> {
327 Ok(chars.rfold(syn::parse2(quote!(()))?, |acc, ref c| {
328 syn::Type::Tuple(syn::TypeTuple {
329 paren_token: paren_token(),
330 elems: [
331 syn::Type::Path(syn::TypePath {
332 qself: None,
333 path: syn::Path {
334 leading_colon: None,
335 segments: [syn::PathSegment {
336 ident: cr2i(c),
337 arguments: syn::PathArguments::None,
338 }]
339 .into_iter()
340 .collect(),
341 },
342 }),
343 acc,
344 ]
345 .into_iter()
346 .collect(),
347 })
348 }))
349}
350
351#[inline]
352fn fn_breadth_first() -> syn::Result<syn::ImplItem> {
353 syn::parse2(quote! {
354 #[inline(always)]
355 #[must_use]
356 fn breadth_first(self) -> BreadthFirstManager<'item, Self::Nested> {
357 BreadthFirstManager::new(self.unflatten())
358 }
359 })
360}
361
362#[inline]
363fn type_flattened_equals(chars: RangeInclusive<u8>) -> syn::Result<syn::ImplItem> {
364 Ok(syn::ImplItem::Type(syn::ImplItemType {
365 attrs: vec![],
366 vis: syn::Visibility::Inherited,
367 defaultness: None,
368 type_token: syn::parse2(quote!(type))?,
369 ident: syn::Ident::new("Flattened", Span::call_site()),
370 generics: syn::Generics {
371 lt_token: None,
372 params: Punctuated::new(),
373 gt_token: None,
374 where_clause: None,
375 },
376 eq_token: syn::parse2(quote!(=))?,
377 ty: flat_tuple_type(chars)?,
378 semi_token: syn::parse2(quote!(;))?,
379 }))
380}
381
382#[inline]
383fn fn_flatten(mut chars: RangeInclusive<u8>) -> syn::Result<syn::ImplItem> {
384 let mut a_good_start: syn::ImplItemFn = syn::parse2(quote! {
385 #[inline(always)]
386 #[must_use]
387 fn flatten(self) -> Self::Flattened {}
388 })?;
389 chars.next(); a_good_start.block.stmts = vec![
391 syn::Stmt::Local(syn::Local {
392 attrs: vec![],
393 let_token: syn::parse2(quote!(let))?,
394 pat: syn::Pat::Tuple(syn::PatTuple {
395 attrs: vec![],
396 paren_token: paren_token(),
397 elems: chars
398 .clone()
399 .map(|ref c| {
400 syn::Pat::Ident(syn::PatIdent {
401 attrs: vec![],
402 by_ref: None,
403 mutability: None,
404 ident: cr2i(&(c + TO_LOWERCASE)),
405 subpat: None,
406 })
407 })
408 .collect(),
409 }),
410 init: Some(syn::LocalInit {
411 eq_token: syn::parse2(quote!(=))?,
412 expr: Box::new(syn::parse2(if chars.len() != 1 {
413 quote!(self.1.flatten())
414 } else {
415 quote!(self.1.flatten().0)
417 })?),
418 diverge: None,
419 }),
420 semi_token: syn::parse2(quote!(;))?,
421 }),
422 syn::Stmt::Expr(
423 syn::Expr::Tuple(syn::ExprTuple {
424 attrs: vec![],
425 paren_token: paren_token(),
426 elems: [syn::parse2(quote!(self.0))?]
427 .into_iter()
428 .chain(chars.map(|c| {
429 syn::Expr::Path(syn::ExprPath {
430 attrs: vec![],
431 qself: None,
432 path: syn::Path {
433 leading_colon: None,
434 segments: [syn::PathSegment {
435 ident: cr2i(&(c + TO_LOWERCASE)),
436 arguments: syn::PathArguments::None,
437 }]
438 .into_iter()
439 .collect(),
440 },
441 })
442 }))
443 .collect(),
444 }),
445 None,
446 ),
447 ];
448 Ok(syn::ImplItem::Fn(a_good_start))
449}
450
451#[inline]
452fn fn_unflatten(chars: RangeInclusive<u8>) -> syn::Result<syn::ImplItem> {
453 let mut a_good_start: syn::ImplItemFn = syn::parse2(quote! {
454 #[inline(always)]
455 #[must_use]
456 fn unflatten(self) -> Self::Nested {}
457 })?;
458 a_good_start.block.stmts = vec![
459 syn::Stmt::Local(syn::Local {
460 attrs: vec![],
461 let_token: syn::parse2(quote!(let))?,
462 pat: syn::Pat::Tuple(syn::PatTuple {
463 attrs: vec![],
464 paren_token: paren_token(),
465 elems: chars
466 .clone()
467 .map(|c| {
468 syn::Pat::Ident(syn::PatIdent {
469 attrs: vec![],
470 by_ref: None,
471 mutability: None,
472 ident: cr2i(&(c + TO_LOWERCASE)),
473 subpat: None,
474 })
475 })
476 .collect(),
477 }),
478 init: Some(syn::LocalInit {
479 eq_token: syn::parse2(quote!(=))?,
480 expr: Box::new(syn::parse2(if chars.len() != 1 {
481 quote!(self)
482 } else {
483 quote!(self.0)
485 })?),
486 diverge: None,
487 }),
488 semi_token: syn::parse2(quote!(;))?,
489 }),
490 syn::Stmt::Expr(
491 chars.rfold(
492 syn::parse2(quote!(BaseCase(::core::cell::Cell::new(true))))?,
493 |acc, ref c| {
494 syn::Expr::Call(syn::ExprCall {
495 attrs: vec![],
496 func: Box::new(syn::Expr::Path(syn::ExprPath {
497 attrs: vec![],
498 qself: None,
499 path: syn::Path {
500 leading_colon: None,
501 segments: [
502 syn::PathSegment {
503 ident: syn::Ident::new(
504 "BreadthFirstZipped",
505 Span::call_site(),
506 ),
507 arguments: syn::PathArguments::None,
508 },
509 syn::PathSegment {
510 ident: syn::Ident::new("new", Span::call_site()),
511 arguments: syn::PathArguments::None,
512 },
513 ]
514 .into_iter()
515 .collect(),
516 },
517 })),
518 paren_token: paren_token(),
519 args: [
520 syn::Expr::Path(syn::ExprPath {
521 attrs: vec![],
522 qself: None,
523 path: syn::Path {
524 leading_colon: None,
525 segments: [syn::PathSegment {
526 ident: cr2i(&(c + TO_LOWERCASE)),
527 arguments: syn::PathArguments::None,
528 }]
529 .into_iter()
530 .collect(),
531 },
532 }),
533 acc,
534 ]
535 .into_iter()
536 .collect(),
537 })
538 },
539 ),
540 None,
541 ),
542 ];
543 Ok(syn::ImplItem::Fn(a_good_start))
544}