extern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::spanned::Spanned;
use syn::{
parse_macro_input, AttributeArgs, FnArg, ItemFn, Lit, LitInt, Meta, NestedMeta, ReturnType,
Signature,
};
#[proc_macro_attribute]
pub fn setup(_args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as ItemFn);
let funcall = match &ast.sig {
Signature {
ident: id,
inputs: params,
asyncness: None,
unsafety: None,
variadic: None,
output: ret,
..
} if params.is_empty() => {
if let ReturnType::Type(_, ty) = ret {
quote_spanned! { ty.span() =>
if let ::std::result::Result::Err(e) = #id() {
return e.to_raw_error();
}
}
} else {
quote_spanned! { id.span() => #id() }
}
}
sig => invalid_function_signature(sig.span()),
};
(quote! {
#[inline(always)]
#ast
#[no_mangle]
pub extern "C" fn WolframLibrary_initialize(
data: ::wll::sys::WolframLibraryData
) -> ::wll::sys::errcode_t {
if let ::std::result::Result::Err(e) = ::wll::global::initialize_lib_data(data) {
e.to_raw_error()
} else {
#funcall;
::wll::sys::LIBRARY_NO_ERROR
}
}
#[no_mangle]
pub extern "C" fn WolframLibrary_getVersion() -> ::wll::sys::mint {
::wll::sys::WolframLibraryVersion
}
})
.into()
}
#[proc_macro_attribute]
pub fn teardown(_args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as ItemFn);
let funcall = match &ast.sig {
Signature {
ident: id,
inputs: params,
asyncness: None,
unsafety: None,
variadic: None,
output: ReturnType::Default,
..
} if params.is_empty() => quote_spanned! { id.span() => #id() },
sig => invalid_function_signature(sig.span()),
};
(quote! {
#[inline(always)]
#ast
#[no_mangle]
pub extern "C" fn WolframLibrary_uninitialize(
_: ::wll::sys::WolframLibraryData
) {
#funcall;
}
})
.into()
}
#[proc_macro_attribute]
pub fn export(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let input = parse_macro_input!(input as ItemFn);
let Signature {
ident: funame,
inputs: params,
paren_token: sig_paren,
..
} = &input.sig;
let exportname = match args.len() {
0 => format_ident!("wll_{}", funame).into_token_stream(),
_ => get_export_name_from_meta(&args[0]),
};
let paramc = params.len();
let paramclit = LitInt::new(¶mc.to_string(), sig_paren.span);
let argvars = (0..paramc).map(|i| format_ident!("arg{:o}", i));
let argvars2 = argvars.clone();
let arggets = params.iter().enumerate().map(|(i, arg)| match arg {
FnArg::Receiver(recr) => invalid_function_signature(recr.span()),
FnArg::Typed(pty) => {
let ty = &pty.ty;
let inner = quote_spanned! { ty.span() =>
<#ty>::try_get_arg(args.add(#i).read())
};
unwrap_errcode(inner, ty.span())
}
});
let (funcall, setres) = match &input.sig {
Signature {
asyncness: None,
variadic: None,
output: ReturnType::Type(_, ty),
..
} => {
let inner = quote_spanned! { input.sig.span() =>
#funame(#(#argvars2),*)
};
let setres = quote_spanned! { ty.span() =>
match ret.try_set_arg(&res) {
::std::result::Result::Ok(()) => ::wll::sys::LIBRARY_NO_ERROR,
::std::result::Result::Err(err) => err.to_raw_error(),
}
};
(unwrap_errcode(inner, ty.span()), setres)
}
sig => (
invalid_function_signature(sig.span()),
invalid_function_signature(sig.span()),
),
};
(quote! {
#[inline]
#input
#[no_mangle]
pub unsafe extern "C" fn #exportname(
lib_data: ::wll::sys::WolframLibraryData,
argc: ::wll::sys::mint,
args: *const ::wll::sys::MArgument,
res: ::wll::sys::MArgument,
) -> ::wll::sys::errcode_t {
use ::wll::adaptor::{MArgumentGetter, MArgumentSetter};
let _lib_data = ::wll::global::LibDataLocalizer::new(lib_data);
if argc != #paramclit {
return ::wll::sys::LIBRARY_TYPE_ERROR;
}
#(let #argvars = #arggets;)*
let ret = #funcall;
#setres
}
})
.into()
}
fn get_export_name_from_meta(meta: &NestedMeta) -> proc_macro2::TokenStream {
match meta {
NestedMeta::Meta(Meta::Path(path)) => {
if let Some(ident) = path.get_ident() {
ident.to_token_stream()
} else {
syn::Error::new(path.span(), "expected identifier for export name.")
.to_compile_error()
}
}
NestedMeta::Lit(Lit::Str(str)) => match str.parse::<syn::Ident>() {
Ok(ident) => ident.into_token_stream(),
Err(e) => e.to_compile_error(),
},
other => {
syn::Error::new(other.span(), "expected identifier for export name.").to_compile_error()
}
}
}
fn unwrap_errcode(
expr: proc_macro2::TokenStream,
span: proc_macro2::Span,
) -> proc_macro2::TokenStream {
quote_spanned! { span =>
match #expr {
::std::result::Result::Ok(val) => val,
::std::result::Result::Err(err) => return err.to_raw_error(),
}
}
}
fn invalid_function_signature(span: proc_macro2::Span) -> proc_macro2::TokenStream {
syn::Error::new(span, "invalid function signature!").to_compile_error()
}