diff options
| author | Mica White <botahamec@outlook.com> | 2026-05-12 13:33:22 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-05-12 13:33:22 -0400 |
| commit | 8b534ea0488701d3312894e3a0ee9531e2cd444c (patch) | |
| tree | 69205b29b22219fd7de56e70f93a99273120c19b | |
| parent | fd835b4ce895d83940cca0107fd03c7b035506ac (diff) | |
Implement Tuple for all tuple lengths
| -rw-r--r-- | Cargo.lock | 24 | ||||
| -rw-r--r-- | src/lib.rs | 152 | ||||
| -rw-r--r-- | varihappy-macros/Cargo.toml | 1 | ||||
| -rw-r--r-- | varihappy-macros/src/lib.rs | 283 |
4 files changed, 280 insertions, 180 deletions
@@ -3,6 +3,12 @@ version = 4 [[package]] +name = "mutants" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126" + +[[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -21,6 +27,12 @@ dependencies = [ ] [[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -38,6 +50,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] +name = "unsynn" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501a7adf1a4bd9951501e5c66621e972ef8874d787628b7f90e64f936ef7ec0a" +dependencies = [ + "mutants", + "proc-macro2", + "rustc-hash", +] + +[[package]] name = "varihappy" version = "0.1.0" dependencies = [ @@ -51,4 +74,5 @@ dependencies = [ "proc-macro2", "quote", "syn", + "unsynn", ] @@ -1,6 +1,5 @@ use varihappy_macros::{ - reverse_expr_type, reverse_tuple_type, tuple_expr_len_rest, tuple_expr_mut, tuple_expr_ref, - tuple_type_len, tuple_type_len_rest, + repeat, tuple_expr_reverse, tuple_new, tuple_reverse, tuple_trait_def, type_to_field_access, }; pub trait Tuple { @@ -53,17 +52,18 @@ impl<A> Tuple for (A,) { = (&'a mut A,) where Self: 'a; - type Reversed = (A,); fn as_ref(&self) -> Self::AsRef<'_> { (&self.0,) } + fn as_mut(&mut self) -> Self::AsMut<'_> { (&mut self.0,) } + fn rev(self) -> Self::Reversed { - self + (self.0,) } } @@ -74,79 +74,112 @@ impl<A> Tuple1 for (A,) { fn into_0(self) -> Self::Item0 { self.0 } - - fn split_into(self) -> (Self::Item0, <Self as Tuple1>::Rest) { + fn split_into(self) -> (Self::Item0, Self::Rest) { (self.0, ()) } fn into_rest(self) -> Self::Rest {} } -macro_rules! impl_tuple { - ($length: expr; $($letter: ident),*) => { - impl<$($letter),*> Tuple for tuple_type_len!($length) { +macro_rules! tuple_apply { + (($($letter: ident,)*), $other: ident) => { + $other!(($($letter,)*)) + }; +} + +macro_rules! tuple_rest { + ((A, $($letter: ident,)*)) => { + ($($letter,)*) + }; +} + +macro_rules! tuple_expr_apply { + ($this: ident, ($($letter: ident,)*), $other: ident) => { + $other!($this, ($($letter,)*)) + }; +} + +macro_rules! tuple_expr_ref { + ($this: ident, ($($letter: ident,)*)) => { + { + let this = $this; + ($(&type_to_field_access!($letter),)*) + } + } +} + +macro_rules! tuple_expr_mut { + ($this: ident, ($($letter: ident,)*)) => { + { + let this = $this; + ($(&mut type_to_field_access!($letter),)*) + } + } +} + +macro_rules! tuple_expr_rest { + ($this: ident, (A, $($letter: ident,)*)) => { + { + let this = $this; + ($(type_to_field_access!($letter),)*) + } + } +} + +macro_rules! tuple_expr_split { + ($this: ident, (A, $($letter: ident,)*)) => { + { + let this = $this; + (this.0, ($(type_to_field_access!($letter),)*)) + } + } +} + +macro_rules! impl_tuple_inner { + (($($letter: ident,)*)) => { + impl<$($letter),*> Tuple for ($($letter,)*) { type AsRef<'a> = ($(&'a $letter,)*) where Self: 'a; type AsMut<'a> = ($(&'a mut $letter,)*) where Self: 'a; - type Reversed = reverse_tuple_type!($length); + type Reversed = tuple_apply!(($($letter,)*), tuple_reverse); fn as_ref(&self) -> Self::AsRef<'_> { - tuple_expr_ref!($length) + tuple_expr_apply!(self, ($($letter,)*), tuple_expr_ref) } fn as_mut(&mut self) -> Self::AsMut<'_> { - tuple_expr_mut!($length) + tuple_expr_apply!(self, ($($letter,)*), tuple_expr_mut) } fn rev(self) -> Self::Reversed { - reverse_expr_type!($length) + tuple_expr_apply!(self, ($($letter,)*), tuple_expr_reverse) } } - impl<$($letter),*> Tuple1 for tuple_type_len!($length) { + impl<$($letter),*> Tuple1 for ($($letter,)*) { type Item0 = A; - type Rest = tuple_type_len_rest!($length); + type Rest = tuple_apply!(($($letter,)*), tuple_rest); fn into_0(self) -> Self::Item0 { self.0 } fn split_into(self) -> (Self::Item0, Self::Rest) { - (self.0, tuple_expr_len_rest!($length)) + tuple_expr_apply!(self, ($($letter,)*), tuple_expr_split) } fn into_rest(self) -> Self::Rest { - tuple_expr_len_rest!($length) + tuple_expr_apply!(self, ($($letter,)*), tuple_expr_rest) } } }; } -impl_tuple!(2; A, B); -impl_tuple!(3; A, B, C); -impl_tuple!(4; A, B, C, D); -impl_tuple!(5; A, B, C, D, E); -impl_tuple!(6; A, B, C, D, E, F); -impl_tuple!(7; A, B, C, D, E, F, G); -impl_tuple!(8; A, B, C, D, E, F, G, H); -impl_tuple!(9; A, B, C, D, E, F, G, H, I); -impl_tuple!(10; A, B, C, D, E, F, G, H, I, J); -impl_tuple!(11; A, B, C, D, E, F, G, H, I, J, K); -impl_tuple!(12; A, B, C, D, E, F, G, H, I, J, K, L); -impl_tuple!(13; A, B, C, D, E, F, G, H, I, J, K, L, M); -impl_tuple!(14; A, B, C, D, E, F, G, H, I, J, K, L, M, N); -impl_tuple!(15; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); -impl_tuple!(16; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); -impl_tuple!(17; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); -impl_tuple!(18; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); -impl_tuple!(19; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); -impl_tuple!(20; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T); -impl_tuple!(21; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U); -impl_tuple!(22; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V); -impl_tuple!(23; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W); -impl_tuple!(24; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X); -impl_tuple!(25; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y); -impl_tuple!(26; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); +macro_rules! impl_tuple { + ($number: literal) => { + tuple_new! { $number, impl_tuple_inner } + }; +} -macro_rules! tuple_trait { +macro_rules! tuple_trait_inner { (trait $trait_name: ident for $trait_minus1: ident { type $item_name: ident; fn $item_getter: ident; @@ -174,28 +207,11 @@ macro_rules! tuple_trait { }; } -tuple_trait!(trait Tuple2 for Tuple1 { type Item1; fn into_1; }); -tuple_trait!(trait Tuple3 for Tuple2 { type Item2; fn into_2; }); -tuple_trait!(trait Tuple4 for Tuple3 { type Item3; fn into_3; }); -tuple_trait!(trait Tuple5 for Tuple4 { type Item4; fn into_4; }); -tuple_trait!(trait Tuple6 for Tuple5 { type Item5; fn into_5; }); -tuple_trait!(trait Tuple7 for Tuple6 { type Item6; fn into_6; }); -tuple_trait!(trait Tuple8 for Tuple7 { type Item7; fn into_7; }); -tuple_trait!(trait Tuple9 for Tuple8 { type Item8; fn into_8; }); -tuple_trait!(trait Tuple10 for Tuple9 { type Item9; fn into_9; }); -tuple_trait!(trait Tuple11 for Tuple10 { type Item10; fn into_10; }); -tuple_trait!(trait Tuple12 for Tuple11 { type Item11; fn into_11; }); -tuple_trait!(trait Tuple13 for Tuple12 { type Item12; fn into_12; }); -tuple_trait!(trait Tuple14 for Tuple13 { type Item13; fn into_13; }); -tuple_trait!(trait Tuple15 for Tuple14 { type Item14; fn into_14; }); -tuple_trait!(trait Tuple16 for Tuple15 { type Item15; fn into_15; }); -tuple_trait!(trait Tuple17 for Tuple16 { type Item16; fn into_16; }); -tuple_trait!(trait Tuple18 for Tuple17 { type Item17; fn into_17; }); -tuple_trait!(trait Tuple19 for Tuple18 { type Item18; fn into_18; }); -tuple_trait!(trait Tuple20 for Tuple19 { type Item19; fn into_19; }); -tuple_trait!(trait Tuple21 for Tuple20 { type Item20; fn into_20; }); -tuple_trait!(trait Tuple22 for Tuple21 { type Item21; fn into_21; }); -tuple_trait!(trait Tuple23 for Tuple22 { type Item22; fn into_22; }); -tuple_trait!(trait Tuple24 for Tuple23 { type Item23; fn into_23; }); -tuple_trait!(trait Tuple25 for Tuple24 { type Item24; fn into_24; }); -tuple_trait!(trait Tuple26 for Tuple25 { type Item25; fn into_25; }); +macro_rules! tuple_trait { + ($number: literal) => { + tuple_trait_def! { $number, tuple_trait_inner } + }; +} + +repeat!(64, impl_tuple); +repeat!(64, tuple_trait); diff --git a/varihappy-macros/Cargo.toml b/varihappy-macros/Cargo.toml index 3999ad5..0f98af9 100644 --- a/varihappy-macros/Cargo.toml +++ b/varihappy-macros/Cargo.toml @@ -10,3 +10,4 @@ proc-macro = true proc-macro2 = "1" syn = "2" quote = "1" +unsynn = "0.3" diff --git a/varihappy-macros/src/lib.rs b/varihappy-macros/src/lib.rs index 522d333..3931899 100644 --- a/varihappy-macros/src/lib.rs +++ b/varihappy-macros/src/lib.rs @@ -2,139 +2,198 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::{Ident, Literal, Span}; -use quote::quote; -use syn::{Field, LitInt}; +use quote::{format_ident, quote}; +use syn::LitInt; +use unsynn::{ + Comma, CommaDelimitedVec, Cons, Either, GroupContaining, IParse, LiteralInteger, TokenIter, +}; -#[proc_macro] -pub fn concat_idents(input: TokenStream) -> TokenStream { +fn number_to_type(mut number: u128) -> String { let mut buffer = String::new(); - for token in input { - buffer.push_str(&token.to_string()); + while number > 25 { + buffer.push({ + let digit = number % 26; + number /= 26; + match digit { + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + _ => panic!("Expected a number less than 26"), + } + }); } - let mut stream = proc_macro2::TokenStream::new(); - stream.extend([Ident::new(&buffer, Span::call_site())]); - stream.into() -} - -#[proc_macro] -pub fn number_to_generic(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let identifier = Ident::new( - match number { - 0 => "A", - 1 => "B", - 2 => "C", - 3 => "D", - 4 => "E", - 5 => "F", - 6 => "G", - 7 => "H", - 8 => "I", - 9 => "J", - 10 => "K", - 11 => "L", - 12 => "M", - 13 => "N", - 14 => "O", - 15 => "P", - 16 => "Q", - 17 => "R", - 18 => "S", - 19 => "T", - 20 => "U", - 21 => "V", - 22 => "W", - 23 => "X", - 24 => "Y", - 25 => "Z", - _ => panic!("Expected a number less than 26"), - }, - Span::call_site(), - ); - - quote! { #identifier }.into() -} - -#[proc_macro] -pub fn tuple_type_len(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = 0..number; - quote! { (#(::varihappy_macros::number_to_generic!(#num),)*) }.into() -} - -#[proc_macro] -pub fn tuple_type_ref(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = 0..number; - quote! { (#(&'a ::varihappy_macros::number_to_generic!(#num),)*) }.into() + buffer.push(match number { + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + _ => panic!("Expected a number less than 26"), + }); + + buffer.chars().rev().collect() } #[proc_macro] -pub fn tuple_type_mut(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = 0..number; - quote! { (#(&'a mut ::varihappy_macros::number_to_generic!(#num),)*) }.into() +pub fn tuple_new(input: TokenStream) -> TokenStream { + let input: proc_macro2::TokenStream = input.into(); + let mut token_iter = TokenIter::new(input); + let ast: Cons<Either<LiteralInteger, GroupContaining<LiteralInteger>>, Comma, unsynn::Ident> = + token_iter.parse().unwrap(); + + let number = ast.first.fold2(|x| x, |x| x.content); + let number_value = number.value(); + let number_token = number.into_inner(); + let types = + (0..number_value).map(|number| Ident::new(&number_to_type(number), number_token.span())); + let ident = Ident::new(&ast.third.to_string(), ast.third.span()); + quote! { #ident! { (#(#types,)*) } }.into() } #[proc_macro] -pub fn reverse_tuple_type(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = (0..number).rev(); - quote! { (#(::varihappy_macros::number_to_generic!(#num),)*) }.into() +pub fn tuple_reverse(input: TokenStream) -> TokenStream { + let tuple = syn::parse_macro_input!(input as syn::TypeTuple); + let reversed_tuple = tuple.elems.iter().rev(); + quote! { (#(#reversed_tuple,)*) }.into() } #[proc_macro] -pub fn tuple_type_len_rest(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = 1..number; - quote! { (#(::varihappy_macros::number_to_generic!(#num),)*) }.into() +pub fn tuple_expr_reverse(input: TokenStream) -> TokenStream { + let input: proc_macro2::TokenStream = input.into(); + let mut token_iter = TokenIter::new(input); + let ast: Cons< + unsynn::Ident, + Comma, + unsynn::ParenthesisGroupContaining<CommaDelimitedVec<unsynn::Ident>>, + > = token_iter.parse().unwrap(); + + let self_id = ast.first; + let reversed_tuple = ast.third.content.into_iter().rev().map(|x| x.value); + quote! {{ + let this = #self_id; + (#(::varihappy_macros::type_to_field_access!(#reversed_tuple),)*) + }} + .into() } #[proc_macro] -pub fn tuple_expr_ref(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = (0..number).map(Literal::u128_unsuffixed); - quote! { (#(&self.#num,)*) }.into() -} - -#[proc_macro] -pub fn tuple_expr_mut(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); +pub fn type_to_field_access(input: TokenStream) -> TokenStream { + let input: proc_macro2::TokenStream = input.into(); + let mut token_iter = TokenIter::new(input); + let type_name: unsynn::Ident = token_iter.parse().unwrap(); + let mut type_name = type_name.to_string(); + type_name.make_ascii_lowercase(); + + let mut number = 0; + for char in type_name.chars() { + number *= 26; + number += match char { + 'a' => 0, + 'b' => 1, + 'c' => 2, + 'd' => 3, + 'e' => 4, + 'f' => 5, + 'g' => 6, + 'h' => 7, + 'i' => 8, + 'j' => 9, + 'k' => 10, + 'l' => 11, + 'm' => 12, + 'n' => 13, + 'o' => 14, + 'p' => 15, + 'q' => 16, + 'r' => 17, + 's' => 18, + 't' => 19, + 'u' => 20, + 'v' => 21, + 'w' => 22, + 'x' => 23, + 'y' => 24, + 'z' => 25, + _ => panic!("invalid type name"), + }; + } - let num = (0..number).map(Literal::u128_unsuffixed); - quote! { (#(&mut self.#num,)*) }.into() + let number = Literal::u128_unsuffixed(number); + quote! { this.#number }.into() } #[proc_macro] -pub fn reverse_expr_type(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = (0..number).rev().map(Literal::u128_unsuffixed); - quote! { (#(self.#num,)*) }.into() +pub fn repeat(input: TokenStream) -> TokenStream { + let input: proc_macro2::TokenStream = input.into(); + let mut token_iter = TokenIter::new(input); + let ast: Cons<LiteralInteger, Comma, unsynn::Ident> = token_iter.parse().unwrap(); + + let numbers = (2..=ast.first.value()).map(Literal::u128_unsuffixed); + let macro_name = ast.third; + quote! { #(#macro_name!(#numbers);)* }.into() } #[proc_macro] -pub fn tuple_expr_len_rest(input: TokenStream) -> TokenStream { - let number = syn::parse_macro_input!(input as LitInt); - let number: u128 = number.base10_parse().expect("a number 0..=u128::MAX"); - - let num = (1..number).map(Literal::u128_unsuffixed); - quote! { (#(self.#num,)*) }.into() +pub fn tuple_trait_def(input: TokenStream) -> TokenStream { + let input: proc_macro2::TokenStream = input.into(); + let mut token_iter = TokenIter::new(input); + let ast: Cons<Either<LiteralInteger, GroupContaining<LiteralInteger>>, Comma, unsynn::Ident> = + token_iter.parse().unwrap(); + + let number = ast.first.fold2(|x| x, |x| x.content); + let number = number.value(); + let minus1 = number - 1; + + let trait_name = format_ident!("Tuple{number}"); + let other_trait = format_ident!("Tuple{minus1}"); + let item_type = format_ident!("Item{minus1}"); + let item_fn = format_ident!("into_{minus1}"); + let ident = ast.third; + quote! { #ident! { trait #trait_name for #other_trait { type #item_type; fn #item_fn; }} } + .into() } |
