Merge pull request #287 from schungx/master
Fix bug with recursive file imports.
This commit is contained in:
commit
c62325edac
@ -11,6 +11,7 @@ Bug fixes
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
* Fixes compilation error when using the `internals` feature. Bug introduced in `0.19.4`.
|
* Fixes compilation error when using the `internals` feature. Bug introduced in `0.19.4`.
|
||||||
|
* Importing script files recursively no longer panics.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
@ -22,6 +23,8 @@ Enhancements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Modules imported via `import` statements at global level can now be used in functions. There is no longer any need to re-`import` the modules at the beginning of each function block.
|
* Modules imported via `import` statements at global level can now be used in functions. There is no longer any need to re-`import` the modules at the beginning of each function block.
|
||||||
|
* `index_of`, `==` and `!=` are defined for arrays.
|
||||||
|
* `==` and `!=` are defined for object maps.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.4
|
Version 0.19.4
|
||||||
|
@ -37,6 +37,9 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but
|
|||||||
| `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end |
|
| `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end |
|
||||||
| `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first |
|
| `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first |
|
||||||
| `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second |
|
| `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second |
|
||||||
|
| `==` operator | 1) first array<br/>2) second array | are the two arrays the same (elements compared with the `==` operator, if defined)? |
|
||||||
|
| `!=` operator | 1) first array<br/>2) second array | are the two arrays different (elements compared with the `==` operator, if defined)? |
|
||||||
|
| `in` operator | item to find | does the array contain the item (compared with the `==` operator, if defined)? |
|
||||||
| `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts an element at a certain index |
|
| `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts 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) |
|
||||||
| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
|
| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
|
||||||
@ -54,6 +57,7 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but
|
|||||||
| `retain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to retain, none if < 0 | retains a portion of the array, removes all other items and returning them (not in original order) |
|
| `retain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to retain, none if < 0 | retains a portion of the array, removes all other items and returning them (not in original order) |
|
||||||
| `splice` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) |
|
| `splice` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) |
|
||||||
| `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
| `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
|
| `index_of` | [function pointer] to predicate (usually a [closure]) | returns the index of the first item in the array that returns `true` when called with the predicate function, or -1 if not found:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
| `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
| `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
| `reduce` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
| `reduce` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
||||||
| `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
| `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
||||||
|
@ -236,7 +236,7 @@ This type is normally provided by the [`Engine`] (e.g. when using [`Engine::regi
|
|||||||
However, it may also be manually constructed from a tuple:
|
However, it may also be manually constructed from a tuple:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, FnPtr};
|
use rhai::{Engine, FnPtr, NativeCallContext};
|
||||||
|
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
@ -254,13 +254,11 @@ let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?;
|
|||||||
// Get rid of the script, retaining only functions
|
// Get rid of the script, retaining only functions
|
||||||
ast.retain_functions(|_, _, _| true);
|
ast.retain_functions(|_, _, _| true);
|
||||||
|
|
||||||
// Create native call context via a tuple
|
// Create native call context
|
||||||
let context =
|
let context = NativeCallContext::new(
|
||||||
(
|
|
||||||
&engine, // the 'Engine'
|
&engine, // the 'Engine'
|
||||||
&[ast.as_ref()] // function namespace from the 'AST'
|
&[ast.as_ref()] // function namespace from the 'AST'
|
||||||
// as a one-element slice
|
);
|
||||||
).into();
|
|
||||||
|
|
||||||
// 'f' captures: the engine, the AST, and the closure
|
// 'f' captures: the engine, the AST, and the closure
|
||||||
let f = move |x: i64| fn_ptr.call_dynamic(context, None, [x.into()]);
|
let f = move |x: i64| fn_ptr.call_dynamic(context, None, [x.into()]);
|
||||||
|
@ -59,12 +59,14 @@ operate on object maps:
|
|||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ---------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `has` | property name | does the object map contain a property of a particular name? |
|
| `has`, `in` operator | property name | does the object map contain a property of a particular name? |
|
||||||
| `len` | _none_ | returns the number of properties |
|
| `len` | _none_ | returns the number of properties |
|
||||||
| `clear` | _none_ | empties the object map |
|
| `clear` | _none_ | empties the object map |
|
||||||
| `remove` | property name | removes a certain property and returns it ([`()`] if the property does not exist) |
|
| `remove` | property name | removes a certain property and returns it ([`()`] if the property does not exist) |
|
||||||
| `+=` operator, `mixin` | second object map | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
|
| `+=` operator, `mixin` | second object map | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
|
||||||
| `+` operator | 1) first object map<br/>2) second object map | merges the first object map with the second |
|
| `+` operator | 1) first object map<br/>2) second object map | merges the first object map with the second |
|
||||||
|
| `==` operator | 1) first object map<br/>2) second object map | are the two object map the same (elements compared with the `==` operator, if defined)? |
|
||||||
|
| `!=` operator | 1) first object map<br/>2) second object map | are the two object map different (elements compared with the `==` operator, if defined)? |
|
||||||
| `fill_with` | second object map | adds in all properties of the second object map that do not exist in the object map |
|
| `fill_with` | second object map | adds in all properties of the second object map that do not exist in the object map |
|
||||||
| `keys` | _none_ | returns an [array] of all the property names (in random order), not available under [`no_index`] |
|
| `keys` | _none_ | returns an [array] of all the property names (in random order), not available under [`no_index`] |
|
||||||
| `values` | _none_ | returns an [array] of all the property values (in random order), not available under [`no_index`] |
|
| `values` | _none_ | returns an [array] of all the property values (in random order), not available under [`no_index`] |
|
||||||
|
@ -20,6 +20,7 @@ There are also a number of examples scripts that showcase Rhai's features, all i
|
|||||||
| [`function_decl3.rhai`]({{repoTree}}/scripts/function_decl3.rhai) | a [function] with many parameters |
|
| [`function_decl3.rhai`]({{repoTree}}/scripts/function_decl3.rhai) | a [function] with many parameters |
|
||||||
| [`if1.rhai`]({{repoTree}}/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example |
|
| [`if1.rhai`]({{repoTree}}/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example |
|
||||||
| [`loop.rhai`]({{repoTree}}/scripts/loop.rhai) | count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop |
|
| [`loop.rhai`]({{repoTree}}/scripts/loop.rhai) | count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop |
|
||||||
|
| [`module.rhai`]({{repoTree}}/scripts/module.rhai) | import a script file as a module |
|
||||||
| [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | simulate [object-oriented programming (OOP)][OOP] with [closures] |
|
| [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | simulate [object-oriented programming (OOP)][OOP] with [closures] |
|
||||||
| [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | just simple addition |
|
| [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | just simple addition |
|
||||||
| [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | simple addition and multiplication |
|
| [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | simple addition and multiplication |
|
||||||
|
3
scripts/module.rhai
Normal file
3
scripts/module.rhai
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import "scripts/loop";
|
||||||
|
|
||||||
|
print("Module test!");
|
@ -164,6 +164,7 @@ pub const FN_IDX_GET: &str = "index$get$";
|
|||||||
pub const FN_IDX_SET: &str = "index$set$";
|
pub const FN_IDX_SET: &str = "index$set$";
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub const FN_ANONYMOUS: &str = "anon$";
|
pub const FN_ANONYMOUS: &str = "anon$";
|
||||||
|
pub const OP_EQUALS: &str = "==";
|
||||||
pub const MARKER_EXPR: &str = "$expr$";
|
pub const MARKER_EXPR: &str = "$expr$";
|
||||||
pub const MARKER_BLOCK: &str = "$block$";
|
pub const MARKER_BLOCK: &str = "$block$";
|
||||||
pub const MARKER_IDENT: &str = "$ident$";
|
pub const MARKER_IDENT: &str = "$ident$";
|
||||||
@ -1474,8 +1475,6 @@ impl Engine {
|
|||||||
match rhs_value {
|
match rhs_value {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(mut rhs_value)) => {
|
Dynamic(Union::Array(mut rhs_value)) => {
|
||||||
const OP_FUNC: &str = "==";
|
|
||||||
|
|
||||||
// Call the `==` operator to compare each value
|
// Call the `==` operator to compare each value
|
||||||
let def_value = Some(false.into());
|
let def_value = Some(false.into());
|
||||||
|
|
||||||
@ -1485,11 +1484,11 @@ impl Engine {
|
|||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||||
let hash =
|
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
|
if self
|
||||||
.call_native_fn(
|
.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()))?
|
.map_err(|err| err.fill_position(rhs.position()))?
|
||||||
.0
|
.0
|
||||||
|
@ -1519,7 +1519,8 @@ impl Engine {
|
|||||||
let mut arg_values = args.into_vec();
|
let mut arg_values = args.into_vec();
|
||||||
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
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());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -1594,7 +1595,7 @@ impl Engine {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
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.
|
/// 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(
|
pub(crate) fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
lib: &Module,
|
lib: &[&Module],
|
||||||
name: &str,
|
name: &str,
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let fn_def = lib
|
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))?;
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
|
||||||
|
|
||||||
let mut state = Default::default();
|
let mut state = Default::default();
|
||||||
@ -1627,16 +1629,7 @@ impl Engine {
|
|||||||
ensure_no_data_race(name, args, false)?;
|
ensure_no_data_race(name, args, false)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.call_script_fn(
|
self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0)
|
||||||
scope,
|
|
||||||
&mut mods,
|
|
||||||
&mut state,
|
|
||||||
&[lib],
|
|
||||||
this_ptr,
|
|
||||||
fn_def,
|
|
||||||
args,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize the `AST` with constants defined in an external Scope.
|
/// Optimize the `AST` with constants defined in an external Scope.
|
||||||
|
@ -571,7 +571,7 @@ impl Engine {
|
|||||||
_level,
|
_level,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
// Normal call of script function - map first argument to `this`
|
// Normal call of script function
|
||||||
// The first argument is a reference?
|
// The first argument is a reference?
|
||||||
let mut backup: ArgBackup = Default::default();
|
let mut backup: ArgBackup = Default::default();
|
||||||
backup.change_first_arg_to_copy(is_ref, args);
|
backup.change_first_arg_to_copy(is_ref, args);
|
||||||
|
@ -81,6 +81,31 @@ impl<'e, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'m M)
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
||||||
|
/// Create a new `NativeCallContext`.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(engine: &'e Engine, lib: &'m impl AsRef<[&'pm Module]>) -> Self {
|
||||||
|
Self {
|
||||||
|
engine,
|
||||||
|
mods: None,
|
||||||
|
lib: lib.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// _[INTERNALS]_ Create a new `NativeCallContext`.
|
||||||
|
/// Available under the `internals` feature only.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new_with_imports(
|
||||||
|
engine: &'e Engine,
|
||||||
|
mods: &'a mut Imports,
|
||||||
|
lib: &'m impl AsRef<[&'pm Module]>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
engine,
|
||||||
|
mods: Some(mods),
|
||||||
|
lib: lib.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// The current `Engine`.
|
/// The current `Engine`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn engine(&self) -> &'e Engine {
|
pub fn engine(&self) -> &'e Engine {
|
||||||
@ -99,6 +124,54 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &'pm Module> + 'm {
|
pub fn iter_namespaces(&self) -> impl Iterator<Item = &'pm Module> + 'm {
|
||||||
self.lib.iter().cloned()
|
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.
|
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
|
||||||
@ -181,12 +254,11 @@ impl FnPtr {
|
|||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
pub fn call_dynamic(
|
pub fn call_dynamic(
|
||||||
&self,
|
&self,
|
||||||
ctx: NativeCallContext,
|
mut ctx: NativeCallContext,
|
||||||
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 arg_values = arg_values.as_mut();
|
||||||
let fn_name = self.fn_name();
|
|
||||||
|
|
||||||
let mut args_data = self
|
let mut args_data = self
|
||||||
.curry()
|
.curry()
|
||||||
@ -195,32 +267,15 @@ impl FnPtr {
|
|||||||
.chain(arg_values.iter_mut().map(mem::take))
|
.chain(arg_values.iter_mut().map(mem::take))
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let has_this = this_ptr.is_some();
|
|
||||||
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
|
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 {
|
if let Some(obj) = this_ptr {
|
||||||
args.insert(0, obj);
|
args.insert(0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mods = ctx.mods.cloned().unwrap_or_default();
|
ctx.call_fn_dynamic_raw(self.fn_name(), has_this, true, args.as_mut(), None)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ use crate::module::{Module, ModuleResolver};
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, collections::HashMap, path::PathBuf, string::String};
|
use crate::stdlib::{
|
||||||
|
boxed::Box, collections::HashMap, io::Error as IoError, path::PathBuf, string::String,
|
||||||
|
};
|
||||||
|
|
||||||
/// Module resolution service that loads module script files from the file system.
|
/// Module resolution service that loads module script files from the file system.
|
||||||
///
|
///
|
||||||
@ -135,20 +137,30 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
let scope = Default::default();
|
let scope = Default::default();
|
||||||
|
|
||||||
// See if it is cached
|
// See if it is cached
|
||||||
let mut module = None;
|
let mut module: Option<Shared<Module>> = None;
|
||||||
|
|
||||||
let module_ref = {
|
let mut module_ref = {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let c = self.cache.borrow();
|
let c = self.cache.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let c = self.cache.read().unwrap();
|
let c = self.cache.read().unwrap();
|
||||||
|
|
||||||
if let Some(module) = c.get(&file_path) {
|
if let Some(module) = c.get(&file_path) {
|
||||||
module.clone()
|
Some(module.clone())
|
||||||
} else {
|
} else {
|
||||||
// Load the file and compile it if not found
|
None
|
||||||
let ast = engine.compile_file(file_path.clone()).map_err(|err| {
|
}
|
||||||
Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos))
|
};
|
||||||
|
|
||||||
|
if module_ref.is_none() {
|
||||||
|
// Load the script file and compile it
|
||||||
|
let ast = engine
|
||||||
|
.compile_file(file_path.clone())
|
||||||
|
.map_err(|err| match *err {
|
||||||
|
EvalAltResult::ErrorSystem(_, err) if err.is::<IoError>() => {
|
||||||
|
Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos))
|
||||||
|
}
|
||||||
|
_ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| {
|
let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| {
|
||||||
@ -157,10 +169,8 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
|
|
||||||
m.build_index();
|
m.build_index();
|
||||||
|
|
||||||
let m: Shared<Module> = m.into();
|
module = Some(m.into());
|
||||||
module = Some(m.clone());
|
module_ref = module.clone();
|
||||||
m
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(module) = module {
|
if let Some(module) = module {
|
||||||
@ -171,6 +181,6 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
self.cache.write().unwrap().insert(file_path, module);
|
self.cache.write().unwrap().insert(file_path, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(module_ref)
|
Ok(module_ref.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::dynamic::Dynamic;
|
use crate::dynamic::Dynamic;
|
||||||
use crate::engine::Array;
|
use crate::engine::{Array, OP_EQUALS};
|
||||||
use crate::fn_native::{FnPtr, NativeCallContext};
|
use crate::fn_native::{FnPtr, NativeCallContext};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -263,6 +263,39 @@ mod array_functions {
|
|||||||
Ok(array.into())
|
Ok(array.into())
|
||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[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(
|
pub fn some(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
@ -619,6 +652,41 @@ mod array_functions {
|
|||||||
|
|
||||||
drained
|
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);
|
gen_array_functions!(basic => INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::dynamic::Dynamic;
|
use crate::dynamic::Dynamic;
|
||||||
use crate::engine::Map;
|
use crate::engine::{Map, OP_EQUALS};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::INT;
|
use crate::INT;
|
||||||
@ -46,6 +46,45 @@ mod map_functions {
|
|||||||
map1.entry(key).or_insert(value);
|
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"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub mod indexing {
|
pub mod indexing {
|
||||||
|
@ -432,3 +432,16 @@ fn test_module_ast_namespace2() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_file() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
let ast = engine.compile(
|
||||||
|
r#"
|
||||||
|
import "scripts/module";
|
||||||
|
print("top");
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
Module::eval_ast_as_new(Default::default(), &ast, &engine)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user