Support multiple names in rhai_fn.

This commit is contained in:
Stephen Chung 2020-09-04 11:57:40 +08:00 committed by J Henry Waugh
parent 92ce5481ad
commit dbfd3df810
3 changed files with 68 additions and 54 deletions

View File

@ -19,7 +19,7 @@ use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct ExportedFnParams { pub(crate) struct ExportedFnParams {
pub name: Option<String>, pub name: Option<Vec<String>>,
pub return_raw: bool, pub return_raw: bool,
pub skip: bool, pub skip: bool,
pub span: Option<proc_macro2::Span>, pub span: Option<proc_macro2::Span>,
@ -55,11 +55,12 @@ impl ExportedParams for ExportedFnParams {
Default::default() Default::default()
} }
fn from_info( fn from_info(info: crate::attrs::ExportInfo) -> syn::Result<Self> {
info: crate::attrs::ExportInfo, let ExportInfo {
) -> syn::Result<Self> { item_span: span,
let ExportInfo { item_span: span, items: attrs } = info; items: attrs,
let mut name = None; } = info;
let mut name = Vec::new();
let mut return_raw = false; let mut return_raw = false;
let mut skip = false; let mut skip = false;
for attr in attrs { for attr in attrs {
@ -73,15 +74,15 @@ impl ExportedParams for ExportedFnParams {
"Rhai function names may not contain dot", "Rhai function names may not contain dot",
)); ));
} }
name = Some(s.value()) name.push(s.value())
} }
("get", Some(s)) => name = Some(make_getter(&s.value())), ("get", Some(s)) => name.push(make_getter(&s.value())),
("set", Some(s)) => name = Some(make_setter(&s.value())), ("set", Some(s)) => name.push(make_setter(&s.value())),
("get", None) | ("set", None) | ("name", None) => { ("get", None) | ("set", None) | ("name", None) => {
return Err(syn::Error::new(key.span(), "requires value")) return Err(syn::Error::new(key.span(), "requires value"))
} }
("index_get", None) => name = Some(FN_IDX_GET.to_string()), ("index_get", None) => name.push(FN_IDX_GET.to_string()),
("index_set", None) => name = Some(FN_IDX_SET.to_string()), ("index_set", None) => name.push(FN_IDX_SET.to_string()),
("return_raw", None) => return_raw = true, ("return_raw", None) => return_raw = true,
("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => { ("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => {
return Err(syn::Error::new(s.span(), "extraneous value")) return Err(syn::Error::new(s.span(), "extraneous value"))
@ -98,7 +99,7 @@ impl ExportedParams for ExportedFnParams {
} }
Ok(ExportedFnParams { Ok(ExportedFnParams {
name, name: if name.is_empty() { None } else { Some(name) },
return_raw, return_raw,
skip, skip,
span: Some(span), span: Some(span),
@ -260,7 +261,7 @@ impl ExportedFn {
pub(crate) fn exported_name<'n>(&'n self) -> Cow<'n, str> { pub(crate) fn exported_name<'n>(&'n self) -> Cow<'n, str> {
if let Some(ref name) = self.params.name { if let Some(ref name) = self.params.name {
Cow::Borrowed(name.as_str()) Cow::Borrowed(name.last().unwrap().as_str())
} else { } else {
Cow::Owned(self.signature.ident.to_string()) Cow::Owned(self.signature.ident.to_string())
} }
@ -346,7 +347,9 @@ impl ExportedFn {
}) })
.collect(); .collect();
let return_span = self.return_type().map(|r| r.span()) let return_span = self
.return_type()
.map(|r| r.span())
.unwrap_or_else(|| proc_macro2::Span::call_site()); .unwrap_or_else(|| proc_macro2::Span::call_site());
if !self.params.return_raw { if !self.params.return_raw {
quote_spanned! { return_span=> quote_spanned! { return_span=>
@ -393,11 +396,10 @@ impl ExportedFn {
pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream { pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream {
let sig_name = self.name().clone(); let sig_name = self.name().clone();
let name = self let name = self.params.name.as_ref().map_or_else(
.params || self.name().to_string(),
.name |names| names.last().unwrap().clone(),
.clone() );
.unwrap_or_else(|| self.name().to_string());
let arg_count = self.arg_count(); let arg_count = self.arg_count();
let is_method_call = self.mutable_receiver(); let is_method_call = self.mutable_receiver();
@ -518,7 +520,9 @@ impl ExportedFn {
// Handle "raw returns", aka cases where the result is a dynamic or an error. // Handle "raw returns", aka cases where the result is a dynamic or an error.
// //
// This allows skipping the Dynamic::from wrap. // This allows skipping the Dynamic::from wrap.
let return_span = self.return_type().map(|r| r.span()) let return_span = self
.return_type()
.map(|r| r.span())
.unwrap_or_else(|| proc_macro2::Span::call_site()); .unwrap_or_else(|| proc_macro2::Span::call_site());
let return_expr = if !self.params.return_raw { let return_expr = if !self.params.return_raw {
quote_spanned! { return_span=> quote_spanned! { return_span=>

View File

@ -67,12 +67,12 @@ pub(crate) fn generate_body(
&format!("{}_token", function.name().to_string()), &format!("{}_token", function.name().to_string()),
function.name().span(), function.name().span(),
); );
let reg_name = function let reg_names = function
.params() .params()
.name .name
.clone() .clone()
.unwrap_or_else(|| function.name().to_string()); .unwrap_or_else(|| vec![function.name().to_string()]);
let fn_literal = syn::LitStr::new(&reg_name, proc_macro2::Span::call_site());
let fn_input_types: Vec<syn::Expr> = function let fn_input_types: Vec<syn::Expr> = function
.arg_list() .arg_list()
.map(|fnarg| match fnarg { .map(|fnarg| match fnarg {
@ -110,13 +110,17 @@ pub(crate) fn generate_body(
}) })
.collect(); .collect();
set_fn_stmts.push( for reg_name in reg_names {
syn::parse2::<syn::Stmt>(quote! { let fn_literal = syn::LitStr::new(&reg_name, proc_macro2::Span::call_site());
m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*],
CallableFunction::from_plugin(#fn_token_name())); set_fn_stmts.push(
}) syn::parse2::<syn::Stmt>(quote! {
.unwrap(), m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*],
); CallableFunction::from_plugin(#fn_token_name()));
})
.unwrap(),
);
}
gen_fn_tokens.push(quote! { gen_fn_tokens.push(quote! {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -155,29 +159,33 @@ pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::
let mut renames = HashMap::<String, proc_macro2::Span>::new(); let mut renames = HashMap::<String, proc_macro2::Span>::new();
let mut names = HashMap::<String, proc_macro2::Span>::new(); let mut names = HashMap::<String, proc_macro2::Span>::new();
for itemfn in fns.iter() { for itemfn in fns.iter() {
if let Some(ref name) = itemfn.params().name { if let Some(ref names) = itemfn.params().name {
let current_span = itemfn.params().span.as_ref().unwrap(); for name in names {
let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| { let current_span = itemfn.params().span.as_ref().unwrap();
let type_string: String = match fnarg { let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| {
syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"), let type_string: String = match fnarg {
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { syn::FnArg::Receiver(_) => {
ty.as_ref().to_token_stream().to_string() unimplemented!("receiver rhai_fns not implemented")
} }
}; syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
argstr.push('.'); ty.as_ref().to_token_stream().to_string()
argstr.push_str(&type_string); }
argstr };
}); argstr.push('.');
if let Some(other_span) = renames.insert(key, *current_span) { argstr.push_str(&type_string);
let mut err = syn::Error::new( argstr
*current_span, });
format!("duplicate Rhai signature for '{}'", &name), if let Some(other_span) = renames.insert(key, *current_span) {
); let mut err = syn::Error::new(
err.combine(syn::Error::new( *current_span,
other_span, format!("duplicate Rhai signature for '{}'", &name),
format!("duplicated function renamed '{}'", &name), );
)); err.combine(syn::Error::new(
return Err(err); other_span,
format!("duplicated function renamed '{}'", &name),
));
return Err(err);
}
} }
} else { } else {
let ident = itemfn.name(); let ident = itemfn.name();

View File

@ -21,7 +21,7 @@ mod test {
} }
} }
#[rhai_fn(name = "test")] #[rhai_fn(name = "test", name = "hi")]
#[inline(always)] #[inline(always)]
pub fn len(array: &mut Array, mul: INT) -> INT { pub fn len(array: &mut Array, mul: INT) -> INT {
(array.len() as INT) * mul (array.len() as INT) * mul
@ -74,6 +74,8 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1); assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1);
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; hi(a, 2)")?, 6);
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6); assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
assert_eq!(engine.eval::<INT>("2 + 2")?, 5); assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
assert_eq!( assert_eq!(