diff options
Diffstat (limited to 'varihappy-macros/src/lib.rs')
| -rw-r--r-- | varihappy-macros/src/lib.rs | 283 |
1 files changed, 171 insertions, 112 deletions
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() } |
