2021-09-09 16:50:58 -04:00
#[ macro_use ]
extern crate syn ;
#[ macro_use ]
extern crate quote ;
use proc_macro ::TokenStream ;
use quote ::ToTokens ;
2022-01-22 13:59:12 -05:00
use syn ::ItemFn ;
2021-09-09 16:50:58 -04:00
2022-01-22 14:36:41 -05:00
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
}
} } ;
}
2021-09-09 16:50:58 -04:00
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 " ) ) ;
}
2022-01-22 13:59:12 -05:00
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 ( ) ] ;
2021-12-09 14:32:56 -05:00
}
2021-09-09 16:50:58 -04:00
#[ proc_macro_attribute ]
pub fn gmod13_open ( _attr : TokenStream , tokens : TokenStream ) -> TokenStream {
2022-01-22 14:36:41 -05:00
wrap_compile_error! ( tokens , {
let mut input = syn ::parse ::< ItemFn > ( tokens ) ? ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
let lua_ident = format_ident! ( " {} " , match & input . sig . inputs [ 0 ] {
syn ::FnArg ::Typed ( arg ) = > arg . pat . to_token_stream ( ) . to_string ( ) ,
_ = > unreachable! ( ) ,
} ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// Capture the Lua state
input . block . stmts . insert ( 0 , syn ::parse2 ( quote! ( ::gmod ::lua ::__set_state__internal ( #lua_ident ) ; ) ) . unwrap ( ) ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// Load lua_shared
input . block . stmts . insert ( 0 , syn ::parse2 ( quote! ( #[ allow(unused_unsafe) ] unsafe { ::gmod ::lua ::load ( ) } ) ) . unwrap ( ) ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// Make sure it's valid
check_lua_function ( & mut input ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// No mangling
input . attrs . push ( parse_quote! ( #[ no_mangle ] ) ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// Make the return type nice and dynamic
genericify_return ( & mut input ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
Ok ( input . into_token_stream ( ) . into ( ) )
} )
2021-09-09 16:50:58 -04:00
}
#[ proc_macro_attribute ]
pub fn gmod13_close ( _attr : TokenStream , tokens : TokenStream ) -> TokenStream {
2022-01-22 14:36:41 -05:00
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 ( ) )
} )
2021-09-09 16:50:58 -04:00
}
#[ proc_macro_attribute ]
pub fn lua_function ( _attr : TokenStream , tokens : TokenStream ) -> TokenStream {
2022-01-22 14:36:41 -05:00
wrap_compile_error! ( tokens , {
let mut input = syn ::parse ::< ItemFn > ( tokens ) ? ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// Make sure it's valid
check_lua_function ( & mut input ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
// Make the return type nice and dynamic
genericify_return ( & mut input ) ;
2022-01-22 13:59:12 -05:00
2022-01-22 14:36:41 -05:00
Ok ( input . into_token_stream ( ) . into ( ) )
} )
2021-09-09 16:50:58 -04:00
}