Add == and != to arrays and maps.

This commit is contained in:
Stephen Chung
2020-11-08 23:00:37 +08:00
parent 487a073caf
commit 48886eacc8
9 changed files with 182 additions and 44 deletions

View File

@@ -164,6 +164,7 @@ pub const FN_IDX_GET: &str = "index$get$";
pub const FN_IDX_SET: &str = "index$set$";
#[cfg(not(feature = "no_function"))]
pub const FN_ANONYMOUS: &str = "anon$";
pub const OP_EQUALS: &str = "==";
pub const MARKER_EXPR: &str = "$expr$";
pub const MARKER_BLOCK: &str = "$block$";
pub const MARKER_IDENT: &str = "$ident$";
@@ -1474,8 +1475,6 @@ impl Engine {
match rhs_value {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(mut rhs_value)) => {
const OP_FUNC: &str = "==";
// Call the `==` operator to compare each value
let def_value = Some(false.into());
@@ -1485,11 +1484,11 @@ impl Engine {
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
let hash =
calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id()));
calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id()));
if self
.call_native_fn(
mods, state, lib, OP_FUNC, hash, args, false, false, def_value,
mods, state, lib, OP_EQUALS, hash, args, false, false, def_value,
)
.map_err(|err| err.fill_position(rhs.position()))?
.0

View File

@@ -1519,7 +1519,8 @@ impl Engine {
let mut arg_values = args.into_vec();
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
let result = self.call_fn_dynamic_raw(scope, ast.lib(), name, &mut None, args.as_mut())?;
let result =
self.call_fn_dynamic_raw(scope, &[ast.lib()], name, &mut None, args.as_mut())?;
let typ = self.map_type_name(result.type_name());
@@ -1594,7 +1595,7 @@ impl Engine {
) -> Result<Dynamic, Box<EvalAltResult>> {
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
self.call_fn_dynamic_raw(scope, lib.as_ref(), name, &mut this_ptr, args.as_mut())
self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut())
}
/// Call a script function defined in an `AST` with multiple `Dynamic` arguments.
@@ -1610,13 +1611,14 @@ impl Engine {
pub(crate) fn call_fn_dynamic_raw(
&self,
scope: &mut Scope,
lib: &Module,
lib: &[&Module],
name: &str,
this_ptr: &mut Option<&mut Dynamic>,
args: &mut FnCallArgs,
) -> Result<Dynamic, Box<EvalAltResult>> {
let fn_def = lib
.get_script_fn(name, args.len(), true)
.iter()
.find_map(|&m| m.get_script_fn(name, args.len(), true))
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
let mut state = Default::default();
@@ -1627,16 +1629,7 @@ impl Engine {
ensure_no_data_race(name, args, false)?;
}
self.call_script_fn(
scope,
&mut mods,
&mut state,
&[lib],
this_ptr,
fn_def,
args,
0,
)
self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0)
}
/// Optimize the `AST` with constants defined in an external Scope.

View File

