#![doc = include_str!("../README.md")]
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{ quote, ToTokens };
use syn::{Attribute, parse_macro_input};
#[proc_macro_attribute]
pub fn impl_uni_op(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Function definition requires an argument (self: T).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let attrs = fn_item.attrs;
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.") }
};
let mut other_tkns = proc_macro2::TokenStream::new();
attrs.into_iter().fold(
&mut other_tkns,
|tkn, attr|{ tkn.extend(attr.to_token_stream()); tkn }
);
let lhs_type = &lhs.ty;
let fn_body = fn_item.block;
let fn_type = match fn_item.sig.output {
syn::ReturnType::Type(_, typ) => typ,
_ => { panic!("Function must contain a return type.") }
};
let where_clause = &fn_generics.where_clause;
quote! {
impl #fn_generics #trait_path for #lhs_type #where_clause {
type Output = #fn_type;
#other_tkns
fn #fn_name (self) -> Self::Output
#fn_body
}
}.into()
}
#[proc_macro_attribute]
pub fn impl_uni_ops(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Function definition requires an argument (self: T).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let attrs = fn_item.attrs;
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let (comments, other_tkns) = extract_comments(&attrs);
let lhs_pat = &lhs.self_token;
let lhs_type = &lhs.ty;
let fn_body = fn_item.block;
let fn_output = match fn_item.sig.output {
syn::ReturnType::Type(_, typ) => typ,
_ => { panic!("Function must contain a return type.") }
};
let where_clause = &fn_generics.where_clause;
let token = quote! {
#comments
#other_tkns
#[opimps::impl_uni_op(#trait_path)]
fn #fn_name #fn_generics (#lhs) -> #fn_output #where_clause
#fn_body
#other_tkns
#[opimps::impl_uni_op(#trait_path)]
fn #fn_name #fn_generics (#lhs_pat: &#lhs_type) -> #fn_output #where_clause
#fn_body
};
TokenStream::from(token)
}
#[proc_macro_attribute]
pub fn impl_op(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Requires two arguments (self: T1, rhs: T2).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let rhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let attrs = fn_item.attrs;
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let rhs = match rhs {
syn::FnArg::Typed(e) => e,
_ => { panic!("Error processing second argument.")}
};
let mut other_tkns = proc_macro2::TokenStream::new();
attrs.into_iter().fold(
&mut other_tkns,
|tkn, attr|{ tkn.extend(attr.to_token_stream()); tkn }
);
let lhs_type = &lhs.ty;
let rhs_type = &rhs.ty;
let fn_body = fn_item.block;
let fn_output = match fn_item.sig.output {
syn::ReturnType::Type(_, typ) => typ,
_ => { panic!("Function must contain a return type.") }
};
let where_clause = &fn_generics.where_clause;
let token = quote! {
impl #fn_generics #trait_path<#rhs_type> for #lhs_type #where_clause {
type Output = #fn_output;
#other_tkns
fn #fn_name (self, #rhs) -> Self::Output {
#fn_body
}
}
};
TokenStream::from(token)
}
#[proc_macro_attribute]
pub fn impl_ops(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics= fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Requires two arguments (self: T1, rhs: T2).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let rhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let rhs = match rhs {
syn::FnArg::Typed(e) => e,
_ => { panic!("Error processing second argument.")}
};
let lhs_pat = &lhs.self_token;
let lhs_type = &lhs.ty;
let rhs_pat = &rhs.pat;
let rhs_type = &rhs.ty;
let fn_body = fn_item.block;
let fn_output = match fn_item.sig.output {
syn::ReturnType::Type(_, typ) => typ,
_ => { panic!("Function must contain a return type.") }
};
let attrs = fn_item.attrs;
let (comments, other_tkns) = extract_comments(&attrs);
let where_clause = &fn_generics.where_clause;
let token = quote!{
#comments
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs, #rhs) -> #fn_output #where_clause
#fn_body
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs_pat: &#lhs_type, #rhs_pat: &#rhs_type) -> #fn_output #where_clause
#fn_body
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs_pat: #lhs_type, #rhs_pat: &#rhs_type) -> #fn_output #where_clause
#fn_body
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs_pat: &#lhs_type, #rhs_pat: #rhs_type) -> #fn_output #where_clause
#fn_body
};
TokenStream::from(token)
}
#[proc_macro_attribute]
pub fn impl_ops_rprim(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Requires two arguments (self: T1, rhs: T2).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let rhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let rhs = match rhs {
syn::FnArg::Typed(e) => e,
_ => { panic!("Error processing second argument.")}
};
let lhs_pat = &lhs.self_token;
let lhs_type = &lhs.ty;
let rhs_pat = &rhs.pat;
let rhs_type = &rhs.ty;
let fn_body = fn_item.block;
let fn_output = match fn_item.sig.output {
syn::ReturnType::Type(_, typ) => typ,
_ => { panic!("Function must contain a return type.") }
};
let attrs = fn_item.attrs;
let (comments, other_tkns) = extract_comments(&attrs);
let where_clause = &fn_generics.where_clause;
let token = quote!{
#comments
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs, #rhs) -> #fn_output #where_clause
#fn_body
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs_pat: &#lhs_type, #rhs_pat: #rhs_type) -> #fn_output #where_clause
#fn_body
};
TokenStream::from(token)
}
#[proc_macro_attribute]
pub fn impl_ops_lprim(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Requires two arguments (self: T1, rhs: T2).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let rhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let rhs = match rhs {
syn::FnArg::Typed(e) => e,
_ => { panic!("Error processing second argument.")}
};
let rhs_pat = &rhs.pat;
let rhs_type = &rhs.ty;
let fn_body = fn_item.block;
let fn_output = match fn_item.sig.output {
syn::ReturnType::Type(_, typ) => typ,
_ => { panic!("Function must contain a return type.") }
};
let attrs = fn_item.attrs;
let (comments, other_tkns) = extract_comments(&attrs);
let where_clause = &fn_generics.where_clause;
let token = quote!{
#comments
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs, #rhs) -> #fn_output #where_clause
#fn_body
#other_tkns
#[opimps::impl_op(#trait_path)]
fn #fn_name #fn_generics (#lhs, #rhs_pat: &#rhs_type) -> #fn_output #where_clause
#fn_body
};
TokenStream::from(token)
}
#[proc_macro_attribute]
pub fn impl_op_assign(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Requires two arguments (self: T1, rhs: T2).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let rhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let attrs = fn_item.attrs;
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let rhs = match rhs {
syn::FnArg::Typed(e) => e,
_ => { panic!("Error processing second argument.")}
};
let mut other_tkns = proc_macro2::TokenStream::new();
attrs.into_iter().fold(
&mut other_tkns,
|tkn, attr|{ tkn.extend(attr.to_token_stream()); tkn }
);
let lhs_type = &lhs.ty;
let rhs_type = &rhs.ty;
let fn_body = fn_item.block;
let where_clause = &fn_generics.where_clause;
let token = quote! {
impl #fn_generics #trait_path<#rhs_type> for #lhs_type #where_clause {
#other_tkns
fn #fn_name (&mut self, #rhs) {
#fn_body
}
}
};
TokenStream::from(token)
}
#[proc_macro_attribute]
pub fn impl_ops_assign(attr: TokenStream, item: TokenStream) -> TokenStream {
let trait_path = parse_macro_input!(attr as syn::TypePath);
let fn_item = parse_macro_input!(item as syn::ItemFn);
let fn_name = fn_item.sig.ident;
let fn_generics = fn_item.sig.generics;
let mut fn_args = fn_item.sig.inputs.into_iter();
const INSUFFICIENT_ARGS_MSG: &str = "Requires two arguments (self: T1, rhs: T2).";
let lhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let rhs = fn_args.next().expect(INSUFFICIENT_ARGS_MSG);
let attrs = fn_item.attrs;
let lhs = match lhs {
syn::FnArg::Receiver(e) => e,
_ => { panic!("Error processing first argument.")}
};
let rhs = match rhs {
syn::FnArg::Typed(e) => e,
_ => { panic!("Error processing second argument.")}
};
let mut other_tkns = proc_macro2::TokenStream::new();
(&attrs).into_iter().fold(
&mut other_tkns,
|tkn, attr|{ tkn.extend(attr.to_token_stream()); tkn }
);
let rhs_type = &rhs.ty;
let rhs_pat = &rhs.pat;
let fn_body = fn_item.block;
let where_clause = &fn_generics.where_clause;
let (comments, other_tkns) = extract_comments(&attrs);
let token = quote! {
#comments
#other_tkns
#[opimps::impl_op_assign(#trait_path)]
fn #fn_name #fn_generics (#lhs, #rhs) #where_clause
#fn_body
#other_tkns
#[opimps::impl_op_assign(#trait_path)]
fn #fn_name #fn_generics (#lhs, #rhs_pat: &#rhs_type) #where_clause
#fn_body
};
TokenStream::from(token)
}
fn extract_comments(attrs: &Vec<Attribute>) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
let mut other_tkns = proc_macro2::TokenStream::new();
let mut comments = proc_macro2::TokenStream::new();
for attr in attrs.into_iter() {
if attr.path().is_ident("doc") {
comments.extend(attr.to_token_stream());
} else {
other_tkns.extend(attr.to_token_stream());
}
}
(comments, other_tkns)
}