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<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 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<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 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<RangeLiteral, Comma, unsynn::Ident> = 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<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()
}
#[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()
}
|