2021-08-08 01:24:19 +10:00
|
|
|
// This macro is based very heavily on the entry one in rust-embedded
|
|
|
|
use proc_macro::TokenStream;
|
|
|
|
|
|
|
|
use proc_macro2::Span;
|
|
|
|
use quote::quote;
|
|
|
|
use syn::{ItemFn, ReturnType, Type, Visibility, Ident};
|
|
|
|
use rand;
|
|
|
|
use rand::Rng;
|
|
|
|
|
|
|
|
#[proc_macro_attribute]
|
|
|
|
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
|
|
let f: ItemFn = syn::parse(input).expect("#[agb::entry] must be applied to a function");
|
|
|
|
|
|
|
|
// Check that the function signature is correct
|
|
|
|
assert!(
|
|
|
|
f.sig.constness.is_none()
|
|
|
|
&& f.vis == Visibility::Inherited
|
|
|
|
&& f.sig.abi.is_none()
|
|
|
|
&& f.sig.inputs.is_empty()
|
|
|
|
&& f.sig.generics.params.is_empty()
|
|
|
|
&& f.sig.generics.where_clause.is_none()
|
|
|
|
&& match f.sig.output {
|
|
|
|
ReturnType::Type(_, ref ty) => match **ty {
|
|
|
|
Type::Never(_) => true,
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
"#[agb::entry] must have signature [unsafe] fn () -> !"
|
|
|
|
);
|
|
|
|
|
2021-08-08 01:46:16 +10:00
|
|
|
assert!(args.to_string() == "", "Must pass no args to #[agb::entry] macro");
|
2021-08-08 01:24:19 +10:00
|
|
|
|
|
|
|
let fn_name = random_ident();
|
|
|
|
|
|
|
|
let attrs = f.attrs;
|
|
|
|
let stmts = f.block.stmts;
|
|
|
|
|
|
|
|
quote!(
|
|
|
|
#[export_name = "main"]
|
|
|
|
#(#attrs)*
|
|
|
|
pub fn #fn_name() -> ! {
|
|
|
|
#(#stmts)*
|
|
|
|
}
|
|
|
|
).into()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn random_ident() -> Ident {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
Ident::new(
|
|
|
|
&(0..16).map(|i| {
|
|
|
|
if i == 0 || rng.gen() {
|
|
|
|
('a' as u8 + rng.gen::<u8>() % 25) as char
|
|
|
|
} else {
|
|
|
|
('0' as u8 + rng.gen::<u8>() % 10) as char
|
|
|
|
}
|
|
|
|
}).collect::<String>(),
|
|
|
|
Span::call_site()
|
|
|
|
)
|
|
|
|
}
|