Expose methods for Engine::register_module.
This commit is contained in:
parent
cd62104296
commit
ef02150afd
@ -10,7 +10,7 @@ New features
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* `switch` statement.
|
* `switch` statement.
|
||||||
* `Engine::register_module` to register a module as a sub-module in the global namespace.
|
* `Engine::register_module` to register a module as a sub-module in the global namespace, while at the same time exposing its method functions globally. This is convenient when registering an API for a custom type.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -12,8 +12,10 @@ enum variants or to extract internal data from them.
|
|||||||
Simulate an Enum API
|
Simulate an Enum API
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
A [plugin module] is extremely handy in creating an entire API for a custom enum type.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, RegisterFn, Dynamic, EvalAltResult};
|
use rhai::{Engine, Dynamic, EvalAltResult};
|
||||||
use rhai::plugin::*;
|
use rhai::plugin::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
@ -25,48 +27,76 @@ enum MyEnum {
|
|||||||
|
|
||||||
// Create a plugin module with functions constructing the 'MyEnum' variants
|
// Create a plugin module with functions constructing the 'MyEnum' variants
|
||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod MyEnumModule {
|
mod MyEnumModule {
|
||||||
// 'MyEnum' variants
|
// Constructors for 'MyEnum' variants
|
||||||
pub const Foo: &MyEnum = MyEnum::Foo;
|
pub const Foo: &MyEnum = MyEnum::Foo;
|
||||||
pub fn Bar(value: i64) -> MyEnum { MyEnum::Bar(value) }
|
pub fn Bar(value: i64) -> MyEnum {
|
||||||
pub fn Baz(val1: String, val2: bool) -> MyEnum { MyEnum::Baz(val1, val2) }
|
MyEnum::Bar(value)
|
||||||
}
|
}
|
||||||
|
pub fn Baz(val1: String, val2: bool) -> MyEnum {
|
||||||
let mut engine = Engine::new();
|
MyEnum::Baz(val1, val2)
|
||||||
|
}
|
||||||
// Register API for 'MyEnum'
|
// Access to fields
|
||||||
engine
|
#[rhai_fn(get = "enum_type")]
|
||||||
// Register enum custom type
|
pub fn get_type(a: &mut MyEnum) -> String {
|
||||||
.register_type_with_name::<MyEnum>("MyEnum")
|
match a {
|
||||||
// Register access to fields
|
|
||||||
.register_get("type", |a: &mut MyEnum| match a {
|
|
||||||
MyEnum::Foo => "Foo".to_string(),
|
MyEnum::Foo => "Foo".to_string(),
|
||||||
MyEnum::Bar(_) => "Bar".to_string(),
|
MyEnum::Bar(_) => "Bar".to_string(),
|
||||||
MyEnum::Baz(_, _) => "Baz".to_string()
|
MyEnum::Baz(_, _) => "Baz".to_string()
|
||||||
})
|
}
|
||||||
.register_get("field_0", |a: &mut MyEnum| match a {
|
}
|
||||||
|
#[rhai_fn(get = "field_0")]
|
||||||
|
pub fn get_field_0(a: &mut MyEnum) -> Dynamic {
|
||||||
|
match a {
|
||||||
MyEnum::Foo => Dynamic::UNIT,
|
MyEnum::Foo => Dynamic::UNIT,
|
||||||
MyEnum::Bar(x) => Dynamic::from(x),
|
MyEnum::Bar(x) => Dynamic::from(x),
|
||||||
MyEnum::Baz(x, _) => Dynamic::from(x)
|
MyEnum::Baz(x, _) => Dynamic::from(x)
|
||||||
})
|
}
|
||||||
.register_get("field_1", |a: &mut MyEnum| match a {
|
}
|
||||||
|
#[rhai_fn(get = "field_1")]
|
||||||
|
pub fn get_field_1(a: &mut MyEnum) -> Dynamic {
|
||||||
|
match a {
|
||||||
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
|
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
|
||||||
MyEnum::Baz(_, x) => Dynamic::from(x)
|
MyEnum::Baz(_, x) => Dynamic::from(x)
|
||||||
})
|
}
|
||||||
// Register printing
|
}
|
||||||
.register_fn("to_string", |a: &mut MyEnum| format!("{:?}", a))
|
// Printing
|
||||||
.register_fn("print", |a: &mut MyEnum| format!("{:?}", a))
|
#[rhai(name = "to_string", name = "print", name = "debug")]
|
||||||
.register_fn("debug", |a: &mut MyEnum| format!("{:?}", a))
|
pub fn to_string(a: &mut MyEnum) -> String {
|
||||||
.register_fn("+", |s: &str, a: MyEnum| format!("{}{:?}", s, a))
|
format!("{:?}", a))
|
||||||
.register_fn("+", |a: &mut MyEnum, s: &str| format!("{:?}", a).push_str(s))
|
}
|
||||||
.register_fn("+=", |s: &mut ImmutableString, a: MyEnum| s += a.to_string())
|
#[rhai_fn(name = "+")]
|
||||||
// Register '==' and '!=' operators
|
pub fn add_to_str(s: &str, a: MyEnum) -> String {
|
||||||
.register_fn("==", |a: &mut MyEnum, b: MyEnum| a == &b)
|
format!("{}{:?}", s, a))
|
||||||
.register_fn("!=", |a: &mut MyEnum, b: MyEnum| a != &b)
|
}
|
||||||
// Register array functions
|
#[rhai_fn(name = "+")]
|
||||||
.register_fn("push", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item)))
|
pub fn add_str(a: &mut MyEnum, s: &str) -> String {
|
||||||
.register_fn("+=", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item)))
|
format!("{:?}", a).push_str(s))
|
||||||
.register_fn("insert", |list: &mut Array, position: i64, item: MyEnum| {
|
}
|
||||||
|
#[rhai_fn(name = "+=")]
|
||||||
|
pub fn append_to_str(s: &mut ImmutableString, a: MyEnum) -> String {
|
||||||
|
s += a.to_string())
|
||||||
|
}
|
||||||
|
// '==' and '!=' operators
|
||||||
|
#[rhai_fn(name = "==")]
|
||||||
|
pub fn eq(a: &mut MyEnum, b: MyEnum) -> bool {
|
||||||
|
a == &b
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "!=")]
|
||||||
|
pub fn neq(a: &mut MyEnum, b: MyEnum) -> bool {
|
||||||
|
a != &b
|
||||||
|
}
|
||||||
|
// Array functions
|
||||||
|
#[rhai_fn(name = "push")]
|
||||||
|
pub fn append_to_array(list: &mut Array, item: MyEnum) {
|
||||||
|
list.push(Dynamic::from(item)));
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "+=")]
|
||||||
|
pub fn append_to_array_op(list: &mut Array, item: MyEnum) {
|
||||||
|
list.push(Dynamic::from(item)));
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "insert")]
|
||||||
|
pub fn insert_to_array(list: &mut Array, position: i64, item: MyEnum) {
|
||||||
if position <= 0 {
|
if position <= 0 {
|
||||||
list.insert(0, Dynamic::from(item));
|
list.insert(0, Dynamic::from(item));
|
||||||
} else if (position as usize) >= list.len() - 1 {
|
} else if (position as usize) >= list.len() - 1 {
|
||||||
@ -74,17 +104,22 @@ engine
|
|||||||
} else {
|
} else {
|
||||||
list.insert(position as usize, Dynamic::from(item));
|
list.insert(position as usize, Dynamic::from(item));
|
||||||
}
|
}
|
||||||
}).register_fn("pad", |list: &mut Array, len: i64, item: MyEnum| {
|
}
|
||||||
|
#[rhai_fn(name = "pad")]
|
||||||
|
pub fn pad_array(list: &mut Array, len: i64, item: MyEnum) {
|
||||||
if len as usize > list.len() { list.resize(len as usize, item); }
|
if len as usize > list.len() { list.resize(len as usize, item); }
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
// Load the module as the module namespace "MyEnum"
|
// Load the module as the module namespace "MyEnum"
|
||||||
|
engine
|
||||||
|
.register_type_with_name::<MyEnum>("MyEnum")
|
||||||
.register_module("MyEnum", exported_module!(MyEnumModule));
|
.register_module("MyEnum", exported_module!(MyEnumModule));
|
||||||
```
|
```
|
||||||
|
|
||||||
Instead of registering all these manually, it is often convenient to wrap them up into
|
With this API in place, working with enums feels almost the same as in Rust:
|
||||||
a [custom package] that can be loaded into any [`Engine`].
|
|
||||||
|
|
||||||
With this API in place, working with enums will be almost the same as in Rust:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = MyEnum::Foo;
|
let x = MyEnum::Foo;
|
||||||
@ -99,11 +134,11 @@ y != MyEnum::Bar(0);
|
|||||||
|
|
||||||
// Detect enum types
|
// Detect enum types
|
||||||
|
|
||||||
x.type == "Foo";
|
x.enum_type == "Foo";
|
||||||
|
|
||||||
y.type == "Bar";
|
y.enum_type == "Bar";
|
||||||
|
|
||||||
z.type == "Baz";
|
z.enum_type == "Baz";
|
||||||
|
|
||||||
// Extract enum fields
|
// Extract enum fields
|
||||||
|
|
||||||
@ -116,24 +151,49 @@ z.field_0 == "hello";
|
|||||||
z.field_1 == true;
|
z.field_1 == true;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Since enums are internally treated as [custom types], they are not _literals_ and cannot be
|
||||||
|
used as a match case in `switch` expressions. This is quite a limitation because the equivalent
|
||||||
|
`match` statement is commonly used in Rust to work with enums and bind variables to
|
||||||
|
variant-internal data.
|
||||||
|
|
||||||
|
It is possible, however, to `switch` through enum variants based on their types:
|
||||||
|
|
||||||
|
```c
|
||||||
|
switch x.enum_type {
|
||||||
|
"Foo" => ...,
|
||||||
|
"Bar" => {
|
||||||
|
let value = foo.field_0;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
"Baz" => {
|
||||||
|
let val1 = foo.field_0;
|
||||||
|
let val2 = foo.field_1;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Use `switch` Through Arrays
|
Use `switch` Through Arrays
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Since enums are internally treated as [custom types], they are not _literals_ and cannot be
|
Another way to work with Rust enums in a `switch` expression is through exposing the internal data
|
||||||
used as a match case in `switch` expressions. This is quite a limitation because the equivalent
|
of each enum variant as a variable-length [array], usually with the name of the variant as
|
||||||
`match` statement is commonly used in Rust to work with enums.
|
the first item for convenience:
|
||||||
|
|
||||||
One way to work with Rust enums in a `switch` expression is through exposing the internal data
|
|
||||||
of each enum variant as an [array], usually with the name of the variant as the first item:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::Array;
|
use rhai::Array;
|
||||||
|
|
||||||
engine.register_get("enum_data", |x: &mut Enum} {
|
engine.register_get("enum_data", |x: &mut Enum| {
|
||||||
match x {
|
match x {
|
||||||
Enum::Foo => vec!["Foo".into()] as Array,
|
Enum::Foo => vec![
|
||||||
Enum::Bar(value) => vec!["Bar".into(), (*value).into()] as Array,
|
"Foo".into()
|
||||||
|
] as Array,
|
||||||
|
|
||||||
|
Enum::Bar(value) => vec![
|
||||||
|
"Bar".into(), (*value).into()
|
||||||
|
] as Array,
|
||||||
|
|
||||||
Enum::Baz(val1, val2) => vec![
|
Enum::Baz(val1, val2) => vec![
|
||||||
"Baz".into(), val1.clone().into(), (*val2).into()
|
"Baz".into(), val1.clone().into(), (*val2).into()
|
||||||
] as Array
|
] as Array
|
||||||
|
@ -123,10 +123,10 @@ x == 43;
|
|||||||
Notice that, when using a [module] as a [package], only functions registered at the _top level_
|
Notice that, when using a [module] as a [package], only functions registered at the _top level_
|
||||||
can be accessed. Variables as well as sub-modules are ignored.
|
can be accessed. Variables as well as sub-modules are ignored.
|
||||||
|
|
||||||
### Use `Engine::load_module`
|
### Use `Engine::register_module`
|
||||||
|
|
||||||
Another simple way to load this into an [`Engine`] is, again, to use the `exported_module!` macro
|
Another simple way to load this into an [`Engine`] is, again, to use the `exported_module!` macro
|
||||||
to turn it into a normal Rhai [module], then use the `Engine::load_module` method on it:
|
to turn it into a normal Rhai [module], then use the `Engine::register_module` method on it:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -136,7 +136,7 @@ fn main() {
|
|||||||
let module = exported_module!(my_module);
|
let module = exported_module!(my_module);
|
||||||
|
|
||||||
// A module can simply be loaded as a globally-available module.
|
// A module can simply be loaded as a globally-available module.
|
||||||
engine.load_module("service", module);
|
engine.register_module("service", module);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -159,6 +159,17 @@ service::increment(x);
|
|||||||
x == 43;
|
x == 43;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the
|
||||||
|
_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected.
|
||||||
|
|
||||||
|
Therefore, in the example able, `increment` works fine when called in method-call style:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 42;
|
||||||
|
x.increment();
|
||||||
|
x == 43;
|
||||||
|
```
|
||||||
|
|
||||||
### Use as loadable `Module`
|
### Use as loadable `Module`
|
||||||
|
|
||||||
Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a
|
Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a
|
||||||
|
@ -45,7 +45,7 @@ engine.eval::<i64>("inc(41)")? == 42; // no need to import module
|
|||||||
Make the `Module` a Global Module
|
Make the `Module` a Global Module
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
`Engine::load_module` loads a [module] and makes it available globally under a specific namespace.
|
`Engine::register_module` loads a [module] and makes it available globally under a specific namespace.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Module};
|
use rhai::{Engine, Module};
|
||||||
@ -55,11 +55,30 @@ module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add f
|
|||||||
|
|
||||||
// Load the module into the Engine as a sub-module named 'calc'
|
// Load the module into the Engine as a sub-module named 'calc'
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.load_module("calc", module);
|
engine.register_module("calc", module);
|
||||||
|
|
||||||
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
|
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the
|
||||||
|
_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Engine, Module};
|
||||||
|
|
||||||
|
let mut module = Module::new(); // new module
|
||||||
|
module.set_fn_1_mut("inc", // add new method
|
||||||
|
|x: &mut i64| Ok(x+1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load the module into the Engine as a sub-module named 'calc'
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
engine.register_module("calc", module);
|
||||||
|
|
||||||
|
// The method 'inc' works as expected because it is exposed to the global namespace
|
||||||
|
engine.eval::<i64>("let x = 41; x.inc()")? == 42;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Make the `Module` Dynamically Loadable
|
Make the `Module` Dynamically Loadable
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::ast::{BinaryExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt};
|
use crate::ast::{BinaryExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt};
|
||||||
use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant};
|
use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant};
|
||||||
use crate::fn_call::run_builtin_op_assignment;
|
use crate::fn_call::run_builtin_op_assignment;
|
||||||
use crate::fn_native::{Callback, FnPtr, OnVarCallback, Shared};
|
use crate::fn_native::{CallableFunction, Callback, FnPtr, IteratorFn, OnVarCallback, Shared};
|
||||||
use crate::module::{Module, NamespaceRef};
|
use crate::module::{Module, NamespaceRef};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||||
@ -85,7 +85,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
|
|||||||
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
|
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
|
||||||
// the AST of the eval script text. The best we can do is a shared reference.
|
// the AST of the eval script text. The best we can do is a shared reference.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Imports(StaticVec<(ImmutableString, Shared<Module>)>);
|
pub struct Imports(StaticVec<(ImmutableString, bool, Shared<Module>)>);
|
||||||
|
|
||||||
impl Imports {
|
impl Imports {
|
||||||
/// Get the length of this stack of imported modules.
|
/// Get the length of this stack of imported modules.
|
||||||
@ -98,7 +98,7 @@ impl Imports {
|
|||||||
}
|
}
|
||||||
/// Get the imported module at a particular index.
|
/// Get the imported module at a particular index.
|
||||||
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
|
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
|
||||||
self.0.get(index).map(|(_, m)| m).cloned()
|
self.0.get(index).map(|(_, _, m)| m).cloned()
|
||||||
}
|
}
|
||||||
/// Get the index of an imported module by name.
|
/// Get the index of an imported module by name.
|
||||||
pub fn find(&self, name: &str) -> Option<usize> {
|
pub fn find(&self, name: &str) -> Option<usize> {
|
||||||
@ -106,12 +106,21 @@ impl Imports {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|(_, (key, _))| key.as_str() == name)
|
.find(|(_, (key, _, _))| key.as_str() == name)
|
||||||
.map(|(index, _)| index)
|
.map(|(index, _)| index)
|
||||||
}
|
}
|
||||||
/// Push an imported module onto the stack.
|
/// Push an imported module onto the stack.
|
||||||
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
|
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
|
||||||
self.0.push((name.into(), module.into()));
|
self.0.push((name.into(), false, module.into()));
|
||||||
|
}
|
||||||
|
/// Push a fixed module onto the stack.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub(crate) fn push_fixed(
|
||||||
|
&mut self,
|
||||||
|
name: impl Into<ImmutableString>,
|
||||||
|
module: impl Into<Shared<Module>>,
|
||||||
|
) {
|
||||||
|
self.0.push((name.into(), true, module.into()));
|
||||||
}
|
}
|
||||||
/// Truncate the stack of imported modules to a particular length.
|
/// Truncate the stack of imported modules to a particular length.
|
||||||
pub fn truncate(&mut self, size: usize) {
|
pub fn truncate(&mut self, size: usize) {
|
||||||
@ -119,26 +128,59 @@ impl Imports {
|
|||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported modules.
|
/// Get an iterator to this stack of imported modules.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Shared<Module>)> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, module)| (name.as_str(), module.clone()))
|
.map(|(name, fixed, module)| (name.as_str(), *fixed, module.clone()))
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported modules.
|
/// Get an iterator to this stack of imported modules.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn iter_raw<'a>(
|
pub(crate) fn iter_raw<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
|
) -> impl Iterator<Item = (ImmutableString, bool, Shared<Module>)> + 'a {
|
||||||
self.0.iter().cloned()
|
self.0.iter().cloned()
|
||||||
}
|
}
|
||||||
/// Get a consuming iterator to this stack of imported modules.
|
/// Get a consuming iterator to this stack of imported modules.
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, bool, Shared<Module>)> {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
/// Add a stream of imported modules.
|
/// Add a stream of imported modules.
|
||||||
pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
|
pub fn extend(
|
||||||
|
&mut self,
|
||||||
|
stream: impl Iterator<Item = (ImmutableString, bool, Shared<Module>)>,
|
||||||
|
) {
|
||||||
self.0.extend(stream)
|
self.0.extend(stream)
|
||||||
}
|
}
|
||||||
|
/// Does the specified function hash key exist in this stack of imported modules?
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.any(|(_, fixed, m)| *fixed && m.contains_qualified_fn(hash))
|
||||||
|
}
|
||||||
|
/// Get specified function via its hash key.
|
||||||
|
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter(|&&(_, fixed, _)| fixed)
|
||||||
|
.find_map(|(_, _, m)| m.get_qualified_fn(hash))
|
||||||
|
}
|
||||||
|
/// Does the specified TypeId iterator exist in this stack of imported modules?
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.any(|(_, fixed, m)| *fixed && m.contains_qualified_iter(id))
|
||||||
|
}
|
||||||
|
/// Get the specified TypeId iterator.
|
||||||
|
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter(|&&(_, fixed, _)| fixed)
|
||||||
|
.find_map(|(_, _, m)| m.get_qualified_iter(id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -1947,7 +1989,8 @@ impl Engine {
|
|||||||
match 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))
|
||||||
|
.or_else(|| mods.get_fn(hash_fn))
|
||||||
{
|
{
|
||||||
// op= function registered as method
|
// op= function registered as method
|
||||||
Some(func) if func.is_method() => {
|
Some(func) if func.is_method() => {
|
||||||
@ -1965,9 +2008,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Overriding exact implementation
|
// Overriding exact implementation
|
||||||
if func.is_plugin_fn() {
|
if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn().call((self, mods, lib).into(), args)?;
|
func.get_plugin_fn()
|
||||||
|
.call((self, &*mods, lib).into(), args)?;
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn()((self, mods, lib).into(), args)?;
|
func.get_native_fn()((self, &*mods, lib).into(), args)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Built-in op-assignment function
|
// Built-in op-assignment function
|
||||||
@ -2142,7 +2186,8 @@ impl Engine {
|
|||||||
let func = self
|
let func = self
|
||||||
.global_module
|
.global_module
|
||||||
.get_iter(iter_type)
|
.get_iter(iter_type)
|
||||||
.or_else(|| self.packages.get_iter(iter_type));
|
.or_else(|| self.packages.get_iter(iter_type))
|
||||||
|
.or_else(|| mods.get_iter(iter_type));
|
||||||
|
|
||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
// Add the loop variable
|
// Add the loop variable
|
||||||
|
@ -811,9 +811,9 @@ impl Engine {
|
|||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = shared_take_or_clone(module);
|
let mut module = shared_take_or_clone(module);
|
||||||
module.build_index();
|
module.build_index();
|
||||||
self.global_sub_modules.push(name, module);
|
self.global_sub_modules.push_fixed(name, module);
|
||||||
} else {
|
} else {
|
||||||
self.global_sub_modules.push(name, module);
|
self.global_sub_modules.push_fixed(name, module);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ impl Engine {
|
|||||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||||
pub(crate) fn call_native_fn(
|
pub(crate) fn call_native_fn(
|
||||||
&self,
|
&self,
|
||||||
mods: &mut Imports,
|
mods: &Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
@ -192,7 +192,8 @@ impl Engine {
|
|||||||
// Then search packages
|
// Then search packages
|
||||||
let func = //lib.get_fn(hash_fn, pub_only)
|
let func = //lib.get_fn(hash_fn, pub_only)
|
||||||
self.global_module.get_fn(hash_fn, pub_only)
|
self.global_module.get_fn(hash_fn, pub_only)
|
||||||
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
|
.or_else(|| self.packages.get_fn(hash_fn))
|
||||||
|
.or_else(|| mods.get_fn(hash_fn));
|
||||||
|
|
||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
assert!(func.is_native());
|
assert!(func.is_native());
|
||||||
@ -428,6 +429,7 @@ impl Engine {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn has_override_by_name_and_arguments(
|
pub(crate) fn has_override_by_name_and_arguments(
|
||||||
&self,
|
&self,
|
||||||
|
mods: &Imports,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
arg_types: impl AsRef<[TypeId]>,
|
arg_types: impl AsRef<[TypeId]>,
|
||||||
@ -437,13 +439,14 @@ impl Engine {
|
|||||||
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned());
|
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned());
|
||||||
let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len());
|
let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len());
|
||||||
|
|
||||||
self.has_override(lib, hash_fn, hash_script, pub_only)
|
self.has_override(mods, lib, hash_fn, hash_script, pub_only)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn has_override(
|
pub(crate) fn has_override(
|
||||||
&self,
|
&self,
|
||||||
|
mods: &Imports,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
hash_fn: u64,
|
hash_fn: u64,
|
||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
@ -456,10 +459,13 @@ impl Engine {
|
|||||||
//|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only))
|
//|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only))
|
||||||
// Then check registered functions
|
// Then check registered functions
|
||||||
//|| self.global_module.contains_fn(hash_script, pub_only)
|
//|| self.global_module.contains_fn(hash_script, pub_only)
|
||||||
|| self.global_module.contains_fn(hash_fn, pub_only)
|
|| self.global_module.contains_fn(hash_fn, false)
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| self.packages.contains_fn(hash_script, pub_only)
|
|| self.packages.contains_fn(hash_script)
|
||||||
|| self.packages.contains_fn(hash_fn, pub_only)
|
|| self.packages.contains_fn(hash_fn)
|
||||||
|
// Then check imported modules
|
||||||
|
|| mods.contains_fn(hash_script)
|
||||||
|
|| mods.contains_fn(hash_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
||||||
@ -497,7 +503,8 @@ impl Engine {
|
|||||||
match fn_name {
|
match fn_name {
|
||||||
// type_of
|
// type_of
|
||||||
KEYWORD_TYPE_OF
|
KEYWORD_TYPE_OF
|
||||||
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
|
if args.len() == 1
|
||||||
|
&& !self.has_override(mods, lib, hash_fn, hash_script, pub_only) =>
|
||||||
{
|
{
|
||||||
Ok((
|
Ok((
|
||||||
self.map_type_name(args[0].type_name()).to_string().into(),
|
self.map_type_name(args[0].type_name()).to_string().into(),
|
||||||
@ -508,7 +515,8 @@ impl Engine {
|
|||||||
// Fn/eval - reaching this point it must be a method-style call, mostly like redirected
|
// Fn/eval - reaching this point it must be a method-style call, mostly like redirected
|
||||||
// by a function pointer so it isn't caught at parse time.
|
// by a function pointer so it isn't caught at parse time.
|
||||||
KEYWORD_FN_PTR | KEYWORD_EVAL
|
KEYWORD_FN_PTR | KEYWORD_EVAL
|
||||||
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
|
if args.len() == 1
|
||||||
|
&& !self.has_override(mods, lib, hash_fn, hash_script, pub_only) =>
|
||||||
{
|
{
|
||||||
EvalAltResult::ErrorRuntime(
|
EvalAltResult::ErrorRuntime(
|
||||||
format!(
|
format!(
|
||||||
@ -523,16 +531,14 @@ impl Engine {
|
|||||||
|
|
||||||
// Script-like function found
|
// Script-like function found
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ if lib.iter().any(|&m| m.contains_fn(hash_script, pub_only))
|
_ if self.has_override(mods, lib, 0, hash_script, pub_only) => {
|
||||||
//|| self.global_module.contains_fn(hash_script, pub_only)
|
|
||||||
|| self.packages.contains_fn(hash_script, pub_only) =>
|
|
||||||
{
|
|
||||||
// Get function
|
// Get function
|
||||||
let func = lib
|
let func = lib
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|&m| m.get_fn(hash_script, pub_only))
|
.find_map(|&m| m.get_fn(hash_script, pub_only))
|
||||||
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
|
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
|
||||||
.or_else(|| self.packages.get_fn(hash_script, pub_only))
|
.or_else(|| self.packages.get_fn(hash_script))
|
||||||
|
//.or_else(|| mods.get_fn(hash_script))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if func.is_script() {
|
if func.is_script() {
|
||||||
@ -860,7 +866,7 @@ impl Engine {
|
|||||||
let hash_fn =
|
let hash_fn =
|
||||||
calc_native_fn_hash(empty(), fn_name, once(TypeId::of::<ImmutableString>()));
|
calc_native_fn_hash(empty(), fn_name, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
return self
|
return self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
||||||
@ -916,7 +922,7 @@ impl Engine {
|
|||||||
|
|
||||||
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(mods, lib, 0, hash_script, pub_only)
|
||||||
{
|
{
|
||||||
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, &args_expr[0], level)?;
|
||||||
|
|
||||||
@ -946,7 +952,7 @@ impl Engine {
|
|||||||
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
|
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
|
||||||
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
|
||||||
let var_name =
|
let var_name =
|
||||||
self.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)?;
|
||||||
let var_name = var_name.as_str().map_err(|err| {
|
let var_name = var_name.as_str().map_err(|err| {
|
||||||
@ -966,7 +972,7 @@ impl Engine {
|
|||||||
.cloned(),
|
.cloned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) {
|
||||||
let fn_name =
|
let fn_name =
|
||||||
self.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)?;
|
||||||
let num_params =
|
let num_params =
|
||||||
@ -993,7 +999,7 @@ impl Engine {
|
|||||||
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||||
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(mods, 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 script =
|
let script =
|
||||||
@ -1194,7 +1200,7 @@ impl Engine {
|
|||||||
Some(f) if f.is_plugin_fn() => f
|
Some(f) if f.is_plugin_fn() => f
|
||||||
.get_plugin_fn()
|
.get_plugin_fn()
|
||||||
.clone()
|
.clone()
|
||||||
.call((self, mods, lib).into(), args.as_mut()),
|
.call((self, &*mods, lib).into(), args.as_mut()),
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
if !f.is_method() {
|
if !f.is_method() {
|
||||||
// Clone first argument
|
// Clone first argument
|
||||||
@ -1205,7 +1211,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.get_native_fn().clone()((self, mods, lib).into(), args.as_mut())
|
f.get_native_fn().clone()((self, &*mods, lib).into(), args.as_mut())
|
||||||
}
|
}
|
||||||
Some(_) => unreachable!(),
|
Some(_) => unreachable!(),
|
||||||
None if def_val.is_some() => Ok(def_val.unwrap().into()),
|
None if def_val.is_some() => Ok(def_val.unwrap().into()),
|
||||||
|
@ -56,10 +56,10 @@ pub struct NativeCallContext<'e, 'a, 'm, 'pm: 'm> {
|
|||||||
lib: &'m [&'pm Module],
|
lib: &'m [&'pm Module],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized>
|
impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'a Imports, &'m M)>
|
||||||
From<(&'e Engine, &'a mut Imports, &'m M)> for NativeCallContext<'e, 'a, 'm, 'pm>
|
for NativeCallContext<'e, 'a, 'm, 'pm>
|
||||||
{
|
{
|
||||||
fn from(value: (&'e Engine, &'a mut Imports, &'m M)) -> Self {
|
fn from(value: (&'e Engine, &'a Imports, &'m M)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine: value.0,
|
engine: value.0,
|
||||||
mods: Some(value.1),
|
mods: Some(value.1),
|
||||||
|
@ -73,11 +73,13 @@ pub struct Module {
|
|||||||
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
|
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
|
||||||
/// External Rust functions.
|
/// External Rust functions.
|
||||||
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>,
|
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>,
|
||||||
/// Flattened collection of all external Rust functions, native or scripted,
|
/// Flattened collection of all external Rust functions, native or scripted.
|
||||||
/// including those in sub-modules.
|
/// including those in sub-modules.
|
||||||
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
||||||
/// Iterator functions, keyed by the type producing the iterator.
|
/// Iterator functions, keyed by the type producing the iterator.
|
||||||
type_iterators: HashMap<TypeId, IteratorFn>,
|
type_iterators: HashMap<TypeId, IteratorFn>,
|
||||||
|
/// Flattened collection of iterator functions, including those in sub-modules.
|
||||||
|
all_type_iterators: HashMap<TypeId, IteratorFn>,
|
||||||
/// Is the module indexed?
|
/// Is the module indexed?
|
||||||
indexed: bool,
|
indexed: bool,
|
||||||
}
|
}
|
||||||
@ -91,6 +93,7 @@ impl Default for Module {
|
|||||||
functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder),
|
functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder),
|
||||||
all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder),
|
all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder),
|
||||||
type_iterators: Default::default(),
|
type_iterators: Default::default(),
|
||||||
|
all_type_iterators: Default::default(),
|
||||||
indexed: false,
|
indexed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,6 +184,7 @@ impl Module {
|
|||||||
&& self.all_variables.is_empty()
|
&& self.all_variables.is_empty()
|
||||||
&& self.modules.is_empty()
|
&& self.modules.is_empty()
|
||||||
&& self.type_iterators.is_empty()
|
&& self.type_iterators.is_empty()
|
||||||
|
&& self.all_type_iterators.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the module indexed?
|
/// Is the module indexed?
|
||||||
@ -1123,6 +1127,15 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does the particular namespace-qualified function exist in the module?
|
||||||
|
///
|
||||||
|
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match
|
||||||
|
/// the hash calculated by `build_index`.
|
||||||
|
#[inline]
|
||||||
|
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
||||||
|
self.all_functions.contains_key(&hash_fn)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a namespace-qualified function.
|
/// Get a namespace-qualified function.
|
||||||
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
||||||
///
|
///
|
||||||
@ -1143,6 +1156,7 @@ impl Module {
|
|||||||
self.type_iterators.extend(other.type_iterators.into_iter());
|
self.type_iterators.extend(other.type_iterators.into_iter());
|
||||||
self.all_functions.clear();
|
self.all_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1160,6 +1174,7 @@ impl Module {
|
|||||||
self.type_iterators.extend(other.type_iterators.into_iter());
|
self.type_iterators.extend(other.type_iterators.into_iter());
|
||||||
self.all_functions.clear();
|
self.all_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1186,6 +1201,7 @@ impl Module {
|
|||||||
});
|
});
|
||||||
self.all_functions.clear();
|
self.all_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1231,6 +1247,7 @@ impl Module {
|
|||||||
self.type_iterators.extend(other.type_iterators.iter());
|
self.type_iterators.extend(other.type_iterators.iter());
|
||||||
self.all_functions.clear();
|
self.all_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1250,6 +1267,7 @@ impl Module {
|
|||||||
|
|
||||||
self.all_functions.clear();
|
self.all_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1388,7 +1406,10 @@ impl Module {
|
|||||||
// Extra modules left in the scope become sub-modules
|
// Extra modules left in the scope become sub-modules
|
||||||
let mut func_mods: Imports = Default::default();
|
let mut func_mods: Imports = Default::default();
|
||||||
|
|
||||||
mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| {
|
mods.into_iter()
|
||||||
|
.skip(orig_mods_len)
|
||||||
|
.filter(|&(_, fixed, _)| !fixed)
|
||||||
|
.for_each(|(alias, _, m)| {
|
||||||
func_mods.push(alias.clone(), m.clone());
|
func_mods.push(alias.clone(), m.clone());
|
||||||
module.set_sub_module(alias, m);
|
module.set_sub_module(alias, m);
|
||||||
});
|
});
|
||||||
@ -1422,13 +1443,14 @@ impl Module {
|
|||||||
fn index_module<'a>(
|
fn index_module<'a>(
|
||||||
module: &'a Module,
|
module: &'a Module,
|
||||||
qualifiers: &mut Vec<&'a str>,
|
qualifiers: &mut Vec<&'a str>,
|
||||||
variables: &mut Vec<(u64, Dynamic)>,
|
variables: &mut HashMap<u64, Dynamic, StraightHasherBuilder>,
|
||||||
functions: &mut Vec<(u64, CallableFunction)>,
|
functions: &mut HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
||||||
|
type_iterators: &mut HashMap<TypeId, IteratorFn>,
|
||||||
) {
|
) {
|
||||||
module.modules.iter().for_each(|(name, m)| {
|
module.modules.iter().for_each(|(name, m)| {
|
||||||
// Index all the sub-modules first.
|
// Index all the sub-modules first.
|
||||||
qualifiers.push(name);
|
qualifiers.push(name);
|
||||||
index_module(m, qualifiers, variables, functions);
|
index_module(m, qualifiers, variables, functions, type_iterators);
|
||||||
qualifiers.pop();
|
qualifiers.pop();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1436,8 +1458,14 @@ impl Module {
|
|||||||
module.variables.iter().for_each(|(var_name, value)| {
|
module.variables.iter().for_each(|(var_name, value)| {
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
let hash_var = calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0);
|
let hash_var = calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0);
|
||||||
variables.push((hash_var, value.clone()));
|
variables.insert(hash_var, value.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Index type iterators
|
||||||
|
module.type_iterators.iter().for_each(|(&type_id, func)| {
|
||||||
|
type_iterators.insert(type_id, func.clone());
|
||||||
|
});
|
||||||
|
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
module
|
module
|
||||||
.functions
|
.functions
|
||||||
@ -1445,7 +1473,7 @@ impl Module {
|
|||||||
.filter(|(_, FuncInfo { access, .. })| access.is_public())
|
.filter(|(_, FuncInfo { access, .. })| access.is_public())
|
||||||
.for_each(
|
.for_each(
|
||||||
|(
|
|(
|
||||||
&_hash,
|
&hash,
|
||||||
FuncInfo {
|
FuncInfo {
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
@ -1454,6 +1482,12 @@ impl Module {
|
|||||||
..
|
..
|
||||||
},
|
},
|
||||||
)| {
|
)| {
|
||||||
|
// Flatten all methods so they can be available without namespace qualifiers
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if func.is_method() {
|
||||||
|
functions.insert(hash, func.clone());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(param_types) = types {
|
if let Some(param_types) = types {
|
||||||
assert_eq!(*params, param_types.len());
|
assert_eq!(*params, param_types.len());
|
||||||
|
|
||||||
@ -1469,15 +1503,17 @@ impl Module {
|
|||||||
// 3) The final hash is the XOR of the two hashes.
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
let hash_qualified_fn = hash_qualified_script ^ hash_fn_args;
|
let hash_qualified_fn = hash_qualified_script ^ hash_fn_args;
|
||||||
|
|
||||||
functions.push((hash_qualified_fn, func.clone()));
|
functions.insert(hash_qualified_fn, func.clone());
|
||||||
} else if cfg!(not(feature = "no_function")) {
|
} else if cfg!(not(feature = "no_function")) {
|
||||||
let hash_qualified_script = if qualifiers.is_empty() {
|
let hash_qualified_script = if cfg!(feature = "no_object")
|
||||||
_hash
|
&& qualifiers.is_empty()
|
||||||
|
{
|
||||||
|
hash
|
||||||
} else {
|
} else {
|
||||||
// Qualifiers + function name + number of arguments.
|
// Qualifiers + function name + number of arguments.
|
||||||
calc_script_fn_hash(qualifiers.iter().map(|&v| v), &name, *params)
|
calc_script_fn_hash(qualifiers.iter().map(|&v| v), &name, *params)
|
||||||
};
|
};
|
||||||
functions.push((hash_qualified_script, func.clone()));
|
functions.insert(hash_qualified_script, func.clone());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1485,19 +1521,32 @@ impl Module {
|
|||||||
|
|
||||||
if !self.indexed {
|
if !self.indexed {
|
||||||
let mut qualifiers = Vec::with_capacity(4);
|
let mut qualifiers = Vec::with_capacity(4);
|
||||||
let mut variables = Vec::with_capacity(16);
|
let mut variables = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
|
||||||
let mut functions = Vec::with_capacity(256);
|
let mut functions = HashMap::with_capacity_and_hasher(256, StraightHasherBuilder);
|
||||||
|
let mut type_iterators = HashMap::with_capacity(16);
|
||||||
|
|
||||||
qualifiers.push("root");
|
qualifiers.push("root");
|
||||||
|
|
||||||
index_module(self, &mut qualifiers, &mut variables, &mut functions);
|
index_module(
|
||||||
|
self,
|
||||||
|
&mut qualifiers,
|
||||||
|
&mut variables,
|
||||||
|
&mut functions,
|
||||||
|
&mut type_iterators,
|
||||||
|
);
|
||||||
|
|
||||||
self.all_variables = variables.into_iter().collect();
|
self.all_variables = variables;
|
||||||
self.all_functions = functions.into_iter().collect();
|
self.all_functions = functions;
|
||||||
|
self.all_type_iterators = type_iterators;
|
||||||
self.indexed = true;
|
self.indexed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does a type iterator exist in the entire module tree?
|
||||||
|
pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
|
||||||
|
self.all_type_iterators.contains_key(&id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Does a type iterator exist in the module?
|
/// Does a type iterator exist in the module?
|
||||||
pub fn contains_iter(&self, id: TypeId) -> bool {
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
||||||
self.type_iterators.contains_key(&id)
|
self.type_iterators.contains_key(&id)
|
||||||
@ -1532,6 +1581,11 @@ impl Module {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the specified type iterator.
|
||||||
|
pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
|
self.all_type_iterators.get(&id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the specified type iterator.
|
/// Get the specified type iterator.
|
||||||
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
self.type_iterators.get(&id).cloned()
|
self.type_iterators.get(&id).cloned()
|
||||||
|
@ -673,7 +673,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
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).
|
||||||
if !state.engine.has_override_by_name_and_arguments(state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
if !state.engine.has_override_by_name_and_arguments(&state.engine.global_sub_modules, state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
||||||
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.ok().flatten()
|
.ok().flatten()
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
|
@ -64,16 +64,12 @@ impl PackagesCollection {
|
|||||||
}
|
}
|
||||||
/// Does the specified function hash key exist in the `PackagesCollection`?
|
/// Does the specified function hash key exist in the `PackagesCollection`?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn contains_fn(&self, hash: u64, public_only: bool) -> bool {
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
self.0.iter().any(|p| p.contains_fn(hash, public_only))
|
self.0.iter().any(|p| p.contains_fn(hash, false))
|
||||||
}
|
}
|
||||||
/// 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) -> Option<&CallableFunction> {
|
||||||
self.0
|
self.0.iter().find_map(|p| p.get_fn(hash, false))
|
||||||
.iter()
|
|
||||||
.map(|p| p.get_fn(hash, public_only))
|
|
||||||
.find(|f| f.is_some())
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
/// Does the specified TypeId iterator exist in the `PackagesCollection`?
|
/// Does the specified TypeId iterator exist in the `PackagesCollection`?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -82,11 +78,7 @@ impl PackagesCollection {
|
|||||||
}
|
}
|
||||||
/// Get the specified TypeId iterator.
|
/// Get the specified TypeId iterator.
|
||||||
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
self.0
|
self.0.iter().find_map(|p| p.get_iter(id))
|
||||||
.iter()
|
|
||||||
.map(|p| p.get_iter(id))
|
|
||||||
.find(|f| f.is_some())
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
tests/for.rs
44
tests/for.rs
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, Module, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[test]
|
#[test]
|
||||||
@ -75,3 +75,45 @@ fn test_for_object() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct MyIterableType(String);
|
||||||
|
|
||||||
|
impl IntoIterator for MyIterableType {
|
||||||
|
type Item = char;
|
||||||
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.chars().collect::<Vec<_>>().into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[test]
|
||||||
|
fn test_for_module_iterator() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Set a type iterator deep inside a nested module chain
|
||||||
|
let mut sub_module = Module::new();
|
||||||
|
sub_module.set_iterable::<MyIterableType>();
|
||||||
|
sub_module.set_fn_0("new_ts", || Ok(MyIterableType("hello".to_string())));
|
||||||
|
|
||||||
|
let mut module = Module::new();
|
||||||
|
module.set_sub_module("inner", sub_module);
|
||||||
|
|
||||||
|
engine.register_module("testing", module);
|
||||||
|
|
||||||
|
let script = r#"
|
||||||
|
let item = testing::inner::new_ts();
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
for x in item {
|
||||||
|
result += x;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<String>(script)?, "hello");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
|
|
||||||
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
||||||
|
sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1));
|
||||||
|
|
||||||
sub_module.set_sub_module("universe", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
@ -39,26 +40,32 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||||
|
|
||||||
let mut resolver = StaticModuleResolver::new();
|
|
||||||
resolver.insert("question", module);
|
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_module_resolver(Some(resolver));
|
engine.register_module("question", module);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("question::MYSTIC_NUMBER")?, 42);
|
||||||
|
assert!(engine.eval::<INT>("MYSTIC_NUMBER").is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(r#"import "question" as q; q::MYSTIC_NUMBER"#)?,
|
engine.eval::<INT>("question::life::universe::answer + 1")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(r#"import "question" as q; q::life::universe::answer + 1"#)?,
|
engine.eval::<INT>("question::life::universe::inc(question::life::universe::answer)")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
assert!(engine
|
||||||
|
.eval::<INT>("inc(question::life::universe::answer)")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>("super_inc(question::life::universe::answer)")?,
|
||||||
r#"import "question" as q; q::life::universe::inc(q::life::universe::answer)"#
|
|
||||||
)?,
|
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
assert!(engine
|
||||||
|
.eval::<INT>("super_inc(question::life::universe::answer)")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user