use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{ItemFn, Visibility, parse, parse_macro_input, spanned::Spanned};
struct EntryArgs {
kernel_type: syn::Ident,
}
impl syn::parse::Parse for EntryArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Err(syn::Error::new(
input.span(),
"this attribute requires a kernel type argument, e.g., `#[entry(Kernel)]`",
));
}
let kernel_type = input.parse::<syn::Ident>()?;
if !input.is_empty() {
return Err(syn::Error::new(
input.span(),
"expected a single type identifier argument",
));
}
Ok(EntryArgs { kernel_type })
}
}
pub fn entry(args: TokenStream, input: TokenStream, name: &str) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
let valid_signature = f.sig.constness.is_none()
&& f.sig.asyncness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& f.sig.variadic.is_none()
;
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[entry]` function must have signature `[unsafe] fn([arg0: usize, ...]) `",
)
.to_compile_error()
.into();
}
let entry_args = match syn::parse::<EntryArgs>(args) {
Ok(args) => args,
Err(e) => return e.to_compile_error().into(),
};
let attrs = f.attrs;
let unsafety = f.sig.unsafety;
let args = f.sig.inputs;
let stmts = f.block.stmts;
let name = format_ident!("{}", name);
let kernel_type = entry_args.kernel_type;
quote!(
#[allow(non_snake_case)]
#[unsafe(no_mangle)]
#(#attrs)*
pub #unsafety extern "C" fn #name(#args) {
somehal::init(&#kernel_type);
#(#stmts)*
}
)
.into()
}