extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::{Ident, Literal}; use quote::{format_ident, quote}; use unsynn::{ Comma, CommaDelimitedVec, Cons, Either, GroupContaining, IParse, LiteralInteger, TokenIter, unsynn, }; fn number_to_type(mut number: u128) -> String { let mut buffer = String::new(); 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"), } }); } 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_new(input: TokenStream) -> TokenStream { let input: proc_macro2::TokenStream = input.into(); let mut token_iter = TokenIter::new(input); let ast: Cons>, 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 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_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>, > = 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 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 number = Literal::u128_unsuffixed(number); quote! { this.#number }.into() } #[proc_macro] pub fn repeat(input: TokenStream) -> TokenStream { unsynn! { struct RangeLiteral { start: LiteralInteger, dotdot: unsynn::DotDot, end: LiteralInteger, } } let input: proc_macro2::TokenStream = input.into(); let mut token_iter = TokenIter::new(input); let ast: Cons = token_iter.parse().unwrap(); let numbers = (ast.first.start.value()..ast.first.end.value()).map(Literal::u128_unsuffixed); let macro_name = ast.third; quote! { #(#macro_name!(#numbers);)* }.into() } #[proc_macro] 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>, 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() } #[proc_macro] pub fn tuple_len(input: TokenStream) -> TokenStream { let tuple = syn::parse_macro_input!(input as syn::TypeTuple); let len = Literal::usize_unsuffixed(tuple.elems.len()); quote! { #len }.into() }