Streamline function pointers and currying.
This commit is contained in:
parent
a16fc71935
commit
39474d6420
11
RELEASES.md
11
RELEASES.md
@ -1,6 +1,17 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.19.3
|
||||||
|
==============
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.19.2
|
||||||
|
==============
|
||||||
|
|
||||||
|
Bug fix on call module functions.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.1
|
Version 0.19.1
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ Function Pointers
|
|||||||
It is possible to store a _function pointer_ in a variable just like a normal value.
|
It is possible to store a _function pointer_ in a variable just like a normal value.
|
||||||
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
||||||
|
|
||||||
|
A function pointer is created via the `Fn` function, which takes a [string] parameter.
|
||||||
|
|
||||||
Call a function pointer using the `call` method.
|
Call a function pointer using the `call` method.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1479,7 +1479,7 @@ impl Engine {
|
|||||||
Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level),
|
Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level),
|
||||||
|
|
||||||
// var op= rhs
|
// var op= rhs
|
||||||
Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => {
|
Expr::Assignment(x) if x.0.get_variable_access(false).is_some() => {
|
||||||
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
||||||
let mut rhs_val = self
|
let mut rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
|
217
src/fn_call.rs
217
src/fn_call.rs
@ -711,16 +711,17 @@ impl Engine {
|
|||||||
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
||||||
// FnPtr call
|
// FnPtr call
|
||||||
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = if native {
|
let hash = if native {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), fn_name, curry.len() + call_args.len(), empty())
|
calc_fn_hash(empty(), fn_name, args_len, empty())
|
||||||
};
|
};
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = curry
|
let mut arg_values = curry
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.chain(call_args.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
@ -737,16 +738,17 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
|
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.get_fn_name().clone();
|
let fn_name = fn_ptr.fn_name();
|
||||||
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = if native {
|
let hash = if native {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), &fn_name, curry.len() + call_args.len(), empty())
|
calc_fn_hash(empty(), fn_name, args_len, empty())
|
||||||
};
|
};
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = once(obj)
|
let mut arg_values = once(obj)
|
||||||
.chain(curry.iter_mut())
|
.chain(curry.iter_mut())
|
||||||
.chain(call_args.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
@ -755,7 +757,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, &fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
|
state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
|
||||||
)
|
)
|
||||||
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
||||||
// Curry call
|
// Curry call
|
||||||
@ -796,14 +798,12 @@ impl Engine {
|
|||||||
_redirected = fn_ptr.get_fn_name().clone();
|
_redirected = fn_ptr.get_fn_name().clone();
|
||||||
_fn_name = &_redirected;
|
_fn_name = &_redirected;
|
||||||
// Add curried arguments
|
// Add curried arguments
|
||||||
if !fn_ptr.curry().is_empty() {
|
fn_ptr
|
||||||
fn_ptr
|
.curry()
|
||||||
.curry()
|
.iter()
|
||||||
.iter()
|
.cloned()
|
||||||
.cloned()
|
.enumerate()
|
||||||
.enumerate()
|
.for_each(|(i, v)| call_args.insert(i, v));
|
||||||
.for_each(|(i, v)| call_args.insert(i, v));
|
|
||||||
}
|
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
hash = if native {
|
hash = if native {
|
||||||
0
|
0
|
||||||
@ -861,35 +861,33 @@ impl Engine {
|
|||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
let expr = args_expr.get(0).unwrap();
|
return self
|
||||||
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
||||||
|
|
||||||
return arg_value
|
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|typ| {
|
.map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
||||||
})
|
})
|
||||||
.and_then(|s| FnPtr::try_from(s))
|
.and_then(|s| FnPtr::try_from(s))
|
||||||
.map(Into::<Dynamic>::into)
|
.map(Into::<Dynamic>::into)
|
||||||
.map_err(|err| err.fill_position(expr.position()));
|
.map_err(|err| err.fill_position(args_expr[0].position()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle curry()
|
// Handle curry()
|
||||||
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
if !fn_ptr.is::<FnPtr>() {
|
if !fn_ptr.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(fn_ptr.type_name()),
|
self.map_type_name(fn_ptr.type_name()),
|
||||||
expr.position(),
|
args_expr[0].position(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (fn_name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
let (fn_name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
|
|
||||||
args_expr
|
args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -904,8 +902,7 @@ impl Engine {
|
|||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
if name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
return Ok(value.is_shared().into());
|
return Ok(value.is_shared().into());
|
||||||
}
|
}
|
||||||
@ -913,25 +910,24 @@ impl Engine {
|
|||||||
// Handle call() - Redirect function call
|
// Handle call() - Redirect function call
|
||||||
let redirected;
|
let redirected;
|
||||||
let mut args_expr = args_expr.as_ref();
|
let mut args_expr = args_expr.as_ref();
|
||||||
let mut curry: StaticVec<_> = Default::default();
|
let mut curry = StaticVec::new();
|
||||||
let mut name = name;
|
let mut name = name;
|
||||||
|
|
||||||
if name == KEYWORD_FN_PTR_CALL
|
if name == KEYWORD_FN_PTR_CALL
|
||||||
&& args_expr.len() >= 1
|
&& args_expr.len() >= 1
|
||||||
&& !self.has_override(lib, 0, hash_script, pub_only)
|
&& !self.has_override(lib, 0, hash_script, pub_only)
|
||||||
{
|
{
|
||||||
let expr = args_expr.get(0).unwrap();
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
if !fn_ptr.is::<FnPtr>() {
|
if !fn_ptr.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(fn_ptr.type_name()),
|
self.map_type_name(fn_ptr.type_name()),
|
||||||
expr.position(),
|
args_expr[0].position(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
||||||
curry = fn_ptr.curry().iter().cloned().collect();
|
curry.extend(fn_ptr.curry().iter().cloned());
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
redirected = fn_ptr.take_data().0;
|
redirected = fn_ptr.take_data().0;
|
||||||
@ -941,7 +937,8 @@ impl Engine {
|
|||||||
args_expr = &args_expr.as_ref()[1..];
|
args_expr = &args_expr.as_ref()[1..];
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
let args_len = args_expr.len() + curry.len();
|
||||||
|
hash_script = calc_fn_hash(empty(), name, args_len, empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
@ -949,10 +946,10 @@ impl Engine {
|
|||||||
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let var_name =
|
||||||
let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let var_name = var_name.as_str().map_err(|err| {
|
let var_name = var_name.as_str().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
if var_name.is_empty() {
|
if var_name.is_empty() {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
@ -974,18 +971,17 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
let expr0 = args_expr.get(0).unwrap();
|
let fn_name =
|
||||||
let expr1 = args_expr.get(1).unwrap();
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
|
let num_params =
|
||||||
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr0, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?;
|
||||||
let num_params = self.eval_expr(scope, mods, state, lib, this_ptr, expr1, level)?;
|
|
||||||
|
|
||||||
let fn_name = fn_name.as_str().map_err(|err| {
|
let fn_name = fn_name.as_str().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, expr0.position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
|
})?;
|
||||||
|
let num_params = num_params.as_int().map_err(|err| {
|
||||||
|
self.make_type_mismatch_err::<INT>(err, args_expr[1].position())
|
||||||
})?;
|
})?;
|
||||||
let num_params = num_params
|
|
||||||
.as_int()
|
|
||||||
.map_err(|err| self.make_type_mismatch_err::<INT>(err, expr1.position()))?;
|
|
||||||
|
|
||||||
if fn_name.is_empty() || num_params < 0 {
|
if fn_name.is_empty() || num_params < 0 {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
@ -1003,14 +999,14 @@ impl Engine {
|
|||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let expr = args_expr.get(0).unwrap();
|
let script =
|
||||||
let script = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let script = script.as_str().map_err(|typ| {
|
let script = script.as_str().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
let result = if !script.is_empty() {
|
let result = if !script.is_empty() {
|
||||||
self.eval_script_expr(scope, mods, state, lib, script, level + 1)
|
self.eval_script_expr(scope, mods, state, lib, script, level + 1)
|
||||||
.map_err(|err| err.fill_position(expr.position()))
|
.map_err(|err| err.fill_position(args_expr[0].position()))
|
||||||
} else {
|
} else {
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
};
|
};
|
||||||
@ -1041,41 +1037,38 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// If the first argument is a variable, and there is no curried arguments, convert to method-call style
|
// If the first argument is a variable, and there is no curried arguments, convert to method-call style
|
||||||
// in order to leverage potential &mut first argument and avoid cloning the value
|
// in order to leverage potential &mut first argument and avoid cloning the value
|
||||||
match args_expr.get(0).unwrap() {
|
if args_expr[0].get_variable_access(false).is_some() && curry.is_empty() {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
lhs @ Expr::Variable(_) if curry.is_empty() => {
|
arg_values = args_expr
|
||||||
arg_values = args_expr
|
.iter()
|
||||||
.iter()
|
.skip(1)
|
||||||
.skip(1)
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
.collect::<Result<_, _>>()?;
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
let (target, _, _, pos) =
|
let (target, _, _, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(pos))?;
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
|
|
||||||
args = if target.is_shared() || target.is_value() {
|
args = if target.is_shared() || target.is_value() {
|
||||||
arg_values.insert(0, target.take_or_clone().flatten());
|
arg_values.insert(0, target.take_or_clone().flatten());
|
||||||
arg_values.iter_mut().collect()
|
arg_values.iter_mut().collect()
|
||||||
} else {
|
} else {
|
||||||
// Turn it into a method call only if the object is not shared and not a simple value
|
// Turn it into a method call only if the object is not shared and not a simple value
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
once(target.take_ref().unwrap())
|
once(target.take_ref().unwrap())
|
||||||
.chain(arg_values.iter_mut())
|
.chain(arg_values.iter_mut())
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
}
|
} else {
|
||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
_ => {
|
arg_values = args_expr
|
||||||
arg_values = args_expr
|
.iter()
|
||||||
.iter()
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
.collect::<Result<_, _>>()?;
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1116,50 +1109,46 @@ impl Engine {
|
|||||||
// See if the first argument is a variable (not module-qualified).
|
// See if the first argument is a variable (not module-qualified).
|
||||||
// If so, convert to method-call style in order to leverage potential
|
// If so, convert to method-call style in order to leverage potential
|
||||||
// &mut first argument and avoid cloning the value
|
// &mut first argument and avoid cloning the value
|
||||||
match args_expr.get(0).unwrap() {
|
if args_expr[0].get_variable_access(true).is_some() {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
Expr::Variable(x) if x.1.is_none() => {
|
arg_values = args_expr
|
||||||
arg_values = args_expr
|
.iter()
|
||||||
.iter()
|
.enumerate()
|
||||||
.enumerate()
|
.map(|(i, expr)| {
|
||||||
.map(|(i, expr)| {
|
// Skip the first argument
|
||||||
// Skip the first argument
|
if i == 0 {
|
||||||
if i == 0 {
|
Ok(Default::default())
|
||||||
Ok(Default::default())
|
} else {
|
||||||
} else {
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
}
|
||||||
}
|
})
|
||||||
})
|
.collect::<Result<_, _>>()?;
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
let var_expr = args_expr.get(0).unwrap();
|
let (target, _, _, pos) =
|
||||||
let (target, _, _, pos) =
|
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
self.search_scope_only(scope, mods, state, lib, this_ptr, var_expr)?;
|
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(pos))?;
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
|
|
||||||
if target.is_shared() || target.is_value() {
|
|
||||||
arg_values[0] = target.take_or_clone().flatten();
|
|
||||||
args = arg_values.iter_mut().collect();
|
|
||||||
} else {
|
|
||||||
let (first, rest) = arg_values.split_first_mut().unwrap();
|
|
||||||
first_arg_value = Some(first);
|
|
||||||
args = once(target.take_ref().unwrap())
|
|
||||||
.chain(rest.iter_mut())
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// func(..., ...) or func(mod::x, ...)
|
|
||||||
_ => {
|
|
||||||
arg_values = args_expr
|
|
||||||
.iter()
|
|
||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
|
if target.is_shared() || target.is_value() {
|
||||||
|
arg_values[0] = target.take_or_clone().flatten();
|
||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
|
} else {
|
||||||
|
let (first, rest) = arg_values.split_first_mut().unwrap();
|
||||||
|
first_arg_value = Some(first);
|
||||||
|
args = once(target.take_ref().unwrap())
|
||||||
|
.chain(rest.iter_mut())
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// func(..., ...) or func(mod::x, ...)
|
||||||
|
arg_values = args_expr
|
||||||
|
.iter()
|
||||||
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
args = arg_values.iter_mut().collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,7 @@ use crate::token::{is_valid_identifier, Position};
|
|||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::{calc_fn_hash, StaticVec};
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String};
|
||||||
boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String, vec::Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::{Arc, RwLock};
|
use crate::stdlib::sync::{Arc, RwLock};
|
||||||
@ -79,12 +77,15 @@ pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
|||||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||||
/// to be passed onto a function during a call.
|
/// to be passed onto a function during a call.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct FnPtr(ImmutableString, Vec<Dynamic>);
|
pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
|
||||||
|
|
||||||
impl FnPtr {
|
impl FnPtr {
|
||||||
/// Create a new function pointer.
|
/// Create a new function pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S, curry: Vec<Dynamic>) -> Self {
|
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(
|
||||||
|
name: S,
|
||||||
|
curry: StaticVec<Dynamic>,
|
||||||
|
) -> Self {
|
||||||
Self(name.into(), curry)
|
Self(name.into(), curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
@ -99,13 +100,13 @@ impl FnPtr {
|
|||||||
}
|
}
|
||||||
/// Get the underlying data of the function pointer.
|
/// Get the underlying data of the function pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn take_data(self) -> (ImmutableString, Vec<Dynamic>) {
|
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
||||||
(self.0, self.1)
|
(self.0, self.1)
|
||||||
}
|
}
|
||||||
/// Get the curried arguments.
|
/// Get the curried arguments.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn curry(&self) -> &[Dynamic] {
|
pub fn curry(&self) -> &[Dynamic] {
|
||||||
&self.1
|
self.1.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call the function pointer with curried arguments (if any).
|
/// Call the function pointer with curried arguments (if any).
|
||||||
@ -125,24 +126,24 @@ impl FnPtr {
|
|||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let arg_values = arg_values.as_mut();
|
||||||
|
let fn_name = self.fn_name();
|
||||||
|
|
||||||
let mut args_data = self
|
let mut args_data = self
|
||||||
.1
|
.curry()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.chain(arg_values.as_mut().iter_mut().map(|v| mem::take(v)))
|
.chain(arg_values.iter_mut().map(mem::take))
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let has_this = this_ptr.is_some();
|
let has_this = this_ptr.is_some();
|
||||||
let args_len = args_data.len();
|
|
||||||
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
|
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
|
||||||
|
let hash_script = calc_fn_hash(empty(), fn_name, args.len(), empty());
|
||||||
|
|
||||||
if let Some(obj) = this_ptr {
|
if let Some(obj) = this_ptr {
|
||||||
args.insert(0, obj);
|
args.insert(0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_name = self.0.as_str();
|
|
||||||
let hash_script = calc_fn_hash(empty(), fn_name, args_len, empty());
|
|
||||||
|
|
||||||
engine
|
engine
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
|
@ -425,6 +425,30 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does the particular Rust function exist in the module?
|
||||||
|
///
|
||||||
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Module;
|
||||||
|
///
|
||||||
|
/// let mut module = Module::new();
|
||||||
|
/// module.set_fn_0("calc", || Ok(42_i64));
|
||||||
|
/// assert!(module.contains_fn_with_name("calc", true));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn contains_fn_with_name(&self, fn_name: &str, public_only: bool) -> bool {
|
||||||
|
self.functions
|
||||||
|
.values()
|
||||||
|
.filter(|(_, access, _, _, _)| !public_only || access.is_public())
|
||||||
|
.find(|(name, _, _, _, _)| name == fn_name)
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a Rust function into the module, returning a hash key.
|
/// Set a Rust function into the module, returning a hash key.
|
||||||
///
|
///
|
||||||
/// If there is an existing Rust function of the same hash, it is replaced.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
|
@ -65,6 +65,13 @@ impl PackagesCollection {
|
|||||||
pub fn contains_fn(&self, hash: u64, public_only: bool) -> bool {
|
pub fn contains_fn(&self, hash: u64, public_only: bool) -> bool {
|
||||||
self.0.iter().any(|p| p.contains_fn(hash, public_only))
|
self.0.iter().any(|p| p.contains_fn(hash, public_only))
|
||||||
}
|
}
|
||||||
|
/// Does the specified function name exist in the `PackagesCollection`?
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn contains_fn_with_name(&self, fn_name: &str, public_only: bool) -> bool {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.any(|p| p.contains_fn_with_name(fn_name, public_only))
|
||||||
|
}
|
||||||
/// Get specified function via its hash key.
|
/// Get specified function via its hash key.
|
||||||
pub fn get_fn(&self, hash: u64, public_only: bool) -> Option<&CallableFunction> {
|
pub fn get_fn(&self, hash: u64, public_only: bool) -> Option<&CallableFunction> {
|
||||||
self.0
|
self.0
|
||||||
|
@ -1079,6 +1079,14 @@ impl Expr {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is the expression a simple variable access?
|
||||||
|
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).0.as_str()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the `Position` of the expression.
|
/// Get the `Position` of the expression.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
|
@ -41,10 +41,7 @@ impl Expression<'_> {
|
|||||||
/// If this expression is a variable name, return it. Otherwise `None`.
|
/// If this expression is a variable name, return it. Otherwise `None`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_variable_name(&self) -> Option<&str> {
|
pub fn get_variable_name(&self) -> Option<&str> {
|
||||||
match self.0 {
|
self.0.get_variable_access(true)
|
||||||
Expr::Variable(x) => Some((x.0).0.as_str()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Get the expression.
|
/// Get the expression.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
Loading…
Reference in New Issue
Block a user