diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index 362dc057..acb3e33c 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -104,7 +104,7 @@ This is similar to Rust and many other modern languages, such as JavaScript's `f `is_def_fn` ----------- -Use `is_def_fn` to detect if a function is defined (and therefore callable), based on its name +Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable), based on its name and the number of parameters. ```rust diff --git a/src/engine.rs b/src/engine.rs index ac747dfb..647050a8 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -178,9 +178,7 @@ pub const KEYWORD_FN_PTR_CURRY: &str = "curry"; #[cfg(not(feature = "no_closure"))] pub const KEYWORD_IS_SHARED: &str = "is_shared"; pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var"; -pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn"; pub const KEYWORD_THIS: &str = "this"; -pub const FN_TO_STRING: &str = "to_string"; #[cfg(not(feature = "no_object"))] pub const FN_GET: &str = "get$"; #[cfg(not(feature = "no_object"))] diff --git a/src/fn_call.rs b/src/fn_call.rs index bbf40192..1c629bf2 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -3,8 +3,7 @@ use crate::ast::{Expr, Stmt}; use crate::engine::{ search_imports, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, - KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, - KEYWORD_PRINT, KEYWORD_TYPE_OF, + KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::fn_native::FnCallArgs; use crate::module::NamespaceRef; @@ -419,7 +418,7 @@ impl Engine { #[inline] pub(crate) fn has_override_by_name_and_arguments( &self, - mods: &Imports, + mods: Option<&Imports>, lib: &[&Module], fn_name: &str, arg_types: impl AsRef<[TypeId]>, @@ -436,7 +435,7 @@ impl Engine { #[inline(always)] pub(crate) fn has_override( &self, - mods: &Imports, + mods: Option<&Imports>, lib: &[&Module], hash_fn: u64, hash_script: u64, @@ -454,8 +453,7 @@ impl Engine { || self.packages.contains_fn(hash_script) || self.packages.contains_fn(hash_fn) // Then check imported modules - || mods.contains_fn(hash_script) - || mods.contains_fn(hash_fn) + || mods.map(|m| m.contains_fn(hash_script) || m.contains_fn(hash_fn)).unwrap_or(false) } /// Perform an actual function call, native Rust or scripted, taking care of special functions. @@ -494,7 +492,7 @@ impl Engine { // type_of KEYWORD_TYPE_OF if args.len() == 1 - && !self.has_override(mods, lib, hash_fn, hash_script, pub_only) => + && !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) => { Ok(( self.map_type_name(args[0].type_name()).to_string().into(), @@ -506,7 +504,7 @@ impl Engine { // by a function pointer so it isn't caught at parse time. KEYWORD_FN_PTR | KEYWORD_EVAL if args.len() == 1 - && !self.has_override(mods, lib, hash_fn, hash_script, pub_only) => + && !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) => { EvalAltResult::ErrorRuntime( format!( @@ -521,7 +519,7 @@ impl Engine { // Script-like function found #[cfg(not(feature = "no_function"))] - _ if self.has_override(mods, lib, 0, hash_script, pub_only) => { + _ if self.has_override(Some(mods), lib, 0, hash_script, pub_only) => { // Get function let func = lib .iter() @@ -856,7 +854,7 @@ impl Engine { let hash_fn = calc_native_fn_hash(empty(), fn_name, once(TypeId::of::())); - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) { // Fn - only in function call style return self .eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)? @@ -912,7 +910,7 @@ impl Engine { if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 - && !self.has_override(mods, lib, 0, hash_script, pub_only) + && !self.has_override(Some(mods), lib, 0, hash_script, pub_only) { let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; @@ -942,7 +940,7 @@ impl Engine { if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) { let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; let var_name = var_name.as_str().map_err(|err| { @@ -952,44 +950,11 @@ impl Engine { } } - // Handle is_def_fn() - if name == KEYWORD_IS_DEF_FN && args_expr.len() == 2 { - let hash_fn = calc_native_fn_hash( - empty(), - name, - [TypeId::of::(), TypeId::of::()] - .iter() - .cloned(), - ); - - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { - let fn_name = - self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; - let num_params = - self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?; - - let fn_name = fn_name.as_str().map_err(|err| { - self.make_type_mismatch_err::(err, args_expr[0].position()) - })?; - let num_params = num_params.as_int().map_err(|err| { - self.make_type_mismatch_err::(err, args_expr[1].position()) - })?; - - return Ok(if num_params < 0 { - false - } else { - let hash = calc_script_fn_hash(empty(), fn_name, num_params as usize); - lib.iter().any(|&m| m.contains_fn(hash, false)) - } - .into()); - } - } - // Handle eval() if name == KEYWORD_EVAL && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) { // eval - only in function call style let prev_len = scope.len(); let script = diff --git a/src/fn_native.rs b/src/fn_native.rs index c29464f9..e605df82 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -49,8 +49,8 @@ pub type Locked = crate::stdlib::sync::RwLock; #[derive(Debug, Copy, Clone)] pub struct NativeCallContext<'e, 'a, 'm, 'pm: 'm> { engine: &'e Engine, - mods: Option<&'a Imports>, - lib: &'m [&'pm Module], + pub(crate) mods: Option<&'a Imports>, + pub(crate) lib: &'m [&'pm Module], } impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'a Imports, &'m M)> diff --git a/src/optimize.rs b/src/optimize.rs index 51fe2d2a..19ee51ce 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,10 +1,7 @@ //! Module implementing the AST optimizer. use crate::ast::{Expr, ScriptFnDef, Stmt}; -use crate::engine::{ - KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, - KEYWORD_TYPE_OF, -}; +use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_call::run_builtin_binary_op; use crate::parser::map_dynamic_to_expr; use crate::stdlib::{ @@ -467,11 +464,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { fn optimize_expr(expr: &mut Expr, state: &mut State) { // These keywords are handled specially const DONT_EVAL_KEYWORDS: &[&str] = &[ - KEYWORD_PRINT, // side effects - KEYWORD_DEBUG, // side effects - KEYWORD_EVAL, // arbitrary scripts - KEYWORD_IS_DEF_FN, // functions collection is volatile - KEYWORD_IS_DEF_VAR, // variables scope is volatile + KEYWORD_PRINT, // side effects + KEYWORD_DEBUG, // side effects + KEYWORD_EVAL, // arbitrary scripts ]; match expr { @@ -650,7 +645,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); // Search for overloaded operators (can override built-in). - if !state.engine.has_override_by_name_and_arguments(&state.engine.global_sub_modules, state.lib, x.name.as_ref(), arg_types.as_ref(), false) { + if !state.engine.has_override_by_name_and_arguments(Some(&state.engine.global_sub_modules), state.lib, x.name.as_ref(), arg_types.as_ref(), false) { if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1]) .ok().flatten() .and_then(|result| map_dynamic_to_expr(result, *pos)) diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index 284c5997..8335c126 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -4,9 +4,34 @@ use super::iter_basic::BasicIteratorPackage; use super::logic::LogicPackage; use super::string_basic::BasicStringPackage; -use crate::def_package; +use crate::fn_native::{CallableFunction, FnCallArgs}; +use crate::stdlib::{any::TypeId, iter::empty}; +use crate::{ + calc_script_fn_hash, def_package, FnAccess, FnNamespace, ImmutableString, NativeCallContext, + INT, +}; def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", lib, { + #[cfg(not(feature = "no_function"))] + { + let f = |ctx: NativeCallContext, args: &mut FnCallArgs| { + let num_params = args[1].clone().cast::(); + let fn_name = args[0].as_str().unwrap(); + + Ok(if num_params < 0 { + false.into() + } else { + let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); + ctx.engine().has_override(ctx.mods, ctx.lib, 0, hash_script, true).into() + }) + }; + + lib.set_fn("is_def_fn", FnNamespace::Global, FnAccess::Public, + Some(&["fn_name: &str", "num_params: INT"]), + &[TypeId::of::(), TypeId::of::()], + CallableFunction::from_method(Box::new(f))); + } + ArithmeticPackage::init(lib); LogicPackage::init(lib); BasicStringPackage::init(lib); diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 2db28c7a..80686547 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -1,6 +1,6 @@ #![allow(non_snake_case)] -use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; +use crate::engine::{KEYWORD_DEBUG, KEYWORD_PRINT}; use crate::plugin::*; use crate::stdlib::{ fmt::{Debug, Display}, @@ -32,7 +32,7 @@ macro_rules! gen_functions { macro_rules! reg_print_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $( - set_exported_fn!($mod_name, FN_TO_STRING, $root::$arg_type::to_string_func); + set_exported_fn!($mod_name, "to_string", $root::$arg_type::to_string_func); set_exported_fn!($mod_name, KEYWORD_PRINT, $root::$arg_type::to_string_func); )* } }