#![feature(proc_macro_diagnostic)]
#[macro_use]
extern crate proc_macro;
#[macro_use]
extern crate proc_macro2;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
use proc_macro::TokenStream as TStream;
use proc_macro2::{TokenStream, TokenTree, Group};
use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
use syn::*;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::synom::{Parser, Synom};
#[proc_macro]
pub fn def_mod(tokens: TStream) -> TStream {
let t = ModuleDecl::parse_all;
let declarations: Vec<ModuleDecl> = t.parse(tokens).unwrap();
let mut output = TokenStream::new();
for module in declarations {
let mut pathed_attrs = vec![];
let mut custom_attrs = vec![];
for attr in module.attrs {
if let (attr, Some(path)) = attr {
pathed_attrs.push((attr, path));
} else {
custom_attrs.push(attr.0);
};
}
let pathed_attrs = &pathed_attrs;
let custom_attrs = &custom_attrs;
let vis = &module.vis;
let mod_token = &module.mod_token;
let module_name = &module.ident;
if pathed_attrs.is_empty() {
let t = quote_spanned! { module_name.span() =>
#(#custom_attrs)*
#vis #mod_token #module_name;
};
t.to_tokens(&mut output);
} else {
for (attr, path) in pathed_attrs {
let t = quote_spanned! { module_name.span() =>
#attr
#[path=#path]
#(#custom_attrs)*
#vis #mod_token #module_name;
};
t.to_tokens(&mut output);
}
}
if let ModuleBody::Content((_brace, body)) = module.body {
let mut index: u32 = 0;
let mut tokenise_method = |type_name: Option<&Ident>, method_item: TraitItemMethod| {
if let Some(body) = method_item.default {
body.span()
.unstable()
.error("A body isn't valid here.")
.emit();
return TokenStream::new();
}
let mapping = if let Some(type_name) = type_name {
let self_replacement = type_name.to_string();
let func = move |ident: Ident| {
if ident.to_string() == "Self" {
Ident::new(&self_replacement, ident.span())
} else {
ident
}
};
Some(func)
} else {
None
};
let t = convert(module_name, type_name, index, method_item, mapping);
index += 1;
t
};
let items: Vec<TokenStream> = body.into_iter()
.map(|item| {
match item {
DeclItem::Method(method_item) => tokenise_method(None, method_item),
DeclItem::Type(type_item) => {
let attrs = &type_item.attrs;
let type_name = &type_item.ident;
let method_items = if let TypeDeclBody::Content((_brace, body)) = type_item.body {
body.into_iter()
.map(|method_item| tokenise_method(Some(type_name), method_item))
.collect()
} else {
vec![]
};
quote! {
#(#attrs)*
{
use self::#module_name::#type_name;
#(#method_items)*
}
}
}
}
})
.filter(|t| !t.is_empty())
.collect();
let function_name = {
let name = format!("_load_{}", module_name);
Ident::new(&name, module_name.span())
};
let t = quote! {
#[allow(dead_code)]
fn #function_name() {
use self::#module_name::*;
#(#items)*
}
};
t.to_tokens(&mut output);
}
}
output.into()
}
#[cfg_attr(feature = "derive-debug", derive(Debug))]
struct ModuleDecl {
attrs: Vec<(Attribute, Option<LitStr>)>,
vis: Visibility,
mod_token: Token![mod],
ident: Ident,
body: ModuleBody,
}
#[cfg_attr(feature = "derive-debug", derive(Debug))]
enum ModuleBody {
Content((token::Brace, Vec<DeclItem>)),
Terminated(Token![;]),
}
impl ModuleDecl {
named!(parse_all -> Vec<ModuleDecl>, do_parse!(
decls: many0!(syn!(ModuleDecl)) >>
(decls)
));
}
impl Synom for ModuleDecl {
named!(parse -> Self, do_parse!(
attrs: many0!(do_parse!(
attr: call!(Attribute::parse_outer) >>
eq: option!(punct!(=)) >>
path: cond!(eq.is_some(), syn!(LitStr))>>
(attr, path)
)) >>
vis: syn!(Visibility) >>
mod_token: keyword!(mod) >>
ident: syn!(Ident) >>
body: alt! (
punct!(;) => { ModuleBody::Terminated }
|
braces!(many0!(DeclItem::parse)) => { ModuleBody::Content }
) >>
(ModuleDecl {
attrs,
vis,
mod_token,
ident,
body,
})
));
}
impl ToTokens for ModuleDecl {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.vis.to_tokens(tokens);
}
}
#[cfg_attr(feature = "derive-debug", derive(Debug))]
enum DeclItem {
Method(TraitItemMethod),
Type(TypeDecl),
}
#[cfg_attr(feature = "derive-debug", derive(Debug))]
struct TypeDecl {
attrs: Vec<Attribute>,
ident: Ident,
body: TypeDeclBody,
}
#[cfg_attr(feature = "derive-debug", derive(Debug))]
enum TypeDeclBody {
Content((token::Brace, Vec<TraitItemMethod>)),
Terminated(Token![;]),
}
impl DeclItem {
named!(parse -> Self, alt!(
syn!(TraitItemMethod) => { DeclItem::Method }
|
syn!(TypeDecl) => { DeclItem::Type }
));
}
impl Synom for TypeDecl {
named!(parse -> Self, do_parse!(
attrs: many0!(Attribute::parse_outer) >>
_type: keyword!(type) >>
ident: syn!(Ident) >>
body: alt!(
punct!(;) => { TypeDeclBody::Terminated }
|
braces!(many0!(TraitItemMethod::parse)) => { TypeDeclBody::Content }
) >>
(TypeDecl {
attrs,
ident,
body,
})
)
);
}
fn convert<F>(module_name: &Ident, type_name: Option<&Ident>, index: u32, method_item: TraitItemMethod, ident_mapping: Option<F>) -> TokenStream
where F: Fn(Ident) -> Ident {
let MethodSig {
constness: _,
unsafety,
abi,
ident,
decl,
} = method_item.sig;
let FnDecl {
fn_token,
generics,
paren_token,
inputs,
variadic,
output,
} = decl;
let inputs = {
let mut values = Punctuated::new();
for arg in inputs {
let bare_fn_arg = match arg {
FnArg::SelfRef(ArgSelfRef{
and_token,
lifetime,
mutability,
self_token: _
}) => {
let t = quote! {
_self: #and_token #lifetime #mutability #type_name
};
parse2::<BareFnArg>(t).expect("Should never happen [self-ref]")
}
FnArg::SelfValue(ArgSelf {
mutability,
self_token: _,
}) => {
let t = quote! {
_self: #mutability #type_name
};
parse2::<BareFnArg>(t).expect("Should never happen [self-value]")
}
FnArg::Captured(ArgCaptured {
pat,
colon_token,
ty,
}) => {
let ts = ty.into_token_stream();
let ty = if let Some(ref func) = ident_mapping {
replace_idents(ts, func)
} else {
ts
};
let t: TokenStream = quote! {
#pat #colon_token #ty
};
parse2::<BareFnArg>(t).expect("Should never happen [arg-captured]")
}
FnArg::Inferred(pat) => {
let t = quote! {
#pat: _
};
parse2::<BareFnArg>(t).expect("Should never happen [inferred]")
}
FnArg::Ignored(ty) => {
ident.span()
.unstable()
.warning("Declaring parameters without a name is deprecated, and will not be supported in the future. [Hint: Just add \"_:\" to the parameter to remove this warning...]")
.emit();
let ts = ty.into_token_stream();
let ts = if let Some(ref func) = ident_mapping {
replace_idents(ts, func)
} else {
ts
};
parse2::<BareFnArg>(ts).expect("Should never happen [ignored]")
}
};
values.push(bare_fn_arg);
}
values
};
let output = {
let ts = output.into_token_stream();
let ts = if let Some(ref func) = ident_mapping {
replace_idents(ts, func)
} else {
ts
};
parse2::<ReturnType>(ts).expect("Should never happen [return-type]")
};
let type_bare_fn = TypeBareFn {
unsafety,
abi,
fn_token,
lifetimes: None,
paren_token,
inputs,
variadic,
output,
};
let attrs = &method_item.attrs;
let load_ident = {
let name = format!("_ASSERT_METHOD_{}", index);
Ident::new(&name, ident.span())
};
let context = type_name.unwrap_or(module_name);
if generics.params.is_empty() {
quote! {
#(#attrs)*
const #load_ident: #type_bare_fn = #context::#ident;
}
} else {
let nested_function_name = {
let name = if let Some(type_name) = type_name {
format!("_load_{}_{}_{}", module_name, type_name, ident)
} else {
format!("_load_{}_{}", module_name, ident)
};
Ident::new(&name, ident.span())
};
let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
quote! {
#(#attrs)*
#[allow(non_snake_case)]
fn #nested_function_name #impl_generics() #where_clause {
let #load_ident: #type_bare_fn = #context::#ident;
}
}
}
}
fn replace_idents<F>(ts: TokenStream, func: &F) -> TokenStream
where F: Fn(Ident) -> Ident {
let mut out = TokenStream::new();
ts.into_iter()
.map(move |tt| {
match tt {
TokenTree::Group(g) => {
let delimiter = g.delimiter();
let ts = g.stream();
let out = replace_idents(ts, func);
TokenTree::Group(Group::new(delimiter, out))
},
TokenTree::Ident(i) => TokenTree::Ident(func(i)),
v => v,
}
})
.for_each(|tt| out.append(tt));
out
}