blob: 6557bd12f969aa26a51dbfb41a041e297967313f [file] [log] [blame]
//! Macro for topshim
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, Block, Ident, Path, Stmt, Token, Type};
/// Parsed structure for callback variant
struct CbVariant {
dispatcher: Type,
fn_pair: (Ident, Path),
arg_pairs: Vec<(Type, Type)>,
stmts: Vec<Stmt>,
}
impl Parse for CbVariant {
fn parse(input: ParseStream) -> Result<Self> {
// First thing should be the dispatcher
let dispatcher: Type = input.parse()?;
input.parse::<Token![,]>()?;
// Name and return type are parsed
let name: Ident = input.parse()?;
input.parse::<Token![->]>()?;
let rpath: Path = input.parse()?;
let mut arg_pairs: Vec<(Type, Type)> = Vec::new();
let mut stmts: Vec<Stmt> = Vec::new();
while input.peek(Token![,]) {
// Discard the comma
input.parse::<Token![,]>()?;
// Check if we're expecting the final Block
if input.peek(syn::token::Brace) {
let block: Block = input.parse()?;
stmts.extend(block.stmts);
break;
}
// Grab the next type argument
let start_type: Type = input.parse()?;
if input.peek(Token![->]) {
// Discard ->
input.parse::<Token![->]>()?;
let end_type: Type = input.parse()?;
arg_pairs.push((start_type, end_type))
} else {
arg_pairs.push((start_type.clone(), start_type));
}
}
// TODO: Validate there are no more tokens; currently they are ignored.
Ok(CbVariant { dispatcher, fn_pair: (name, rpath), arg_pairs, stmts })
}
}
#[proc_macro]
/// Implement C function to convert callback into enum variant.
///
/// Expected syntax:
/// cb_variant(DispatcherType, function_name -> EnumType::Variant, args..., {
/// // Statements (maybe converting types)
/// // Args in order will be _0, _1, etc.
/// })
pub fn cb_variant(input: TokenStream) -> TokenStream {
let parsed_cptr = parse_macro_input!(input as CbVariant);
let dispatcher = parsed_cptr.dispatcher;
let (ident, rpath) = parsed_cptr.fn_pair;
let mut params = proc_macro2::TokenStream::new();
let mut args = proc_macro2::TokenStream::new();
for (i, (start, end)) in parsed_cptr.arg_pairs.iter().enumerate() {
let ident = format_ident!("_{}", i);
params.extend(quote! { #ident: #start, });
// Argument needs an into translation if it doesn't match the start
if start != end {
args.extend(quote! { #end::from(#ident), });
} else {
args.extend(quote! {#ident,});
}
}
let mut stmts = proc_macro2::TokenStream::new();
for stmt in parsed_cptr.stmts {
stmts.extend(quote! { #stmt });
}
let tokens = quote! {
#[no_mangle]
extern "C" fn #ident(#params) {
#stmts
unsafe {
(get_dispatchers().lock().unwrap().get::<#dispatcher>().unwrap().clone().lock().unwrap().dispatch)(#rpath(#args));
}
}
};
TokenStream::from(tokens)
}