Merge pull request #374 from schungx/master
Deprecate RegisterFn and RegisterResultFn traits.
This commit is contained in:
commit
4ca3cefc06
22
CHANGELOG.md
22
CHANGELOG.md
@ -1,6 +1,23 @@
|
||||
Rhai Release Notes
|
||||
==================
|
||||
|
||||
Version 0.19.15
|
||||
===============
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`.
|
||||
* `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it.
|
||||
* All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`.
|
||||
* `protected`, `super` are now reserved keywords.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any type.
|
||||
|
||||
|
||||
Version 0.19.14
|
||||
===============
|
||||
|
||||
@ -559,6 +576,11 @@ Bug fixes
|
||||
* Fixes bug that prevents calling functions in closures.
|
||||
* Fixes bug that erroneously consumes the first argument to a namespace-qualified function call.
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* `Module::contains_fn` and `Module::get_script_fn` no longer take the `public_only` parameter.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
||||
|
||||
[package]
|
||||
name = "rhai"
|
||||
version = "0.19.14"
|
||||
version = "0.19.15"
|
||||
edition = "2018"
|
||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||
description = "Embedded scripting for Rust"
|
||||
|
@ -3,7 +3,7 @@
|
||||
///! Test evaluating expressions
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Array, Engine, Map, RegisterFn, INT};
|
||||
use rhai::{Array, Engine, Map, INT};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
|
@ -3,7 +3,7 @@
|
||||
///! Test evaluating expressions
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel, RegisterFn, Scope, INT};
|
||||
use rhai::{Engine, OptimizationLevel, Scope, INT};
|
||||
use test::Bencher;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -35,7 +35,7 @@
|
||||
//! # Register a Rust Function with a Rhai `Module`
|
||||
//!
|
||||
//! ```
|
||||
//! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn};
|
||||
//! use rhai::{EvalAltResult, FLOAT, Module};
|
||||
//! use rhai::plugin::*;
|
||||
//! use rhai::module_resolvers::*;
|
||||
//!
|
||||
@ -65,7 +65,7 @@
|
||||
//! # Register a Plugin Function with an `Engine`
|
||||
//!
|
||||
//! ```
|
||||
//! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn};
|
||||
//! use rhai::{EvalAltResult, FLOAT, Module};
|
||||
//! use rhai::plugin::*;
|
||||
//! use rhai::module_resolvers::*;
|
||||
//!
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rhai::module_resolvers::*;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Module, RegisterFn, FLOAT};
|
||||
use rhai::{Engine, EvalAltResult, Module, FLOAT};
|
||||
|
||||
pub mod raw_fn {
|
||||
use rhai::plugin::*;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rhai::module_resolvers::*;
|
||||
use rhai::{Array, Engine, EvalAltResult, RegisterFn, FLOAT, INT};
|
||||
use rhai::{Array, Engine, EvalAltResult, FLOAT, INT};
|
||||
|
||||
pub mod empty_module {
|
||||
use rhai::plugin::*;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rhai::module_resolvers::*;
|
||||
use rhai::{Array, Engine, EvalAltResult, RegisterFn, FLOAT, INT};
|
||||
use rhai::{Array, Engine, EvalAltResult, FLOAT, INT};
|
||||
|
||||
pub mod one_fn_module_nested_attr {
|
||||
use rhai::plugin::*;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TestStruct {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TestStruct {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
fn add(x: INT, y: INT) -> INT {
|
||||
x + y
|
||||
|
@ -1,6 +1,6 @@
|
||||
///! This example registers a variety of functions that operate on strings.
|
||||
///! Remember to use `ImmutableString` or `&str` instead of `String` as parameters.
|
||||
use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT};
|
||||
use rhai::{Engine, EvalAltResult, ImmutableString, Scope, INT};
|
||||
use std::io::{stdin, stdout, Write};
|
||||
|
||||
/// Trim whitespace from a string. The original string argument is changed.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, RegisterFn, INT};
|
||||
use rhai::{Engine, INT};
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
use std::sync::Mutex;
|
||||
|
53
src/ast.rs
53
src/ast.rs
@ -74,7 +74,7 @@ impl fmt::Display for ScriptFnDef {
|
||||
"{}{}({})",
|
||||
match self.access {
|
||||
FnAccess::Public => "",
|
||||
FnAccess::Private => "private",
|
||||
FnAccess::Private => "private ",
|
||||
},
|
||||
self.name,
|
||||
self.params
|
||||
@ -118,7 +118,7 @@ impl fmt::Display for ScriptFnMetadata<'_> {
|
||||
"{}{}({})",
|
||||
match self.access {
|
||||
FnAccess::Public => "",
|
||||
FnAccess::Private => "private",
|
||||
FnAccess::Private => "private ",
|
||||
},
|
||||
self.name,
|
||||
self.params.iter().cloned().collect::<Vec<_>>().join(", ")
|
||||
@ -1301,18 +1301,41 @@ pub struct OpAssignment {
|
||||
/// _(INTERNALS)_ An set of function call hashes.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Two separate hashes are pre-calculated because of the following pattern:
|
||||
///
|
||||
/// ```,ignore
|
||||
/// func(a, b, c); // Native: func(a, b, c) - 3 parameters
|
||||
/// // Script: func(a, b, c) - 3 parameters
|
||||
///
|
||||
/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters
|
||||
/// // Script: func(b, c) - 2 parameters
|
||||
/// ```
|
||||
///
|
||||
/// For normal function calls, the native hash equals the script hash.
|
||||
/// For method-style calls, the script hash contains one fewer parameter.
|
||||
///
|
||||
/// Function call hashes are used in the following manner:
|
||||
///
|
||||
/// * First, the script hash is tried, which contains only the called function's name plus the
|
||||
/// of parameters.
|
||||
///
|
||||
/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is
|
||||
/// then used to search for a native function.
|
||||
/// In other words, a native function call hash always contains the called function's name plus
|
||||
/// the types of the arguments. This is to due to possible function overloading for different parameter types.
|
||||
///
|
||||
/// # Volatile Data Structure
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)]
|
||||
pub struct FnHash {
|
||||
pub struct FnCallHash {
|
||||
/// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
|
||||
script: Option<u64>,
|
||||
pub script: Option<u64>,
|
||||
/// Pre-calculated hash for a native Rust function with no parameter types.
|
||||
native: u64,
|
||||
pub native: u64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FnHash {
|
||||
impl fmt::Debug for FnCallHash {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(script) = self.script {
|
||||
if script == self.native {
|
||||
@ -1326,8 +1349,8 @@ impl fmt::Debug for FnHash {
|
||||
}
|
||||
}
|
||||
|
||||
impl FnHash {
|
||||
/// Create a [`FnHash`] with only the native Rust hash.
|
||||
impl FnCallHash {
|
||||
/// Create a [`FnCallHash`] with only the native Rust hash.
|
||||
#[inline(always)]
|
||||
pub fn from_native(hash: u64) -> Self {
|
||||
Self {
|
||||
@ -1335,7 +1358,7 @@ impl FnHash {
|
||||
native: hash,
|
||||
}
|
||||
}
|
||||
/// Create a [`FnHash`] with both native Rust and script function hashes set to the same value.
|
||||
/// Create a [`FnCallHash`] with both native Rust and script function hashes set to the same value.
|
||||
#[inline(always)]
|
||||
pub fn from_script(hash: u64) -> Self {
|
||||
Self {
|
||||
@ -1343,7 +1366,7 @@ impl FnHash {
|
||||
native: hash,
|
||||
}
|
||||
}
|
||||
/// Create a [`FnHash`] with both native Rust and script function hashes.
|
||||
/// Create a [`FnCallHash`] with both native Rust and script function hashes.
|
||||
#[inline(always)]
|
||||
pub fn from_script_and_native(script: u64, native: u64) -> Self {
|
||||
Self {
|
||||
@ -1351,21 +1374,21 @@ impl FnHash {
|
||||
native,
|
||||
}
|
||||
}
|
||||
/// Is this [`FnHash`] native Rust only?
|
||||
/// Is this [`FnCallHash`] native Rust only?
|
||||
#[inline(always)]
|
||||
pub fn is_native_only(&self) -> bool {
|
||||
self.script.is_none()
|
||||
}
|
||||
/// Get the script function hash from this [`FnHash`].
|
||||
/// Get the script function hash from this [`FnCallHash`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`FnHash`] is native Rust only.
|
||||
/// Panics if the [`FnCallHash`] is native Rust only.
|
||||
#[inline(always)]
|
||||
pub fn script_hash(&self) -> u64 {
|
||||
self.script.unwrap()
|
||||
}
|
||||
/// Get the naive Rust function hash from this [`FnHash`].
|
||||
/// Get the naive Rust function hash from this [`FnCallHash`].
|
||||
#[inline(always)]
|
||||
pub fn native_hash(&self) -> u64 {
|
||||
self.native
|
||||
@ -1381,7 +1404,7 @@ impl FnHash {
|
||||
#[derive(Debug, Clone, Default, Hash)]
|
||||
pub struct FnCallExpr {
|
||||
/// Pre-calculated hash.
|
||||
pub hash: FnHash,
|
||||
pub hash: FnCallHash,
|
||||
/// Does this function call capture the parent scope?
|
||||
pub capture: bool,
|
||||
/// List of function call arguments.
|
||||
|
@ -218,7 +218,7 @@ fn main() {
|
||||
// println!(
|
||||
// "{}",
|
||||
// engine
|
||||
// .gen_fn_metadata_to_json(Some(&main_ast), false)
|
||||
// .gen_fn_metadata_with_ast_to_json(&main_ast, true)
|
||||
// .unwrap()
|
||||
// );
|
||||
// continue;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Main module defining the script evaluation [`Engine`].
|
||||
|
||||
use crate::ast::{Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock};
|
||||
use crate::ast::{Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock};
|
||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||
use crate::fn_native::{
|
||||
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
||||
@ -527,10 +527,8 @@ pub struct State {
|
||||
/// Embedded module resolver.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
|
||||
/// function resolution cache.
|
||||
fn_resolution_caches: StaticVec<FnResolutionCache>,
|
||||
/// Free resolution caches.
|
||||
fn_resolution_caches_free_list: Vec<FnResolutionCache>,
|
||||
/// Function resolution cache and free list.
|
||||
fn_resolution_caches: (StaticVec<FnResolutionCache>, Vec<FnResolutionCache>),
|
||||
}
|
||||
|
||||
impl State {
|
||||
@ -541,20 +539,19 @@ impl State {
|
||||
}
|
||||
/// Get a mutable reference to the current function resolution cache.
|
||||
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
||||
if self.fn_resolution_caches.is_empty() {
|
||||
if self.fn_resolution_caches.0.is_empty() {
|
||||
self.fn_resolution_caches
|
||||
.push(HashMap::with_capacity_and_hasher(16, StraightHasherBuilder));
|
||||
.0
|
||||
.push(HashMap::with_capacity_and_hasher(64, StraightHasherBuilder));
|
||||
}
|
||||
self.fn_resolution_caches.last_mut().unwrap()
|
||||
self.fn_resolution_caches.0.last_mut().unwrap()
|
||||
}
|
||||
/// Push an empty function resolution cache onto the stack and make it current.
|
||||
#[allow(dead_code)]
|
||||
pub fn push_fn_resolution_cache(&mut self) {
|
||||
self.fn_resolution_caches.push(
|
||||
self.fn_resolution_caches_free_list
|
||||
.pop()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
self.fn_resolution_caches
|
||||
.0
|
||||
.push(self.fn_resolution_caches.1.pop().unwrap_or_default());
|
||||
}
|
||||
/// Remove the current function resolution cache from the stack and make the last one current.
|
||||
///
|
||||
@ -562,9 +559,9 @@ impl State {
|
||||
///
|
||||
/// Panics if there are no more function resolution cache in the stack.
|
||||
pub fn pop_fn_resolution_cache(&mut self) {
|
||||
let mut cache = self.fn_resolution_caches.pop().unwrap();
|
||||
let mut cache = self.fn_resolution_caches.0.pop().unwrap();
|
||||
cache.clear();
|
||||
self.fn_resolution_caches_free_list.push(cache);
|
||||
self.fn_resolution_caches.1.push(cache);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1133,7 +1130,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()),
|
||||
// Any other error - return
|
||||
err => return Err(Box::new(err)),
|
||||
err => return err.into(),
|
||||
},
|
||||
};
|
||||
|
||||
@ -1143,7 +1140,7 @@ impl Engine {
|
||||
let ((_, val_pos), _) = new_val;
|
||||
|
||||
let hash_set =
|
||||
FnHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3));
|
||||
FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3));
|
||||
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
||||
|
||||
self.exec_fn_call(
|
||||
@ -1221,7 +1218,7 @@ impl Engine {
|
||||
// xxx.id = ???
|
||||
Expr::Property(x) if new_val.is_some() => {
|
||||
let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref();
|
||||
let hash = FnHash::from_native(*hash_set);
|
||||
let hash = FnCallHash::from_native(*hash_set);
|
||||
let mut new_val = new_val;
|
||||
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
||||
self.exec_fn_call(
|
||||
@ -1233,7 +1230,7 @@ impl Engine {
|
||||
// xxx.id
|
||||
Expr::Property(x) => {
|
||||
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref();
|
||||
let hash = FnHash::from_native(*hash_get);
|
||||
let hash = FnCallHash::from_native(*hash_get);
|
||||
let mut args = [target_val];
|
||||
self.exec_fn_call(
|
||||
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
||||
@ -1282,8 +1279,8 @@ impl Engine {
|
||||
Expr::Property(p) => {
|
||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
||||
p.as_ref();
|
||||
let hash_get = FnHash::from_native(*hash_get);
|
||||
let hash_set = FnHash::from_native(*hash_set);
|
||||
let hash_get = FnCallHash::from_native(*hash_get);
|
||||
let hash_set = FnCallHash::from_native(*hash_set);
|
||||
let arg_values = &mut [target_val, &mut Default::default()];
|
||||
let args = &mut arg_values[..1];
|
||||
let (mut val, updated) = self.exec_fn_call(
|
||||
@ -1615,7 +1612,7 @@ impl Engine {
|
||||
let type_name = target.type_name();
|
||||
let mut idx = idx;
|
||||
let args = &mut [target, &mut idx];
|
||||
let hash_get = FnHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2));
|
||||
let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2));
|
||||
self.exec_fn_call(
|
||||
_mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None,
|
||||
_level,
|
||||
@ -1986,10 +1983,11 @@ impl Engine {
|
||||
|
||||
if lhs_ptr.as_ref().is_read_only() {
|
||||
// Assignment to constant variable
|
||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
EvalAltResult::ErrorAssignmentToConstant(
|
||||
lhs_expr.get_variable_access(false).unwrap().to_string(),
|
||||
pos,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
self.eval_op_assignment(
|
||||
mods,
|
||||
|
@ -3,17 +3,18 @@
|
||||
use crate::dynamic::Variant;
|
||||
use crate::engine::{EvalContext, Imports, State};
|
||||
use crate::fn_native::{FnCallArgs, SendSync};
|
||||
use crate::fn_register::RegisterNativeFunction;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
format,
|
||||
string::String,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use crate::{
|
||||
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
|
||||
ParseError, Position, RhaiResult, Shared, AST,
|
||||
ParseError, Position, RhaiResult, Shared, StaticVec, AST,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -24,6 +25,106 @@ use crate::Map;
|
||||
|
||||
/// Engine public API
|
||||
impl Engine {
|
||||
/// Register a custom function with the [`Engine`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// // Normal function
|
||||
/// fn add(x: i64, y: i64) -> i64 {
|
||||
/// x + y
|
||||
/// }
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// engine.register_fn("add", add);
|
||||
///
|
||||
/// assert_eq!(engine.eval::<i64>("add(40, 2)")?, 42);
|
||||
///
|
||||
/// // You can also register a closure.
|
||||
/// engine.register_fn("sub", |x: i64, y: i64| x - y );
|
||||
///
|
||||
/// assert_eq!(engine.eval::<i64>("sub(44, 2)")?, 42);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn register_fn<A, F>(&mut self, name: &str, func: F) -> &mut Self
|
||||
where
|
||||
F: RegisterNativeFunction<A, ()>,
|
||||
{
|
||||
let param_types = F::param_types();
|
||||
let mut param_type_names: StaticVec<_> = F::param_names()
|
||||
.iter()
|
||||
.map(|ty| format!("_: {}", self.map_type_name(ty)))
|
||||
.collect();
|
||||
if F::return_type() != TypeId::of::<()>() {
|
||||
param_type_names.push(self.map_type_name(F::return_type_name()).to_string());
|
||||
}
|
||||
let param_type_names: StaticVec<_> =
|
||||
param_type_names.iter().map(|ty| ty.as_str()).collect();
|
||||
|
||||
self.global_namespace.set_fn(
|
||||
name,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
Some(¶m_type_names),
|
||||
¶m_types,
|
||||
func.into_callable_function(),
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Register a custom fallible function with the [`Engine`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, EvalAltResult};
|
||||
///
|
||||
/// // Normal function
|
||||
/// fn div(x: i64, y: i64) -> Result<i64, Box<EvalAltResult>> {
|
||||
/// if y == 0 {
|
||||
/// // '.into()' automatically converts to 'Box<EvalAltResult::ErrorRuntime>'
|
||||
/// Err("division by zero!".into())
|
||||
/// } else {
|
||||
/// Ok(x / y)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// engine.register_result_fn("div", div);
|
||||
///
|
||||
/// engine.eval::<i64>("div(42, 0)")
|
||||
/// .expect_err("expecting division by zero error!");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn register_result_fn<A, F, R>(&mut self, name: &str, func: F) -> &mut Self
|
||||
where
|
||||
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
|
||||
{
|
||||
let param_types = F::param_types();
|
||||
let mut param_type_names: StaticVec<_> = F::param_names()
|
||||
.iter()
|
||||
.map(|ty| format!("_: {}", self.map_type_name(ty)))
|
||||
.collect();
|
||||
param_type_names.push(self.map_type_name(F::return_type_name()).to_string());
|
||||
let param_type_names: StaticVec<&str> =
|
||||
param_type_names.iter().map(|ty| ty.as_str()).collect();
|
||||
|
||||
self.global_namespace.set_fn(
|
||||
name,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
Some(¶m_type_names),
|
||||
¶m_types,
|
||||
func.into_callable_function(),
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Register a function of the [`Engine`].
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
@ -76,7 +177,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -115,7 +216,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -177,7 +278,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -199,7 +300,7 @@ impl Engine {
|
||||
name: &str,
|
||||
get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
use crate::{engine::make_getter, RegisterFn};
|
||||
use crate::engine::make_getter;
|
||||
self.register_fn(&make_getter(name), get_fn)
|
||||
}
|
||||
/// Register a getter function for a member of a registered type with the [`Engine`].
|
||||
@ -209,7 +310,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn};
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult};
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct TestStruct {
|
||||
@ -220,8 +321,8 @@ impl Engine {
|
||||
/// fn new() -> Self { Self { field: 1 } }
|
||||
///
|
||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// Ok(self.field.into())
|
||||
/// fn get_field(&mut self) -> Result<i64, Box<EvalAltResult>> {
|
||||
/// Ok(self.field)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -241,12 +342,12 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn register_get_result<T: Variant + Clone>(
|
||||
pub fn register_get_result<T: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
get_fn: impl Fn(&mut T) -> RhaiResult + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T) -> Result<U, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
use crate::{engine::make_getter, RegisterResultFn};
|
||||
use crate::engine::make_getter;
|
||||
self.register_result_fn(&make_getter(name), get_fn)
|
||||
}
|
||||
/// Register a setter function for a member of a registered type with the [`Engine`].
|
||||
@ -266,7 +367,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -292,7 +393,7 @@ impl Engine {
|
||||
name: &str,
|
||||
set_fn: impl Fn(&mut T, U) + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
use crate::{engine::make_setter, RegisterFn};
|
||||
use crate::engine::make_setter;
|
||||
self.register_fn(&make_setter(name), set_fn)
|
||||
}
|
||||
/// Register a setter function for a member of a registered type with the [`Engine`].
|
||||
@ -300,7 +401,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn};
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult};
|
||||
///
|
||||
/// #[derive(Debug, Clone, Eq, PartialEq)]
|
||||
/// struct TestStruct {
|
||||
@ -341,9 +442,9 @@ impl Engine {
|
||||
name: &str,
|
||||
set_fn: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
use crate::{engine::make_setter, RegisterResultFn};
|
||||
use crate::engine::make_setter;
|
||||
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
|
||||
set_fn(obj, value).map(Into::into)
|
||||
set_fn(obj, value)
|
||||
})
|
||||
}
|
||||
/// Short-hand for registering both getter and setter functions
|
||||
@ -369,7 +470,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -420,7 +521,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -457,8 +558,7 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
use crate::{engine::FN_IDX_GET, RegisterFn};
|
||||
self.register_fn(FN_IDX_GET, get_fn)
|
||||
self.register_fn(crate::engine::FN_IDX_GET, get_fn)
|
||||
}
|
||||
/// Register an index getter for a custom type with the [`Engine`].
|
||||
///
|
||||
@ -472,7 +572,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn};
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult};
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct TestStruct {
|
||||
@ -483,8 +583,8 @@ impl Engine {
|
||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||
///
|
||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||
/// fn get_field(&mut self, index: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// Ok(self.fields[index as usize].into())
|
||||
/// fn get_field(&mut self, index: i64) -> Result<i64, Box<EvalAltResult>> {
|
||||
/// Ok(self.fields[index as usize])
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -506,9 +606,13 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>(
|
||||
pub fn register_indexer_get_result<
|
||||
T: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl Fn(&mut T, X) -> RhaiResult + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T, X) -> Result<U, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
@ -524,8 +628,7 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
use crate::{engine::FN_IDX_GET, RegisterResultFn};
|
||||
self.register_result_fn(FN_IDX_GET, get_fn)
|
||||
self.register_result_fn(crate::engine::FN_IDX_GET, get_fn)
|
||||
}
|
||||
/// Register an index setter for a custom type with the [`Engine`].
|
||||
///
|
||||
@ -549,7 +652,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -589,8 +692,7 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
use crate::{engine::FN_IDX_SET, RegisterFn};
|
||||
self.register_fn(FN_IDX_SET, set_fn)
|
||||
self.register_fn(crate::engine::FN_IDX_SET, set_fn)
|
||||
}
|
||||
/// Register an index setter for a custom type with the [`Engine`].
|
||||
///
|
||||
@ -602,7 +704,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn};
|
||||
/// use rhai::{Engine, Dynamic, EvalAltResult};
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct TestStruct {
|
||||
@ -661,10 +763,10 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
use crate::{engine::FN_IDX_SET, RegisterResultFn};
|
||||
self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| {
|
||||
set_fn(obj, index, value).map(Into::into)
|
||||
})
|
||||
self.register_result_fn(
|
||||
crate::engine::FN_IDX_SET,
|
||||
move |obj: &mut T, index: X, value: U| set_fn(obj, index, value),
|
||||
)
|
||||
}
|
||||
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
|
||||
///
|
||||
@ -691,7 +793,7 @@ impl Engine {
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@ -743,8 +845,7 @@ impl Engine {
|
||||
pub fn load_package(&mut self, module: impl Into<Shared<Module>>) -> &mut Self {
|
||||
self.register_global_module(module.into())
|
||||
}
|
||||
/// Register a shared [`Module`] as a static module namespace with the
|
||||
/// [`Engine`].
|
||||
/// Register a shared [`Module`] as a static module namespace with the [`Engine`].
|
||||
///
|
||||
/// Functions marked [`FnNamespace::Global`] and type iterators are exposed to scripts without
|
||||
/// namespace qualifications.
|
||||
@ -759,7 +860,7 @@ impl Engine {
|
||||
///
|
||||
/// // Create the module
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||
/// module.set_native_fn("calc", |x: i64| Ok(x + 1));
|
||||
///
|
||||
/// let module: Shared<Module> = module.into();
|
||||
///
|
||||
@ -1780,7 +1881,7 @@ impl Engine {
|
||||
|
||||
let fn_def = ast
|
||||
.lib()
|
||||
.get_script_fn(name, args.len(), false)
|
||||
.get_script_fn(name, args.len())
|
||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||
|
||||
// Check for data race.
|
||||
|
@ -250,7 +250,7 @@ impl Engine {
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
|
294
src/fn_call.rs
294
src/fn_call.rs
@ -1,6 +1,6 @@
|
||||
//! Implement function-calling mechanism for [`Engine`].
|
||||
|
||||
use crate::ast::FnHash;
|
||||
use crate::ast::FnCallHash;
|
||||
use crate::engine::{
|
||||
FnResolutionCacheEntry, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
|
||||
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||
@ -33,28 +33,6 @@ use crate::{
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::Map;
|
||||
|
||||
/// Extract the property name from a getter function name.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> {
|
||||
if _fn_name.starts_with(crate::engine::FN_GET) {
|
||||
Some(&_fn_name[crate::engine::FN_GET.len()..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the property name from a setter function name.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> {
|
||||
if _fn_name.starts_with(crate::engine::FN_SET) {
|
||||
Some(&_fn_name[crate::engine::FN_SET.len()..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that temporarily stores a mutable reference to a `Dynamic`,
|
||||
/// replacing it with a cloned copy.
|
||||
#[derive(Debug, Default)]
|
||||
@ -146,6 +124,7 @@ pub fn ensure_no_data_race(
|
||||
|
||||
impl Engine {
|
||||
/// Generate the signature for a function call.
|
||||
#[inline]
|
||||
fn gen_call_signature(
|
||||
&self,
|
||||
namespace: Option<&NamespaceRef>,
|
||||
@ -198,7 +177,7 @@ impl Engine {
|
||||
.fn_resolution_cache_mut()
|
||||
.entry(hash)
|
||||
.or_insert_with(|| {
|
||||
let num_args = args.as_ref().map(|a| a.len()).unwrap_or(0);
|
||||
let num_args = args.as_ref().map_or(0, |a| a.len());
|
||||
let max_bitmask = if !allow_dynamic {
|
||||
0
|
||||
} else {
|
||||
@ -210,43 +189,37 @@ impl Engine {
|
||||
let func = lib
|
||||
.iter()
|
||||
.find_map(|m| {
|
||||
m.get_fn(hash, false)
|
||||
.cloned()
|
||||
.map(|func| FnResolutionCacheEntry {
|
||||
func,
|
||||
source: m.id_raw().cloned(),
|
||||
})
|
||||
m.get_fn(hash).cloned().map(|func| {
|
||||
let source = m.id_raw().cloned();
|
||||
FnResolutionCacheEntry { func, source }
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
self.global_namespace
|
||||
.get_fn(hash, false)
|
||||
.get_fn(hash)
|
||||
.cloned()
|
||||
.map(|func| FnResolutionCacheEntry { func, source: None })
|
||||
})
|
||||
.or_else(|| {
|
||||
self.global_modules.iter().find_map(|m| {
|
||||
m.get_fn(hash, false)
|
||||
.cloned()
|
||||
.map(|func| FnResolutionCacheEntry {
|
||||
func,
|
||||
source: m.id_raw().cloned(),
|
||||
})
|
||||
m.get_fn(hash).cloned().map(|func| {
|
||||
let source = m.id_raw().cloned();
|
||||
FnResolutionCacheEntry { func, source }
|
||||
})
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
mods.get_fn(hash)
|
||||
.map(|(func, source)| FnResolutionCacheEntry {
|
||||
func: func.clone(),
|
||||
source: source.cloned(),
|
||||
})
|
||||
mods.get_fn(hash).map(|(func, source)| {
|
||||
let func = func.clone();
|
||||
let source = source.cloned();
|
||||
FnResolutionCacheEntry { func, source }
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
self.global_sub_modules.values().find_map(|m| {
|
||||
m.get_qualified_fn(hash).cloned().map(|func| {
|
||||
FnResolutionCacheEntry {
|
||||
func,
|
||||
source: m.id_raw().cloned(),
|
||||
}
|
||||
let source = m.id_raw().cloned();
|
||||
FnResolutionCacheEntry { func, source }
|
||||
})
|
||||
})
|
||||
});
|
||||
@ -257,41 +230,31 @@ impl Engine {
|
||||
|
||||
// Stop when all permutations are exhausted
|
||||
None if bitmask >= max_bitmask => {
|
||||
return if num_args != 2 {
|
||||
None
|
||||
} else if let Some(ref args) = args {
|
||||
if num_args != 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
return args.and_then(|args| {
|
||||
if !is_op_assignment {
|
||||
if let Some(f) =
|
||||
get_builtin_binary_op_fn(fn_name, &args[0], &args[1])
|
||||
{
|
||||
Some(FnResolutionCacheEntry {
|
||||
func: CallableFunction::from_method(
|
||||
Box::new(f) as Box<FnAny>
|
||||
),
|
||||
source: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| {
|
||||
let func = CallableFunction::from_method(
|
||||
Box::new(f) as Box<FnAny>
|
||||
);
|
||||
FnResolutionCacheEntry { func, source: None }
|
||||
})
|
||||
} else {
|
||||
let (first, second) = args.split_first().unwrap();
|
||||
|
||||
if let Some(f) =
|
||||
get_builtin_op_assignment_fn(fn_name, *first, second[0])
|
||||
{
|
||||
Some(FnResolutionCacheEntry {
|
||||
func: CallableFunction::from_method(
|
||||
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(
|
||||
|f| {
|
||||
let func = CallableFunction::from_method(
|
||||
Box::new(f) as Box<FnAny>
|
||||
),
|
||||
source: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
);
|
||||
FnResolutionCacheEntry { func, source: None }
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Try all permutations with `Dynamic` wildcards
|
||||
@ -407,69 +370,79 @@ impl Engine {
|
||||
});
|
||||
}
|
||||
|
||||
// Getter function not found?
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
||||
return EvalAltResult::ErrorDotExpr(
|
||||
format!(
|
||||
"Unknown property '{}' - a getter is not registered for type '{}'",
|
||||
prop,
|
||||
self.map_type_name(args[0].type_name())
|
||||
),
|
||||
match fn_name {
|
||||
// index getter function not found?
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
crate::engine::FN_IDX_GET => {
|
||||
assert!(args.len() == 2);
|
||||
|
||||
EvalAltResult::ErrorFunctionNotFound(
|
||||
format!(
|
||||
"{} [{}]",
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
// index setter function not found?
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
crate::engine::FN_IDX_SET => {
|
||||
assert!(args.len() == 3);
|
||||
|
||||
EvalAltResult::ErrorFunctionNotFound(
|
||||
format!(
|
||||
"{} [{}]=",
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
// Getter function not found?
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
_ if fn_name.starts_with(crate::engine::FN_GET) => {
|
||||
assert!(args.len() == 1);
|
||||
|
||||
EvalAltResult::ErrorDotExpr(
|
||||
format!(
|
||||
"Unknown property '{}' - a getter is not registered for type '{}'",
|
||||
&fn_name[crate::engine::FN_GET.len()..],
|
||||
self.map_type_name(args[0].type_name())
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
// Setter function not found?
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
_ if fn_name.starts_with(crate::engine::FN_SET) => {
|
||||
assert!(args.len() == 2);
|
||||
|
||||
EvalAltResult::ErrorDotExpr(
|
||||
format!(
|
||||
"No writable property '{}' - a setter is not registered for type '{}' to handle '{}'",
|
||||
&fn_name[crate::engine::FN_SET.len()..],
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
// Raise error
|
||||
_ => EvalAltResult::ErrorFunctionNotFound(
|
||||
self.gen_call_signature(None, fn_name, args.as_ref()),
|
||||
pos,
|
||||
)
|
||||
.into();
|
||||
.into(),
|
||||
}
|
||||
|
||||
// Setter function not found?
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
||||
return EvalAltResult::ErrorDotExpr(
|
||||
format!(
|
||||
"No writable property '{}' - a setter is not registered for type '{}' to handle '{}'",
|
||||
prop,
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into();
|
||||
}
|
||||
|
||||
// index getter function not found?
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if fn_name == crate::engine::FN_IDX_GET && args.len() == 2 {
|
||||
return EvalAltResult::ErrorFunctionNotFound(
|
||||
format!(
|
||||
"{} [{}]",
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into();
|
||||
}
|
||||
|
||||
// index setter function not found?
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if fn_name == crate::engine::FN_IDX_SET {
|
||||
return EvalAltResult::ErrorFunctionNotFound(
|
||||
format!(
|
||||
"{} [{}]=",
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
.into();
|
||||
}
|
||||
|
||||
// Raise error
|
||||
EvalAltResult::ErrorFunctionNotFound(
|
||||
self.gen_call_signature(None, fn_name, args.as_ref()),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Call a script-defined function.
|
||||
@ -500,7 +473,7 @@ impl Engine {
|
||||
err: Box<EvalAltResult>,
|
||||
pos: Position,
|
||||
) -> RhaiResult {
|
||||
Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
EvalAltResult::ErrorInFunctionCall(
|
||||
name,
|
||||
fn_def
|
||||
.lib
|
||||
@ -510,7 +483,8 @@ impl Engine {
|
||||
.to_string(),
|
||||
err,
|
||||
pos,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
self.inc_operations(state, pos)?;
|
||||
@ -523,7 +497,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if level > self.max_call_levels() {
|
||||
return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
|
||||
return EvalAltResult::ErrorStackOverflow(pos).into();
|
||||
}
|
||||
|
||||
let orig_scope_level = state.scope_level;
|
||||
@ -586,10 +560,10 @@ impl Engine {
|
||||
make_error(fn_name, fn_def, state, err, pos)
|
||||
}
|
||||
// System errors are passed straight-through
|
||||
mut err if err.is_system_exception() => Err(Box::new({
|
||||
mut err if err.is_system_exception() => {
|
||||
err.set_position(pos);
|
||||
err
|
||||
})),
|
||||
err.into()
|
||||
}
|
||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
||||
_ => make_error(fn_def.name.to_string(), fn_def, state, err, pos),
|
||||
});
|
||||
@ -623,11 +597,11 @@ impl Engine {
|
||||
}
|
||||
|
||||
// First check script-defined functions
|
||||
let result = lib.iter().any(|&m| m.contains_fn(hash_script, false))
|
||||
let result = lib.iter().any(|&m| m.contains_fn(hash_script))
|
||||
// Then check registered functions
|
||||
|| self.global_namespace.contains_fn(hash_script, false)
|
||||
|| self.global_namespace.contains_fn(hash_script)
|
||||
// Then check packages
|
||||
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script, false))
|
||||
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script))
|
||||
// Then check imported modules
|
||||
|| mods.map_or(false, |m| m.contains_fn(hash_script))
|
||||
// Then check sub-modules
|
||||
@ -653,7 +627,7 @@ impl Engine {
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
fn_name: &str,
|
||||
hash: FnHash,
|
||||
hash: FnCallHash,
|
||||
args: &mut FnCallArgs,
|
||||
is_ref: bool,
|
||||
_is_method: bool,
|
||||
@ -699,36 +673,39 @@ impl Engine {
|
||||
// Handle is_shared()
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => {
|
||||
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
return EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"'{}' should not be called this way. Try {}(...);",
|
||||
fn_name, fn_name
|
||||
)
|
||||
.into(),
|
||||
pos,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if args.len() == 1 => {
|
||||
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
return EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"'{}' should not be called this way. Try {}(...);",
|
||||
fn_name, fn_name
|
||||
)
|
||||
.into(),
|
||||
pos,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !args.is_empty() => {
|
||||
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
return EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"'{}' should not be called this way. Try {}(...);",
|
||||
fn_name, fn_name
|
||||
)
|
||||
.into(),
|
||||
pos,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
_ => (),
|
||||
@ -865,7 +842,6 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Evaluate a text script in place - used primarily for 'eval'.
|
||||
#[inline]
|
||||
fn eval_script_expr_in_place(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -917,7 +893,7 @@ impl Engine {
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
fn_name: &str,
|
||||
mut hash: FnHash,
|
||||
mut hash: FnCallHash,
|
||||
target: &mut crate::engine::Target,
|
||||
(call_args, call_arg_positions): &mut (StaticVec<Dynamic>, StaticVec<Position>),
|
||||
pos: Position,
|
||||
@ -937,7 +913,7 @@ impl Engine {
|
||||
let fn_name = fn_ptr.fn_name();
|
||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||
// Recalculate hashes
|
||||
let new_hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len));
|
||||
let new_hash = FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args_len));
|
||||
// Arguments are passed as-is, adding the curried arguments
|
||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||
let mut arg_values = curry
|
||||
@ -973,7 +949,7 @@ impl Engine {
|
||||
let fn_name = fn_ptr.fn_name();
|
||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||
// Recalculate hash
|
||||
let new_hash = FnHash::from_script_and_native(
|
||||
let new_hash = FnCallHash::from_script_and_native(
|
||||
calc_fn_hash(empty(), fn_name, args_len),
|
||||
calc_fn_hash(empty(), fn_name, args_len + 1),
|
||||
);
|
||||
@ -1048,7 +1024,7 @@ impl Engine {
|
||||
call_arg_positions.insert(i, Position::NONE);
|
||||
});
|
||||
// Recalculate the hash based on the new function name and new arguments
|
||||
hash = FnHash::from_script_and_native(
|
||||
hash = FnCallHash::from_script_and_native(
|
||||
calc_fn_hash(empty(), fn_name, call_args.len()),
|
||||
calc_fn_hash(empty(), fn_name, call_args.len() + 1),
|
||||
);
|
||||
@ -1086,7 +1062,7 @@ impl Engine {
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
fn_name: &str,
|
||||
args_expr: &[Expr],
|
||||
mut hash: FnHash,
|
||||
mut hash: FnCallHash,
|
||||
pos: Position,
|
||||
capture_scope: bool,
|
||||
level: usize,
|
||||
@ -1125,9 +1101,9 @@ impl Engine {
|
||||
// Recalculate hash
|
||||
let args_len = args_expr.len() + curry.len();
|
||||
hash = if !hash.is_native_only() {
|
||||
FnHash::from_script(calc_fn_hash(empty(), name, args_len))
|
||||
FnCallHash::from_script(calc_fn_hash(empty(), name, args_len))
|
||||
} else {
|
||||
FnHash::from_native(calc_fn_hash(empty(), name, args_len))
|
||||
FnCallHash::from_native(calc_fn_hash(empty(), name, args_len))
|
||||
};
|
||||
}
|
||||
// Handle Fn()
|
||||
|
@ -21,9 +21,9 @@ pub trait Func<ARGS, RET> {
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Func}; // use 'Func' for 'create_from_ast'
|
||||
/// use rhai::{Engine, Func}; // use 'Func' for 'create_from_ast'
|
||||
///
|
||||
/// let engine = Engine::new(); // create a new 'Engine' just for this
|
||||
/// let engine = Engine::new(); // create a new 'Engine' just for this
|
||||
///
|
||||
/// let ast = engine.compile("fn calc(x, y) { x + len(y) < 42 }")?;
|
||||
///
|
||||
@ -32,15 +32,15 @@ pub trait Func<ARGS, RET> {
|
||||
/// // 2) the return type of the script function
|
||||
///
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
/// let func = Func::<(i64, String), bool>::create_from_ast(
|
||||
/// // ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
/// let func = Func::<(i64, &str), bool>::create_from_ast(
|
||||
/// // ^^^^^^^^^^^ function parameter types in tuple
|
||||
///
|
||||
/// engine, // the 'Engine' is consumed into the closure
|
||||
/// ast, // the 'AST'
|
||||
/// "calc" // the entry-point function name
|
||||
/// );
|
||||
///
|
||||
/// func(123, "hello".to_string())? == false; // call the anonymous function
|
||||
/// func(123, "hello")? == false; // call the anonymous function
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;
|
||||
@ -53,9 +53,9 @@ pub trait Func<ARGS, RET> {
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Func}; // use 'Func' for 'create_from_script'
|
||||
/// use rhai::{Engine, Func}; // use 'Func' for 'create_from_script'
|
||||
///
|
||||
/// let engine = Engine::new(); // create a new 'Engine' just for this
|
||||
/// let engine = Engine::new(); // create a new 'Engine' just for this
|
||||
///
|
||||
/// let script = "fn calc(x, y) { x + len(y) < 42 }";
|
||||
///
|
||||
@ -64,15 +64,15 @@ pub trait Func<ARGS, RET> {
|
||||
/// // 2) the return type of the script function
|
||||
///
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
/// let func = Func::<(i64, String), bool>::create_from_script(
|
||||
/// // ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
/// let func = Func::<(i64, &str), bool>::create_from_script(
|
||||
/// // ^^^^^^^^^^^ function parameter types in tuple
|
||||
///
|
||||
/// engine, // the 'Engine' is consumed into the closure
|
||||
/// script, // the script, notice number of parameters must match
|
||||
/// "calc" // the entry-point function name
|
||||
/// )?;
|
||||
///
|
||||
/// func(123, "hello".to_string())? == false; // call the anonymous function
|
||||
/// func(123, "hello")? == false; // call the anonymous function
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Module defining interfaces to native-Rust functions.
|
||||
|
||||
use crate::ast::{FnAccess, FnHash};
|
||||
use crate::ast::{FnAccess, FnCallHash};
|
||||
use crate::engine::Imports;
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::stdlib::{
|
||||
@ -49,7 +49,7 @@ pub use crate::stdlib::cell::RefCell as Locked;
|
||||
pub use crate::stdlib::sync::RwLock as Locked;
|
||||
|
||||
/// Context of a native Rust function call.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct NativeCallContext<'a> {
|
||||
engine: &'a Engine,
|
||||
fn_name: &'a str,
|
||||
@ -190,12 +190,12 @@ impl<'a> NativeCallContext<'a> {
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
let hash = if is_method {
|
||||
FnHash::from_script_and_native(
|
||||
FnCallHash::from_script_and_native(
|
||||
calc_fn_hash(empty(), fn_name, args.len() - 1),
|
||||
calc_fn_hash(empty(), fn_name, args.len()),
|
||||
)
|
||||
} else {
|
||||
FnHash::from_script(calc_fn_hash(empty(), fn_name, args.len()))
|
||||
FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args.len()))
|
||||
};
|
||||
|
||||
self.engine()
|
||||
@ -320,7 +320,7 @@ impl FnPtr {
|
||||
#[inline(always)]
|
||||
pub fn call_dynamic(
|
||||
&self,
|
||||
ctx: NativeCallContext,
|
||||
ctx: &NativeCallContext,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
mut arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> RhaiResult {
|
||||
|
@ -4,71 +4,15 @@
|
||||
|
||||
use crate::dynamic::{DynamicWriteLock, Variant};
|
||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||
use crate::r#unsafe::unsafe_cast_box;
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
||||
use crate::{Dynamic, Engine, FnAccess, FnNamespace, NativeCallContext, RhaiResult};
|
||||
|
||||
/// Trait to register custom functions with the [`Engine`].
|
||||
pub trait RegisterFn<FN, ARGS, RET> {
|
||||
/// Register a custom function with the [`Engine`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// // Normal function
|
||||
/// fn add(x: i64, y: i64) -> i64 {
|
||||
/// x + y
|
||||
/// }
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// // You must use the trait rhai::RegisterFn to get this method.
|
||||
/// engine.register_fn("add", add);
|
||||
///
|
||||
/// assert_eq!(engine.eval::<i64>("add(40, 2)")?, 42);
|
||||
///
|
||||
/// // You can also register a closure.
|
||||
/// engine.register_fn("sub", |x: i64, y: i64| x - y );
|
||||
///
|
||||
/// assert_eq!(engine.eval::<i64>("sub(44, 2)")?, 42);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self;
|
||||
}
|
||||
|
||||
/// Trait to register fallible custom functions returning [`Result`]`<`[`Dynamic`]`, `[`Box`]`<`[`EvalAltResult`][crate::EvalAltResult]`>>` with the [`Engine`].
|
||||
pub trait RegisterResultFn<FN, ARGS> {
|
||||
/// Register a custom fallible function with the [`Engine`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Dynamic, RegisterResultFn, EvalAltResult};
|
||||
///
|
||||
/// // Normal function
|
||||
/// fn div(x: i64, y: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// if y == 0 {
|
||||
/// // '.into()' automatically converts to 'Box<EvalAltResult::ErrorRuntime>'
|
||||
/// Err("division by zero!".into())
|
||||
/// } else {
|
||||
/// Ok((x / y).into())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// // You must use the trait rhai::RegisterResultFn to get this method.
|
||||
/// engine.register_result_fn("div", div);
|
||||
///
|
||||
/// engine.eval::<i64>("div(42, 0)")
|
||||
/// .expect_err("expecting division by zero error!");
|
||||
/// ```
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self;
|
||||
}
|
||||
use crate::r#unsafe::unsafe_try_cast;
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
mem,
|
||||
string::String,
|
||||
vec,
|
||||
};
|
||||
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
||||
|
||||
// These types are used to build a unique _marker_ tuple type for each combination
|
||||
// of function parameter types in order to make each trait implementation unique.
|
||||
@ -77,7 +21,7 @@ pub trait RegisterResultFn<FN, ARGS> {
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// `RegisterFn<FN, (Mut<A>, B, Ref<C>), R>`
|
||||
// `NativeFunction<(Mut<A>, B, Ref<C>), R>`
|
||||
//
|
||||
// will have the function prototype constraint to:
|
||||
//
|
||||
@ -107,7 +51,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
ref_t.clone()
|
||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
||||
*unsafe_cast_box(Box::new(mem::take(data).take_string().unwrap())).unwrap()
|
||||
unsafe_try_cast(mem::take(data).take_string().unwrap()).unwrap()
|
||||
} else {
|
||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||
@ -115,41 +59,18 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
/// This macro creates a closure wrapping a registered function.
|
||||
macro_rules! make_func {
|
||||
($fn:ident : $map:expr ; $($par:ident => $let:stmt => $convert:expr => $arg:expr),*) => {
|
||||
// ^ function pointer
|
||||
// ^ result mapping function
|
||||
// ^ function parameter generic type name (A, B, C etc.)
|
||||
// ^ argument let statement(e.g. let mut A ...)
|
||||
// ^ dereferencing function
|
||||
// ^ argument reference expression(like A, *B, &mut C etc)
|
||||
|
||||
Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($convert)(_drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = $fn($($arg),*);
|
||||
|
||||
// Map the result
|
||||
$map(r)
|
||||
}) as Box<FnAny>
|
||||
};
|
||||
}
|
||||
|
||||
/// To Dynamic mapping function.
|
||||
#[inline(always)]
|
||||
pub fn map_dynamic(data: impl Variant + Clone) -> RhaiResult {
|
||||
Ok(data.into_dynamic())
|
||||
}
|
||||
|
||||
/// To Dynamic mapping function.
|
||||
#[inline(always)]
|
||||
pub fn map_result(data: RhaiResult) -> RhaiResult {
|
||||
data
|
||||
/// Trait to register custom Rust functions.
|
||||
pub trait RegisterNativeFunction<Args, Result> {
|
||||
/// Get the type ID's of this function's parameters.
|
||||
fn param_types() -> Box<[TypeId]>;
|
||||
/// Get the type names of this function's parameters.
|
||||
fn param_names() -> Box<[&'static str]>;
|
||||
/// Get the type ID of this function's return value.
|
||||
fn return_type() -> TypeId;
|
||||
/// Get the type name of this function's return value.
|
||||
fn return_type_name() -> &'static str;
|
||||
/// Convert this function into a [`CallableFunction`].
|
||||
fn into_callable_function(self) -> CallableFunction;
|
||||
}
|
||||
|
||||
macro_rules! def_register {
|
||||
@ -160,37 +81,97 @@ macro_rules! def_register {
|
||||
// ^ function ABI type
|
||||
// ^ function parameter generic type name (A, B, C etc.)
|
||||
// ^ call argument(like A, *B, &mut C etc)
|
||||
// ^ function parameter marker type (T, Ref<T> or Mut<T>)
|
||||
// ^ function parameter marker type (T, Ref<T> or Mut<T>)
|
||||
// ^ function parameter actual type (T, &T or &mut T)
|
||||
// ^ argument let statement
|
||||
|
||||
impl<
|
||||
$($par: Variant + Clone,)*
|
||||
FN: Fn($($param),*) -> RET + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
||||
{
|
||||
#[inline(always)]
|
||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
||||
&[$(TypeId::of::<$par>()),*],
|
||||
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*))
|
||||
);
|
||||
self
|
||||
> RegisterNativeFunction<($($mark,)*), ()> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<RET>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = self($($arg),*);
|
||||
|
||||
// Map the result
|
||||
Ok(r.into_dynamic())
|
||||
}) as Box<FnAny>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
FN: Fn($($param),*) -> RhaiResult + SendSync + 'static,
|
||||
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
||||
{
|
||||
#[inline(always)]
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
||||
&[$(TypeId::of::<$par>()),*],
|
||||
CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*))
|
||||
);
|
||||
self
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<RET>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = self(ctx, $($arg),*);
|
||||
|
||||
// Map the result
|
||||
Ok(r.into_dynamic())
|
||||
}) as Box<FnAny>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
FN: Fn($($param),*) -> Result<RET, Box<EvalAltResult>> + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<($($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<Result<RET, Box<EvalAltResult>>>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
self($($arg),*).map(Dynamic::from)
|
||||
}) as Box<FnAny>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> Result<RET, Box<EvalAltResult>> + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<Result<RET, Box<EvalAltResult>>>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
self(ctx, $($arg),*).map(Dynamic::from)
|
||||
}) as Box<FnAny>)
|
||||
}
|
||||
}
|
||||
|
||||
|
75
src/lib.rs
75
src/lib.rs
@ -25,7 +25,7 @@
|
||||
//! ## The Rust part
|
||||
//!
|
||||
//! ```,no_run
|
||||
//! use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||
//! use rhai::{Engine, EvalAltResult};
|
||||
//!
|
||||
//! fn main() -> Result<(), Box<EvalAltResult>>
|
||||
//! {
|
||||
@ -126,7 +126,7 @@ pub use ast::{FnAccess, AST};
|
||||
pub use dynamic::Dynamic;
|
||||
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
|
||||
pub use fn_native::{FnPtr, NativeCallContext};
|
||||
pub use fn_register::{RegisterFn, RegisterResultFn};
|
||||
pub use fn_register::RegisterNativeFunction;
|
||||
pub use module::{FnNamespace, Module};
|
||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||
pub use result::EvalAltResult;
|
||||
@ -191,8 +191,8 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated = "this type is volatile and may change"]
|
||||
pub use ast::{
|
||||
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnHash, Ident, OpAssignment,
|
||||
ReturnType, ScriptFnDef, Stmt, StmtBlock,
|
||||
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHash, Ident,
|
||||
OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock,
|
||||
};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
@ -207,15 +207,72 @@ pub use engine::Limits;
|
||||
#[deprecated = "this type is volatile and may change"]
|
||||
pub use module::NamespaceRef;
|
||||
|
||||
/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
||||
/// which is a specialized [`Vec`] backed by a small, fixed-size array when there are <= 4 items stored.
|
||||
/// Exported under the `internals` feature only.
|
||||
/// Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), which is a
|
||||
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored.
|
||||
///
|
||||
/// # Background
|
||||
///
|
||||
/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec`
|
||||
/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy."
|
||||
///
|
||||
/// And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies
|
||||
/// and orangutans and breakfast cereals and fruit bats and large chu...
|
||||
///
|
||||
/// And the Lord spake, saying, "First shalt thou depend on the [`smallvec`](https://crates.io/crates/smallvec) crate.
|
||||
/// Then, shalt thou keep four inline. No more. No less. Four shalt be the number thou shalt keep inline,
|
||||
/// and the number to keep inline shalt be four. Five shalt thou not keep inline, nor either keep inline
|
||||
/// thou two or three, excepting that thou then proceed to four. Six is right out. Once the number four,
|
||||
/// being the forth number, be reached, then, lobbest thou thy `SmallVec` towards thy heap, who,
|
||||
/// being slow and cache-naughty in My sight, shall snuff it."
|
||||
///
|
||||
/// # Explanation on the Number Four
|
||||
///
|
||||
/// `StaticVec` is used frequently to keep small lists of items in inline (non-heap) storage in
|
||||
/// order to improve cache friendliness and reduce indirections.
|
||||
///
|
||||
/// The number 4, other than being the holy number, is carefully chosen for a balance between
|
||||
/// storage space and reduce allocations. That is because most function calls (and most functions,
|
||||
/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a
|
||||
/// large number of external variables.
|
||||
///
|
||||
/// In addition, most scripts blocks either contain many statements, or just a few lines;
|
||||
/// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels
|
||||
/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long).
|
||||
#[cfg(not(feature = "internals"))]
|
||||
type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||
|
||||
/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
||||
/// which is a specialized [`Vec`] backed by a small, fixed-size array when there are <= 4 items stored.
|
||||
/// _(INTERNALS)_ Alias to [`smallvec`](https://crates.io/crates/smallvec), which is a specialized
|
||||
/// [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// # Background
|
||||
///
|
||||
/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec`
|
||||
/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy."
|
||||
///
|
||||
/// And the Lord did grin, and the people did feast upon the lambs and sloths and carp and anchovies
|
||||
/// and orangutans and breakfast cereals and fruit bats and large chu...
|
||||
///
|
||||
/// And the Lord spake, saying, "First shalt thou depend on the [`smallvec`](https://crates.io/crates/smallvec) crate.
|
||||
/// Then, shalt thou keep four inline. No more. No less. Four shalt be the number thou shalt keep inline,
|
||||
/// and the number to keep inline shalt be four. Five shalt thou not keep inline, nor either keep inline
|
||||
/// thou two or three, excepting that thou then proceed to four. Six is right out. Once the number four,
|
||||
/// being the forth number, be reached, then, lobbest thou thy `SmallVec` towards thy heap, who,
|
||||
/// being slow and cache-naughty in My sight, shall snuff it."
|
||||
///
|
||||
/// # Explanation on the Number Four
|
||||
///
|
||||
/// `StaticVec` is used frequently to keep small lists of items in inline (non-heap) storage in
|
||||
/// order to improve cache friendliness and reduce indirections.
|
||||
///
|
||||
/// The number 4, other than being the holy number, is carefully chosen for a balance between
|
||||
/// storage space and reduce allocations. That is because most function calls (and most functions,
|
||||
/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a
|
||||
/// large number of external variables.
|
||||
///
|
||||
/// In addition, most scripts blocks either contain many statements, or just a few lines;
|
||||
/// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels
|
||||
/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long).
|
||||
#[cfg(feature = "internals")]
|
||||
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
use crate::ast::{FnAccess, Ident};
|
||||
use crate::dynamic::Variant;
|
||||
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
||||
use crate::fn_register::by_value as cast_arg;
|
||||
use crate::fn_register::RegisterNativeFunction;
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
@ -513,15 +513,10 @@ impl Module {
|
||||
&self,
|
||||
name: &str,
|
||||
num_params: usize,
|
||||
public_only: bool,
|
||||
) -> Option<&Shared<crate::ast::ScriptFnDef>> {
|
||||
self.functions
|
||||
.values()
|
||||
.find(|f| {
|
||||
(!public_only || f.access == FnAccess::Public)
|
||||
&& f.params == num_params
|
||||
&& f.name == name
|
||||
})
|
||||
.find(|f| f.params == num_params && f.name == name)
|
||||
.map(|f| f.func.get_fn_def())
|
||||
}
|
||||
|
||||
@ -607,7 +602,7 @@ impl Module {
|
||||
|
||||
/// Does the particular Rust function exist in the [`Module`]?
|
||||
///
|
||||
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -615,26 +610,17 @@ impl Module {
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// let hash = module.set_native_fn("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool {
|
||||
if public_only {
|
||||
self.functions
|
||||
.get(&hash_fn)
|
||||
.map_or(false, |f| match f.access {
|
||||
FnAccess::Public => true,
|
||||
FnAccess::Private => false,
|
||||
})
|
||||
} else {
|
||||
self.functions.contains_key(&hash_fn)
|
||||
}
|
||||
pub fn contains_fn(&self, hash_fn: u64) -> bool {
|
||||
self.functions.contains_key(&hash_fn)
|
||||
}
|
||||
|
||||
/// Update the metadata (parameter names/types and return type) of a registered function.
|
||||
///
|
||||
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||
///
|
||||
/// ## Parameter Names and Types
|
||||
///
|
||||
@ -654,7 +640,7 @@ impl Module {
|
||||
|
||||
/// Update the namespace of a registered function.
|
||||
///
|
||||
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||
#[inline(always)]
|
||||
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||
@ -796,7 +782,7 @@ impl Module {
|
||||
/// Ok(orig) // return Result<T, Box<EvalAltResult>>
|
||||
/// });
|
||||
///
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_raw_fn<T: Variant + Clone>(
|
||||
@ -822,13 +808,19 @@ impl Module {
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking no parameters into the [`Module`], returning a hash key.
|
||||
/// Set a Rust function into the [`Module`], returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Namespace
|
||||
///
|
||||
/// The default function namespace is [`FnNamespace::Internal`].
|
||||
/// Use [`update_fn_namespace`][Module::update_fn_namespace] to change it.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
/// No metadata for the function is registered.
|
||||
/// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -836,101 +828,22 @@ impl Module {
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// let hash = module.set_native_fn("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_0<T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn() -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from);
|
||||
let arg_types = [];
|
||||
pub fn set_native_fn<ARGS, T, F>(&mut self, name: impl Into<String>, func: F) -> u64
|
||||
where
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||
{
|
||||
self.set_fn(
|
||||
name,
|
||||
FnNamespace::Internal,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_pure(Box::new(f)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking one parameter into the [`Module`], returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>()];
|
||||
self.set_fn(
|
||||
name,
|
||||
FnNamespace::Internal,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_pure(Box::new(f)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking one mutable parameter into the [`Module`], returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, FnNamespace};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_1_mut("calc", FnNamespace::Internal,
|
||||
/// |x: &mut i64| { *x += 1; Ok(*x) }
|
||||
/// );
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
namespace: FnNamespace,
|
||||
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>()];
|
||||
self.set_fn(
|
||||
name,
|
||||
namespace,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_method(Box::new(f)),
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -950,108 +863,24 @@ impl Module {
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
self.set_fn_1_mut(
|
||||
pub fn set_getter_fn<ARGS, A, T, F>(&mut self, name: impl Into<String>, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||
F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
{
|
||||
self.set_fn(
|
||||
crate::engine::make_getter(&name.into()),
|
||||
FnNamespace::Global,
|
||||
func,
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters into the [`Module`], returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_2("calc", |x: i64, y: ImmutableString| {
|
||||
/// Ok(x + y.len() as i64)
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let a = cast_arg::<A>(&mut args[0]);
|
||||
let b = cast_arg::<B>(&mut args[1]);
|
||||
|
||||
func(a, b).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(
|
||||
name,
|
||||
FnNamespace::Internal,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_pure(Box::new(f)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters (the first one mutable) into the [`Module`],
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, FnNamespace, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_2_mut("calc", FnNamespace::Internal,
|
||||
/// |x: &mut i64, y: ImmutableString| {
|
||||
/// *x += y.len() as i64;
|
||||
/// Ok(*x)
|
||||
/// }
|
||||
/// );
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
namespace: FnNamespace,
|
||||
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let b = cast_arg::<B>(&mut args[1]);
|
||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||
|
||||
func(a, b).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(
|
||||
name,
|
||||
namespace,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_method(Box::new(f)),
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1075,19 +904,24 @@ impl Module {
|
||||
/// *x = y.len() as i64;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
self.set_fn_2_mut(
|
||||
pub fn set_setter_fn<ARGS, A, B, F>(&mut self, name: impl Into<String>, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
||||
F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
{
|
||||
self.set_fn(
|
||||
crate::engine::make_setter(&name.into()),
|
||||
FnNamespace::Global,
|
||||
func,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1115,14 +949,18 @@ impl Module {
|
||||
/// let hash = module.set_indexer_get_fn(|x: &mut i64, y: ImmutableString| {
|
||||
/// Ok(*x + y.len() as i64)
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
{
|
||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@ -1137,107 +975,13 @@ impl Module {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
self.set_fn_2_mut(crate::engine::FN_IDX_GET, FnNamespace::Global, func)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters into the [`Module`], returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_3("calc", |x: i64, y: ImmutableString, z: i64| {
|
||||
/// Ok(x + y.len() as i64 + z)
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_3<
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let a = cast_arg::<A>(&mut args[0]);
|
||||
let b = cast_arg::<B>(&mut args[1]);
|
||||
let c = cast_arg::<C>(&mut args[2]);
|
||||
|
||||
func(a, b, c).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(
|
||||
name,
|
||||
FnNamespace::Internal,
|
||||
crate::engine::FN_IDX_GET,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_pure(Box::new(f)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters (the first one mutable) into the [`Module`],
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, FnNamespace, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_3_mut("calc", FnNamespace::Internal,
|
||||
/// |x: &mut i64, y: ImmutableString, z: i64| {
|
||||
/// *x += y.len() as i64 + z;
|
||||
/// Ok(*x)
|
||||
/// }
|
||||
/// );
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_3_mut<
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
namespace: FnNamespace,
|
||||
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let b = cast_arg::<B>(&mut args[2]);
|
||||
let c = cast_arg::<C>(&mut args[3]);
|
||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||
|
||||
func(a, b, c).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(
|
||||
name,
|
||||
namespace,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_method(Box::new(f)),
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1266,14 +1010,18 @@ impl Module {
|
||||
/// *x = y.len() as i64 + value;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||
&mut self,
|
||||
func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
||||
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
{
|
||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@ -1288,21 +1036,13 @@ impl Module {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let b = cast_arg::<B>(&mut args[1]);
|
||||
let c = cast_arg::<C>(&mut args[2]);
|
||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||
|
||||
func(a, b, c).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(
|
||||
crate::engine::FN_IDX_SET,
|
||||
FnNamespace::Internal,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_method(Box::new(f)),
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1336,8 +1076,8 @@ impl Module {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// );
|
||||
/// assert!(module.contains_fn(hash_get, true));
|
||||
/// assert!(module.contains_fn(hash_set, true));
|
||||
/// assert!(module.contains_fn(hash_get));
|
||||
/// assert!(module.contains_fn(hash_set));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
@ -1352,131 +1092,12 @@ impl Module {
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking four parameters into the [`Module`], returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_4("calc", |x: i64, y: ImmutableString, z: i64, _w: ()| {
|
||||
/// Ok(x + y.len() as i64 + z)
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_4<
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
D: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let a = cast_arg::<A>(&mut args[0]);
|
||||
let b = cast_arg::<B>(&mut args[1]);
|
||||
let c = cast_arg::<C>(&mut args[2]);
|
||||
let d = cast_arg::<D>(&mut args[3]);
|
||||
|
||||
func(a, b, c, d).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [
|
||||
TypeId::of::<A>(),
|
||||
TypeId::of::<B>(),
|
||||
TypeId::of::<C>(),
|
||||
TypeId::of::<D>(),
|
||||
];
|
||||
self.set_fn(
|
||||
name,
|
||||
FnNamespace::Internal,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_pure(Box::new(f)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking four parameters (the first one mutable) into the [`Module`],
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Function Metadata
|
||||
///
|
||||
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, FnNamespace, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_4_mut("calc", FnNamespace::Internal,
|
||||
/// |x: &mut i64, y: ImmutableString, z: i64, _w: ()| {
|
||||
/// *x += y.len() as i64 + z;
|
||||
/// Ok(*x)
|
||||
/// }
|
||||
/// );
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_fn_4_mut<
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
D: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
namespace: FnNamespace,
|
||||
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||
let b = cast_arg::<B>(&mut args[1]);
|
||||
let c = cast_arg::<C>(&mut args[2]);
|
||||
let d = cast_arg::<D>(&mut args[3]);
|
||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||
|
||||
func(a, b, c, d).map(Dynamic::from)
|
||||
};
|
||||
let arg_types = [
|
||||
TypeId::of::<A>(),
|
||||
TypeId::of::<B>(),
|
||||
TypeId::of::<C>(),
|
||||
TypeId::of::<D>(),
|
||||
];
|
||||
self.set_fn(
|
||||
name,
|
||||
namespace,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&arg_types,
|
||||
CallableFunction::from_method(Box::new(f)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a Rust function.
|
||||
///
|
||||
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> {
|
||||
self.functions.get(&hash_fn).and_then(|f| match f.access {
|
||||
_ if !public_only => Some(&f.func),
|
||||
FnAccess::Public => Some(&f.func),
|
||||
FnAccess::Private => None,
|
||||
})
|
||||
pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> {
|
||||
self.functions.get(&hash_fn).map(|f| &f.func)
|
||||
}
|
||||
|
||||
/// Does the particular namespace-qualified function exist in the [`Module`]?
|
||||
@ -1777,12 +1398,14 @@ impl Module {
|
||||
|
||||
scope.into_iter().for_each(|(_, value, mut aliases)| {
|
||||
// Variables with an alias left in the scope become module variables
|
||||
if aliases.len() > 1 {
|
||||
aliases.into_iter().for_each(|alias| {
|
||||
match aliases.len() {
|
||||
0 => (),
|
||||
1 => {
|
||||
module.variables.insert(aliases.pop().unwrap(), value);
|
||||
}
|
||||
_ => aliases.into_iter().for_each(|alias| {
|
||||
module.variables.insert(alias, value.clone());
|
||||
});
|
||||
} else if aliases.len() == 1 {
|
||||
module.variables.insert(aliases.pop().unwrap(), value);
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -139,9 +139,9 @@ fn has_native_fn(state: &State, hash_script: u64, arg_types: &[TypeId]) -> bool
|
||||
let hash = combine_hashes(hash_script, hash_params);
|
||||
|
||||
// First check registered functions
|
||||
state.engine.global_namespace.contains_fn(hash, false)
|
||||
state.engine.global_namespace.contains_fn(hash)
|
||||
// Then check packages
|
||||
|| state.engine.global_modules.iter().any(|m| m.contains_fn(hash, false))
|
||||
|| state.engine.global_modules.iter().any(|m| m.contains_fn(hash))
|
||||
// Then check sub-modules
|
||||
|| state.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
||||
}
|
||||
@ -774,7 +774,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
||||
=> {
|
||||
// First search for script-defined functions (can override built-in)
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args.len(), false).is_some());
|
||||
let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args.len()).is_some());
|
||||
#[cfg(feature = "no_function")]
|
||||
let has_script_fn = false;
|
||||
|
||||
|
@ -175,12 +175,12 @@ mod array_functions {
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
ar.push(
|
||||
mapper
|
||||
.call_dynamic(ctx, None, [item.clone()])
|
||||
.call_dynamic(&ctx, None, [item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(mapper.fn_name()) =>
|
||||
{
|
||||
mapper.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||
mapper.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -207,12 +207,12 @@ mod array_functions {
|
||||
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
if filter
|
||||
.call_dynamic(ctx, None, [item.clone()])
|
||||
.call_dynamic(&ctx, None, [item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(filter.fn_name()) =>
|
||||
{
|
||||
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||
filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -303,12 +303,12 @@ mod array_functions {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
if filter
|
||||
.call_dynamic(ctx, None, [item.clone()])
|
||||
.call_dynamic(&ctx, None, [item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(filter.fn_name()) =>
|
||||
{
|
||||
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||
filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -337,12 +337,12 @@ mod array_functions {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
if filter
|
||||
.call_dynamic(ctx, None, [item.clone()])
|
||||
.call_dynamic(&ctx, None, [item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(filter.fn_name()) =>
|
||||
{
|
||||
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||
filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -371,12 +371,12 @@ mod array_functions {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
if !filter
|
||||
.call_dynamic(ctx, None, [item.clone()])
|
||||
.call_dynamic(&ctx, None, [item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(filter.fn_name()) =>
|
||||
{
|
||||
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||
filter.call_dynamic(&ctx, None, [item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -407,12 +407,12 @@ mod array_functions {
|
||||
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
result = reducer
|
||||
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||
.call_dynamic(&ctx, None, [result.clone(), item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||
{
|
||||
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -435,7 +435,7 @@ mod array_functions {
|
||||
reducer: FnPtr,
|
||||
initial: FnPtr,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
|
||||
let mut result = initial.call_dynamic(&ctx, None, []).map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
@ -446,12 +446,12 @@ mod array_functions {
|
||||
|
||||
for (i, item) in array.iter().enumerate() {
|
||||
result = reducer
|
||||
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||
.call_dynamic(&ctx, None, [result.clone(), item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||
{
|
||||
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -477,12 +477,12 @@ mod array_functions {
|
||||
|
||||
for (i, item) in array.iter().enumerate().rev() {
|
||||
result = reducer
|
||||
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||
.call_dynamic(&ctx, None, [result.clone(), item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||
{
|
||||
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -505,7 +505,7 @@ mod array_functions {
|
||||
reducer: FnPtr,
|
||||
initial: FnPtr,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
|
||||
let mut result = initial.call_dynamic(&ctx, None, []).map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce_rev".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
@ -516,12 +516,12 @@ mod array_functions {
|
||||
|
||||
for (i, item) in array.iter().enumerate().rev() {
|
||||
result = reducer
|
||||
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||
.call_dynamic(&ctx, None, [result.clone(), item.clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||
{
|
||||
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
reducer.call_dynamic(&ctx, None, [result, item.clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -545,7 +545,7 @@ mod array_functions {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
array.sort_by(|x, y| {
|
||||
comparer
|
||||
.call_dynamic(ctx, None, [x.clone(), y.clone()])
|
||||
.call_dynamic(&ctx, None, [x.clone(), y.clone()])
|
||||
.ok()
|
||||
.and_then(|v| v.as_int().ok())
|
||||
.map(|v| {
|
||||
@ -587,12 +587,12 @@ mod array_functions {
|
||||
i -= 1;
|
||||
|
||||
if filter
|
||||
.call_dynamic(ctx, None, [array[i].clone()])
|
||||
.call_dynamic(&ctx, None, [array[i].clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(filter.fn_name()) =>
|
||||
{
|
||||
filter.call_dynamic(ctx, None, [array[i].clone(), (i as INT).into()])
|
||||
filter.call_dynamic(&ctx, None, [array[i].clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
@ -647,12 +647,12 @@ mod array_functions {
|
||||
i -= 1;
|
||||
|
||||
if !filter
|
||||
.call_dynamic(ctx, None, [array[i].clone()])
|
||||
.call_dynamic(&ctx, None, [array[i].clone()])
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||
if fn_sig.starts_with(filter.fn_name()) =>
|
||||
{
|
||||
filter.call_dynamic(ctx, None, [array[i].clone(), (i as INT).into()])
|
||||
filter.call_dynamic(&ctx, None, [array[i].clone(), (i as INT).into()])
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
|
@ -108,8 +108,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
||||
});
|
||||
module.iter_sub_modules().for_each(|(ns, m)| {
|
||||
let ns: ImmutableString = format!("{}::{}", namespace, ns).into();
|
||||
scan_module(list, dict, ns, m.as_ref())
|
||||
let ns = format!("{}::{}", namespace, ns);
|
||||
scan_module(list, dict, ns.into(), m.as_ref())
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ where
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if let Some(r) = from.checked_add(&step) {
|
||||
if r == from {
|
||||
return Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
return EvalAltResult::ErrorInFunctionCall(
|
||||
"range".to_string(),
|
||||
"".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
@ -37,7 +37,8 @@ where
|
||||
crate::Position::NONE,
|
||||
)),
|
||||
crate::Position::NONE,
|
||||
)));
|
||||
)
|
||||
.into();
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +142,7 @@ macro_rules! reg_range {
|
||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||
$(
|
||||
$lib.set_iterator::<Range<$y>>();
|
||||
let hash = $lib.set_fn_2($x, get_range::<$y>);
|
||||
let hash = $lib.set_native_fn($x, get_range::<$y>);
|
||||
$lib.update_fn_metadata(hash, &[
|
||||
concat!("from: ", stringify!($y)),
|
||||
concat!("to: ", stringify!($y)),
|
||||
@ -152,7 +153,7 @@ macro_rules! reg_range {
|
||||
($lib:ident | step $x:expr => $( $y:ty ),*) => {
|
||||
$(
|
||||
$lib.set_iterator::<StepRange<$y>>();
|
||||
let hash = $lib.set_fn_3($x, get_step_range::<$y>);
|
||||
let hash = $lib.set_native_fn($x, get_step_range::<$y>);
|
||||
$lib.update_fn_metadata(hash, &[
|
||||
concat!("from: ", stringify!($y)),
|
||||
concat!("to: ", stringify!($y)),
|
||||
@ -204,10 +205,10 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
if step.is_zero() {
|
||||
use crate::stdlib::string::ToString;
|
||||
|
||||
return Err(Box::new(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||
crate::Position::NONE,
|
||||
)));
|
||||
).into();
|
||||
}
|
||||
|
||||
Ok(Self(from, to, step))
|
||||
@ -246,12 +247,14 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::stdlib::iter::FusedIterator for StepDecimalRange {}
|
||||
|
||||
lib.set_iterator::<StepDecimalRange>();
|
||||
|
||||
let hash = lib.set_fn_2("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
|
||||
let hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
|
||||
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]);
|
||||
|
||||
let hash = lib.set_fn_3("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
||||
let hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
||||
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||
}
|
||||
});
|
||||
|
@ -29,15 +29,11 @@ mod map_functions {
|
||||
}
|
||||
#[rhai_fn(name = "mixin", name = "+=")]
|
||||
pub fn mixin(map: &mut Map, map2: Map) {
|
||||
map2.into_iter().for_each(|(key, value)| {
|
||||
map.insert(key, value);
|
||||
});
|
||||
map.extend(map2.into_iter());
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn merge(mut map: Map, map2: Map) -> Map {
|
||||
map2.into_iter().for_each(|(key, value)| {
|
||||
map.insert(key, value);
|
||||
});
|
||||
map.extend(map2.into_iter());
|
||||
map
|
||||
}
|
||||
pub fn fill_with(map: &mut Map, map2: Map) {
|
||||
|
@ -53,8 +53,7 @@ pub trait Package {
|
||||
/// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module])
|
||||
/// and register functions into it.
|
||||
///
|
||||
/// Functions can be added to the package using the standard module methods such as
|
||||
/// [`set_fn_2`][Module::set_fn_2], [`set_fn_3_mut`][Module::set_fn_3_mut], [`set_fn_0`][Module::set_fn_0] etc.
|
||||
/// Functions can be added to the package using [`Module::set_native_fn`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -69,7 +68,7 @@ pub trait Package {
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// // Load a binary function with all value parameters.
|
||||
/// lib.set_fn_2("my_add", add);
|
||||
/// lib.set_native_fn("my_add", add);
|
||||
/// });
|
||||
/// ```
|
||||
#[macro_export]
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Main module defining the lexer and parser.
|
||||
|
||||
use crate::ast::{
|
||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, ScriptFnDef,
|
||||
Stmt, StmtBlock,
|
||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType,
|
||||
ScriptFnDef, Stmt, StmtBlock,
|
||||
};
|
||||
use crate::dynamic::{AccessMode, Union};
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
@ -359,9 +359,9 @@ fn parse_fn_call(
|
||||
capture,
|
||||
namespace,
|
||||
hash: if is_valid_identifier(id.chars()) {
|
||||
FnHash::from_script(hash)
|
||||
FnCallHash::from_script(hash)
|
||||
} else {
|
||||
FnHash::from_native(hash)
|
||||
FnCallHash::from_native(hash)
|
||||
},
|
||||
args,
|
||||
..Default::default()
|
||||
@ -402,9 +402,9 @@ fn parse_fn_call(
|
||||
capture,
|
||||
namespace,
|
||||
hash: if is_valid_identifier(id.chars()) {
|
||||
FnHash::from_script(hash)
|
||||
FnCallHash::from_script(hash)
|
||||
} else {
|
||||
FnHash::from_native(hash)
|
||||
FnCallHash::from_native(hash)
|
||||
},
|
||||
args,
|
||||
..Default::default()
|
||||
@ -1291,7 +1291,7 @@ fn parse_unary(
|
||||
Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: op.into(),
|
||||
hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||
hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
@ -1318,7 +1318,7 @@ fn parse_unary(
|
||||
Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: op.into(),
|
||||
hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||
hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
@ -1339,7 +1339,7 @@ fn parse_unary(
|
||||
Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: op.into(),
|
||||
hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||
hash: FnCallHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
@ -1538,7 +1538,7 @@ fn make_dot_expr(
|
||||
}
|
||||
Expr::FnCall(mut func, func_pos) => {
|
||||
// Recalculate hash
|
||||
func.hash = FnHash::from_script_and_native(
|
||||
func.hash = FnCallHash::from_script_and_native(
|
||||
calc_fn_hash(empty(), &func.name, func.args.len()),
|
||||
calc_fn_hash(empty(), &func.name, func.args.len() + 1),
|
||||
);
|
||||
@ -1594,7 +1594,7 @@ fn make_dot_expr(
|
||||
// lhs.func(...)
|
||||
(lhs, Expr::FnCall(mut func, func_pos)) => {
|
||||
// Recalculate hash
|
||||
func.hash = FnHash::from_script_and_native(
|
||||
func.hash = FnCallHash::from_script_and_native(
|
||||
calc_fn_hash(empty(), &func.name, func.args.len()),
|
||||
calc_fn_hash(empty(), &func.name, func.args.len() + 1),
|
||||
);
|
||||
@ -1682,7 +1682,7 @@ fn parse_binary_op(
|
||||
|
||||
let op_base = FnCallExpr {
|
||||
name: op,
|
||||
hash: FnHash::from_native(hash),
|
||||
hash: FnCallHash::from_native(hash),
|
||||
capture: false,
|
||||
..Default::default()
|
||||
};
|
||||
@ -1747,7 +1747,7 @@ fn parse_binary_op(
|
||||
let hash = calc_fn_hash(empty(), OP_CONTAINS, 2);
|
||||
Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
hash: FnHash::from_script(hash),
|
||||
hash: FnCallHash::from_script(hash),
|
||||
args,
|
||||
name: OP_CONTAINS.into(),
|
||||
..op_base
|
||||
@ -1768,9 +1768,9 @@ fn parse_binary_op(
|
||||
Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
hash: if is_valid_identifier(s.chars()) {
|
||||
FnHash::from_script(hash)
|
||||
FnCallHash::from_script(hash)
|
||||
} else {
|
||||
FnHash::from_native(hash)
|
||||
FnCallHash::from_native(hash)
|
||||
},
|
||||
args,
|
||||
..op_base
|
||||
@ -2783,7 +2783,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
|
||||
let expr = Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: curry_func.into(),
|
||||
hash: FnHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)),
|
||||
hash: FnCallHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
@ -2884,7 +2884,7 @@ fn parse_anon_fn(
|
||||
|
||||
// Create unique function name by hashing the script body plus the parameters.
|
||||
let hasher = &mut get_hasher();
|
||||
params.iter().for_each(|p| p.as_str().hash(hasher));
|
||||
params.iter().for_each(|p| p.hash(hasher));
|
||||
body.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
|
@ -5,7 +5,7 @@ pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString,
|
||||
use crate::RhaiResult;
|
||||
pub use crate::{
|
||||
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
||||
NativeCallContext, Position, RegisterFn, RegisterResultFn,
|
||||
NativeCallContext, Position,
|
||||
};
|
||||
|
||||
#[cfg(not(features = "no_module"))]
|
||||
|
@ -434,10 +434,10 @@ impl<'a> Scope<'a> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.for_each(|(index, (name, alias))| {
|
||||
.for_each(|(i, (name, alias))| {
|
||||
if !entries.names.iter().any(|(key, _)| key == name) {
|
||||
entries.names.push((name.clone(), alias.clone()));
|
||||
entries.values.push(self.values[index].clone());
|
||||
entries.values.push(self.values[i].clone());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -228,8 +228,8 @@ impl Engine {
|
||||
if include_global {
|
||||
self.global_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter_fn().map(|f| f.into()))
|
||||
.for_each(|info| global.functions.push(info));
|
||||
.flat_map(|m| m.iter_fn())
|
||||
.for_each(|f| global.functions.push(f.into()));
|
||||
}
|
||||
|
||||
self.global_sub_modules.iter().for_each(|(name, m)| {
|
||||
@ -238,13 +238,11 @@ impl Engine {
|
||||
|
||||
self.global_namespace
|
||||
.iter_fn()
|
||||
.map(|f| f.into())
|
||||
.for_each(|info| global.functions.push(info));
|
||||
.for_each(|f| global.functions.push(f.into()));
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
_ast.iter_functions()
|
||||
.map(|f| f.into())
|
||||
.for_each(|info| global.functions.push(info));
|
||||
.for_each(|f| global.functions.push(f.into()));
|
||||
|
||||
global.functions.sort();
|
||||
|
||||
|
@ -576,10 +576,10 @@ impl Token {
|
||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||
|
||||
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "public"
|
||||
| "new" | "use" | "module" | "package" | "var" | "static" | "begin" | "end"
|
||||
| "shared" | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match"
|
||||
| "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||
| "async" | "await" | "yield" => Reserved(syntax.into()),
|
||||
| "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static"
|
||||
| "begin" | "end" | "shared" | "with" | "each" | "then" | "goto" | "unless"
|
||||
| "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn"
|
||||
| "thread" | "go" | "sync" | "async" | "await" | "yield" => Reserved(syntax.into()),
|
||||
|
||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => {
|
||||
|
20
src/utils.rs
20
src/utils.rs
@ -72,17 +72,16 @@ pub fn get_hasher() -> ahash::AHasher {
|
||||
///
|
||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||
#[inline(always)]
|
||||
pub fn calc_fn_hash<'a>(
|
||||
mut modules: impl Iterator<Item = &'a str>,
|
||||
fn_name: &str,
|
||||
num: usize,
|
||||
) -> u64 {
|
||||
pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, num: usize) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
|
||||
// Hash a boolean indicating whether the hash is namespace-qualified.
|
||||
modules.next().is_some().hash(s);
|
||||
// We always skip the first module
|
||||
modules.for_each(|m| m.hash(s));
|
||||
let mut len = 0;
|
||||
modules
|
||||
.inspect(|_| len += 1)
|
||||
.skip(1)
|
||||
.for_each(|m| m.hash(s));
|
||||
len.hash(s);
|
||||
fn_name.hash(s);
|
||||
num.hash(s);
|
||||
s.finish()
|
||||
@ -96,10 +95,7 @@ pub fn calc_fn_hash<'a>(
|
||||
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
let mut len = 0;
|
||||
params.for_each(|t| {
|
||||
t.hash(s);
|
||||
len += 1;
|
||||
});
|
||||
params.inspect(|_| len += 1).for_each(|t| t.hash(s));
|
||||
len.hash(s);
|
||||
s.finish()
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg(not(feature = "no_index"))]
|
||||
use rhai::{Array, Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Array, Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, RegisterFn, Scope, INT};
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, Scope, INT};
|
||||
use std::{any::TypeId, iter::once};
|
||||
|
||||
#[test]
|
||||
@ -131,7 +131,7 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
|
||||
let value = args[2].clone();
|
||||
let this_ptr = args.get_mut(0).unwrap();
|
||||
|
||||
fp.call_dynamic(context, Some(this_ptr), [value])
|
||||
fp.call_dynamic(&context, Some(this_ptr), [value])
|
||||
},
|
||||
);
|
||||
|
||||
@ -215,5 +215,13 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423);
|
||||
|
||||
let calc_func = Func::<(INT, &str, INT), INT>::create_from_script(
|
||||
Engine::new(),
|
||||
"fn calc(x, y, z) { (x + len(y)) * z }",
|
||||
"calc",
|
||||
)?;
|
||||
|
||||
assert_eq!(calc_func(42, "hello", 9)?, 423);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{
|
||||
Engine, EvalAltResult, FnPtr, NativeCallContext, ParseErrorType, RegisterFn, Scope, INT,
|
||||
};
|
||||
use rhai::{Engine, EvalAltResult, FnPtr, NativeCallContext, ParseErrorType, Scope, INT};
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::mem::take;
|
||||
@ -20,7 +18,7 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
|
||||
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|
||||
|context, args| {
|
||||
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
|
||||
fn_ptr.call_dynamic(context, None, [std::mem::take(args[1])])
|
||||
fn_ptr.call_dynamic(&context, None, [std::mem::take(args[1])])
|
||||
},
|
||||
);
|
||||
|
||||
@ -159,7 +157,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
||||
|context, args| {
|
||||
let func = take(args[1]).cast::<FnPtr>();
|
||||
|
||||
func.call_dynamic(context, None, [])
|
||||
func.call_dynamic(&context, None, [])
|
||||
},
|
||||
);
|
||||
|
||||
@ -343,7 +341,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
|
||||
let context = NativeCallContext::new(&engine, &fn_name, &lib);
|
||||
|
||||
// Closure 'f' captures: the engine, the AST, and the curried function pointer
|
||||
let f = move |x: INT| fn_ptr.call_dynamic(context, None, [x.into()]);
|
||||
let f = move |x: INT| fn_ptr.call_dynamic(&context, None, [x.into()]);
|
||||
|
||||
assert_eq!(f(42)?.take_string(), Ok("hello42".to_string()));
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, ParseErrorType, RegisterFn, Scope, INT};
|
||||
use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg(not(feature = "no_float"))]
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, FLOAT};
|
||||
use rhai::{Engine, EvalAltResult, FLOAT};
|
||||
|
||||
const EPSILON: FLOAT = 0.000_000_000_1;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -171,7 +171,7 @@ fn test_for_module_iterator() -> Result<(), Box<EvalAltResult>> {
|
||||
// Set a type iterator deep inside a nested module chain
|
||||
let mut sub_module = Module::new();
|
||||
sub_module.set_iterable::<MyIterableType>();
|
||||
sub_module.set_fn_0("new_ts", || Ok(MyIterableType("hello".to_string())));
|
||||
sub_module.set_native_fn("new_ts", || Ok(MyIterableType("hello".to_string())));
|
||||
|
||||
let mut module = Module::new();
|
||||
module.set_sub_module("inner", sub_module);
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{Engine, EvalAltResult, FnNamespace, Module, ParseErrorType, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, FnNamespace, Module, NativeCallContext, ParseErrorType, INT};
|
||||
|
||||
#[test]
|
||||
fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -51,6 +51,22 @@ fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[test]
|
||||
fn test_functions_context() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.set_max_modules(40);
|
||||
engine.register_fn("test", |context: NativeCallContext, x: INT| {
|
||||
context.engine().max_modules() as INT + x
|
||||
});
|
||||
|
||||
assert_eq!(engine.eval::<INT>("test(2)")?, 42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_functions_params() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
@ -67,7 +83,6 @@ fn test_functions_params() -> Result<(), Box<EvalAltResult>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[test]
|
||||
fn test_functions_namespaces() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
@ -75,18 +90,22 @@ fn test_functions_namespaces() -> Result<(), Box<EvalAltResult>> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
let mut m = Module::new();
|
||||
let hash = m.set_fn_0("test", || Ok(999 as INT));
|
||||
let hash = m.set_native_fn("test", || Ok(999 as INT));
|
||||
m.update_fn_namespace(hash, FnNamespace::Global);
|
||||
|
||||
engine.register_static_module("hello", m.into());
|
||||
|
||||
assert_eq!(engine.eval::<INT>("test()")?, 999);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
assert_eq!(engine.eval::<INT>("fn test() { 123 } test()")?, 123);
|
||||
}
|
||||
|
||||
engine.register_fn("test", || 42 as INT);
|
||||
|
||||
assert_eq!(engine.eval::<INT>("test()")?, 42);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
assert_eq!(engine.eval::<INT>("fn test() { 123 } test()")?, 123);
|
||||
|
||||
Ok(())
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![cfg(not(feature = "no_object"))]
|
||||
|
||||
use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, ImmutableString, INT};
|
||||
|
||||
#[test]
|
||||
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![cfg(not(feature = "no_object"))]
|
||||
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_method_call() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_mismatched_op() {
|
||||
|
@ -23,11 +23,12 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut sub_module2 = Module::new();
|
||||
sub_module2.set_var("answer", 41 as INT);
|
||||
|
||||
let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1));
|
||||
let hash_inc = sub_module2.set_native_fn("inc", |x: &mut INT| Ok(*x + 1));
|
||||
sub_module2.build_index();
|
||||
assert!(!sub_module2.contains_indexed_global_functions());
|
||||
|
||||
sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1));
|
||||
let super_hash = sub_module2.set_native_fn("super_inc", |x: &mut INT| Ok(*x + 1));
|
||||
sub_module2.update_fn_namespace(super_hash, FnNamespace::Global);
|
||||
sub_module2.build_index();
|
||||
assert!(sub_module2.contains_indexed_global_functions());
|
||||
|
||||
@ -48,7 +49,7 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
||||
let m2 = m.get_sub_module("universe").unwrap();
|
||||
|
||||
assert!(m2.contains_var("answer"));
|
||||
assert!(m2.contains_fn(hash_inc, false));
|
||||
assert!(m2.contains_fn(hash_inc));
|
||||
|
||||
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||
|
||||
@ -91,16 +92,16 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut module = Module::new();
|
||||
|
||||
module.set_var("answer", 42 as INT);
|
||||
module.set_fn_4("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w));
|
||||
module.set_fn_1_mut("double", FnNamespace::Global, |x: &mut INT| {
|
||||
module.set_native_fn("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w));
|
||||
let double_hash = module.set_native_fn("double", |x: &mut INT| {
|
||||
*x *= 2;
|
||||
Ok(())
|
||||
});
|
||||
module.update_fn_namespace(double_hash, FnNamespace::Global);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
module.set_fn_4_mut(
|
||||
module.set_native_fn(
|
||||
"sum_of_three_args",
|
||||
FnNamespace::Internal,
|
||||
|target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| {
|
||||
*target = a + b + c as INT;
|
||||
Ok(())
|
||||
@ -407,9 +408,9 @@ fn test_module_str() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
let mut engine = rhai::Engine::new();
|
||||
let mut module = Module::new();
|
||||
module.set_fn_1("test", test_fn);
|
||||
module.set_fn_1("test2", test_fn2);
|
||||
module.set_fn_1("test3", test_fn3);
|
||||
module.set_native_fn("test", test_fn);
|
||||
module.set_native_fn("test2", test_fn2);
|
||||
module.set_native_fn("test3", test_fn3);
|
||||
|
||||
let mut static_modules = rhai::module_resolvers::StaticModuleResolver::new();
|
||||
static_modules.insert("test", module);
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![cfg(not(feature = "no_optimize"))]
|
||||
|
||||
use rhai::{Engine, EvalAltResult, OptimizationLevel, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, OptimizationLevel, INT};
|
||||
|
||||
#[test]
|
||||
fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT};
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
|
@ -1,5 +1,5 @@
|
||||
///! This test simulates an external command object that is driven by a script.
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT};
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
/// Simulate a command object.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT};
|
||||
use rhai::{Engine, EvalAltResult, ImmutableString, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_string() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, INT};
|
||||
|
||||
#[test]
|
||||
fn test_tokens_disabled() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_type_of() -> Result<(), Box<EvalAltResult>> {
|
||||
|
Loading…
Reference in New Issue
Block a user