105 lines
3.1 KiB
Rust
105 lines
3.1 KiB
Rust
#[macro_use]
|
|
extern crate syn;
|
|
|
|
#[macro_use]
|
|
extern crate quote;
|
|
|
|
use proc_macro::TokenStream;
|
|
use quote::ToTokens;
|
|
use syn::ItemFn;
|
|
|
|
macro_rules! wrap_compile_error {
|
|
($input:ident, $code:expr) => {{
|
|
let orig_tokens = $input.clone();
|
|
match (|| -> Result<TokenStream, syn::Error> { $code })() {
|
|
Ok(tokens) => tokens,
|
|
Err(_) => return orig_tokens
|
|
}
|
|
}};
|
|
}
|
|
|
|
fn check_lua_function(input: &mut ItemFn) {
|
|
assert!(input.sig.asyncness.is_none(), "Cannot be async");
|
|
assert!(input.sig.constness.is_none(), "Cannot be const");
|
|
assert!(input.sig.inputs.len() == 1, "There can only be one argument, and it should be a pointer to the Lua state (gmod::lua::State)");
|
|
assert!(input.sig.abi.is_none() || input.sig.abi.as_ref().and_then(|abi| abi.name.as_ref()).map(|abi| abi.value() == "C-unwind").unwrap_or(true), "Do not specify an ABI");
|
|
input.sig.abi = Some(syn::parse_quote!(extern "C-unwind"));
|
|
}
|
|
|
|
fn genericify_return(item_fn: &mut ItemFn) {
|
|
let stmts = std::mem::take(&mut item_fn.block.stmts);
|
|
let output = std::mem::replace(&mut item_fn.sig.output, parse_quote!(-> i32));
|
|
item_fn.block.stmts = vec![syn::parse2(quote!({::gmod::lua::ValuesReturned::from((|| #output {#(#stmts);*})()).into()})).unwrap()];
|
|
}
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
|
wrap_compile_error!(tokens, {
|
|
let mut input = syn::parse::<ItemFn>(tokens)?;
|
|
|
|
let lua_ident = format_ident!("{}", match &input.sig.inputs[0] {
|
|
syn::FnArg::Typed(arg) => arg.pat.to_token_stream().to_string(),
|
|
_ => unreachable!(),
|
|
});
|
|
|
|
// Capture the Lua state
|
|
input.block.stmts.insert(0, syn::parse2(quote!(::gmod::lua::__set_state__internal(#lua_ident);)).unwrap());
|
|
|
|
// Load lua_shared
|
|
input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
|
|
|
|
// Make sure it's valid
|
|
check_lua_function(&mut input);
|
|
|
|
// No mangling
|
|
input.attrs.push(parse_quote!(#[no_mangle]));
|
|
|
|
// Make the return type nice and dynamic
|
|
genericify_return(&mut input);
|
|
|
|
Ok(input.into_token_stream().into())
|
|
})
|
|
}
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
|
wrap_compile_error!(tokens, {
|
|
let mut input = syn::parse::<ItemFn>(tokens)?;
|
|
|
|
// Make sure it's valid
|
|
check_lua_function(&mut input);
|
|
|
|
// No mangling
|
|
input.attrs.push(parse_quote!(#[no_mangle]));
|
|
|
|
// Shutdown gmcl thread if it's running
|
|
#[cfg(feature = "gmcl")] {
|
|
let stmts = std::mem::take(&mut input.block.stmts);
|
|
input.block.stmts = vec![syn::parse2(quote!({
|
|
let ret = (|| {#(#stmts);*})();
|
|
::gmod::gmcl::restore_stdout();
|
|
ret
|
|
})).unwrap()];
|
|
}
|
|
|
|
// Make the return type nice and dynamic
|
|
genericify_return(&mut input);
|
|
|
|
Ok(input.into_token_stream().into())
|
|
})
|
|
}
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
|
wrap_compile_error!(tokens, {
|
|
let mut input = syn::parse::<ItemFn>(tokens)?;
|
|
|
|
// Make sure it's valid
|
|
check_lua_function(&mut input);
|
|
|
|
// Make the return type nice and dynamic
|
|
genericify_return(&mut input);
|
|
|
|
Ok(input.into_token_stream().into())
|
|
})
|
|
} |