@@ -571,7 +571,7 @@ impl Engine {
_level,
)?
} else {
// Normal call of script function - map first argument to `this`
// Normal call of script function
// The first argument is a reference?
let mut backup: ArgBackup = Default::default();
backup.change_first_arg_to_copy(is_ref, args);

View File

@@ -124,6 +124,54 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
pub fn iter_namespaces(&self) -> impl Iterator<Item = &'pm Module> + 'm {
self.lib.iter().cloned()
}
/// Call a function inside the call context.
///
/// ## WARNING
///
/// All arguments may be _consumed_, meaning that they may be replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments.
/// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function.
///
/// If `is_method` is `true`, the first argument is assumed to be passed
/// by reference and is not consumed.
pub fn call_fn_dynamic_raw(
&mut self,
fn_name: &str,
is_method: bool,
public_only: bool,
args: &mut [&mut Dynamic],
def_value: Option<Dynamic>,
) -> Result<Dynamic, Box<EvalAltResult>> {
let mut mods = self.mods.cloned().unwrap_or_default();
let hash_script = calc_script_fn_hash(
empty(),
fn_name,
if is_method {
args.len() - 1
} else {
args.len()
},
);
self.engine()
.exec_fn_call(
&mut mods,
&mut Default::default(),
self.lib,
fn_name,
hash_script,
args,
is_method,
is_method,
public_only,
None,
def_value,
0,
)
.map(|(r, _)| r)
}
}
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
@@ -206,12 +254,11 @@ impl FnPtr {
/// clone them _before_ calling this function.
pub fn call_dynamic(
&self,
ctx: NativeCallContext,
mut ctx: NativeCallContext,
this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>,
) -> Result<Dynamic, Box<EvalAltResult>> {
let arg_values = arg_values.as_mut();
let fn_name = self.fn_name();
let mut args_data = self
.curry()
@@ -220,32 +267,15 @@ impl FnPtr {
.chain(arg_values.iter_mut().map(mem::take))
.collect::<StaticVec<_>>();
let has_this = this_ptr.is_some();
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
let hash_script = calc_script_fn_hash(empty(), fn_name, args.len());
let has_this = this_ptr.is_some();
if let Some(obj) = this_ptr {
args.insert(0, obj);
}
let mut mods = ctx.mods.cloned().unwrap_or_default();
ctx.engine()
.exec_fn_call(
&mut mods,
&mut Default::default(),
ctx.lib,
fn_name,
hash_script,
args.as_mut(),
has_this,
has_this,
true,
None,
None,
0,
)
.map(|(v, _)| v)
ctx.call_fn_dynamic_raw(self.fn_name(), has_this, true, args.as_mut(), None)
}
}

View File

@@ -3,7 +3,7 @@
use crate::def_package;
use crate::dynamic::Dynamic;
use crate::engine::Array;
use crate::engine::{Array, OP_EQUALS};
use crate::fn_native::{FnPtr, NativeCallContext};
use crate::plugin::*;
use crate::result::EvalAltResult;
@@ -263,6 +263,39 @@ mod array_functions {
Ok(array.into())
}
#[rhai_fn(return_raw)]
pub fn index_of(
ctx: NativeCallContext,
list: &mut Array,
filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() {
if filter
.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()])
}
_ => Err(err),
})
.map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall(
"filter".to_string(),
err,
NO_POS,
))
})?
.as_bool()
.unwrap_or(false)
{
return Ok((i as INT).into());
}
}
Ok((-1 as INT).into())
}
#[rhai_fn(return_raw)]
pub fn some(
ctx: NativeCallContext,
list: &mut Array,
@@ -619,6 +652,41 @@ mod array_functions {
drained
}
#[rhai_fn(name = "==", return_raw)]
pub fn equals(
mut ctx: NativeCallContext,
arr1: &mut Array,
mut arr2: Array,
) -> Result<Dynamic, Box<EvalAltResult>> {
if arr1.len() != arr2.len() {
return Ok(false.into());
}
if arr1.is_empty() {
return Ok(true.into());
}
let def_value = Some(false.into());
for (a1, a2) in arr1.iter_mut().zip(arr2.iter_mut()) {
let equals = ctx
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2], def_value.clone())
.map(|v| v.as_bool().unwrap_or(false))?;
if !equals {
return Ok(false.into());
}
}
Ok(true.into())
}
#[rhai_fn(name = "!=", return_raw)]
pub fn not_equals(
ctx: NativeCallContext,
arr1: &mut Array,
arr2: Array,
) -> Result<Dynamic, Box<EvalAltResult>> {
equals(ctx, arr1, arr2).map(|r| (!r.as_bool().unwrap()).into())
}
}
gen_array_functions!(basic => INT, bool, char, ImmutableString, FnPtr, Array, Unit);

View File

@@ -2,7 +2,7 @@
use crate::def_package;
use crate::dynamic::Dynamic;
use crate::engine::Map;
use crate::engine::{Map, OP_EQUALS};
use crate::plugin::*;
use crate::utils::ImmutableString;
use crate::INT;
@@ -46,6 +46,45 @@ mod map_functions {
map1.entry(key).or_insert(value);
});
}
#[rhai_fn(name = "==", return_raw)]
pub fn equals(
mut ctx: NativeCallContext,
map1: &mut Map,
mut map2: Map,
) -> Result<Dynamic, Box<EvalAltResult>> {
if map1.len() != map2.len() {
return Ok(false.into());
}
if map1.is_empty() {
return Ok(true.into());
}
let def_value = Some(false.into());
for (m1, v1) in map1.iter_mut() {
if let Some(v2) = map2.get_mut(m1) {
let equals = ctx
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2], def_value.clone())
.map(|v| v.as_bool().unwrap_or(false))?;
if !equals {
return Ok(false.into());
}
} else {
return Ok(false.into());
}
}
Ok(true.into())
}
#[rhai_fn(name = "!=", return_raw)]
pub fn not_equals(
ctx: NativeCallContext,
map1: &mut Map,
map2: Map,
) -> Result<Dynamic, Box<EvalAltResult>> {
equals(ctx, map1, map2).map(|r| (!r.as_bool().unwrap()).into())
}
#[cfg(not(feature = "no_index"))]
pub mod indexing {