agb/agb-entry/src/lib.rs

57 lines
1.6 KiB
Rust
Raw Normal View History

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::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 {
2021-08-08 01:51:14 +10:00
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
2021-08-08 01:24:19 +10:00
_ => 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() {
2021-08-08 01:51:14 +10:00
(b'a' + rng.gen::<u8>() % 25) as char
2021-08-08 01:24:19 +10:00
} else {
2021-08-08 01:51:14 +10:00
(b'0' + rng.gen::<u8>() % 10) as char
2021-08-08 01:24:19 +10:00
}
}).collect::<String>(),
Span::call_site()
)
}