use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::Ident;
use std::cell::Cell;
#[derive(Clone, Debug)]
pub(crate) struct TyArray(pub(crate) Vec<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyTuple(pub(crate) Vec<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyGroup(pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TySlice(pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyFixedArray(pub(crate) Box<Ty>, pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyPrimitive(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyGeneric(pub(crate) Box<Ty>, pub(crate) TyTypeParam);
#[derive(Clone, Debug)]
pub(crate) struct TyTrait(pub(crate) TokenStream, pub(crate) TyTypeParam);
#[derive(Clone, Debug)]
pub(crate) struct TyTypeParam {
pub(crate) params: Vec<(TokenStream, Option<Ty>)>,
pub(crate) bindings: Vec<(TokenStream, TokenStream)>,
}
impl TyTypeParam {
pub(crate) fn single(arg: &Ty) -> Self {
TyTypeParam { params: vec![(arg.to_token_stream(), None)], bindings: vec![] }
}
pub(crate) fn push_arg(&mut self, arg: &Ty) {
self.params.push((arg.to_token_stream(), None));
}
pub(crate) fn extend(&mut self, other: TyTypeParam) {
self.params.extend(other.params);
self.bindings.extend(other.bindings);
}
}
#[derive(Clone, Debug)]
pub(crate) struct TyCodeBlock(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithCode(pub(crate) Box<Ty>, pub(crate) TokenStream);
#[derive(Copy, Clone, Debug)]
pub(crate) enum TyPrefix {
Ref,
RefMut,
PtrConst,
PtrMut,
SelfType,
Fn,
Unsafe,
}
#[derive(Clone, Debug)]
pub(crate) struct TyModified(pub(crate) TyPrefix, pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyFn(pub(crate) Vec<Ty>, pub(crate) Option<Box<Ty>>);
#[derive(Clone, Debug)]
pub(crate) struct TyUnsafe(pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyAttr(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithAttr(pub(crate) TyAttr, pub(crate) Box<Ty>);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TyNum(pub(crate) u8);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TyRange { pub(crate) start: u8, pub(crate) end: u8, pub(crate) inclusive: bool }
#[derive(Clone, Debug)]
pub(crate) struct TyWithTrait(pub(crate) TyTrait, pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyWithType(pub(crate) TyTypeParam, pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyError(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) enum Ty {
Array(TyArray),
Tuple(TyTuple),
Group(TyGroup),
Slice(TySlice),
FixedArray(TyFixedArray),
Primitive(TyPrimitive),
Generic(TyGeneric),
Trait(TyTrait),
TypeParam(TyTypeParam),
CodeBlock(TyCodeBlock),
Prefix(TyPrefix),
Modified(TyModified),
Fn(TyFn),
Unsafe(TyUnsafe),
Attr(TyAttr),
WithAttr(TyWithAttr),
WithTrait(TyWithTrait),
WithType(TyWithType),
WithCode(TyWithCode),
Num(TyNum),
Range(TyRange),
Error(TyError),
}
fn params_to_tokens(base: &TokenStream, tp: &TyTypeParam) -> TokenStream {
let mut all = tp.params.iter()
.map(|(name, _)| name.clone())
.collect::<Vec<_>>();
for (name, value) in &tp.bindings {
all.push(quote!(#name = #value));
}
quote!(#base < #(#all),* >)
}
fn params_to_tokens_no_base(tp: &TyTypeParam) -> TokenStream {
let mut all = vec![];
for (name, bound) in &tp.params {
match bound {
Some(b) => {
let b_tokens = b.to_token_stream();
all.push(quote!(#name: #b_tokens));
}
None => all.push(name.clone()),
}
}
for (name, value) in &tp.bindings {
all.push(quote!(#name = #value));
}
quote!(<#(#all),*>)
}
impl Ty {
pub(crate) fn expand(self) -> Result<Vec<Ty>, Ty> {
match self {
Ty::Array(ty) => Ok(ty.0),
Ty::WithCode(wc) => match (*wc.0).expand() {
Ok(expanded) => Ok(expanded
.into_iter()
.map(|inner| Ty::WithCode(TyWithCode(Box::new(inner), wc.1.clone())))
.collect()),
Err(leaf) => Err(Ty::WithCode(TyWithCode(Box::new(leaf), wc.1))),
},
Ty::Group(g) => (*g.0).expand(),
other => Err(other),
}
}
}
impl ToTokens for Ty {
fn to_tokens(&self, out: &mut TokenStream) {
out.extend(match self {
Ty::Primitive(p) => p.0.clone(),
Ty::Generic(g) => params_to_tokens(&g.0.to_token_stream(), &g.1),
Ty::Trait(t) => params_to_tokens(&t.0, &t.1),
Ty::Array(a) => {
let elems = a.0.iter().map(|e| e.to_token_stream()).collect::<Vec<_>>();
quote!([#(#elems),*])
}
Ty::Tuple(t) => {
let elems = t.0.iter().map(|e| e.to_token_stream()).collect::<Vec<_>>();
quote!((#(#elems,)*))
}
Ty::Group(g) => {
let inner = g.0.to_token_stream();
quote!((#inner))
}
Ty::Slice(s) => {
let inner = s.0.to_token_stream();
quote!([#inner])
}
Ty::FixedArray(f) => {
let inner = f.0.to_token_stream();
let size = &f.1;
quote!([#inner; #size])
}
Ty::Modified(m) => {
let prefix_tokens = match m.0 {
TyPrefix::Ref => quote!(&),
TyPrefix::RefMut => quote!(&mut),
TyPrefix::PtrConst => quote!(*const),
TyPrefix::PtrMut => quote!(*mut),
_ => quote!(compile_error!("batch-impl: 内部错误:TyModified 含有非引用前缀")),
};
let inner = m.1.to_token_stream();
quote!(#prefix_tokens #inner)
}
Ty::Fn(f) => {
let params = f.0.iter().map(|p| p.to_token_stream()).collect::<Vec<_>>();
match &f.1 {
Some(ret) => {
let ret_tokens = ret.to_token_stream();
quote!(fn(#(#params),*) -> #ret_tokens)
}
None => quote!(fn(#(#params),*)),
}
}
Ty::TypeParam(tp) => params_to_tokens_no_base(tp),
Ty::Unsafe(u) => {
let inner = u.0.to_token_stream();
quote!(unsafe #inner)
}
Ty::Attr(a) => {
let stream = &a.0;
quote!(#[#stream])
}
Ty::WithAttr(w) => {
let stream = &w.0 .0;
let inner = w.1.to_token_stream();
quote!(#[#stream] #inner)
}
Ty::Num(n) => {
let n = n.0;
quote!(#n)
}
Ty::Range(r) => {
let start = r.start;
let end = r.end;
if r.inclusive {
quote!(#start ..= #end)
} else {
quote!(#start .. #end)
}
}
Ty::CodeBlock(b) => {
let stream = &b.0;
quote!({#stream})
}
Ty::WithTrait(wt) => {
let trait_tokens = params_to_tokens(&wt.0.0, &wt.0.1);
let inner = wt.1.to_token_stream();
quote!(#trait_tokens #inner)
}
Ty::WithType(wt) => {
let tp_tokens = params_to_tokens_no_base(&wt.0);
let inner = wt.1.to_token_stream();
quote!(#tp_tokens #inner)
}
Ty::WithCode(wc) => {
let inner = wc.0.to_token_stream();
let stream = &wc.1;
quote!(#inner {#stream})
}
Ty::Prefix(p) => match p {
TyPrefix::Ref => quote![&],
TyPrefix::RefMut => quote![&mut],
TyPrefix::PtrConst => quote![*const],
TyPrefix::PtrMut => quote![*mut],
TyPrefix::SelfType => quote![self],
TyPrefix::Fn => quote![fn],
TyPrefix::Unsafe => quote![unsafe],
},
Ty::Error(e) => e.0.clone(),
})
}
}
macro_rules! impl_from_for_ty {
($($struct:ident => $variant:ident),* $(,)?) => {
$(
impl From<$struct> for Ty {
fn from(value: $struct) -> Self {
Ty::$variant(value)
}
}
impl From<$struct> for Box<Ty> {
fn from(value: $struct) -> Self {
Box::new(Ty::$variant(value))
}
}
)*
};
}
impl_from_for_ty! {
TyArray => Array,
TyTuple => Tuple,
TyGroup => Group,
TySlice => Slice,
TyFixedArray => FixedArray,
TyPrimitive => Primitive,
TyGeneric => Generic,
TyTrait => Trait,
TyTypeParam => TypeParam,
TyCodeBlock => CodeBlock,
TyPrefix => Prefix,
TyModified => Modified,
TyFn => Fn,
TyUnsafe => Unsafe,
TyAttr => Attr,
TyWithAttr => WithAttr,
TyWithTrait => WithTrait,
TyWithType => WithType,
TyWithCode => WithCode,
TyNum => Num,
TyRange => Range,
TyError => Error,
}
#[derive(Copy, Clone)]
pub(crate) enum Op {
Semi,
Comma,
Dash,
Caret,
Prim,
}
impl Op {
pub(crate) fn next(self) -> Option<Op> {
match self {
Op::Semi => Some(Op::Comma),
Op::Comma => Some(Op::Dash),
Op::Dash => Some(Op::Caret),
Op::Caret => Some(Op::Prim),
Op::Prim => None,
}
}
pub(crate) fn stop_chars(self) -> &'static [char] {
match self {
Op::Semi => &[',', ';'],
Op::Comma => &[','],
Op::Dash => &['-', ','],
Op::Caret => &['^', '-', ','],
Op::Prim => &[],
}
}
}
thread_local! {
static FRESH_COUNTER: Cell<usize> = 0.into();
}
pub(crate) fn reset_fresh_counter() {
FRESH_COUNTER.set(0);
}
pub(crate) fn fresh_param() -> TokenStream {
FRESH_COUNTER.with(|c| {
let n = c.get();
c.set(n + 1);
let name = format!("_Param_{}_BatchGen_", n);
let ident = Ident::new(&name, proc_macro2::Span::call_site());
quote!(#ident)
})
}