#![recursion_limit="128"]
extern crate proc_macro;
use self::proc_macro::{TokenStream, TokenTree};
use indexmap::{
IndexMap,
IndexSet,
};
use quote::{
quote,
quote_spanned,
};
use std::{
cell::Cell,
hash::{Hash, Hasher},
iter::FromIterator,
mem,
ops::Range,
};
use syn::{
Attribute,
DeriveInput,
Expr,
ExprClosure,
ExprMacro,
ExprRange,
Fields,
GenericParam,
Generics,
Ident,
ImplItem,
Item,
ItemEnum,
ItemFn,
ItemImpl,
ItemMacro,
Lit,
MacroDelimiter,
Pat,
Path,
PathArguments,
RangeLimits,
ReturnType,
Stmt,
Token,
Type,
TypeParamBound,
TypeMacro,
TypeParam,
TypePath,
Variant,
Visibility,
WhereClause,
braced,
bracketed,
parse_macro_input,
parse_quote,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
visit::{self, Visit},
visit_mut::{self, VisitMut},
};
extern crate proc_macro2;
use proc_macro2::Span;
macro_rules! syntax_error {
() => {
panic!( "{}", "`enum`s deriving `FromVariant`/`Proto` should be in the form of \"enum MyEnum { Foo(Type), Bar(AnotherType),... }\"" );
}
}
macro_rules! parse_quote_spanned {
( $span:expr => $($tt:tt)+ ) => {{
let quoted = quote_spanned!( $span => $($tt)+ );
parse_quote!( #quoted )
}};
}
#[proc_macro]
#[allow( non_snake_case )]
pub fn Enum( input: TokenStream ) -> TokenStream {
let type_list = parse_macro_input!( input as TypeList );
let types = type_list.0.iter();
let name = make_ident( &format!( "Enum{}", types.len() ));
let expanded = if types.len() == 0 {
quote!( #name )
} else {
quote!( #name::<#(#types),*> )
};
expanded.into()
}
struct TypeList( Punctuated<Type, Token![,]> );
impl Parse for TypeList {
fn parse( input: ParseStream ) -> syn::Result<Self> {
let types = Punctuated::<Type, Token![,]>::parse_terminated( input )?;
Ok( TypeList( types ))
}
}
struct RangedEnums {
attrs : Vec<Attribute>,
vis : Visibility,
ident : Ident,
range : Range<usize>,
where_clause : Option<WhereClause>,
}
enum EnumDef {
Ranged( RangedEnums ),
Single( ItemEnum ),
Snapshot( ItemEnum ),
None,
}
struct EnumDefImpls( EnumDef, Vec<ItemImpl> );
impl Parse for EnumDefImpls {
fn parse( input: ParseStream ) -> syn::Result<Self> {
if input.peek( Ident ) && input.peek2( Token![!] ) {
let def = input.parse::<ItemMacro>()?;
match path_ident_name( &def.mac.path ).as_deref() {
Some( "_def" ) => (),
_ => panic!( "{}", "expect _def!{{}}" ),
}
let item_enum = def.mac.parse_body::<ItemEnum>()?;
let mut impls = vec![];
while !input.is_empty() {
impls.push( input.parse::<ItemImpl>()? );
}
return Ok( EnumDefImpls(
EnumDef::Snapshot( item_enum ),
impls,
));
}
if input.peek( Token![impl] ) || input.peek( Token![unsafe] ) && input.peek2( Token![impl] ) {
let mut impls = vec![];
while !input.is_empty() {
impls.push( input.parse::<ItemImpl>()? );
}
return Ok( EnumDefImpls(
EnumDef::None,
impls,
));
}
let attrs = if input.peek( Token![#] ) {
input.call( Attribute::parse_outer )?
} else {
vec![]
};
let vis = input.parse::<Visibility>()?;
let enum_token = input.parse::<Token![enum]>()?;
let ident = input.parse::<Ident>()?;
if input.peek( Token![!] ) {
input.parse::<Token![!]>()?;
let content;
bracketed!( content in input );
let range = parse_range( content.parse::<ExprRange>()? )
.expect("ranges should be expressed in literial integers");
let where_clause = if input.peek( Token![where] ) {
Some( input.parse::<WhereClause>()? )
} else {
None
};
if input.peek( Token![;] ) {
input.parse::<Token![;]>()?;
}
let mut impls = vec![];
while !input.is_empty() {
impls.push( input.parse::<ItemImpl>()? );
}
return Ok( EnumDefImpls(
EnumDef::Ranged( RangedEnums{ attrs, vis, ident, range, where_clause }),
impls,
));
} else {
let content;
let generics = input.parse::<Generics>()?;
let brace_token = braced!( content in input );
let variants = Punctuated::<Variant, Token![,]>::parse_terminated( &content )?;
let mut impls = vec![];
while !input.is_empty() {
impls.push( input.parse::<ItemImpl>()? );
}
return Ok( EnumDefImpls(
EnumDef::Single( ItemEnum{ attrs, vis, enum_token, ident, generics, brace_token, variants }),
impls,
));
}
}
}
fn path_ident( path: &Path ) -> Option<Ident> {
if path.leading_colon.is_some() || path.segments.len() != 1 {
return None;
}
let the_segment = path.segments.first().unwrap();
if the_segment.arguments != PathArguments::None {
return None;
}
Some( the_segment.ident.clone() )
}
fn path_ident_name( path: &Path ) -> Option<String> {
if path.leading_colon.is_some() || path.segments.len() != 1 {
return None;
}
let the_segment = path.segments.first().unwrap();
if the_segment.arguments != PathArguments::None {
return None;
}
Some( the_segment.ident.to_string() )
}
fn parse_range( expr_range: ExprRange ) -> Option<Range<usize>> {
let from = match expr_range.from {
Some( from ) => loop {
if let Expr::Lit( expr_lit ) = *from {
if let Lit::Int( lit_int ) = expr_lit.lit {
if let Ok( from ) = lit_int.base10_parse::<usize>() {
break from;
}
}
}
return None;
},
None => 0,
};
let to = match expr_range.to {
Some( to ) => loop {
if let Expr::Lit( expr_lit ) = *to {
if let Lit::Int( lit_int ) = expr_lit.lit {
if let Ok( to ) = lit_int.base10_parse::<usize>() {
break to;
}
}
}
return None;
},
None => 0,
};
let to = match expr_range.limits {
RangeLimits::HalfOpen(_) => to,
RangeLimits::Closed(_) => to+1,
};
Some( from..to )
}
#[proc_macro]
pub fn def_impls( input: TokenStream ) -> TokenStream {
match parse_macro_input!( input as EnumDefImpls ) {
EnumDefImpls( EnumDef::Ranged( RangedEnums{ attrs, vis, ident, range, where_clause }), item_impls ) => {
let vnames = (0..range.end).map( |i| make_ident( &format!( "_{}", i )));
let vtypes = (0..range.end).map( |i| make_ident( &format!( "_T{}", i )));
let itypes = (0..range.end).map( |i| make_ident( &format!( "_I{}", i )));
let is_proto = ident.to_string() == "__";
let mut enums = Vec::<ItemEnum>::new();
let mut impls = Vec::<ItemImpl>::new();
for index in range.clone() {
let ident = make_ident( &format!( "{}{}", ident, index ));
let vnames1 = vnames.clone().take( index );
let vnames2 = vnames1.clone();
let vtypes1 = vtypes.clone().take( index );
let vtypes2 = vtypes1.clone();
let vtypes3 = vtypes1.clone();
let vtypes4 = vtypes.clone();
let itypes1 = itypes.clone().take( index );
let itypes2 = itypes1.clone();
let itypes3 = itypes1.clone();
let generics: Option<Generics>;
let mut expanded_where_clause: Option<WhereClause>;
if index == 0 {
generics = None;
expanded_where_clause = None;
} else {
generics = Some( parse_quote!( <#(#vtypes1),*> ));
expanded_where_clause = where_clause.clone();
EnumWhere::expand(
&mut expanded_where_clause,
vtypes.clone().map( |ident| {
let ty: Type = parse_quote!( #ident );
ty
}).collect::<Vec<_>>()
);
}
enums.push( parse_quote! {
#(#attrs)* #vis enum #ident #generics #expanded_where_clause {
#( #vnames1( #vtypes2 ), )*
}
});
if is_proto && index != 0 {
impls.push( parse_quote! {
impl<#(#itypes1,)* #(#vtypes3,)* Src, Dest> enumx::ExchangeFrom<Src, enumx::EnumToEnum<(#(#itypes2,)*)>> for Dest
where Src : enumx::Proto<Type=enumx::proto::#ident #generics>
, Dest : #( enumx::ExchangeFrom<#vtypes4,#itypes3> )+*
{
fn exchange_from( src: Src ) -> Dest {
match src.into_proto() {
#( enumx::proto::#ident::#vnames2(v) => Dest::exchange_from(v) ),*
}
}
}
});
}
}
let mut expanded = TokenStream::from( quote!( #( #enums )* #( #impls )* ));
for item_impl in item_impls {
Extend::<TokenTree>::extend( &mut expanded, expand_enum_impl( item_impl, None ));
}
expanded.into()
},
EnumDefImpls( EnumDef::Single( item_enum ), item_impls ) => {
let mut expanded_impls = TokenStream::new();
for item_impl in item_impls {
expanded_impls.extend( expand_enum_impl( item_impl, Some( &item_enum )));
}
let mut expanded_item_enum = TokenStream::from( quote!( #item_enum ));
Extend::<TokenTree>::extend( &mut expanded_item_enum, expanded_impls );
expanded_item_enum.into()
},
EnumDefImpls( EnumDef::Snapshot( item_enum ), item_impls ) => {
let mut expanded_impls = TokenStream::new();
for item_impl in item_impls {
expanded_impls.extend( expand_enum_impl( item_impl, Some( &item_enum )));
}
expanded_impls
},
EnumDefImpls( EnumDef::None, item_impls ) => {
let mut expanded_impls = TokenStream::new();
for item_impl in item_impls {
expanded_impls.extend( expand_enum_impl( item_impl, None ));
}
expanded_impls
},
}
}
#[proc_macro_derive( FromVariant )]
pub fn derive_from_variant( input: TokenStream ) -> TokenStream {
let input: DeriveInput = syn::parse( input ).unwrap();
match input.data {
syn::Data::Enum( ref data ) => {
if data.variants.len() == 0 {
return TokenStream::new();
}
let name = &input.ident;
let variant_names = data.variants.iter().map( |v| &v.ident );
let (ref impl_generics, ref ty_generics, ref where_clause) = input.generics.split_for_impl();
let variant_types = data.variants.iter().map( |ref v| {
if let syn::Fields::Unnamed( ref fields ) = v.fields {
let mut iter = fields.unnamed.iter();
if iter.len() == 1 {
let field = iter.next().unwrap();
return &field.ty;
}
}
syntax_error!();
});
let variant_types = variant_types.clone();
let mut impls = Vec::<ItemImpl>::new();
let mut indices = Vec::<Ident>::new();
for (i, (vname,vtype) ) in variant_names.zip( variant_types ).enumerate() {
let v: Type = parse_quote!( [(); #i] );
indices.push( make_ident( &format!( "_I{}", i )));
impls.push( parse_quote! {
impl #impl_generics enumx::FromVariant<#vtype,#v> for #name #ty_generics #where_clause {
fn from_variant( src: #vtype ) -> Self { #name::#vname( src )}
}
});
}
let impls = impls.iter();
let expanded = quote!( #(#impls)* );
expanded.into()
},
_ => panic!( "Only `enum`s can be constructed `FromVariant`s." ),
}
}
#[proc_macro_derive( Proto )]
pub fn derive_proto( input: TokenStream ) -> TokenStream {
let input: DeriveInput = syn::parse( input ).unwrap();
match input.data {
syn::Data::Enum( ref data ) => {
let name = &input.ident;
let named_variant_fp = data.variants.iter().map( |v| &v.ident );
let named_variant_tp = data.variants.iter().map( |v| &v.ident );
let variant_cnt = data.variants.len();
let protox = &make_ident( &format!( "__{}", variant_cnt ));
let named_enum_fp = (0..variant_cnt).map( |_| name );
let named_enum_tp = named_enum_fp.clone();
let unamed_enum_fp = (0..variant_cnt).map( |_| protox );
let unamed_enum_tp = unamed_enum_fp.clone();
let unamed_variant_fp = (0..variant_cnt).map( |index| make_ident( &format!( "_{}", index )));
let unamed_variant_tp = unamed_variant_fp.clone();
let ( ref impl_generics, ref ty_generics, ref where_clause ) = input.generics.split_for_impl();
let variant_ty = data.variants.iter().map( |ref v| {
if let syn::Fields::Unnamed( ref fields ) = v.fields {
let mut iter = fields.unnamed.iter();
if iter.len() == 1 {
let field = iter.next().unwrap();
return &field.ty;
}
}
syntax_error!();
});
let enumx_ty: syn::Type = parse_quote!{ enumx::proto::#protox<#(#variant_ty),*> };
let expanded = quote! {
impl #impl_generics enumx::Proto for #name #ty_generics #where_clause {
type Type = #enumx_ty;
fn from_proto( src: #enumx_ty ) -> Self {
match src {
#( enumx::proto::#unamed_enum_fp::#unamed_variant_fp(v) => #named_enum_fp::#named_variant_fp(v), )*
}
}
fn into_proto( self ) -> #enumx_ty {
match self {
#( #named_enum_tp::#named_variant_tp(v) => enumx::proto::#unamed_enum_tp::#unamed_variant_tp(v), )*
}
}
}
};
expanded.into()
},
_ => panic!( "Only `enum`s can have `Proto`-type." ),
}
}
#[proc_macro_derive( Exchange )]
pub fn derive_exchange( input: TokenStream ) -> TokenStream {
let mut output = derive_from_variant( input.clone() );
output.extend( derive_proto( input ));
output
}
fn make_ident( sym: &str ) -> Ident {
Ident::new( sym, Span::call_site() )
}
fn add_generics( generics: &mut Generics, type_param: TypeParam ) {
generics.params.push( GenericParam::Type( type_param ));
}
struct TypePathList( Vec<Path> );
impl Parse for TypePathList {
fn parse( input: ParseStream ) -> syn::Result<Self> {
let types = Punctuated::<Type, Token![,]>::parse_terminated( input )?;
Ok( TypePathList( types.into_iter().map( |ty| match ty {
Type::Path( type_path ) => type_path.path,
_ => parse_quote!( TyPat::<#ty> ),
}).collect::<Vec<_>>() ))
}
}
#[derive( Eq )]
struct TypeIndex( Path, Cell<u32> );
impl PartialEq for TypeIndex {
fn eq( &self, other: &Self ) -> bool { self.0.eq( &other.0 )}
fn ne( &self, other: &Self ) -> bool { self.0.ne( &other.0 )}
}
impl Hash for TypeIndex {
fn hash<H:Hasher>( &self, state: &mut H ) { self.0.hash( state )}
}
type Variants = IndexSet<TypeIndex>;
struct Enum {
variants : IndexSet<TypeIndex>,
}
struct EnumxTag {
enum_ : Option<Enum>,
}
impl EnumxTag {
fn new() -> Self {
EnumxTag{ enum_: None }
}
fn parse_type_path_list( input: TokenStream ) -> syn::Result<TypePathList> {
Ok( syn::parse::<TypePathList>( input )? )
}
}
#[derive( PartialEq )]
enum TyPatAttr {
None,
GenVariants,
Gen( Variants ),
}
fn parse_ty_pat_attr( attr: &Attribute ) -> Option<TyPatAttr> {
if attr.path.leading_colon.is_none() && attr.path.segments.len() == 1 {
if attr.path.segments.first().unwrap().ident == "ty_pat" {
let ts = TokenStream::from( attr.tokens.clone() );
let mut iter = ts.into_iter();
if let Some( TokenTree::Group( group )) = iter.next() {
let mut iter = group.stream().into_iter();
match iter.next() {
Some( TokenTree::Ident( ident )) => match ident.to_string().as_str() {
"gen_variants" => {
return Some( TyPatAttr::GenVariants );
},
"gen" => {
let mut variants = IndexSet::new();
let types = EnumxTag::parse_type_path_list( TokenStream::from_iter( iter )).expect("type list");
if types.0.len() == 0 {
return Some( TyPatAttr::GenVariants );
} else {
types.0.into_iter().for_each( |ty| { variants.insert( TypeIndex( ty, Cell::new(0) )); });
return Some( TyPatAttr::Gen( variants ));
}
},
_ => panic!("invalid #[ty_pat] argument: only #[ty_pat(gen_variants)] and #[ty_pat(gen)] are supported"),
},
Some( _ ) => panic!("invalid #[ty_pat] argument"),
None => return Some( TyPatAttr::None ),
}
} else {
return Some( TyPatAttr::None );
}
}
}
return None;
}
impl VisitMut for EnumxTag {
fn visit_type_mut( &mut self, node: &mut Type ) {
visit_mut::visit_type_mut( self, node );
if let Type::Macro( type_macro ) = node {
let mac = &type_macro.mac;
if mac.path.leading_colon.is_none() && mac.path.segments.len() == 1 {
let seg = mac.path.segments.first().unwrap();
if seg.arguments == PathArguments::None && seg.ident == "Enum" {
let ts = TokenStream::from( mac.tokens.clone() );
let mut variants = IndexSet::new();
let types = EnumxTag::parse_type_path_list( ts ).expect("type list");
types.0.into_iter().for_each( |ty| {
let mut type_ = Type::Path( TypePath{ qself: None, path: ty });
self.visit_type_mut( &mut type_ );
match type_ {
Type::Path( type_path ) => { variants.insert( TypeIndex( type_path.path, Cell::new(0) )); },
_ => unreachable!(),
}
});
let variant = variants.iter().map( |type_index| &type_index.0 );
let ty: Type = parse_quote_spanned!( mac.span() => Enum!(#(#variant),*) );
*node = ty.clone();
self.enum_ = Some( Enum{ variants });
}
}
}
}
fn visit_expr_mut( &mut self, expr: &mut Expr ) {
match expr {
Expr::Match( expr_match ) => {
self.visit_expr_mut( &mut *expr_match.expr );
expr_match.arms.iter_mut().for_each( |arm| {
arm.guard.as_mut().map( |guard| self.visit_expr_mut( &mut *guard.1 ));
self.visit_expr_mut( &mut *arm.body );
});
let mut ty_pat_attrs = None;
for (index, attr) in expr_match.attrs.iter().enumerate() {
if let Some( attrs ) = parse_ty_pat_attr( attr ) {
ty_pat_attrs = Some(( attrs, index ));
break;
}
}
ty_pat_attrs.as_ref().map( |(_,index)| expr_match.attrs.remove( *index ));
if let Some( ty_pat_attrs ) = ty_pat_attrs {
let match_expr = &*expr_match.expr;
let match_span = match_expr.span();
expr_match.expr = Box::new( parse_quote_spanned!( match_span => enumx::ExchangeFrom::exchange_from( #match_expr ) ));
let mut index = 0_u32;
let checked = expr_match.arms.iter_mut().fold( IndexMap::<Path,Cell<u32>>::new(), |mut acc, arm| {
let mut add_type_pattern = |path: &mut Path| {
let mut nth = index;
acc.entry( path.clone() )
.and_modify( |n| { nth = n.get() })
.or_insert( Cell::new( nth ));
if nth == index { index += 1; }
let _n = make_ident( &format!( "_{}", nth ));
*path = parse_quote!{ __EnumxAdhocEnum::#_n };
};
match &mut arm.pat {
Pat::TupleStruct( pat_tuple_struct ) => {
if pat_tuple_struct.pat.elems.len() > 1 {
panic!("#[enumx] supports tuple struct variant in newtype form only.");
}
add_type_pattern( &mut pat_tuple_struct.path );
acc
},
Pat::Path( pat_path ) => {
add_type_pattern( &mut pat_path.path );
acc
},
Pat::Wild(_) if arm.guard.is_none() => {
acc
},
_ => {
let pat_ident;
if let Pat::Ident( ident ) = &mut arm.pat {
if ident.by_ref.is_some() || ident.mutability.is_some() || ident.subpat.is_some() {
panic!("#[enumx] dost not support ident in arm with ref/mut/sub pattern.");
} else {
pat_ident = Some( ident.clone() );
}
} else {
panic!("#[enumx] unsupported pattern in match arm");
}
pat_ident.map( |pat_ident| {
let ident = &pat_ident.ident;
let mut path: Path = parse_quote!( #ident );
add_type_pattern( &mut path );
arm.pat = parse_quote!( #path(_) );
});
acc
},
}
});
let checked = IndexSet::<TypeIndex>::from_iter( checked.clone().into_iter().map( |(t,i)| TypeIndex(t,i) ));
let unexhausted = match &ty_pat_attrs.0 {
TyPatAttr::None => Vec::new(),
TyPatAttr::GenVariants => self.enum_.as_ref().expect("def_impls!( variants ... ) parsed").variants.difference( &checked ).collect::<Vec<_>>(),
TyPatAttr::Gen( variants ) => variants.difference( &checked ).collect::<Vec<_>>(),
};
unexhausted.iter().for_each( |TypeIndex(_,i)| {
i.set( index );
let _n = make_ident( &format!( "_{}", index ));
expr_match.arms.push(
parse_quote_spanned!( match_span => __EnumxAdhocEnum::#_n(v) => v.exchange_into(), ),
);
index += 1;
});
let (unexhausted_types, unexhausted_indices): (Vec<_>, Vec<_>) = unexhausted.iter().map( |TypeIndex(t,i)| (t,i) ).unzip();
let unexhausted_indices = unexhausted_indices.iter().map( |n| make_ident( &format!( "_{}", n.get() )));
let (checked_types, checked_indices): (Vec<_>, Vec<_>) = checked.iter().map( |TypeIndex(t,i)| (t,i) ).unzip();
let checked_indices = checked_indices.iter().map( |n| make_ident( &format!( "_{}", n.get() )));
let adhoc_enum = quote_spanned!{ match_span =>
#[derive( ::enumx::FromVariant, ::enumx::Proto )]
enum __EnumxAdhocEnum {
#( #checked_indices( #checked_types ), )*
#( #unexhausted_indices( #unexhausted_types ), )*
}
};
*expr = syn::parse::<Expr>( quote_spanned!{ match_span => {
#adhoc_enum
#expr
}}.into() ).unwrap();
}
},
_ => {
visit_mut::visit_expr_mut( self, expr );
},
}
}
}
#[proc_macro_attribute]
pub fn enumx( _args: TokenStream, input: TokenStream ) -> TokenStream {
if let Ok( mut stmt ) = syn::parse::<Stmt>( input.clone() ) {
match stmt {
Stmt::Item( Item::Fn( mut item_fn )) => {
let mut enumx_tag = EnumxTag::new();
enumx_tag.visit_signature_mut( &mut item_fn.sig );
enumx_tag.visit_block_mut( &mut *item_fn.block );
let expanded = quote_spanned!( item_fn.span() => #item_fn );
return expanded.into();
},
Stmt::Local(_) => {
let mut enumx_tag = EnumxTag::new();
visit_mut::visit_stmt_mut( &mut enumx_tag, &mut stmt );
let expanded = quote_spanned!( stmt.span() => #stmt );
return expanded.into();
},
_ => (),
}
} else if let Ok( mut expr_closure ) = syn::parse::<ExprClosure>( input ) {
let mut enumx_tag = EnumxTag::new();
enumx_tag.visit_return_type_mut( &mut expr_closure.output );
enumx_tag.visit_expr_mut( &mut *expr_closure.body );
let expanded = quote_spanned!( expr_closure.span() => #expr_closure );
return expanded.into();
}
panic!( "#[enumx] for functions, closures, local let-bindings and impl blocks only!!!" );
}
#[derive( Default )]
struct IterativeImpl {
self_ty : Option<Type>,
index : usize,
vnames : Vec<Ident>,
vtypes : Vec<Type>,
}
impl VisitMut for IterativeImpl {
fn visit_expr_mut( &mut self, expr: &mut Expr ) {
visit_mut::visit_expr_mut( self, expr );
if let Expr::Macro( expr_macro ) = expr {
if let Some( name ) = path_ident_name( &expr_macro.mac.path ) {
if name == "_match" {
let MatchInput{ self_expr, arm_expr } = expr_macro.mac.parse_body::<MatchInput>()
.expect("def_impls!{ impl }: expects _match!( expr => expr ) or _match!( expr )");
let mut match_expander = MatchExpander::from( self );
let arm_exprs = (0..=self.index).fold( Vec::new(), |mut arm_exprs, i| {
match_expander.index = i;
let mut arm_expr = arm_expr.clone();
match_expander.visit_expr_mut( &mut arm_expr );
arm_exprs.push( arm_expr );
arm_exprs
});
let self_ty = &self.self_ty;
let enum_ty = (0..=self.index).map( |_| self_ty.clone() ).collect::<Vec<_>>();
let vnames = self.vnames.iter();
*expr = parse_quote! {
match #self_expr {
#( #enum_ty::#vnames( __variant ) => #arm_exprs ),*
}
};
}
}
}
}
}
struct MatchInput {
self_expr : Expr,
arm_expr : Expr,
}
impl Parse for MatchInput {
fn parse( input: ParseStream ) -> syn::Result<Self> {
let expr = input.parse::<Expr>()?;
if input.peek( Token![=>] ) {
input.parse::<Token![=>]>()?;
let arm_expr = input.parse::<Expr>()?;
Ok( MatchInput {
self_expr : expr,
arm_expr ,
})
} else {
Ok( MatchInput {
self_expr : parse_quote!( self ),
arm_expr : expr,
})
}
}
}
struct MatchExpander {
self_ty : Option<Type>,
index : usize,
vtypes : Vec<Type>,
}
impl MatchExpander {
fn from( iterative_impl: &IterativeImpl ) -> Self {
MatchExpander {
self_ty : iterative_impl.self_ty.clone(),
index : 0,
vtypes : iterative_impl.vtypes.clone(),
}
}
}
impl VisitMut for MatchExpander {
fn visit_expr_mut( &mut self, expr: &mut Expr ) {
visit_mut::visit_expr_mut( self, expr );
if let Expr::Macro( expr_macro ) = expr {
if let Some( name ) = path_ident_name( &expr_macro.mac.path ) {
match name.as_str() {
"_variant" => {
if expr_macro.mac.tokens.is_empty() || expr_macro.mac.tokens.to_string() == "self" {
*expr = parse_quote_spanned!( expr.span() => __variant );
} else {
let other = expr_macro.mac.parse_body::<Expr>()
.expect("def_impls!{ impl }: _variant!()'s argument should be an expression");
let self_ty = self.self_ty.as_ref().unwrap();
let vname = make_ident( &format!( "_{}", self.index ));
*expr = parse_quote_spanned! { expr.span() =>
if let #self_ty::#vname(v) = #other {
v
} else {
panic!("def_impls!{ impl }: _variant!()'s argument should be of the same variant with `self`");
}
};
}
},
"_enum" => {
let mut inner = expr_macro.mac.parse_body::<Expr>()
.expect("def_impls!{ impl }: _enum!()'s argument should be an expression");
self.visit_expr_mut( &mut inner );
let self_ty = self.self_ty.as_ref().unwrap();
let vname = make_ident( &format!( "_{}", self.index ));
*expr = parse_quote_spanned! { expr.span() =>
#self_ty::#vname( #inner )
};
},
_ => (),
}
}
}
}
fn visit_type_mut( &mut self, node: &mut Type ) {
visit_mut::visit_type_mut( self, node );
if let Type::Macro( type_macro ) = node {
if let Some( name ) = path_ident_name( &type_macro.mac.path ) {
if name == "_Variant" {
if type_macro.mac.tokens.is_empty() {
let vtype = &self.vtypes[ self.index ];
*node = parse_quote!( #vtype );
} else {
panic!( "{}", "def_impls!{ impl }: Variant!() should have no argument" );
}
}
}
}
}
}
struct EnumWhere {
nth_variant : usize,
variant_types : Vec<Type>,
contains_variants : bool,
}
impl EnumWhere {
fn new( variant_types: Vec<Type> ) -> Self {
EnumWhere {
nth_variant : 0,
variant_types ,
contains_variants : false,
}
}
fn expand( where_clause: &mut Option<WhereClause>, variant_types: Vec<Type> ) {
if let Some( where_clause ) = where_clause.as_mut() {
let mut predicates = Punctuated::new();
mem::swap( &mut predicates, &mut where_clause.predicates );
for predicate in predicates.into_iter() {
let variant_count = variant_types.len();
let mut enum_where = EnumWhere::new( variant_types.clone() );
enum_where.visit_where_predicate( &predicate );
if enum_where.contains_variants {
for i in 0..variant_count {
let mut predicate = predicate.clone();
enum_where.nth_variant = i;
enum_where.visit_where_predicate_mut( &mut predicate );
where_clause.predicates.push( predicate );
}
} else {
where_clause.predicates.push( predicate );
}
}
}
}
}
impl<'a> Visit<'a> for EnumWhere {
fn visit_type_macro( &mut self, type_macro: &TypeMacro ) {
visit::visit_type_macro( self, type_macro );
if let Some( name ) = path_ident_name( &type_macro.mac.path ) {
if name == "_Variants" {
if type_macro.mac.tokens.is_empty() {
self.contains_variants = true;
} else {
panic!( "{}", "def_impls!{ impl }: Variants!() should have no argument" );
}
}
}
}
}
impl VisitMut for EnumWhere {
fn visit_type_mut( &mut self, node: &mut Type ) {
visit_mut::visit_type_mut( self, node );
if let Type::Macro( type_macro ) = node {
if let Some( name ) = path_ident_name( &type_macro.mac.path ) {
if name == "_Variants" {
if type_macro.mac.tokens.is_empty() {
let ty = &self.variant_types[ self.nth_variant ];
*node = parse_quote!( #ty );
} else {
panic!( "{}", "def_impls!{ impl }: Variants!() should have no argument" );
}
}
}
}
}
}
fn expand_enum_impl( mut item_impl: ItemImpl, item_enum: Option<&ItemEnum> ) -> TokenStream {
let mut iterative_impl = IterativeImpl::default();
let mut def_enum_in_self_ty = None;
let mut item_enum = item_enum;
loop {
if let Type::Macro( type_macro ) = &*item_impl.self_ty {
let ident = path_ident( &type_macro.mac.path ).expect("def_impls!{}: the path of the macro as self type in impl blocks should be an ident");
if ident == "_def" {
def_enum_in_self_ty = Some( type_macro.mac.parse_body::<ItemEnum>().expect( "def_impls!{}: expect _def!{ ItemEnum }" ));
item_enum = def_enum_in_self_ty.as_ref();
break;
}
let mut range;
match type_macro.mac.delimiter {
MacroDelimiter::Bracket(_) => match type_macro.mac.parse_body::<ExprRange>() {
Ok( expr_range ) => {
range = parse_range( expr_range ).expect( &format!( "expect {ident}![ literial_range ], e.g. {ident}![ 1..=16 ]", ident = ident ));
},
Err( _ ) => panic!( "expect {}![ expr_range ]", ident ),
},
_ => panic!( "expect {}![]", ident ),
}
if range.start > 0 {
range.start -= 1;
}
if range.end > 0 {
range.end -= 1;
}
let mut vtypes = Vec::<Type>::new();
for i in 0..range.start {
let gen_ty = make_ident( &format!( "_T{}", i ));
let gen_ty: Type = parse_quote!( #gen_ty );
{
let gen_ty = &gen_ty;
add_generics( &mut item_impl.generics, parse_quote!( #gen_ty ));
}
vtypes.push( gen_ty.clone() );
iterative_impl.vtypes.push( gen_ty );
let vname = make_ident( &format!( "_{}", i ));
iterative_impl.vnames.push( vname );
}
let mut impls = Vec::<ItemImpl>::new();
for index in range {
let gen_ty = make_ident( &format!( "_T{}", index ));
let gen_ty: Type = parse_quote!( #gen_ty );
{
let gen_ty = &gen_ty;
add_generics( &mut item_impl.generics, parse_quote!( #gen_ty ));
}
vtypes.push( gen_ty.clone() );
iterative_impl.vtypes.push( gen_ty );
let vname = make_ident( &format!( "_{}", index ));
iterative_impl.vnames.push( vname );
let mut item_impl = item_impl.clone();
let self_ty = make_ident( &format!( "{}{}", ident, index+1 ));
{
let self_ty = &self_ty;
item_impl.self_ty = parse_quote!( #self_ty <#(#vtypes),*> );
iterative_impl.self_ty = Some( parse_quote!( #self_ty ));
}
EnumWhere::expand( &mut item_impl.generics.where_clause, vtypes.clone() );
item_impl.items = item_impl.items.into_iter().map( |mut item| {
match item {
ImplItem::Method( ref mut impl_item_method ) => {
iterative_impl.index = index;
iterative_impl.visit_block_mut( &mut impl_item_method.block );
},
_ => (),
}
item
}).collect();
impls.push( item_impl );
}
let expanded = quote_spanned!( item_impl.span() => #( #impls )* );
return expanded.into();
}
break;
}
if let Some( item_enum ) = item_enum {
let enum_ident = &item_enum.ident;
iterative_impl.self_ty = Some( parse_quote!( #enum_ident ));
if def_enum_in_self_ty.is_some() {
let (_, ty_generics, _) = item_enum.generics.split_for_impl();
item_impl.self_ty = parse_quote!( #enum_ident #ty_generics );
}
for variant in item_enum.variants.iter() {
loop {
if let Fields::Unnamed( fields_unnamed ) = &variant.fields {
if fields_unnamed.unnamed.len() == 1 {
let field = fields_unnamed.unnamed.first().unwrap();
if field.ident.is_none() && field.colon_token.is_none() {
iterative_impl.vnames.push( variant.ident.clone() );
iterative_impl.vtypes.push( field.ty.clone() );
break;
}
}
}
panic!( "expect `{}( VariantType )`", variant.ident );
}
}
let variant_count = item_enum.variants.len();
EnumWhere::expand( &mut item_impl.generics.where_clause, iterative_impl.vtypes.clone() );
item_impl.items = item_impl.items.into_iter().map( |mut item| {
match item {
ImplItem::Method( ref mut impl_item_method ) => {
iterative_impl.index = variant_count-1;
iterative_impl.visit_block_mut( &mut impl_item_method.block );
},
_ => (),
}
item
}).collect();
let expanded = quote!( #item_impl );
return expanded.into();
} else {
panic!( "{}", "The enum's variants are unknown to `def_impls!{}`. Consider using `_def!{}` to provide the variants of the enum defined elsewhere." );
};
}
#[derive( Default )]
struct VariantLabel {
which_impl_trait : Option<Ident>,
label : Option<Ident>,
}
impl Parse for VariantLabel {
fn parse( input: ParseStream ) -> syn::Result<Self> {
if input.is_empty() {
Ok( VariantLabel::default() )
} else {
let ident = Some( input.parse::<Ident>()? );
if input.is_empty() {
Ok( VariantLabel{ which_impl_trait: None, label: ident })
} else if input.peek( Token![=>] ) {
input.parse::<Token![=>]>()?;
if input.peek( Token![_] ) {
input.parse::< Token![_] >()?;
if !input.is_empty() {
panic!("#[variant( which_impl_trait => _ )] expected, extra tokens after `_` got ");
}
Ok( VariantLabel{ which_impl_trait: ident, label: None })
} else {
Ok( VariantLabel{ which_impl_trait: ident, label: Some( input.parse::<Ident>()? )})
}
} else {
panic!("#[variant]: expected `.`");
}
}
}
}
fn take_variant_label( attrs: &mut Vec<Attribute>, which_impl_trait: &Option<Ident> ) -> Option<VariantLabel> {
let mut variant_label = None::<VariantLabel>;
let mut attributes = Vec::new();
mem::swap( &mut attributes, attrs );
for attr in attributes {
if attr.path.leading_colon.is_none() && attr.path.segments.len() == 1 {
if attr.path.segments.first().unwrap().ident == "variant" {
let ts = TokenStream::from( attr.tokens.clone() );
let mut iter = ts.into_iter();
if let Some( TokenTree::Group( group )) = iter.next() {
let inner_stream = group.stream();
let label: VariantLabel = syn::parse::<VariantLabel>( inner_stream )
.expect("expect #[variant( tag )] or #[variant( impl_tag.variant_tag )]");
if &label.which_impl_trait == which_impl_trait {
variant_label = Some( label );
} else {
attrs.push( attr );
continue;
}
} else {
variant_label = Some( VariantLabel::default() );
}
continue;
}
}
attrs.push( attr );
}
return variant_label;
}
struct Sum {
which_impl_trait : Option<Ident>,
variant_count : usize,
labeled_variants : IndexMap<Ident, usize>,
}
impl Sum {
fn new( which_impl_trait: Option<Ident> ) -> Self {
Sum {
which_impl_trait ,
variant_count : 0,
labeled_variants : IndexMap::new(),
}
}
}
impl VisitMut for Sum {
fn visit_expr_mut( &mut self, expr: &mut Expr ) {
macro_rules! wrap {
( $expr:expr, $expr_:expr ) => {{
let mut vname = None;
let mut is_variant = true;
loop {
let self_variant_count = self.variant_count;
if let Some( variant_label ) = take_variant_label( &mut $expr_.attrs, &self.which_impl_trait ) {
if let Some( label ) = variant_label.label {
if label != "" {
let variant_count = self.labeled_variants
.entry( label )
.or_insert( self_variant_count );
if *variant_count != self_variant_count {
vname = Some( make_ident( &format!( "_{}", *variant_count )));
break;
}
}
}
} else {
is_variant = false;
break;
}
vname = Some( make_ident( &format!( "_{}", self.variant_count )));
self.variant_count += 1;
break;
}
if is_variant {
let expr_ = $expr_;
*$expr = parse_quote!{
__SumType::#vname( #expr_ )
}
} else {
visit_mut::visit_expr_mut( self, expr );
}
}};
}
match expr {
Expr::Array( expr_array ) => wrap!( expr, expr_array ),
Expr::Assign( expr_assign ) => wrap!( expr, expr_assign ),
Expr::AssignOp( expr_assign_op ) => wrap!( expr, expr_assign_op ),
Expr::Async( expr_async ) => wrap!( expr, expr_async ),
Expr::Await( expr_await ) => wrap!( expr, expr_await ),
Expr::Binary( expr_binary ) => wrap!( expr, expr_binary ),
Expr::Block( expr_block ) => wrap!( expr, expr_block ),
Expr::Box( expr_box ) => wrap!( expr, expr_box ),
Expr::Break( expr_break ) => wrap!( expr, expr_break ),
Expr::Call( expr_call ) => wrap!( expr, expr_call ),
Expr::Cast( expr_cast ) => wrap!( expr, expr_cast ),
Expr::Closure( expr_closure ) => wrap!( expr, expr_closure ),
Expr::Continue( expr_break ) => wrap!( expr, expr_break ),
Expr::Field( expr_field ) => wrap!( expr, expr_field ),
Expr::ForLoop( expr_for_loop ) => wrap!( expr, expr_for_loop ),
Expr::Group( expr_group ) => wrap!( expr, expr_group ),
Expr::If( expr_if ) => wrap!( expr, expr_if ),
Expr::Index( expr_index ) => wrap!( expr, expr_index ),
Expr::Let( expr_let ) => wrap!( expr, expr_let ),
Expr::Lit( expr_lit ) => wrap!( expr, expr_lit ),
Expr::Loop( expr_loop ) => wrap!( expr, expr_loop ),
Expr::Macro( expr_macro ) => wrap!( expr, expr_macro ),
Expr::Match( expr_match ) => wrap!( expr, expr_match ),
Expr::MethodCall( expr_method_call ) => wrap!( expr, expr_method_call ),
Expr::Paren( expr_paren ) => wrap!( expr, expr_paren ),
Expr::Path( expr_path ) => wrap!( expr, expr_path ),
Expr::Range( expr_range ) => wrap!( expr, expr_range ),
Expr::Reference( expr_reference ) => wrap!( expr, expr_reference ),
Expr::Repeat( expr_repeat ) => wrap!( expr, expr_repeat ),
Expr::Return( expr_return ) => wrap!( expr, expr_return ),
Expr::Struct( expr_struct ) => wrap!( expr, expr_struct ),
Expr::Try( expr_try ) => wrap!( expr, expr_try ),
Expr::TryBlock( expr_try_block ) => wrap!( expr, expr_try_block ),
Expr::Tuple( expr_tuple ) => wrap!( expr, expr_tuple ),
Expr::Type( expr_type ) => wrap!( expr, expr_type ),
Expr::Unary( expr_unary ) => wrap!( expr, expr_unary ),
Expr::Unsafe( expr_unsafe ) => wrap!( expr, expr_unsafe ),
Expr::Verbatim( _ ) => (),
Expr::While( expr_while ) => wrap!( expr, expr_while ),
Expr::Yield( expr_yield ) => wrap!( expr, expr_yield ),
_ => (),
}
}
fn visit_item_mut( &mut self, _item: &mut Item ) {}
}
struct ReplaceIdent {
placehoder : Ident,
real_ident : Ident,
}
impl ReplaceIdent {
fn new( placehoder: Ident, real_ident: Ident ) -> Self {
ReplaceIdent{ placehoder, real_ident }
}
}
impl VisitMut for ReplaceIdent {
fn visit_ident_mut( &mut self, ident: &mut Ident ) {
if *ident == self.placehoder {
*ident = self.real_ident.clone();
}
}
fn visit_item_mut( &mut self, _item: &mut Item ) {}
}
#[derive( Default )]
struct SumArgs {
which_impl_trait : Option<Ident>,
impl_generics : Option<Punctuated<GenericParam, Token![,]>>,
trait_path : Option<Path>,
enum_prefix : Option<Ident>,
}
impl Parse for SumArgs {
fn parse( input: ParseStream ) -> syn::Result<Self> {
if input.is_empty() {
Ok( SumArgs::default() )
} else {
let which_impl_trait = if input.peek( Ident ) && input.peek2( Token![=>] ) {
let which_impl_trait = input.parse::<Ident>()?;
input.parse::<Token![=>]>()?;
Some( which_impl_trait )
} else {
None
};
if input.peek( Token![impl] ) {
input.parse::<Token![impl]>()?;
let impl_generics = if input.peek( Token![<] ) {
input.parse::<Token![<]>()?;
let impl_generics = Punctuated::<GenericParam, Token![,]>::parse_terminated( input )?;
input.parse::<Token![>]>()?;
Some( impl_generics )
} else {
None
};
let trait_path = Some( input.parse::<Path>()? );
if input.is_empty() {
Ok( SumArgs {
which_impl_trait ,
impl_generics ,
trait_path ,
enum_prefix : None,
})
} else {
input.parse::<Token![for]>()?;
Ok( SumArgs {
which_impl_trait ,
impl_generics ,
trait_path ,
enum_prefix : Some( input.parse::<Ident>()? ),
})
}
} else if input.peek( Ident ) {
Ok( SumArgs {
which_impl_trait ,
impl_generics : None,
trait_path : None,
enum_prefix : Some( input.parse::<Ident>()? ),
})
} else {
input.parse::<Token![_]>()?;
if !input.is_empty() {
panic!("#[sum( which_impl_trait => _ )] expected, extra tokens after `_` got ");
}
Ok( SumArgs {
which_impl_trait ,
impl_generics : None,
trait_path : None,
enum_prefix : None,
})
}
}
}
}
#[proc_macro_attribute]
pub fn sum( args: TokenStream, input: TokenStream ) -> TokenStream {
let SumArgs{ which_impl_trait, mut impl_generics, trait_path, enum_prefix } = parse_macro_input!( args as SumArgs );
let mut item_fn = parse_macro_input!( input as ItemFn );
let mut sum = Sum::new( which_impl_trait );
sum.visit_block_mut( &mut item_fn.block );
let variant_count = sum.variant_count;
let (enum_basename, enum_ident);
let enum_def;
let enum_impl: Option<ExprMacro>;
match enum_prefix {
None => {
enum_basename = make_ident( "__SumType" );
enum_ident = make_ident( &format!( "__SumType{}", variant_count ));
let enum_basename = &enum_basename;
let trait_path = trait_path.unwrap_or_else( || loop {
let type_impl_trait = loop {
if let ReturnType::Type( _, ty ) = &item_fn.sig.output {
if let Type::ImplTrait( it ) = &**ty {
break it.clone();
}
}
panic!("#[sum] fn should return `impl Trait`");
};
let mut iter = type_impl_trait.bounds.iter();
match iter.next() {
Some( bound ) => if let TypeParamBound::Trait( trait_bound ) = bound {
let mut path = trait_bound.path.clone();
path.segments.last_mut().map( |path_seg| {
match path_seg.ident.to_string().as_str() {
"Fn" | "FnMut" | "FnOnce" => {
path_seg.arguments = PathArguments::AngleBracketed( parse_quote_spanned!( path_seg.arguments.span() => <Args> ));
let mut punct = Punctuated::new();
punct.push( parse_quote_spanned!( path_seg.arguments.span() => Args ));
impl_generics = Some( punct );
},
_ => {
path_seg.arguments = PathArguments::None;
},
}
});
break path;
},
None => panic!("#[sum]: expected trait bound after `-> impl`"),
}
});
enum_def = Some( quote!( enum #enum_basename![#variant_count..=#variant_count]; ));
enum_impl = Some( parse_quote!( impl_all_traits!{ _impl!(#impl_generics) #trait_path _for!( #enum_basename![#variant_count..=#variant_count] )}));
},
Some( enum_prefix ) => {
enum_ident = make_ident( &format!( "{}{}", enum_prefix.to_string(), variant_count ));
enum_def = None;
enum_impl = None;
},
}
let enum_def = match enum_def {
Some( enum_def ) => {
let token_stream = def_impls( enum_def.into() );
Some( parse_macro_input!( token_stream as ItemEnum ))
},
None => None,
};
let placeholder = make_ident( "__SumType" );
ReplaceIdent::new( placeholder, enum_ident ).visit_block_mut( &mut item_fn.block );
let mut block = parse_quote!({});
mem::swap( &mut block, &mut item_fn.block );
item_fn.block = parse_quote_spanned! { block.span() => {
#enum_def
#enum_impl
#block
}};
let expanded = quote!( #item_fn );
expanded.into()
}
struct SumErr;
impl VisitMut for SumErr {
fn visit_expr_mut( &mut self, expr: &mut Expr ) {
visit_mut::visit_expr_mut( self, expr );
if let Expr::Try( expr_try ) = expr {
let the_expr = &*expr_try.expr;
*expr = parse_quote! {
match #the_expr {
Ok( v ) => v,
Err( e ) => return Err( #[variant] e ),
}
};
}
}
fn visit_item_mut( &mut self, _item: &mut Item ) {}
}
#[proc_macro_attribute]
pub fn sum_err( _args: TokenStream, input: TokenStream ) -> TokenStream {
let mut item_fn = parse_macro_input!( input as ItemFn );
SumErr.visit_block_mut( &mut *item_fn.block );
let expanded = quote!( #item_fn );
expanded.into()
}