#![feature(proc_macro_diagnostic)]
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;
use proc_macro::TokenStream as TStream;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
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 items: Vec<TokenStream> = body.into_iter()
.map(|item| {
match item {
DeclItem::Method(method_item) => {
let t = gen_method_assertion(index, module_name, method_item);
index += 1;
t
}
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| {
let t = gen_method_assertion(index, type_name, method_item);
index += 1;
t
})
.collect()
} else {
vec![]
};
quote! {
#(#attrs)*
{
use self::#module_name::#type_name;
#(#method_items)*
}
}
}
}
})
.collect();
let function_name = format!("_load_{}", module_name);
let function_name = Ident::new(&function_name, Span::call_site());
let t = quote! {
#[allow(dead_code)]
fn #function_name() {
#(#items)*
}
};
t.to_tokens(&mut output);
}
}
output.into()
}
#[derive(Debug)]
struct ModuleDecl {
attrs: Vec<(Attribute, Option<LitStr>)>,
vis: Visibility,
mod_token: Token![mod],
ident: Ident,
body: ModuleBody,
}
#[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);
}
}
#[derive(Debug)]
enum DeclItem {
Method(TraitItemMethod),
Type(TypeDecl),
}
#[derive(Debug)]
struct TypeDecl {
attrs: Vec<Attribute>,
ident: Ident,
body: TypeDeclBody,
}
#[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 gen_method_assertion(index: u32, context: &Ident, method_item: TraitItemMethod) -> TokenStream {
if let Some(body) = method_item.default {
body.span()
.unstable()
.error("A body isn't valid here.")
.emit();
return TokenStream::new();
}
let attrs = &method_item.attrs;
let load_name = format!("_ASSERT_METHOD_{}", index);
let load_ident = Ident::new(&load_name, Span::call_site());
let (ty, path) = convert(context, method_item.sig);
quote! {
#(#attrs)*
const #load_ident: #ty = #path;
}
}
fn convert(context: &Ident, sig: MethodSig) -> (TypeBareFn, ExprPath) {
let MethodSig {
constness,
unsafety,
abi,
ident,
decl,
} = 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(_v) => {
continue;
}
FnArg::SelfValue(_v) => {
continue;
}
FnArg::Captured(ArgCaptured {
pat,
colon_token,
ty,
}) => {
BareFnArg {
name: None,
ty,
}
}
FnArg::Inferred(_v) => {
continue;
}
FnArg::Ignored(_v) => {
continue;
}
};
values.push(bare_fn_arg);
}
values
};
let type_bare_fn = TypeBareFn {
unsafety,
abi,
fn_token,
lifetimes: None,
paren_token,
inputs,
variadic,
output,
};
let mut segments = Punctuated::new();
segments.push(PathSegment {
ident: (*context).clone(),
arguments: PathArguments::None,
});
segments.push(PathSegment {
ident: ident.clone(),
arguments: PathArguments::None,
});
let path = Path {
leading_colon: None,
segments,
};
let path = ExprPath {
attrs: Vec::new(),
qself: None,
path,
};
(type_bare_fn, path)
}