Extract constant arguments from function calls.
This commit is contained in:
parent
8e8c367034
commit
7bdc2e3d20
15
src/ast.rs
15
src/ast.rs
@ -1400,6 +1400,8 @@ pub struct FnCallExpr {
|
|||||||
pub capture: bool,
|
pub capture: bool,
|
||||||
/// List of function call arguments.
|
/// List of function call arguments.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
|
/// List of function call arguments that are constants.
|
||||||
|
pub constant_args: StaticVec<(Dynamic, Position)>,
|
||||||
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
||||||
pub namespace: Option<NamespaceRef>,
|
pub namespace: Option<NamespaceRef>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
@ -1408,6 +1410,19 @@ pub struct FnCallExpr {
|
|||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FnCallExpr {
|
||||||
|
/// Are there no arguments to this function call?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_args_empty(&self) -> bool {
|
||||||
|
self.args.is_empty() && self.constant_args.is_empty()
|
||||||
|
}
|
||||||
|
/// Get the number of arguments to this function call.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn num_args(&self) -> usize {
|
||||||
|
self.args.len() + self.constant_args.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
|
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
@ -1440,16 +1440,21 @@ impl Engine {
|
|||||||
Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && x.namespace.is_none() => {
|
Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && x.namespace.is_none() => {
|
||||||
let mut arg_positions: StaticVec<_> = Default::default();
|
let mut arg_positions: StaticVec<_> = Default::default();
|
||||||
|
|
||||||
let arg_values = x
|
let mut arg_values = x
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
|
.inspect(|arg_expr| arg_positions.push(arg_expr.position()))
|
||||||
.map(|arg_expr| {
|
.map(|arg_expr| {
|
||||||
arg_positions.push(arg_expr.position());
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
|
x.constant_args
|
||||||
|
.iter()
|
||||||
|
.inspect(|(_, pos)| arg_positions.push(*pos))
|
||||||
|
.for_each(|(v, _)| arg_values.push(v.clone()));
|
||||||
|
|
||||||
idx_values.push((arg_values, arg_positions).into());
|
idx_values.push((arg_values, arg_positions).into());
|
||||||
}
|
}
|
||||||
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => {
|
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => {
|
||||||
@ -1475,16 +1480,21 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
let mut arg_positions: StaticVec<_> = Default::default();
|
let mut arg_positions: StaticVec<_> = Default::default();
|
||||||
|
|
||||||
let arg_values = x
|
let mut arg_values = x
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
|
.inspect(|arg_expr| arg_positions.push(arg_expr.position()))
|
||||||
.map(|arg_expr| {
|
.map(|arg_expr| {
|
||||||
arg_positions.push(arg_expr.position());
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
|
x.constant_args
|
||||||
|
.iter()
|
||||||
|
.inspect(|(_, pos)| arg_positions.push(*pos))
|
||||||
|
.for_each(|(v, _)| arg_values.push(v.clone()));
|
||||||
|
|
||||||
(arg_values, arg_positions).into()
|
(arg_values, arg_positions).into()
|
||||||
}
|
}
|
||||||
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => {
|
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => {
|
||||||
@ -1700,10 +1710,12 @@ impl Engine {
|
|||||||
capture,
|
capture,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
|
constant_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, *hash, *pos, *capture, level,
|
scope, mods, state, lib, this_ptr, name, args, c_args, *hash, *pos, *capture,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1714,12 +1726,14 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
|
constant_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace.as_ref();
|
||||||
let hash = hash.native_hash();
|
let hash = hash.native_hash();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, hash, *pos, level,
|
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
175
src/fn_call.rs
175
src/fn_call.rs
@ -1058,33 +1058,39 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
|
constant_args: &[(Dynamic, Position)],
|
||||||
mut hash: FnCallHash,
|
mut hash: FnCallHash,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let args_expr = args_expr.as_ref();
|
|
||||||
|
|
||||||
// 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;
|
||||||
|
let mut constant_args = constant_args;
|
||||||
|
let mut total_args = args_expr.len() + constant_args.len();
|
||||||
let mut curry = StaticVec::new();
|
let mut curry = StaticVec::new();
|
||||||
let mut name = fn_name;
|
let mut name = fn_name;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if args_expr.len() >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let fn_ptr =
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
|| Ok(constant_args[0].clone()),
|
||||||
|
|arg| {
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
|
.map(|v| (v, arg.position()))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
if !fn_ptr.is::<FnPtr>() {
|
if !arg.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(arg.type_name()),
|
||||||
args_expr[0].position(),
|
arg_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
let fn_ptr = arg.cast::<FnPtr>();
|
||||||
curry.extend(fn_ptr.curry().iter().cloned());
|
curry.extend(fn_ptr.curry().iter().cloned());
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
@ -1092,10 +1098,15 @@ impl Engine {
|
|||||||
name = &redirected;
|
name = &redirected;
|
||||||
|
|
||||||
// Skip the first argument
|
// Skip the first argument
|
||||||
args_expr = &args_expr.as_ref()[1..];
|
if !args_expr.is_empty() {
|
||||||
|
args_expr = &args_expr[1..];
|
||||||
|
} else {
|
||||||
|
constant_args = &constant_args[1..];
|
||||||
|
}
|
||||||
|
total_args -= 1;
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let args_len = args_expr.len() + curry.len();
|
let args_len = total_args + curry.len();
|
||||||
hash = if !hash.is_native_only() {
|
hash = if !hash.is_native_only() {
|
||||||
FnCallHash::from_script(calc_fn_hash(empty(), name, args_len))
|
FnCallHash::from_script(calc_fn_hash(empty(), name, args_len))
|
||||||
} else {
|
} else {
|
||||||
@ -1103,66 +1114,95 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
KEYWORD_FN_PTR if args_expr.len() == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
|
|| Ok(constant_args[0].clone()),
|
||||||
|
|arg| {
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
|
.map(|v| (v, arg.position()))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
return self
|
return arg
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|typ| {
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
|
||||||
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(args_expr[0].position()));
|
.map_err(|err| err.fill_position(arg_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle curry()
|
// Handle curry()
|
||||||
KEYWORD_FN_PTR_CURRY if args_expr.len() > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let fn_ptr =
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
|| Ok(constant_args[0].clone()),
|
||||||
|
|arg| {
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
|
.map(|v| (v, arg.position()))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
if !fn_ptr.is::<FnPtr>() {
|
if !arg.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(arg.type_name()),
|
||||||
args_expr[0].position(),
|
arg_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
let (name, mut fn_curry) = arg.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
|
if !args_expr.is_empty() {
|
||||||
args_expr.iter().skip(1).try_for_each(|expr| {
|
args_expr.iter().skip(1).try_for_each(|expr| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(|value| fn_curry.push(value))
|
.map(|value| fn_curry.push(value))
|
||||||
})?;
|
})?;
|
||||||
|
fn_curry.extend(constant_args.iter().map(|(v, _)| v.clone()));
|
||||||
|
} else {
|
||||||
|
fn_curry.extend(constant_args.iter().skip(1).map(|(v, _)| v.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if args_expr.len() == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let value =
|
let arg = args_expr.get(0).map_or_else(
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
|| Ok(constant_args[0].0.clone()),
|
||||||
return Ok(value.is_shared().into());
|
|arg| self.eval_expr(scope, mods, state, lib, this_ptr, arg, level),
|
||||||
|
)?;
|
||||||
|
return Ok(arg.is_shared().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_fn()
|
// Handle is_def_fn()
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if args_expr.len() == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
||||||
let fn_name = self
|
let (arg, arg_pos) = if !args_expr.is_empty() {
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
(
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?,
|
||||||
|
args_expr[0].position(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
constant_args[0].clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let fn_name = arg
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|err| {
|
.map_err(|err| self.make_type_mismatch_err::<ImmutableString>(err, arg_pos))?;
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
|
||||||
})?;
|
let (arg, arg_pos) = if args_expr.len() > 1 {
|
||||||
let num_params = self
|
(
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?,
|
||||||
|
args_expr[1].position(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
constant_args[if args_expr.is_empty() { 1 } else { 0 }].clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let num_params = arg
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|err| {
|
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, arg_pos))?;
|
||||||
self.make_type_mismatch_err::<crate::INT>(err, args_expr[0].position())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
return Ok(if num_params < 0 {
|
return Ok(if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
@ -1174,27 +1214,32 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
KEYWORD_IS_DEF_VAR if args_expr.len() == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let var_name = self
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
|| Ok(constant_args[0].clone()),
|
||||||
|
|arg| {
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
|
.map(|v| (v, arg.position()))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
let var_name = arg
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|err| {
|
.map_err(|err| self.make_type_mismatch_err::<ImmutableString>(err, arg_pos))?;
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
|
||||||
})?;
|
|
||||||
return Ok(scope.contains(&var_name).into());
|
return Ok(scope.contains(&var_name).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle eval()
|
// Handle eval()
|
||||||
KEYWORD_EVAL if args_expr.len() == 1 => {
|
KEYWORD_EVAL if total_args == 1 => {
|
||||||
let script_expr = &args_expr[0];
|
|
||||||
let script_pos = script_expr.position();
|
|
||||||
|
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let script = self
|
let (script, script_pos) = args_expr.get(0).map_or_else(
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?
|
|| Ok(constant_args[0].clone()),
|
||||||
.take_immutable_string()
|
|script_expr| {
|
||||||
.map_err(|typ| {
|
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)
|
||||||
|
.map(|v| (v, script_expr.position()))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
let script = script.take_immutable_string().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
||||||
})?;
|
})?;
|
||||||
let result = self.eval_script_expr_in_place(
|
let result = self.eval_script_expr_in_place(
|
||||||
@ -1240,14 +1285,17 @@ impl Engine {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if args_expr.is_empty() && curry.is_empty() {
|
if args_expr.is_empty() && constant_args.is_empty() && curry.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
// If the first argument is a variable, and there is no curried arguments,
|
// 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
|
// convert to method-call style in order to leverage potential &mut first argument and
|
||||||
// avoid cloning the value
|
// avoid cloning the value
|
||||||
if curry.is_empty() && args_expr[0].get_variable_access(false).is_some() {
|
if curry.is_empty()
|
||||||
|
&& !args_expr.is_empty()
|
||||||
|
&& args_expr[0].get_variable_access(false).is_some()
|
||||||
|
{
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
@ -1256,6 +1304,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
|
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (mut target, pos) =
|
let (mut target, pos) =
|
||||||
@ -1285,6 +1334,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
|
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||||
@ -1310,25 +1360,24 @@ impl Engine {
|
|||||||
namespace: Option<&NamespaceRef>,
|
namespace: Option<&NamespaceRef>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
|
constant_args: &[(Dynamic, Position)],
|
||||||
hash: u64,
|
hash: u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let args_expr = args_expr.as_ref();
|
|
||||||
|
|
||||||
let namespace = namespace.unwrap();
|
let namespace = namespace.unwrap();
|
||||||
let mut arg_values: StaticVec<_>;
|
let mut arg_values: StaticVec<_>;
|
||||||
let mut first_arg_value = None;
|
let mut first_arg_value = None;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
|
|
||||||
if args_expr.is_empty() {
|
if args_expr.is_empty() && constant_args.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
// See if the first argument is a variable (not namespace-qualified).
|
// See if the first argument is a variable (not namespace-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
|
||||||
if args_expr[0].get_variable_access(true).is_some() {
|
if !args_expr.is_empty() && args_expr[0].get_variable_access(true).is_some() {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
@ -1342,6 +1391,7 @@ impl Engine {
|
|||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
@ -1368,6 +1418,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
|
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
|
@ -740,11 +740,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
Expr::FnCall(x, pos)
|
Expr::FnCall(x, pos)
|
||||||
if x.namespace.is_none() // Non-qualified
|
if x.namespace.is_none() // Non-qualified
|
||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.args.len() == 2 // binary call
|
&& x.num_args() == 2 // binary call
|
||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap())
|
||||||
|
.chain(x.constant_args.iter().map(|(v, _)| v).cloned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
// Search for overloaded operators (can override built-in).
|
||||||
@ -764,6 +767,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state));
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state));
|
||||||
|
|
||||||
|
// Move constant arguments to the right
|
||||||
|
while x.args.last().map(Expr::is_constant).unwrap_or(false) {
|
||||||
|
let arg = x.args.pop().unwrap();
|
||||||
|
let arg_pos = arg.position();
|
||||||
|
x.constant_args.insert(0, (arg.get_constant_value().unwrap(), arg_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
x.args.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
@ -774,12 +786,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
=> {
|
=> {
|
||||||
// First search for script-defined functions (can override built-in)
|
// First search for script-defined functions (can override built-in)
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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()).is_some());
|
let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.num_args()).is_some());
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let has_script_fn = false;
|
let has_script_fn = false;
|
||||||
|
|
||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap())
|
||||||
|
.chain(x.constant_args.iter().map(|(v, _)| v).cloned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Save the typename of the first argument if it is `type_of()`
|
// Save the typename of the first argument if it is `type_of()`
|
||||||
// This is to avoid `call_args` being passed into the closure
|
// This is to avoid `call_args` being passed into the closure
|
||||||
@ -810,7 +824,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(x, _) => x.args.iter_mut().for_each(|a| optimize_expr(a, state)),
|
Expr::FnCall(x, _) => {
|
||||||
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state));
|
||||||
|
|
||||||
|
// Move constant arguments to the right
|
||||||
|
while x.args.last().map(Expr::is_constant).unwrap_or(false) {
|
||||||
|
let arg = x.args.pop().unwrap();
|
||||||
|
let arg_pos = arg.position();
|
||||||
|
x.constant_args.insert(0, (arg.get_constant_value().unwrap(), arg_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
x.args.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(x) if x.1.is_none() && state.find_constant(&x.2.name).is_some() => {
|
Expr::Variable(x) if x.1.is_none() && state.find_constant(&x.2.name).is_some() => {
|
||||||
|
@ -1531,8 +1531,8 @@ fn make_dot_expr(
|
|||||||
Expr::FnCall(mut func, func_pos) => {
|
Expr::FnCall(mut func, func_pos) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
func.hash = FnCallHash::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.num_args()),
|
||||||
calc_fn_hash(empty(), &func.name, func.args.len() + 1),
|
calc_fn_hash(empty(), &func.name, func.num_args() + 1),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rhs = Expr::Dot(
|
let rhs = Expr::Dot(
|
||||||
@ -1563,7 +1563,7 @@ fn make_dot_expr(
|
|||||||
}
|
}
|
||||||
// lhs.Fn() or lhs.eval()
|
// lhs.Fn() or lhs.eval()
|
||||||
(_, Expr::FnCall(x, pos))
|
(_, Expr::FnCall(x, pos))
|
||||||
if x.args.len() == 0
|
if x.is_args_empty()
|
||||||
&& [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL]
|
&& [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL]
|
||||||
.contains(&x.name.as_ref()) =>
|
.contains(&x.name.as_ref()) =>
|
||||||
{
|
{
|
||||||
@ -1587,8 +1587,8 @@ fn make_dot_expr(
|
|||||||
(lhs, Expr::FnCall(mut func, func_pos)) => {
|
(lhs, Expr::FnCall(mut func, func_pos)) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
func.hash = FnCallHash::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.num_args()),
|
||||||
calc_fn_hash(empty(), &func.name, func.args.len() + 1),
|
calc_fn_hash(empty(), &func.name, func.num_args() + 1),
|
||||||
);
|
);
|
||||||
let rhs = Expr::FnCall(func, func_pos);
|
let rhs = Expr::FnCall(func, func_pos);
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
|
@ -72,7 +72,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn inc(x) { x + 1 }
|
fn inc(x) { x + 1 }
|
||||||
let x = 0;
|
let x = 0;
|
||||||
|
|
||||||
while x < 28 {
|
while x < 31 {
|
||||||
print(x);
|
print(x);
|
||||||
x = inc(x);
|
x = inc(x);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user