feat: a lot
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use quote::{ToTokens, format_ident, quote};
|
||||
use syn::{
|
||||
parse_macro_input, FnArg, Item, ItemConst, ItemFn, ItemMod, Pat, PatType, Type,
|
||||
Visibility,
|
||||
FnArg, Item, ItemFn, ItemMod, Pat, PatType, Type, Visibility, parse_macro_input,
|
||||
};
|
||||
|
||||
pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
||||
let item_mod = parse_macro_input!(input as ItemMod);
|
||||
let mod_name = &item_mod.ident;
|
||||
let visibility = &item_mod.vis;
|
||||
|
||||
let (_brace, items) = match item_mod.content.clone() {
|
||||
let (_brace, items) = match item_mod.content {
|
||||
Some(content) => content,
|
||||
None => {
|
||||
return syn::Error::new_spanned(
|
||||
@@ -23,63 +24,76 @@ pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
let mut pub_item_mod: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||
let mut const_inserters = Vec::new();
|
||||
let mut global_inserters = Vec::new();
|
||||
let mut scoped_inserters = Vec::new();
|
||||
let mut consts = Vec::new();
|
||||
let mut global = Vec::new();
|
||||
let mut scoped = Vec::new();
|
||||
let mut wrappers = Vec::new();
|
||||
|
||||
for item in &items {
|
||||
match item {
|
||||
Item::Const(item_const) => {
|
||||
let inserter = generate_const_inserter(item_const);
|
||||
const_inserters.push(inserter);
|
||||
pub_item_mod.push(quote! {
|
||||
pub #item_const
|
||||
}.into());
|
||||
let name_str = item_const
|
||||
.ident
|
||||
.to_string()
|
||||
.from_case(Case::UpperSnake)
|
||||
.to_case(Case::Camel);
|
||||
let const_name = &item_const.ident;
|
||||
consts.push(quote! { (#name_str, builtins::#const_name) });
|
||||
pub_item_mod.push(
|
||||
quote! {
|
||||
pub #item_const
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
Item::Fn(item_fn) => {
|
||||
let (inserter, wrapper) = match generate_fn_wrapper(item_fn) {
|
||||
let (primop, wrapper) = match generate_primop_wrapper(item_fn) {
|
||||
Ok(result) => result,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
if matches!(item_fn.vis, Visibility::Public(_)) {
|
||||
global_inserters.push(inserter);
|
||||
global.push(primop);
|
||||
pub_item_mod.push(quote! { #item_fn }.into());
|
||||
} else {
|
||||
scoped_inserters.push(inserter);
|
||||
pub_item_mod.push(quote! {
|
||||
pub #item_fn
|
||||
}.into());
|
||||
scoped.push(primop);
|
||||
pub_item_mod.push(
|
||||
quote! {
|
||||
pub #item_fn
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
wrappers.push(wrapper);
|
||||
}
|
||||
item => pub_item_mod.push(item.to_token_stream())
|
||||
item => pub_item_mod.push(item.to_token_stream()),
|
||||
}
|
||||
}
|
||||
|
||||
let consts_len = consts.len();
|
||||
let global_len = global.len();
|
||||
let scoped_len = scoped.len();
|
||||
let output = quote! {
|
||||
mod builtins {
|
||||
#visibility mod #mod_name {
|
||||
#(#pub_item_mod)*
|
||||
#(#wrappers)*
|
||||
pub const CONSTS_LEN: usize = #consts_len;
|
||||
pub const GLOBAL_LEN: usize = #global_len;
|
||||
pub const SCOPED_LEN: usize = #scoped_len;
|
||||
}
|
||||
|
||||
pub struct Builtins<Ctx: BuiltinsContext> {
|
||||
pub consts: ::std::vec::Vec<(String, ::nixjit_value::Const)>,
|
||||
pub global: ::std::vec::Vec<(String, fn(&mut Ctx, Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>>)>,
|
||||
pub scoped: ::std::vec::Vec<(String, fn(&mut Ctx, Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>>)>,
|
||||
pub consts: [(&'static str, ::nixjit_value::Const); #mod_name::CONSTS_LEN],
|
||||
pub global: [(&'static str, usize, fn(&mut Ctx, Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value>); #mod_name::GLOBAL_LEN],
|
||||
pub scoped: [(&'static str, usize, fn(&mut Ctx, Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value>); #mod_name::SCOPED_LEN],
|
||||
}
|
||||
|
||||
impl<Ctx: BuiltinsContext> Builtins<Ctx> {
|
||||
pub fn new() -> Self {
|
||||
let mut consts = ::std::vec::Vec::new();
|
||||
let mut global = ::std::vec::Vec::new();
|
||||
let mut scoped = ::std::vec::Vec::new();
|
||||
|
||||
#(#const_inserters)*
|
||||
#(#global_inserters)*
|
||||
#(#scoped_inserters)*
|
||||
|
||||
Self { consts, global, scoped }
|
||||
Self {
|
||||
consts: [#(#consts,)*],
|
||||
global: [#(#global,)*],
|
||||
scoped: [#(#scoped,)*],
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -87,26 +101,17 @@ pub fn builtins_impl(input: TokenStream) -> TokenStream {
|
||||
output.into()
|
||||
}
|
||||
|
||||
fn generate_const_inserter(
|
||||
item_const: &ItemConst,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name_str = item_const.ident.to_string().from_case(Case::UpperSnake).to_case(Case::Camel);
|
||||
let const_name = &item_const.ident;
|
||||
|
||||
quote! {
|
||||
consts.push((#name_str.to_string(), builtins::#const_name));
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_fn_wrapper(
|
||||
fn generate_primop_wrapper(
|
||||
item_fn: &ItemFn,
|
||||
) -> syn::Result<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
|
||||
let fn_name = &item_fn.sig.ident;
|
||||
let name_str = fn_name.to_string().from_case(Case::Snake).to_case(Case::Camel);
|
||||
let name_str = fn_name
|
||||
.to_string()
|
||||
.from_case(Case::Snake)
|
||||
.to_case(Case::Camel);
|
||||
let wrapper_name = format_ident!("wrapper_{}", fn_name);
|
||||
let mod_name = format_ident!("builtins");
|
||||
|
||||
let is_pub = matches!(item_fn.vis, Visibility::Public(_));
|
||||
let mut user_args = item_fn.sig.inputs.iter().peekable();
|
||||
|
||||
let has_ctx = if let Some(FnArg::Typed(first_arg)) = user_args.peek() {
|
||||
@@ -185,15 +190,13 @@ fn generate_fn_wrapper(
|
||||
quote! { Ok(#fn_name(#call_args).into()) }
|
||||
};
|
||||
|
||||
let fn_type = quote! { fn(&mut Ctx, Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>> };
|
||||
let inserter = if is_pub {
|
||||
quote! { global.push((#name_str.to_string(), #mod_name::#wrapper_name as #fn_type)); }
|
||||
} else {
|
||||
quote! { scoped.push((#name_str.to_string(), #mod_name::#wrapper_name as #fn_type)); }
|
||||
};
|
||||
let arity = arg_names.len();
|
||||
let fn_type = quote! { fn(&mut Ctx, Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value> };
|
||||
let primop =
|
||||
quote! { (#name_str, #arity, #mod_name::#wrapper_name as #fn_type) };
|
||||
|
||||
let wrapper = quote! {
|
||||
pub fn #wrapper_name<Ctx: BuiltinsContext>(ctx: &mut Ctx, mut args: Vec<::nixjit_eval::Value<Ctx>>) -> ::nixjit_error::Result<::nixjit_eval::Value<Ctx>> {
|
||||
pub fn #wrapper_name<Ctx: BuiltinsContext>(ctx: &mut Ctx, mut args: Vec<::nixjit_eval::Value>) -> ::nixjit_error::Result<::nixjit_eval::Value> {
|
||||
if args.len() != #arg_count {
|
||||
return Err(::nixjit_error::Error::EvalError(format!("Function '{}' expects {} arguments, but received {}", #name_str, #arg_count, args.len())));
|
||||
}
|
||||
@@ -203,5 +206,5 @@ fn generate_fn_wrapper(
|
||||
}
|
||||
};
|
||||
|
||||
Ok((inserter, wrapper))
|
||||
Ok((primop, wrapper))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user