extern crate proc_macro;
use self::proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parenthesized, braced, parse_macro_input, Block, Ident, Token, token::Paren, Type, Visibility};
struct Function {
argument_idents : Vec<Ident>, argument_types: Vec<Type>, arugment_mutability : Vec<bool>, body : Block, }
struct FunctionGroup {
visibility : Visibility, name: Ident, output: Option<Type>, self_input : Option<Type>, self_type : Option<Type>, self_mut : bool,
functions : Vec<Function>, }
impl Parse for Function {
fn parse(input: ParseStream) -> Result<Self> {
let mut argument_idents = vec!();
let mut argument_types = vec!();
let mut arugment_mutability = vec!();
let content;
parenthesized!(content in input);
loop {
let lookahead = content.lookahead1();
if lookahead.peek(Token![mut]){
content.parse::<Token![mut]>()?;
arugment_mutability.push(true)
} else {
arugment_mutability.push(false)
}
let lookahead = content.lookahead1();
if !lookahead.peek(Ident) {
break;
}
argument_idents.push(content.parse()?);
content.parse::<Token![:]>()?;
argument_types.push(content.parse()?);
let lookahead = content.lookahead1();
if lookahead.peek(Token![,]) {
content.parse::<Token![,]>()?;
}
}
let body : Block = input.parse()?;
let lookahead = input.lookahead1();
if lookahead.peek(Token![;]) {
input.parse::<Token![;]>()?;
}
Ok(Function{argument_idents, argument_types, arugment_mutability, body})
}
}
impl Parse for FunctionGroup {
fn parse(input: ParseStream) -> Result<Self> {
let visibility: Visibility = input.parse()?;
input.parse::<Token![fn]>()?;
let name: Ident = input.parse()?;
let lookahead = input.lookahead1();
let mut self_input : Option<Type> = None;
let mut self_type : Option<Type> = None;
let mut self_mut : bool = false;
if lookahead.peek(Paren) {
let content;
parenthesized!(content in input);
let lookahead = content.lookahead1();
if lookahead.peek(Token![mut]){
content.parse::<Token![mut]>()?;
self_mut = true;
} else {
self_mut = false;
}
self_input = Some(content.parse()?);
content.parse::<Token![:]>()?;
self_type = Some(content.parse()?);
}
let lookahead = input.lookahead1();
let mut output : Option<Type> = None;
if lookahead.peek(Token![-]) {
input.parse::<Token![-]>()?;
input.parse::<Token![>]>()?;
output = Some(input.parse()?);
}
let mut functions : Vec<Function> = vec!();
let content;
braced!(content in input);
loop {
let lookahead = content.lookahead1();
if !lookahead.peek(Paren) {
break;
}
functions.push(content.parse()?);
}
Ok(FunctionGroup {visibility,name,output,self_input,self_type,self_mut,functions})
}
}
#[proc_macro]
pub fn function_group(input: TokenStream) -> TokenStream {
let function_group : FunctionGroup = parse_macro_input!(input as FunctionGroup);
if function_group.self_input.is_some() && function_group.self_type.is_some() {
return function_group_method(function_group);
}
return function_group_fn(function_group);
}
fn function_group_method(group : FunctionGroup) -> TokenStream {
let FunctionGroup {visibility,name,output,self_input,self_type,self_mut,functions} = group;
let output = if output.is_none() {
quote!{ () }
} else {
quote!{ #output }
};
let self_input = self_input.unwrap();
let self_input = if self_mut {
quote!{mut #self_input}
} else {
quote!{#self_input}
};
let self_type = self_type.unwrap();
let group_trait = quote! {
#[allow(non_camel_case_types)]
#visibility trait #name<Arg> {
fn #name(#self_input, _args : Arg) -> #output;
}
};
let mut group_impl = quote!{};
for function in functions {
let mut func_types = quote!{};
for fn_type in function.argument_types{
func_types = quote!{ #func_types #fn_type,};
}
let mut func_idents = quote!{};
for (fn_ident, fn_mut) in function.argument_idents.iter().zip(function.arugment_mutability.iter()) {
if *fn_mut {
func_idents = quote!{ #func_idents mut #fn_ident,};
} else {
func_idents = quote!{ #func_idents #fn_ident,};
}
}
let func_block = function.body;
group_impl = quote! {
#group_impl
impl #name<(#func_types)> for #self_type {
fn #name(#self_input, (#func_idents) : (#func_types)) -> #output {
#func_block
}
}
}
}
let expanded = quote! {
#group_trait
#group_impl
};
TokenStream::from(expanded)
}
fn function_group_fn(group : FunctionGroup) -> TokenStream {
let FunctionGroup {visibility,name,output,self_input: _,self_type: _,self_mut: _,functions} = group;
let output = if output.is_none() {
quote!{ () }
} else {
quote!{ #output }
};
let group_trait = quote! {
#[allow(non_camel_case_types)]
#visibility trait #name {
fn #name(self) -> #output;
}
};
let group_fn = quote! {
#visibility fn #name<T: #name>(a : T) -> #output {
a.#name()
}
};
let mut group_impl = quote!{};
for function in functions {
let mut func_types = quote!{};
for fn_type in function.argument_types {
func_types = quote!{ #func_types #fn_type,};
}
let mut func_idents = quote!{};
for (fn_ident, fn_mut) in function.argument_idents.iter().zip(function.arugment_mutability.iter()) {
if *fn_mut {
func_idents = quote!{ #func_idents mut #fn_ident,};
} else {
func_idents = quote!{ #func_idents #fn_ident,};
}
}
let func_block = function.body;
group_impl = quote! {
#group_impl
impl #name for (#func_types) {
fn #name(self) -> #output {
let (#func_idents) = self;
#func_block
}
}
}
}
let expanded = quote! {
#group_trait
#group_fn
#group_impl
};
TokenStream::from(expanded)
}