Fix op-assignment overrides.
This commit is contained in:
parent
748d4d2f36
commit
83f9df2852
@ -11,6 +11,7 @@ Bug fixes
|
|||||||
* Fixes a bug in `Module::set_fn_4_mut`.
|
* Fixes a bug in `Module::set_fn_4_mut`.
|
||||||
* Module API's now properly handle `&str` and `String` parameters.
|
* Module API's now properly handle `&str` and `String` parameters.
|
||||||
* Indexers are available under `no_object`.
|
* Indexers are available under `no_object`.
|
||||||
|
* Registered operator-assignment functions (e.g. `+=`) now work correctly.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
@ -33,7 +33,9 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but
|
|||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
| `push` | element to insert | inserts an element at the end |
|
| `push` | element to insert | inserts an element at the end |
|
||||||
| `+=` operator, `append` | array to append | concatenates the second array to the end of the first |
|
| `+=` operator | array, element to insert (not another array) | inserts an element at the end |
|
||||||
|
| `append` | array to append | concatenates the second array to the end of the first |
|
||||||
|
| `+=` operator | array, array to append | concatenates the second array to the end of the first |
|
||||||
| `+` operator | first array, second array | concatenates the first array with the second |
|
| `+` operator | first array, second array | concatenates the first array with the second |
|
||||||
| `insert` | element to insert, position<br/>(beginning if <= 0, end if >= length) | insert an element at a certain index |
|
| `insert` | element to insert, position<br/>(beginning if <= 0, end if >= length) | insert an element at a certain index |
|
||||||
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
||||||
@ -49,7 +51,9 @@ Use Custom Types With Arrays
|
|||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
To use a [custom type] with arrays, a number of array functions need to be manually implemented,
|
To use a [custom type] with arrays, a number of array functions need to be manually implemented,
|
||||||
in particular `push`, `pad` and the `==` operator (in order to support the `in` operator).
|
in particular `push`, `pad` and the `+=` operator. In addition, the `==` operator must be
|
||||||
|
implemented for the [custom type] in order to support the `in` operator which uses `==` to
|
||||||
|
compare elements.
|
||||||
|
|
||||||
See the section on [custom types] for more details.
|
See the section on [custom types] for more details.
|
||||||
|
|
||||||
@ -104,7 +108,7 @@ let foo = y[0];
|
|||||||
foo == 1;
|
foo == 1;
|
||||||
|
|
||||||
y.push(4); // 4 elements
|
y.push(4); // 4 elements
|
||||||
y.push(5); // 5 elements
|
y += 5; // 5 elements
|
||||||
|
|
||||||
y.len == 5;
|
y.len == 5;
|
||||||
|
|
||||||
|
@ -161,13 +161,15 @@ x.type_of() == "Hello";
|
|||||||
Use the Custom Type With Arrays
|
Use the Custom Type With Arrays
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
The `push` and `pad` functions for [arrays] are only defined for standard built-in types.
|
The `push` and `pad` functions, as well as the `+=` operator, for [arrays] are only defined for
|
||||||
For custom types, type-specific versions must be registered:
|
standard built-in types. For custom types, type-specific versions must be registered:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
engine
|
engine
|
||||||
.register_fn("push", |list: &mut Array, item: TestStruct| {
|
.register_fn("push", |list: &mut Array, item: TestStruct| {
|
||||||
list.push(Dynamic::from(item));
|
list.push(Dynamic::from(item));
|
||||||
|
}).register_fn("+=", |list: &mut Array, item: TestStruct| {
|
||||||
|
list.push(Dynamic::from(item));
|
||||||
}).register_fn("pad", |list: &mut Array, len: i64, item: TestStruct| {
|
}).register_fn("pad", |list: &mut Array, len: i64, item: TestStruct| {
|
||||||
if len as usize > list.len() {
|
if len as usize > list.len() {
|
||||||
list.resize(len as usize, item);
|
list.resize(len as usize, item);
|
||||||
@ -176,7 +178,7 @@ engine
|
|||||||
```
|
```
|
||||||
|
|
||||||
In particular, in order to use the `in` operator with a custom type for an [array],
|
In particular, in order to use the `in` operator with a custom type for an [array],
|
||||||
the `==` operator must be registered for that custom type:
|
the `==` operator must be registered for the custom type:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Assume 'TestStruct' implements `PartialEq`
|
// Assume 'TestStruct' implements `PartialEq`
|
||||||
|
@ -1360,40 +1360,56 @@ impl Engine {
|
|||||||
let arg_types = once(lhs_ptr.type_id()).chain(once(rhs_val.type_id()));
|
let arg_types = once(lhs_ptr.type_id()).chain(once(rhs_val.type_id()));
|
||||||
let hash_fn = calc_fn_hash(empty(), op, 2, arg_types);
|
let hash_fn = calc_fn_hash(empty(), op, 2, arg_types);
|
||||||
|
|
||||||
if let Some(CallableFunction::Method(func)) = self
|
match self
|
||||||
.global_module
|
.global_module
|
||||||
.get_fn(hash_fn, false)
|
.get_fn(hash_fn, false)
|
||||||
.or_else(|| self.packages.get_fn(hash_fn, false))
|
.or_else(|| self.packages.get_fn(hash_fn, false))
|
||||||
{
|
{
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
// op= function registered as method
|
||||||
let mut lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
Some(func) if func.is_method() => {
|
||||||
let lhs_ptr_inner = lock_guard.deref_mut();
|
let mut lock_guard;
|
||||||
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
|
lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
||||||
|
lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
|
} else {
|
||||||
|
lhs_ptr_inner = lhs_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
||||||
|
|
||||||
// Overriding exact implementation
|
// Overriding exact implementation
|
||||||
func(self, lib, &mut [lhs_ptr_inner, &mut rhs_val])?;
|
if func.is_plugin_fn() {
|
||||||
} else {
|
func.get_plugin_fn().call(args)?;
|
||||||
// Overriding exact implementation
|
} else {
|
||||||
func(self, lib, &mut [lhs_ptr, &mut rhs_val])?;
|
func.get_native_fn()(self, lib, args)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() {
|
// Built-in op-assignment function
|
||||||
// Not built in, map to `var = var op rhs`
|
_ if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_some() => {}
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
// Not built-in: expand to `var = var op rhs`
|
||||||
|
_ => {
|
||||||
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
|
||||||
// Clone the LHS value
|
// Clone the LHS value
|
||||||
let args = &mut [&mut lhs_ptr.clone(), &mut rhs_val];
|
let args = &mut [&mut lhs_ptr.clone(), &mut rhs_val];
|
||||||
|
|
||||||
// Run function
|
// Run function
|
||||||
let (value, _) = self
|
let (value, _) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
state, lib, op, 0, args, false, false, false, None, None, level,
|
state, lib, op, 0, args, false, false, false, None, None,
|
||||||
)
|
level,
|
||||||
.map_err(|err| err.new_position(*op_pos))?;
|
)
|
||||||
|
.map_err(|err| err.new_position(*op_pos))?;
|
||||||
|
|
||||||
let value = value.flatten();
|
let value = value.flatten();
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
|
||||||
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
} else {
|
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
|
||||||
*lhs_ptr = value;
|
} else {
|
||||||
|
*lhs_ptr = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
|
@ -49,6 +49,7 @@ macro_rules! gen_array_functions {
|
|||||||
macro_rules! reg_functions {
|
macro_rules! reg_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
set_exported_fn!($mod_name, "push", $root::$arg_type::push);
|
set_exported_fn!($mod_name, "push", $root::$arg_type::push);
|
||||||
|
set_exported_fn!($mod_name, "+=", $root::$arg_type::push);
|
||||||
set_exported_fn!($mod_name, "insert", $root::$arg_type::insert);
|
set_exported_fn!($mod_name, "insert", $root::$arg_type::insert);
|
||||||
|
|
||||||
$mod_name.set_raw_fn("pad",
|
$mod_name.set_raw_fn("pad",
|
||||||
@ -58,8 +59,6 @@ macro_rules! reg_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
||||||
combine_with_exported_module!(lib, "array", array_functions);
|
|
||||||
|
|
||||||
reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
@ -77,6 +76,9 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
reg_functions!(lib += map; Map);
|
reg_functions!(lib += map; Map);
|
||||||
|
|
||||||
|
// Merge in the module at the end to override `+=` for arrays
|
||||||
|
combine_with_exported_module!(lib, "array", array_functions);
|
||||||
|
|
||||||
// Register array iterator
|
// Register array iterator
|
||||||
lib.set_iter(
|
lib.set_iter(
|
||||||
TypeId::of::<Array>(),
|
TypeId::of::<Array>(),
|
||||||
|
@ -13,6 +13,10 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
'3'
|
'3'
|
||||||
);
|
);
|
||||||
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
||||||
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y += 4; y[3]")?, 4);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y.push(4); y[3]")?, 4);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user