Merge pull request #313 from schungx/master

Revise packages terminology.
This commit is contained in:
Stephen Chung 2020-12-23 21:38:38 +08:00 committed by GitHub
commit a2220e7f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 506 additions and 546 deletions

View File

@ -6,7 +6,7 @@ members = [
[package]
name = "rhai"
version = "0.19.8"
version = "0.19.9"
edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
description = "Embedded scripting for Rust"

View File

@ -1,6 +1,20 @@
Rhai Release Notes
==================
Version 0.19.9
==============
This version removes the confusing differences between _packages_ and _modules_
by unifying the terminology and API under the global umbrella of _modules_.
Breaking changes
----------------
* `Engine::load_package` is renamed `Engine::register_global_module` and now must explicitly pass a shared [`Module`].
* `Engine::register_module` is renamed `Engine::register_static_module` and now must explicitly pass a shared [`Module`].
* `Package::get` is renamed `Package::as_shared_module`.
Version 0.19.8
==============
@ -14,13 +28,15 @@ A new API, `Engine::gen_fn_metadata_to_json` and `Engine::gen_fn_metadata_with_a
paired with the new `metadata` feature, exports the full list of functions metadata
(including those in an `AST`) as a JSON document.
There are also a sizable number of bug fixes.
Bug fixes
---------
* Unary prefix operators `-`, `+` and `!` now bind correctly when applied to an expression. Previously, `-x.len` is parsed as `(-x).len` which is obviously counter-intuitive.
* Indexing of namespace-qualified variables now work properly, such as `path::to::var[x]`.
* Constants are no longer propagated by the optimizer if shadowed by a non-constant variable.
* Constants passed as the `this` parameter to Rhai functions now throws an error if assigned to.
* A constant passed as the `this` parameter to Rhai functions now throws an error if assigned to.
* Generic type parameter of `Engine::register_iterator` is `IntoIterator` instead of `Iterator`.
* Fixes parsing of block comments ending with `**/` or inner blocks starting with `//*`.
@ -37,8 +53,8 @@ New features
* `AST::iter_functions` now returns `ScriptFnMetadata` which includes, among others, _doc-comments_ for functions prefixed by `///` or `/**`.
* _Doc-comments_ can be enabled/disabled with the new `Engine::set_doc_comments` method.
* A new feature `metadata` is added that pulls in `serde_json` and enables `Engine::gen_fn_metadata_to_json` and ``Engine::gen_fn_metadata_with_ast_to_json` which exports the full list of functions metadata (including those inside an `AST`) in JSON format.
* `Engine::on_debug` provides two additional parameters: `source: Option<&str>` and `pos: Position`.
* A new feature `metadata` is added that pulls in `serde_json` and enables `Engine::gen_fn_metadata_to_json` and `Engine::gen_fn_metadata_with_ast_to_json` which exports the full list of functions metadata (including those inside an `AST`) in JSON format.
* `Engine::on_debug` provides two additional parameters: `source: Option<&str>` and `pos: Position`, containing the current source (if any) and position of the `debug` statement.
* `NativeCallContext` and `EvalContext` both expose `source()` which returns the current source, if any.
Enhancements
@ -46,7 +62,6 @@ Enhancements
* A functions lookup cache is added to make function call resolution faster.
* Capturing a constant variable in a closure is now supported, with no cloning.
* Provides position info for `debug` statements.
* A _look-ahead_ symbol is provided to custom syntax parsers, which can be used to parse variable-length symbol streams.
@ -98,7 +113,7 @@ New features
* New `switch` statement.
* New `do ... while` and `do ... until` statements.
* New `Engine::gen_fn_signatures`, `Module::gen_fn_signatures` and `PackagesCollection::gen_fn_signatures` to generate a list of signatures for functions registered.
* New `Engine::register_module` to register a module as a sub-module in the global namespace.
* New `Engine::register_static_module` to register a module as a sub-module in the global namespace.
* New `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace.
* `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type.
* `Module::set_getter_fn`, `Module::set_setter_fn`, `Module::set_indexer_get_fn`, `Module::set_indexer_set_fn` all expose the function to the global namespace by default. This is convenient when registering an API for a custom type.
@ -365,7 +380,7 @@ Breaking changes
* `Engine::register_raw_fn_XXX` API shortcuts are removed.
* `PackagesCollection::get_fn`, `PackagesCollection::contains_fn`, `Module::get_fn` and `Module::contains_fn` now take an additional `public_only` parameter indicating whether only public functions are accepted.
* The iterator returned by `Scope::iter` now contains a clone of the `Dynamic` value (unshared).
* `Engine::load_package` takes any type that is `Into<PackageLibrary>`.
* `Engine::register_global_module` takes any type that is `Into<PackageLibrary>`.
* Error in `Engine::register_custom_syntax` is no longer `Box`-ed.
Housekeeping

View File

@ -23,7 +23,7 @@ fn bench_engine_new_raw_core(bench: &mut Bencher) {
bench.iter(|| {
let mut engine = Engine::new_raw();
engine.load_package(package.get());
engine.register_global_module(package.as_shared_module());
});
}

View File

@ -20,7 +20,7 @@ fn bench_eval_module(bench: &mut Bencher) {
let module = Module::eval_ast_as_new(Default::default(), &ast, &engine).unwrap();
engine.register_module("testing", module);
engine.register_static_module("testing", module.into());
let ast = engine
.compile(

View File

@ -160,7 +160,7 @@ pub fn export_fn(
///
/// let module = exported_module!(my_plugin_module);
///
/// engine.load_package(module);
/// engine.register_global_module(module.into());
///
/// assert_eq!(engine.eval::<i64>("foo(bar())")?, 42);
/// # Ok(())
@ -203,7 +203,7 @@ pub fn export_module(
///
/// let module = exported_module!(my_plugin_module);
///
/// engine.load_package(module);
/// engine.register_global_module(module.into());
///
/// assert_eq!(engine.eval::<i64>("foo(bar())")?, 42);
/// # Ok(())
@ -250,7 +250,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke
/// let mut module = Module::new();
/// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module);
///
/// engine.load_package(module);
/// engine.register_global_module(module.into());
///
/// assert_eq!(engine.eval::<i64>("foo(bar())")?, 42);
/// # Ok(())
@ -324,7 +324,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
/// let mut module = Module::new();
/// set_exported_fn!(module, "func", my_plugin_function);
///
/// engine.load_package(module);
/// engine.register_global_module(module.into());
///
/// assert_eq!(engine.eval::<i64>("func(21)")?, 42);
/// # Ok(())
@ -366,7 +366,7 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream
/// let mut module = Module::new();
/// set_exported_global_fn!(module, "func", my_plugin_function);
///
/// engine.register_module("test", module);
/// engine.register_static_module("test", module.into());
///
/// assert_eq!(engine.eval::<i64>("func(21)")?, 42);
/// # Ok(())

View File

@ -257,7 +257,7 @@ mod multiple_fn_rename {
fn multiple_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::multiple_fn_rename::my_adds);
engine.load_package(m);
engine.register_global_module(m.into());
let output_array = engine.eval::<Array>(
r#"

View File

@ -41,18 +41,17 @@ The Rhai Scripting Language
2. [Indexers](rust/indexers.md)
3. [Disable Custom Types](rust/disable-custom.md)
4. [Printing Custom Types](rust/print-custom.md)
9. [Packages](rust/packages/index.md)
1. [Built-in Packages](rust/packages/builtin.md)
2. [Load a Plugin Module as a Package](rust/packages/plugin.md)
3. [Manually Create a Custom Package](rust/packages/create.md)
10. [Modules](rust/modules/index.md)
9. [Modules](rust/modules/index.md)
1. [Create from Rust](rust/modules/create.md)
2. [Create from AST](rust/modules/ast.md)
3. [Module Resolvers](rust/modules/resolvers.md)
1. [Custom Implementation](rust/modules/imp-resolver.md)
11. [Plugins](plugins/index.md)
1. [Custom Module Resolvers](rust/modules/imp-resolver.md)
10. [Plugins](plugins/index.md)
1. [Export a Rust Module](plugins/module.md)
2. [Export a Rust Function](plugins/function.md)
11. [Packages](rust/packages/index.md)
1. [Built-in Packages](rust/packages/builtin.md)
2. [Custom Packages](rust/packages/create.md)
5. [Rhai Language Reference](language/index.md)
1. [Comments](language/comments.md)
1. [Doc-Comments](language/doc-comments.md)
@ -128,18 +127,18 @@ The Rhai Scripting Language
7. [One Engine Instance Per Call](patterns/parallel.md)
8. [Scriptable Event Handler with State](patterns/events.md)
9. [Dynamic Constants Provider](patterns/dynamic-const.md)
9. [Advanced Topics](advanced.md)
1. [Capture Scope for Function Call](language/fn-capture.md)
2. [Low-Level API](rust/register-raw.md)
3. [Variable Resolver](engine/var.md)
4. [Use as DSL](engine/dsl.md)
9. [Advanced Topics](advanced.md)
10. [Capture Scope for Function Call](language/fn-capture.md)
11. [Low-Level API](rust/register-raw.md)
12. [Variable Resolver](engine/var.md)
13. [Use as DSL](engine/dsl.md)
1. [Disable Keywords and/or Operators](engine/disable.md)
2. [Custom Operators](engine/custom-op.md)
3. [Extending with Custom Syntax](engine/custom-syntax.md)
5. [Multiple Instantiation](patterns/multiple.md)
6. [Functions Metadata](engine/metadata/index.md)
1. [Generate Function Signatures](engine/metadata/gen_fn_sig.md)
2. [Export Metadata to JSON](engine/metadata/export_to_json.md)
14. [Multiple Instantiation](patterns/multiple.md)
15. [Functions Metadata](engine/metadata/index.md)
4. [Generate Function Signatures](engine/metadata/gen_fn_sig.md)
5. [Export Metadata to JSON](engine/metadata/export_to_json.md)
10. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md)
2. [Operators and Symbols](appendix/operators.md)

View File

@ -1,5 +1,5 @@
{
"version": "0.19.8",
"version": "0.19.9",
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
"rootUrl": "",

View File

@ -4,24 +4,25 @@ Export Functions Metadata to JSON
{{#include ../../links.md}}
`Engine::gen_fn_metadata_to_json`
--------------------------------
`Engine::gen_fn_metadata_to_json`<br/>`Engine::gen_fn_metadata_with_ast_to_json`
------------------------------------------------------------------------------
As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` exports the full list
of [functions metadata] in JSON format.
As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` and the corresponding
`Engine::gen_fn_metadata_with_ast_to_json` export the full list of [functions metadata]
in JSON format.
The [`metadata`] feature must be used to turn on this method, which requires
The [`metadata`] feature must be used to turn on this API, which requires
the [`serde_json`](https://crates.io/crates/serde_json) crate.
### Sources
Functions from the following sources are included:
1) Script-defined functions in an [`AST`], if provided
1) Script-defined functions in an [`AST`] (for `Engine::gen_fn_metadata_with_ast_to_json`)
2) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API
3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules registered via
[`Engine::register_module`]({{rootUrl}}/rust/modules/create.md)
4) Native Rust functions in registered [packages] (optional)
3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in static modules
registered via `Engine::register_static_module`
4) Native Rust functions in global modules registered via `Engine::register_global_module` (optional)
Notice that if a function has been [overloaded][function overloading], only the overriding function's
metadata is included.

View File

@ -17,9 +17,9 @@ As part of a _reflections_ API, `Engine::gen_fn_signatures` returns a list of fu
Functions from the following sources are included, in order:
1) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API
2) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules registered via
[`Engine::register_module`]({{rootUrl}}/rust/modules/create.md)
3) Native Rust functions in registered [packages] (optional)
2) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules
registered via `Engine::register_static_module`.
3) Native Rust functions in global modules registered via `Engine::register_global_module` (optional)
Functions Metadata

View File

@ -1,6 +1,8 @@
Doc-Comments
============
{{#include ../links.md}}
Similar to Rust, comments starting with `///` (three slashes) or `/**` (two asterisks) are
_doc-comments_.
@ -56,7 +58,6 @@ This is consistent with popular comment block styles for C-like languages.
```
Using Doc-Comments
------------------

View File

@ -15,18 +15,20 @@ allow combining all functions in one [`AST`] into another, forming a new, unifie
In general, there are two types of _namespaces_ where functions are looked up:
| Namespace | How Many | Source | Lookup method | Sub-modules? | Variables? |
| --------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: |
| Global | One | 1) [`AST`] being evaluated<br/>2) `Engine::register_XXX` API<br/>3) [packages] loaded<br/>4) functions in [modules] loaded via `Engine::register_module` and marked _global_ | simple function name | ignored | ignored |
| Module | Many | [`Module`] | namespace-qualified function name | yes | yes |
| Namespace | How Many | Source | Lookup | Sub-modules? | Variables? |
| --------- | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | :----------: | :--------: |
| Global | One | 1) [`AST`] being evaluated<br/>2) `Engine::register_XXX` API<br/>3) global [modules] registered via `Engine::register_global_module`<br/>4) functions in static [modules] registered via `Engine::register_static_module` and marked _global_ | simple name | ignored | ignored |
| Module | Many | 1) [Module] registered via `Engine::register_static_module`<br/>2) [Module] loaded via [`import`] statement | namespace-qualified name | yes | yes |
Module Namespace
----------------
Module Namespaces
-----------------
There can be multiple module namespaces at any time during a script evaluation, loaded via the
There can be multiple module namespaces at any time during a script evaluation, usually loaded via the
[`import`] statement.
_Static_ module namespaces can also be registered into an [`Engine`] via `Engine::register_static_module`.
Functions and variables in module namespaces are isolated and encapsulated within their own environments.
They must be called or accessed in a _namespace-qualified_ manner.
@ -52,10 +54,11 @@ There is one _global_ namespace for every [`Engine`], which includes (in the fol
* All native Rust functions and iterators registered via the `Engine::register_XXX` API.
* All functions and iterators defined in [packages] that are loaded into the [`Engine`].
* All functions and iterators defined in global [modules] that are registered into the [`Engine`] via
`Engine::register_global_module`.
* Functions defined in [modules] loaded via `Engine::register_module` that are specifically marked
for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]).
* Functions defined in [modules] registered via `Engine::register_static_module` that are specifically
marked for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]).
Anywhere in a Rhai script, when a function call is made, the function is searched within the
global namespace, in the above search order.

View File

@ -172,7 +172,7 @@ Functions from the following sources are returned, in order:
1) Encapsulated script environment (e.g. when loading a [module] from a script file),
2) Current script,
3) [Modules] imported via the [`import`] statement (latest imports first),
4) [Modules] added via [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) (latest registrations first)
4) [Modules] added via [`Engine::register_static_module`]({{rootUrl}}/rust/modules/create.md) (latest registrations first)
The return value is an [array] of [object maps] (so `get_fn_metadata_list` is not available under
[`no_index`] or [`no_object`]), containing the following fields:

View File

@ -4,7 +4,7 @@ Export Variables, Functions and Sub-Modules in Module
{{#include ../../links.md}}
The easiest way to expose a package of functions as a self-contained [module] is to do it via a Rhai script itself.
The easiest way to expose a collection of functions as a self-contained [module] is to do it via a Rhai script itself.
See the section on [_Creating a Module from AST_]({{rootUrl}}/rust/modules/ast.md) for more details.

View File

@ -116,7 +116,7 @@ let mut engine = Engine::new();
// Load the module as the module namespace "MyEnum"
engine
.register_type_with_name::<MyEnum>("MyEnum")
.register_module("MyEnum", exported_module!(MyEnumModule));
.register_static_module("MyEnum", exported_module!(MyEnumModule).into());
```
With this API in place, working with enums feels almost the same as in Rust:

View File

@ -100,7 +100,7 @@ impl Handler {
// Register custom types and API's
engine
.register_type_with_name::<SomeType>("SomeType")
.load_package(exported_module!(SomeTypeAPI));
.register_global_module(exported_module!(SomeTypeAPI));
// Create a custom 'Scope' to hold state
let mut scope = Scope::new();

View File

@ -20,7 +20,8 @@ Usage Scenario
Key Concepts
------------
* Create a single instance of each standard [package] required. To duplicate `Engine::new`, create a [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md).
* Create a single instance of each standard [package] required.
To duplicate `Engine::new`, create a [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md).
* Gather up all common custom functions into a [custom package].
@ -29,10 +30,14 @@ Key Concepts
* Always use `Engine::new_raw` to create a [raw `Engine`], instead of `Engine::new` which is _much_ more expensive.
A [raw `Engine`] is _extremely_ cheap to create.
Loading the [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md) into a [raw `Engine`] via `Engine::load_package` is essentially the same as `Engine::new`.
But because packages are shared, loading an existing package is _much cheaper_ than registering all the functions one by one.
Registering the [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md) into a [raw `Engine`] via
`Engine::register_global_module` is essentially the same as `Engine::new`.
However, because packages are shared, using existing package is _much cheaper_ than
registering all the functions one by one.
* Load the required packages into the [raw `Engine`] via `Engine::load_package`, using `Package::get` to obtain a shared copy.
* Register the required packages with the [raw `Engine`] via `Engine::register_global_module`,
using `Package::as_shared_module` to obtain a shared [module].
Examples
@ -46,17 +51,17 @@ let std_pkg = StandardPackage::new();
let custom_pkg = MyCustomPackage::new();
let make_call = |x: i64| -> Result<(), Box<EvalAltResult>> {
// Create a raw Engine - extremely cheap.
// Create a raw Engine - extremely cheap
let mut engine = Engine::new_raw();
// Load packages - cheap.
engine.load_package(std_pkg.get());
engine.load_package(custom_pkg.get());
// Register packages as global modules - cheap
engine.register_global_module(std_pkg.as_shared_module());
engine.register_global_module(custom_pkg.as_shared_module());
// Create custom scope - cheap.
// Create custom scope - cheap
let mut scope = Scope::new();
// Push variable into scope - relatively cheap.
// Push variable into scope - relatively cheap
scope.push("x", x);
// Evaluate script.

View File

@ -146,7 +146,7 @@ pub mod bunny_api {
}
}
engine.load_package(exported_module!(bunny_api));
engine.register_global_module(exported_module!(bunny_api).into());
```
### Push Constant Command Object into Custom Scope

View File

@ -28,11 +28,11 @@ and is custom fit to each exported item.
All `pub` functions become registered functions, all `pub` constants become [module] constant variables,
and all sub-modules become Rhai sub-modules.
This Rust module can then either be loaded into an [`Engine`] as a normal [module] or
registered as a [package]. This is done by using the `exported_module!` macro.
This Rust module can then be registered into an [`Engine`] as a normal [module].
This is done via the `exported_module!` macro.
The macro `combine_with_exported_module!` can also be used to _combine_ all the functions
and variables into an existing module, _flattening_ the namespace - i.e. all sub-modules
The macro `combine_with_exported_module!` can be used to _combine_ all the functions
and variables into an existing [module], _flattening_ the namespace - i.e. all sub-modules
are eliminated and their contents promoted to the top level. This is typical for
developing [custom packages].
@ -42,7 +42,7 @@ use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
mod my_module {
// This constant will be registered as the constant variable 'MY_NUMBER'.
// Ignored when loaded as a package.
// Ignored when registered as a global module.
pub const MY_NUMBER: i64 = 42;
// This function will be registered as 'greet'.
@ -64,9 +64,9 @@ mod my_module {
42
}
// Sub-modules are ignored when the Module is loaded as a package.
// Sub-modules are ignored when the module is registered globally.
pub mod my_sub_module {
// This function is ignored when loaded as a package.
// This function is ignored when registered globally.
// Otherwise it is a valid registered function under a sub-module.
pub fn get_info() -> String {
"hello".to_string()
@ -78,7 +78,7 @@ mod my_module {
// This is currently a limitation of the plugin procedural macros.
#[cfg(feature = "advanced_functions")]
pub mod advanced {
// This function is ignored when loaded as a package.
// This function is ignored when registered globally.
// Otherwise it is a valid registered function under a sub-module
// which only exists when the 'advanced_functions' feature is used.
pub fn advanced_calc(input: i64) -> i64 {
@ -88,10 +88,10 @@ mod my_module {
}
```
### Use `Engine::load_package`
### Use `Engine::register_global_module`
The simplest way to load this into an [`Engine`] is to first use the `exported_module!` macro
to turn it into a normal Rhai [module], then use the `Engine::load_package` method on it:
The simplest way to register this into an [`Engine`] is to first use the `exported_module!` macro
to turn it into a normal Rhai [module], then use the `Engine::register_global_module` method on it:
```rust
fn main() {
@ -100,13 +100,13 @@ fn main() {
// The macro call creates a Rhai module from the plugin module.
let module = exported_module!(my_module);
// A module can simply be loaded, registering all public functions.
engine.load_package(module);
// A module can simply be registered into the global namespace.
engine.register_global_module(module.into());
}
```
The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`)
are automatically registered into the [`Engine`] when `Engine::load_package` is called.
are automatically registered into the [`Engine`] when `Engine::register_global_module` is called.
```rust
let x = greet("world");
@ -123,12 +123,14 @@ x == 43;
```
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.
### Use `Engine::register_module`
Variables as well as sub-modules are **ignored**.
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::register_module` method on it:
### Use `Engine::register_static_module`
Another simple way to register this into an [`Engine`] is, again, to use the `exported_module!` macro
to turn it into a normal Rhai [module], then use the `Engine::register_static_module` method on it:
```rust
fn main() {
@ -137,13 +139,13 @@ fn main() {
// The macro call creates a Rhai module from the plugin module.
let module = exported_module!(my_module);
// A module can simply be loaded as a globally-available module.
engine.register_module("service", module);
// A module can simply be registered as a static module namespace.
engine.register_static_module("service", module.into());
}
```
The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`),
plus the constant `MY_NUMBER`, are automatically loaded under the module namespace `service`:
plus the constant `MY_NUMBER`, are automatically registered under the module namespace `service`:
```rust
let x = service::greet("world");
@ -178,14 +180,14 @@ x.increment();
x == 43;
```
### Use as loadable `Module`
### Use Dynamically
Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a
[module resolver] must be used to serve the module, and the module is loaded via `import` statements.
See the [module] section for more information.
### Use as custom package
### Combine into Custom Package
Finally the plugin module can also be used to develop a [custom package],
using `combine_with_exported_module!`:

View File

@ -19,14 +19,16 @@ Manually creating a [module] is possible via the `Module` API.
For the complete `Module` API, refer to the [documentation](https://docs.rs/rhai/{{version}}/rhai/struct.Module.html) online.
Make the `Module` Available to the `Engine`
------------------------------------------
Use Case 1 - Make the `Module` Globally Available
------------------------------------------------
`Engine::load_package` supports loading a [module] as a [package].
`Engine::register_global_module` registers a shared [module] into the _global_ namespace.
Since it acts as a [package], all functions will be registered into the _global_ namespace
and can be accessed without _namespace qualifiers_. This is by far the easiest way to expose
a module's functionalities to Rhai.
All [functions] and [type iterators] can be accessed without _namespace qualifiers_.
Variables and sub-modules are **ignored**.
This is by far the easiest way to expose a module's functionalities to Rhai.
```rust
use rhai::{Engine, Module};
@ -40,18 +42,18 @@ let hash = module.set_fn_1("inc", |x: i64| Ok(x+1));
// 'set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: i64", "i64"]);
// Load the module into the Engine as a new package.
// Register the module into the global namespace of the Engine.
let mut engine = Engine::new();
engine.load_package(module);
engine.register_global_module(module.into());
engine.eval::<i64>("inc(41)")? == 42; // no need to import module
```
Make the `Module` a Global Module
------------------------------------
Use Case 2 - Make the `Module` a Static Module
---------------------------------------------
`Engine::register_module` loads a [module] and makes it available globally under a specific namespace.
`Engine::register_static_module` registers a [module] and under a specific module namespace.
```rust
use rhai::{Engine, Module};
@ -65,17 +67,20 @@ let hash = module.set_fn_1("inc", |x: i64| Ok(x+1));
// 'set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: i64", "i64"]);
// Load the module into the Engine as a sub-module named 'calc'
// Register the module into the Engine as a static module namespace 'calc'
let mut engine = Engine::new();
engine.register_module("calc", module);
engine.register_static_module("calc", module.into());
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
```
`Module::set_fn_XXX_mut` can expose functions (usually _methods_) in the module
to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected.
### Expose Functions to the Global Namespace
[Type iterators], because of their special nature, are always exposed to the _global_ namespace.
`Module::set_fn_mut` and `Module::set_fn_XXX_mut` can optionally expose functions (usually _methods_)
in the module to the _global_ namespace, so [getters/setters] and [indexers] for [custom types]
can work as expected.
[Type iterators], because of their special nature, are _always_ exposed to the _global_ namespace.
```rust
use rhai::{Engine, Module, FnNamespace};
@ -89,17 +94,17 @@ let hash = module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x+1)
// 'set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: &mut i64", "i64"]);
// Load the module into the Engine as a sub-module named 'calc'
// Register the module into the Engine as a static module namespace 'calc'
let mut engine = Engine::new();
engine.register_module("calc", module);
engine.register_static_module("calc", module.into());
// 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
-------------------------------------
Use Case 3 - Make the `Module` Dynamically Loadable
--------------------------------------------------
In order to dynamically load a custom module, there must be a [module resolver] which serves
the module when loaded via `import` statements.

View File

@ -3,12 +3,27 @@ Modules
{{#include ../../links.md}}
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_.
Modules can be disabled via the [`no_module`] feature.
Rhai allows organizing functionalities (functions, both Rust-based or script-based, and variables)
into independent _modules_. Modules can be disabled via the [`no_module`] feature.
A module is of the type `Module` and holds a collection of functions, variables, [type iterators] and sub-modules.
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions
and variables defined by that script.
A module is of the type `Module` and holds a collection of functions, variables,
[type iterators] and sub-modules.
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together
with the functions and variables defined by that script.
Other scripts can then load this module and use the functions and variables exported
as if they were defined inside the same script.
Alternatively, modules can be registered directly into an [`Engine`] and made available
to scripts either globally or under individual static module [_namespaces_][function namespaces].
Usage Patterns
--------------
| Usage | API | Lookup | Sub-modules? | Variables? |
| -------------- | :-------------------------------: | :----------------------: | :----------: | :--------: |
| Global module | `Engine:: register_global_module` | simple name | ignored | ignored |
| Static module | `Engine:: register_static_module` | namespace-qualified name | yes | yes |
| Dynamic module | [`import`] statement | namespace-qualified name | yes | yes |

View File

@ -23,10 +23,10 @@ Built-In Packages
| `StandardPackage` | standard library (default for `Engine::new`) | no | yes |
Load the `CorePackage`
---------------------
`CorePackage`
-------------
If only minimal functionalities is required, load the `CorePackage` instead:
If only minimal functionalities are required, register the `CorePackage` instead:
```rust
use rhai::Engine;
@ -35,5 +35,6 @@ use rhai::packages::{Package, CorePackage};
let mut engine = Engine::new_raw();
let package = CorePackage::new();
engine.load_package(package.get());
// Register the package into the 'Engine' by converting it into a shared module.
engine.register_global_module(package.as_shared_module());
```

View File

@ -1,35 +1,33 @@
Manually Create a Custom Package
===============================
Create a Custom Package
=======================
{{#include ../../links.md}}
Sometimes specific functionalities are needed, so custom packages can be created.
A custom package is a convenient means to gather up a number of functions for later use.
An [`Engine`] only needs to `Engine::load_package` the custom package once to gain access
to the entire set of functions within.
The macro `def_package!` can be used to create a custom [package].
Loading a package into an [`Engine`] is functionally equivalent to calling `Engine::register_fn` etc.
on _each_ of the functions inside the package. But because packages are _shared_, loading an existing
package is _much_ cheaper than registering all the functions one by one.
The macro `rhai::def_package!` can be used to create a new custom package.
A custom package can aggregate many other packages into a single self-contained unit.
More functions can be added on top of others.
Macro Parameters
---------------
`def_package!`
--------------
`def_package!(root:package_name:description, variable, block)`
> `def_package!(root:package_name:description, variable, block)`
* `root` - root namespace, usually `"rhai"`.
where:
* `package_name` - name of the package, usually ending in `Package`.
| Parameter | Description |
| :------------: | ----------------------------------------------------------------------------------------------- |
| `root` | root namespace, usually `rhai` |
| `package_name` | name of the package, usually ending in `...Package` |
| `description` | doc-comment for the package |
| `variable` | a variable name holding a reference to the [module] (`&mut Module`) that is to form the package |
| `block` | a code block that initializes the package |
* `description` - doc comment for the package.
* `variable` - a variable name holding a reference to the [module] that is to form the package.
* `block` - a code block that initializes the package.
Examples
--------
```rust
// Import necessary types and traits.
@ -43,7 +41,7 @@ use rhai::{
// Define the package 'MyPackage'.
def_package!(rhai:MyPackage:"My own personal super package", module, {
// Aggregate existing packages simply by calling 'init' on each.
// Aggregate other packages simply by calling 'init' on each.
ArithmeticPackage::init(module);
LogicPackage::init(module);
BasicArrayPackage::init(module);
@ -64,14 +62,14 @@ def_package!(rhai:MyPackage:"My own personal super package", module, {
Create a Custom Package from a Plugin Module
-------------------------------------------
By far the easiest way to create a custom module is to call `rhai::plugin::combine_with_exported_module!`
from within `rhai::def_package!` which simply merges in all the functions defined within a [plugin module].
By far the easiest way to create a custom module is to call `plugin::combine_with_exported_module!`
from within `def_package!` which simply merges in all the functions defined within a [plugin module].
In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage`, are implemented.
Because of the specific requirements of a [package], all sub-modules are _flattened_
(i.e. all functions defined within sub-modules are pulled up and registered at the top level instead)
and so there will not be any sub-modules added to the package.
Due to specific requirements of a [package], `plugin::combine_with_exported_module!`
_flattens_ all sub-modules (i.e. all functions and [type iterators] defined within sub-modules
are pulled up to the top level instead) and so there will not be any sub-modules added to the package.
Variables in the [plugin module] are ignored.
@ -107,7 +105,7 @@ mod my_module {
// Define the package 'MyPackage'.
def_package!(rhai:MyPackage:"My own personal super package", module, {
// Aggregate existing packages simply by calling 'init' on each.
// Aggregate other packages simply by calling 'init' on each.
ArithmeticPackage::init(module);
LogicPackage::init(module);
BasicArrayPackage::init(module);

View File

@ -3,61 +3,56 @@ Packages
{{#include ../../links.md}}
Standard built-in Rhai features are provided in various _packages_ that can be loaded via a call to `Engine::load_package`.
The built-in library of Rhai is provided as various _packages_ that can be
turned into _shared_ [modules], which in turn can be registered into the
_global namespace_ of an [`Engine`] via `Engine::register_global_module`.
Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` must be loaded in order for
packages to be used.
Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package`
must be loaded in order for packages to be used.
### Packages _are_ Modules
Internally, a _package_ is a _newtype_ wrapping a pre-defined [module],
with some conveniences to make it easier to define and use as a standard
_library_ for an [`Engine`].
Packages typically contain Rust functions that are callable within a Rhai script.
All functions registered in a package is loaded under the _global namespace_
All _top-level_ functions in a package are available under the _global namespace_
(i.e. they're available without namespace qualifiers).
Once a package is created (e.g. via `Package::new`), it can be _shared_ (via `Package::get`)
among multiple instances of [`Engine`], even across threads (under [`sync`]).
Therefore, a package only has to be created _once_.
Sub-modules and variables are ignored in packages.
Share a Package Among Multiple `Engine`'s
----------------------------------------
`Engine::register_global_module` and `Engine::register_static_module` both require _shared_ [modules].
Once a package is created (e.g. via `Package::new`), it can create _shared_ [modules]
(via `Package::as_shared_module`) and register them into multiple instances of [`Engine`],
even across threads (under the [`sync`] feature).
Therefore, a package only has to be created _once_ and essentially shared among multiple
[`Engine`] instances. This is particular useful when spawning large number of [raw `Engine`'s][raw `Engine`].
```rust
use rhai::Engine;
use rhai::packages::Package // load the 'Package' trait to use packages
use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic)
let mut engine = Engine::new_raw(); // create a 'raw' Engine
let package = CorePackage::new(); // create a package - can be shared among multiple `Engine` instances
// Create a package - can be shared among multiple 'Engine' instances
let package = CorePackage::new();
engine.load_package(package.get()); // load the package manually. 'get' returns a reference to the shared package
let mut engines_collection: Vec<Engine> = Vec::new();
// Create 100 'raw' Engines
for _ in 0..100 {
let mut engine = Engine::new_raw();
// Register the package into the global namespace.
// 'Package::as_shared_module' converts the package into a shared module.
engine.register_global_module(package.as_shared_module());
engines_collection.push(engine);
}
```
Difference Between a Package and a Module
----------------------------------------
Packages are internally implemented as [modules], so they share a lot of behavior and characteristics.
The main difference is that a package loads under the _global_ namespace, while a module loads under its own
namespace alias specified in an [`import`] statement (see also [modules]).
A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with
the `import` statement).
Sub-modules in a package are _flattened_, meaning that functions from them must be pulled up to the root level.
In other words, there can be no sub-modules in a package, and everything should reside in one, flat namespace.
Only functions matter for a package. Constant variables registered in a package are ignored.
Load a Module as a Package
--------------------------
Stand-alone [modules] can be loaded directly into an [`Engine`] as a package via the same `Engine::load_package` API.
```rust
let mut module = Module::new();
:
// add functions into module
:
engine.load_package(module);
```
[Modules], however, are not _shared_, so use a [custom package] if it must be shared among multiple
instances of [`Engine`].

View File

@ -1,42 +0,0 @@
Load a Plugin Module as a Package
================================
{{#include ../../links.md}}
[Plugin modules] can be loaded as a package just like a normal [module]
via the `exported_module!` macro.
```rust
use rhai::Engine;
use rhai::plugin::*;
// Define plugin module.
#[export_module]
mod my_module {
pub fn greet(name: &str) -> String {
format!("hello, {}!", name)
}
pub fn get_num() -> i64 {
42
}
}
fn main() {
let mut engine = Engine::new();
// Create plugin module.
let module = exported_module!(my_module);
// Make the 'greet' and 'get_num' functions available to scripts.
engine.load_package(module);
}
```
Share a Package Among `Engine`s
------------------------------
Loading a [module] via `Engine::load_package` consumes the module and it cannot be used again.
If the functions are needed for multiple instances of [`Engine`], create a [custom package] from the
[plugin module], which can then be shared with `Package::get`.

View File

@ -49,6 +49,6 @@ Use a Raw [`Engine`]
A _raw_ engine supports, out of the box, only a very [restricted set]({{rootUrl}}/engine/raw.md#built-in-operators)
of basic arithmetic and logical operators.
Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint.
Selectively include other necessary functionalities by picking specific [packages] to minimize the footprint.
Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once.
Packages are shared (even across threads via the [`sync`] feature), so they only have to be created once.

View File

@ -228,7 +228,7 @@ impl AST {
/// _(INTERNALS)_ Get the statements.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[deprecated(note = "this method is volatile and may change")]
#[deprecated = "this method is volatile and may change"]
#[inline(always)]
pub fn statements(&self) -> &[Stmt] {
&self.statements
@ -255,7 +255,7 @@ impl AST {
/// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[deprecated(note = "this method is volatile and may change")]
#[deprecated = "this method is volatile and may change"]
#[inline(always)]
pub fn lib(&self) -> &Module {
&self.functions
@ -640,25 +640,6 @@ impl AsRef<Module> for AST {
}
}
/// _(INTERNALS)_ An identifier containing a [string][String] name and a [position][Position].
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Ident {
pub name: String,
pub pos: Position,
}
impl Ident {
/// Create a new `Identifier`.
pub fn new(name: String, pos: Position) -> Self {
Self { name, pos }
}
}
/// _(INTERNALS)_ An identifier containing an [immutable string][ImmutableString] name and a [position][Position].
/// Exported under the `internals` feature only.
///
@ -666,30 +647,13 @@ impl Ident {
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct IdentX {
pub struct Ident {
/// Identifier name.
pub name: ImmutableString,
/// Declaration position.
pub pos: Position,
}
impl From<Ident> for IdentX {
fn from(value: Ident) -> Self {
Self {
name: value.name.into(),
pos: value.pos,
}
}
}
impl IdentX {
/// Create a new `Identifier`.
pub fn new(name: impl Into<ImmutableString>, pos: Position) -> Self {
Self {
name: name.into(),
pos,
}
}
}
/// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement.
/// Exported under the `internals` feature only.
///
@ -729,9 +693,9 @@ pub enum Stmt {
/// `for` id `in` expr `{` stmt `}`
For(Expr, Box<(String, Stmt)>, Position),
/// \[`export`\] `let` id `=` expr
Let(Box<IdentX>, Option<Expr>, bool, Position),
Let(Box<Ident>, Option<Expr>, bool, Position),
/// \[`export`\] `const` id `=` expr
Const(Box<IdentX>, Option<Expr>, bool, Position),
Const(Box<Ident>, Option<Expr>, bool, Position),
/// expr op`=` expr
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
/// `{` stmt`;` ... `}`
@ -748,13 +712,13 @@ pub enum Stmt {
Return((ReturnType, Position), Option<Expr>, Position),
/// `import` expr `as` var
#[cfg(not(feature = "no_module"))]
Import(Expr, Option<Box<IdentX>>, Position),
Import(Expr, Option<Box<Ident>>, Position),
/// `export` var `as` var `,` ...
#[cfg(not(feature = "no_module"))]
Export(Vec<(IdentX, Option<IdentX>)>, Position),
Export(Vec<(Ident, Option<Ident>)>, Position),
/// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))]
Share(IdentX),
Share(Ident),
}
impl Default for Stmt {
@ -996,13 +960,13 @@ pub enum Expr {
/// [ expr, ... ]
Array(Box<StaticVec<Expr>>, Position),
/// #{ name:expr, ... }
Map(Box<StaticVec<(IdentX, Expr)>>, Position),
Map(Box<StaticVec<(Ident, Expr)>>, Position),
/// ()
Unit(Position),
/// Variable access - (optional index, optional modules, hash, variable name)
Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, IdentX)>),
Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, Ident)>),
/// Property access - (getter, setter), prop
Property(Box<((ImmutableString, ImmutableString), IdentX)>),
Property(Box<((ImmutableString, ImmutableString), Ident)>),
/// { [statement][Stmt] }
Stmt(Box<StaticVec<Stmt>>, Position),
/// Wrapped [expression][`Expr`] - should not be optimized away.

View File

@ -1,6 +1,6 @@
//! Main module defining the script evaluation [`Engine`].
use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt};
use crate::ast::{Expr, FnCallExpr, Ident, ReturnType, Stmt};
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
use crate::fn_call::run_builtin_op_assignment;
use crate::fn_native::{
@ -9,7 +9,7 @@ use crate::fn_native::{
};
use crate::module::NamespaceRef;
use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackagesCollection, StandardPackage};
use crate::packages::{Package, StandardPackage};
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::stdlib::{
any::{type_name, TypeId},
@ -55,74 +55,60 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
// the module name will live beyond the AST of the eval script text.
// The best we can do is a shared reference.
#[derive(Debug, Clone, Default)]
pub struct Imports(Option<StaticVec<(ImmutableString, Shared<Module>)>>);
pub struct Imports(StaticVec<(ImmutableString, Shared<Module>)>);
impl Imports {
/// Get the length of this stack of imported [modules][Module].
pub fn len(&self) -> usize {
self.0.as_ref().map_or(0, StaticVec::len)
self.0.len()
}
/// Is this stack of imported [modules][Module] empty?
pub fn is_empty(&self) -> bool {
self.0.as_ref().map_or(true, StaticVec::is_empty)
self.0.is_empty()
}
/// Get the imported [modules][Module] at a particular index.
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
self.0
.as_ref()
.and_then(|x| x.get(index))
.map(|(_, m)| m)
.cloned()
self.0.get(index).map(|(_, m)| m).cloned()
}
/// Get the index of an imported [modules][Module] by name.
pub fn find(&self, name: &str) -> Option<usize> {
self.0.as_ref().and_then(|x| {
x.iter()
.enumerate()
.rev()
.find(|(_, (key, _))| key.as_str() == name)
.map(|(index, _)| index)
})
self.0
.iter()
.enumerate()
.rev()
.find(|(_, (key, _))| key.as_str() == name)
.map(|(index, _)| index)
}
/// Push an imported [modules][Module] onto the stack.
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
if self.0.is_none() {
self.0 = Some(Default::default());
}
self.0.as_mut().unwrap().push((name.into(), module.into()));
self.0.push((name.into(), module.into()));
}
/// Truncate the stack of imported [modules][Module] to a particular length.
pub fn truncate(&mut self, size: usize) {
if self.0.is_some() {
self.0.as_mut().unwrap().truncate(size);
}
self.0.truncate(size);
}
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)]
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a str, &'a Module)> + 'a {
self.0.iter().flat_map(|lib| {
lib.iter()
.rev()
.map(|(name, module)| (name.as_str(), module.as_ref()))
})
self.0
.iter()
.rev()
.map(|(name, module)| (name.as_str(), module.as_ref()))
}
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)]
pub(crate) fn iter_raw<'a>(
&'a self,
) -> impl Iterator<Item = (&'a ImmutableString, &'a Shared<Module>)> + 'a {
self.0
.iter()
.flat_map(|lib| lib.iter().rev().map(|(n, m)| (n, m)))
self.0.iter().rev().map(|(n, m)| (n, m))
}
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
self.0.into_iter().flat_map(|lib| lib.into_iter().rev())
self.0.into_iter().rev()
}
/// Add a stream of imported [modules][Module].
pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
self.0.as_mut().unwrap().extend(stream)
self.0.extend(stream)
}
/// Does the specified function hash key exist in this stack of imported [modules][Module]?
#[allow(dead_code)]
@ -130,9 +116,7 @@ impl Imports {
if hash == 0 {
false
} else {
self.0.as_ref().map_or(false, |x| {
x.iter().any(|(_, m)| m.contains_qualified_fn(hash))
})
self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
}
}
/// Get specified function via its hash key.
@ -141,22 +125,22 @@ impl Imports {
None
} else {
self.0
.as_ref()
.and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_fn(hash)))
.iter()
.rev()
.find_map(|(_, m)| m.get_qualified_fn(hash))
}
}
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of imported [modules][Module]?
#[allow(dead_code)]
pub fn contains_iter(&self, id: TypeId) -> bool {
self.0.as_ref().map_or(false, |x| {
x.iter().any(|(_, m)| m.contains_qualified_iter(id))
})
self.0.iter().any(|(_, m)| m.contains_qualified_iter(id))
}
/// Get the specified [`TypeId`][std::any::TypeId] iterator.
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.0
.as_ref()
.and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_iter(id)))
.iter()
.rev()
.find_map(|(_, m)| m.get_qualified_iter(id))
}
}
@ -629,8 +613,8 @@ pub struct Engine {
/// A module containing all functions directly loaded into the Engine.
pub(crate) global_namespace: Module,
/// A collection of all library packages loaded into the Engine.
pub(crate) packages: PackagesCollection,
/// A collection of all modules loaded into the global namespace of the Engine.
pub(crate) global_modules: StaticVec<Shared<Module>>,
/// A collection of all sub-modules directly loaded into the Engine.
pub(crate) global_sub_modules: Imports,
@ -734,7 +718,7 @@ pub fn search_imports(
state: &mut State,
namespace: &NamespaceRef,
) -> Result<Shared<Module>, Box<EvalAltResult>> {
let IdentX { name: root, pos } = &namespace[0];
let Ident { name: root, pos } = &namespace[0];
// Qualified - check if the root module is directly indexed
let index = if state.always_search {
@ -761,8 +745,8 @@ impl Engine {
let mut engine = Self {
id: Default::default(),
packages: Default::default(),
global_namespace: Default::default(),
global_modules: Default::default(),
global_sub_modules: Default::default(),
#[cfg(not(feature = "no_module"))]
@ -814,20 +798,20 @@ impl Engine {
disable_doc_comments: false,
};
engine.load_package(StandardPackage::new().get());
engine.register_global_module(StandardPackage::new().as_shared_module());
engine
}
/// Create a new [`Engine`] with minimal built-in functions.
/// Use the [`load_package`][Engine::load_package] method to load additional packages of functions.
/// Use the [`register_global_module`][Engine::register_global_module] method to load additional packages of functions.
#[inline]
pub fn new_raw() -> Self {
Self {
id: Default::default(),
packages: Default::default(),
global_namespace: Default::default(),
global_modules: Default::default(),
global_sub_modules: Default::default(),
#[cfg(not(feature = "no_module"))]
@ -884,7 +868,7 @@ impl Engine {
match expr {
Expr::Variable(v) => match v.as_ref() {
// Qualified variable
(_, Some(modules), hash_var, IdentX { name, pos }) => {
(_, Some(modules), hash_var, Ident { name, pos }) => {
let module = search_imports(mods, state, modules)?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err {
@ -918,7 +902,7 @@ impl Engine {
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr,
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
let (index, _, _, IdentX { name, pos }) = match expr {
let (index, _, _, Ident { name, pos }) = match expr {
Expr::Variable(v) => v.as_ref(),
_ => unreachable!(),
};
@ -1115,7 +1099,7 @@ impl Engine {
Expr::FnCall(_, _) => unreachable!(),
// {xxx:map}.id = ???
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
let IdentX { name, pos } = &x.1;
let Ident { name, pos } = &x.1;
let index = name.clone().into();
let mut val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
@ -1128,7 +1112,7 @@ impl Engine {
}
// {xxx:map}.id
Expr::Property(x) if target_val.is::<Map>() => {
let IdentX { name, pos } = &x.1;
let Ident { name, pos } = &x.1;
let index = name.clone().into();
let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
@ -1138,7 +1122,7 @@ impl Engine {
}
// xxx.id = ???
Expr::Property(x) if new_val.is_some() => {
let ((_, setter), IdentX { pos, .. }) = x.as_ref();
let ((_, setter), Ident { pos, .. }) = x.as_ref();
let mut new_val = new_val;
let mut args = [target_val, &mut new_val.as_mut().unwrap().0];
self.exec_fn_call(
@ -1150,7 +1134,7 @@ impl Engine {
}
// xxx.id
Expr::Property(x) => {
let ((getter, _), IdentX { pos, .. }) = x.as_ref();
let ((getter, _), Ident { pos, .. }) = x.as_ref();
let mut args = [target_val];
self.exec_fn_call(
mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos,
@ -1163,7 +1147,7 @@ impl Engine {
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
let mut val = match &x.lhs {
Expr::Property(p) => {
let IdentX { name, pos } = &p.1;
let Ident { name, pos } = &p.1;
let index = name.clone().into();
self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, true,
@ -1204,7 +1188,7 @@ impl Engine {
match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => {
let ((getter, setter), IdentX { pos, .. }) = p.as_ref();
let ((getter, setter), Ident { pos, .. }) = p.as_ref();
let arg_values = &mut [target_val, &mut Default::default()];
let args = &mut arg_values[..1];
let (mut val, updated) = self
@ -1319,7 +1303,7 @@ impl Engine {
match lhs {
// id.??? or id[???]
Expr::Variable(x) => {
let IdentX {
let Ident {
name: var_name,
pos: var_pos,
} = &x.3;
@ -1675,7 +1659,7 @@ impl Engine {
if target.is::<Map>() {
// map.prop - point directly to the item
let (_, IdentX { name, pos }) = p.as_ref();
let (_, Ident { name, pos }) = p.as_ref();
let idx = name.clone().into();
if target.is_shared() || target.is_value() {
@ -1693,7 +1677,7 @@ impl Engine {
.map(|v| (v, *pos))
} else {
// var.prop - call property getter
let ((getter, _), IdentX { pos, .. }) = p.as_ref();
let ((getter, _), Ident { pos, .. }) = p.as_ref();
let mut args = [target.as_mut()];
self.exec_fn_call(
mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos,
@ -1781,7 +1765,7 @@ impl Engine {
Expr::Map(x, _) => {
let mut map =
Map::with_capacity(crate::stdlib::cmp::max(TYPICAL_MAP_SIZE, x.len()));
for (IdentX { name: key, .. }, expr) in x.as_ref() {
for (Ident { name: key, .. }, expr) in x.as_ref() {
map.insert(
key.clone(),
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
@ -1987,7 +1971,11 @@ impl Engine {
match self
.global_namespace
.get_fn(hash_fn, false)
.or_else(|| self.packages.get_fn(hash_fn))
.or_else(|| {
self.global_modules
.iter()
.find_map(|m| m.get_fn(hash_fn, false))
})
.or_else(|| mods.get_fn(hash_fn))
{
// op= function registered as method
@ -2196,7 +2184,11 @@ impl Engine {
let func = self
.global_namespace
.get_iter(iter_type)
.or_else(|| self.packages.get_iter(iter_type))
.or_else(|| {
self.global_modules
.iter()
.find_map(|m| m.get_iter(iter_type))
})
.or_else(|| mods.get_iter(iter_type));
if let Some(func) = func {
@ -2272,9 +2264,9 @@ impl Engine {
if let Some(Ident { name, .. }) = var_def {
let var_name: Cow<'_, str> = if state.is_global() {
name.clone().into()
name.to_string().into()
} else {
unsafe_cast_var_name_to_lifetime(name).into()
unsafe_cast_var_name_to_lifetime(&name).into()
};
scope.push(var_name, value);
}
@ -2410,7 +2402,7 @@ impl Engine {
// Export statement
#[cfg(not(feature = "no_module"))]
Stmt::Export(list, _) => {
for (IdentX { name, pos: id_pos }, rename) in list.iter() {
for (Ident { name, pos: id_pos }, rename) in list.iter() {
// Mark scope variables as public
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);

View File

@ -14,8 +14,8 @@ use crate::stdlib::{
};
use crate::utils::get_hasher;
use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext,
ParseError, Position, AST,
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
ParseError, Position, Shared, AST,
};
#[cfg(not(feature = "no_index"))]
@ -48,7 +48,7 @@ impl Engine {
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated(note = "this function is volatile and may change")]
#[deprecated = "this function is volatile and may change"]
#[inline(always)]
pub fn register_raw_fn<T: Variant + Clone>(
&mut self,
@ -723,7 +723,33 @@ impl Engine {
self.register_indexer_get(getter)
.register_indexer_set(setter)
}
/// Register a [`Module`][crate::Module] as a fixed module namespace with the [`Engine`].
/// Register a shared [`Module`][crate::Module] into the global namespace of [`Engine`].
///
/// All functions and type iterators are automatically available to scripts without namespace qualifications.
///
/// Sub-modules and variables are **ignored**.
///
/// When searching for functions, modules loaded later are preferred.
/// In other words, loaded modules are searched in reverse order.
#[inline(always)]
pub fn register_global_module(&mut self, module: Shared<Module>) -> &mut Self {
// Insert the module into the front
self.global_modules.insert(0, module);
self
}
/// Register a shared [`Module`][crate::Module] into the global namespace of [`Engine`].
///
/// ## Deprecated
///
/// Use `register_global_module` instead.
#[inline(always)]
#[deprecated = "use `register_global_module` instead"]
pub fn load_package(&mut self, module: impl Into<Shared<Module>>) -> &mut Self {
self.register_global_module(module.into())
}
/// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`].
///
/// Functions marked `FnNamespace::Global` and type iterators are exposed to scripts without namespace qualifications.
///
/// # Example
///
@ -738,20 +764,18 @@ impl Engine {
/// module.set_fn_1("calc", |x: i64| Ok(x + 1));
///
/// // Register the module as a fixed sub-module
/// engine.register_module("CalcService", module);
/// engine.register_static_module("CalcService", module.into());
///
/// assert_eq!(engine.eval::<i64>("CalcService::calc(41)")?, 42);
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_module"))]
pub fn register_module(
pub fn register_static_module(
&mut self,
name: impl Into<crate::ImmutableString>,
module: impl Into<crate::Shared<crate::Module>>,
module: Shared<Module>,
) -> &mut Self {
let module = module.into();
if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module);
@ -762,6 +786,21 @@ impl Engine {
}
self
}
/// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`].
///
/// ## Deprecated
///
/// Use `register_static_module` instead.
#[cfg(not(feature = "no_module"))]
#[inline(always)]
#[deprecated = "use `register_static_module` instead"]
pub fn register_module(
&mut self,
name: impl Into<crate::ImmutableString>,
module: impl Into<Shared<Module>>,
) -> &mut Self {
self.register_static_module(name, module.into())
}
/// Compile a string into an [`AST`], which can be used later for evaluation.
///
/// # Example
@ -1681,7 +1720,11 @@ impl Engine {
});
if include_packages {
signatures.extend(self.packages.gen_fn_signatures());
signatures.extend(
self.global_modules
.iter()
.flat_map(|m| m.gen_fn_signatures()),
);
}
signatures

View File

@ -1,6 +1,5 @@
//! Configuration settings for [`Engine`].
use crate::packages::PackageLibrary;
use crate::stdlib::{format, string::String};
use crate::token::{is_valid_identifier, Token};
use crate::Engine;
@ -9,16 +8,6 @@ use crate::Engine;
use crate::stdlib::boxed::Box;
impl Engine {
/// Load a new package into the [`Engine`].
/// A simple [`Module`][crate::Module] is automatically converted into a package.
///
/// When searching for functions, packages loaded later are preferred.
/// In other words, loaded packages are searched in reverse order.
#[inline(always)]
pub fn load_package(&mut self, package: impl Into<PackageLibrary>) -> &mut Self {
self.packages.add(package.into());
self
}
/// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation.
///
/// Not available under the `no_optimize` feature.

View File

@ -184,7 +184,11 @@ impl Engine {
let f = self
.global_namespace
.get_fn(hash_fn, pub_only)
.or_else(|| self.packages.get_fn(hash_fn))
.or_else(|| {
self.global_modules
.iter()
.find_map(|m| m.get_fn(hash_fn, false))
})
.or_else(|| mods.get_fn(hash_fn));
state.functions_cache.insert(hash_fn, f.cloned());
@ -460,8 +464,8 @@ impl Engine {
//|| (hash_script != 0 && self.global_namespace.contains_fn(hash_script, pub_only))
|| self.global_namespace.contains_fn(hash_fn, false)
// Then check packages
|| (hash_script != 0 && self.packages.contains_fn(hash_script))
|| self.packages.contains_fn(hash_fn)
|| (hash_script != 0 && self.global_modules.iter().any(|m| m.contains_fn(hash_script, false)))
|| self.global_modules.iter().any(|m| m.contains_fn(hash_fn, false))
// Then check imported modules
|| (hash_script != 0 && mods.map(|m| m.contains_fn(hash_script)).unwrap_or(false))
|| mods.map(|m| m.contains_fn(hash_fn)).unwrap_or(false)
@ -541,7 +545,12 @@ impl Engine {
.map(|f| (f, m.id_raw().clone()))
})
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
.or_else(|| self.packages.get_fn(hash_script).map(|f| (f, None)))
.or_else(|| {
self.global_modules
.iter()
.find_map(|m| m.get_fn(hash_script, false))
.map(|f| (f, None))
})
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone()))))
.unwrap();

View File

@ -174,17 +174,15 @@ pub use optimize::OptimizationLevel;
// Expose internal data structures.
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]
#[deprecated = "this type is volatile and may change"]
pub use token::{get_next_token, parse_string_literal, InputStream, Token, TokenizeState};
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]
pub use ast::{
BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
};
#[deprecated = "this type is volatile and may change"]
pub use ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt};
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]
#[deprecated = "this type is volatile and may change"]
pub use engine::{Imports, State as EvalState};
#[cfg(feature = "internals")]
@ -192,7 +190,7 @@ pub use engine::{Imports, State as EvalState};
pub use engine::Limits;
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]
#[deprecated = "this type is volatile and may change"]
pub use module::NamespaceRef;
/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),

View File

@ -1,6 +1,6 @@
//! Module defining external-loaded modules for Rhai.
use crate::ast::{FnAccess, IdentX};
use crate::ast::{FnAccess, Ident};
use crate::dynamic::Variant;
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
use crate::fn_register::by_value as cast_arg;
@ -1906,7 +1906,7 @@ impl Module {
///
/// This type is volatile and may change.
#[derive(Clone, Eq, PartialEq, Default, Hash)]
pub struct NamespaceRef(StaticVec<IdentX>, Option<NonZeroUsize>);
pub struct NamespaceRef(StaticVec<Ident>, Option<NonZeroUsize>);
impl fmt::Debug for NamespaceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1921,7 +1921,7 @@ impl fmt::Debug for NamespaceRef {
}
impl Deref for NamespaceRef {
type Target = StaticVec<IdentX>;
type Target = StaticVec<Ident>;
fn deref(&self) -> &Self::Target {
&self.0
@ -1936,15 +1936,15 @@ impl DerefMut for NamespaceRef {
impl fmt::Display for NamespaceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for IdentX { name, .. } in self.0.iter() {
for Ident { name, .. } in self.0.iter() {
write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
}
Ok(())
}
}
impl From<StaticVec<IdentX>> for NamespaceRef {
fn from(modules: StaticVec<IdentX>) -> Self {
impl From<StaticVec<Ident>> for NamespaceRef {
fn from(modules: StaticVec<Ident>) -> Self {
Self(modules, None)
}
}

View File

@ -1,8 +1,6 @@
//! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages.
use crate::fn_native::{CallableFunction, IteratorFn};
use crate::stdlib::{any::TypeId, string::String};
use crate::{Module, Shared, StaticVec};
use crate::{Module, Shared};
pub(crate) mod arithmetic;
mod array_basic;
@ -39,70 +37,16 @@ pub trait Package {
fn init(lib: &mut Module);
/// Retrieve the generic package library from this package.
fn get(&self) -> PackageLibrary;
}
fn as_shared_module(&self) -> Shared<Module>;
/// A sharable [`Module`][crate::Module] to facilitate sharing library instances.
pub type PackageLibrary = Shared<Module>;
/// Type containing a collection of [`PackageLibrary`] instances.
/// All function and type iterator keys in the loaded packages are indexed for fast access.
#[derive(Debug, Clone, Default)]
pub(crate) struct PackagesCollection(Option<StaticVec<PackageLibrary>>);
impl PackagesCollection {
/// Add a [`PackageLibrary`] into the [`PackagesCollection`].
/// Retrieve the generic package library from this package.
///
/// Packages are searched in reverse order.
pub fn add(&mut self, package: PackageLibrary) {
if self.0.is_none() {
self.0 = Some(Default::default());
}
// Later packages override previous ones.
self.0.as_mut().unwrap().insert(0, package);
}
/// Does the specified function hash key exist in the [`PackagesCollection`]?
#[allow(dead_code)]
pub fn contains_fn(&self, hash: u64) -> bool {
if hash == 0 {
false
} else {
self.0
.as_ref()
.map_or(false, |x| x.iter().any(|p| p.contains_fn(hash, false)))
}
}
/// Get specified function via its hash key.
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
if hash == 0 {
None
} else {
self.0
.as_ref()
.and_then(|x| x.iter().find_map(|p| p.get_fn(hash, false)))
}
}
/// Does the specified [`TypeId`] iterator exist in the [`PackagesCollection`]?
#[allow(dead_code)]
pub fn contains_iter(&self, id: TypeId) -> bool {
self.0
.as_ref()
.map_or(false, |x| x.iter().any(|p| p.contains_iter(id)))
}
/// Get the specified [`TypeId`] iterator.
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.0
.as_ref()
.and_then(|x| x.iter().find_map(|p| p.get_iter(id)))
}
/// Get an iterator over all the packages in the [`PackagesCollection`].
pub(crate) fn iter(&self) -> impl Iterator<Item = &PackageLibrary> {
self.0.iter().flat_map(|p| p.iter())
}
/// Generate signatures for all the functions in the [`PackagesCollection`].
pub fn gen_fn_signatures<'a>(&'a self) -> impl Iterator<Item = String> + 'a {
self.iter().flat_map(|m| m.gen_fn_signatures())
/// ## Deprecated
///
/// Use `as_shared_module` instead.
#[deprecated = "use `as_shared_module` instead"]
fn get(&self) -> Shared<Module> {
self.as_shared_module()
}
}
@ -132,10 +76,10 @@ impl PackagesCollection {
macro_rules! def_package {
($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => {
#[doc=$comment]
pub struct $package($root::packages::PackageLibrary);
pub struct $package($root::Shared<$root::Module>);
impl $root::packages::Package for $package {
fn get(&self) -> $root::packages::PackageLibrary {
fn as_shared_module(&self) -> $root::Shared<$root::Module> {
self.0.clone()
}

View File

@ -1,8 +1,6 @@
//! Main module defining the lexer and parser.
use crate::ast::{
BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
};
use crate::ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt};
use crate::dynamic::{AccessMode, Union};
use crate::engine::KEYWORD_THIS;
use crate::module::NamespaceRef;
@ -703,7 +701,7 @@ fn parse_map_literal(
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut map: StaticVec<(IdentX, Expr)> = Default::default();
let mut map: StaticVec<(Ident, Expr)> = Default::default();
loop {
const MISSING_RBRACE: &str = "to end this object map literal";
@ -773,7 +771,8 @@ fn parse_map_literal(
}
let expr = parse_expr(input, state, lib, settings.level_up())?;
map.push((IdentX::new(name, pos), expr));
let name = state.get_interned_string(name);
map.push((Ident { name, pos }, expr));
match input.peek().unwrap() {
(Token::Comma, _) => {
@ -970,7 +969,10 @@ fn parse_primary(
{
state.allow_capture = true;
}
let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
let var_name_def = Ident {
name: state.get_interned_string(s),
pos: settings.pos,
};
Expr::Variable(Box::new((None, None, 0, var_name_def)))
}
// Namespace qualification
@ -981,20 +983,29 @@ fn parse_primary(
{
state.allow_capture = true;
}
let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
let var_name_def = Ident {
name: state.get_interned_string(s),
pos: settings.pos,
};
Expr::Variable(Box::new((None, None, 0, var_name_def)))
}
// Normal variable access
Token::Identifier(s) => {
let index = state.access_var(&s, settings.pos);
let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
let var_name_def = Ident {
name: state.get_interned_string(s),
pos: settings.pos,
};
Expr::Variable(Box::new((index, None, 0, var_name_def)))
}
// Function call is allowed to have reserved keyword
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
if is_keyword_function(&s) {
let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
let var_name_def = Ident {
name: state.get_interned_string(s),
pos: settings.pos,
};
Expr::Variable(Box::new((None, None, 0, var_name_def)))
} else {
return Err(PERR::Reserved(s).into_err(settings.pos));
@ -1007,7 +1018,10 @@ fn parse_primary(
let msg = format!("'{}' can only be used in functions", s);
return Err(LexError::ImproperSymbol(s, msg).into_err(settings.pos));
} else {
let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
let var_name_def = Ident {
name: state.get_interned_string(s),
pos: settings.pos,
};
Expr::Variable(Box::new((None, None, 0, var_name_def)))
}
}
@ -1067,13 +1081,13 @@ fn parse_primary(
.into_err(pos));
}
let (_, modules, _, IdentX { name, pos }) = *x;
let (_, modules, _, Ident { name, pos }) = *x;
settings.pos = pos;
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
}
// Function call
(Expr::Variable(x), Token::LeftParen) => {
let (_, modules, _, IdentX { name, pos }) = *x;
let (_, modules, _, Ident { name, pos }) = *x;
settings.pos = pos;
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
}
@ -1091,7 +1105,10 @@ fn parse_primary(
modules = Some(Box::new(m));
}
let var_name_def = IdentX::new(state.get_interned_string(id2), pos2);
let var_name_def = Ident {
name: state.get_interned_string(id2),
pos: pos2,
};
Expr::Variable(Box::new((index, modules, 0, var_name_def)))
}
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
@ -1129,7 +1146,7 @@ fn parse_primary(
_ => None,
}
.map(|x| {
let (_, modules, hash, IdentX { name, .. }) = x.as_mut();
let (_, modules, hash, Ident { name, .. }) = x.as_mut();
let namespace = modules.as_mut().unwrap();
// Qualifiers + variable name
@ -1311,58 +1328,46 @@ fn make_assignment_stmt<'a>(
state: &mut ParseState,
lhs: Expr,
rhs: Expr,
pos: Position,
op_pos: Position,
) -> Result<Stmt, ParseError> {
match &lhs {
// var (non-indexed) = rhs
Expr::Variable(x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
}
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// var (indexed) = rhs
Expr::Variable(x) => {
let (
index,
_,
_,
IdentX {
name,
pos: name_pos,
},
) = x.as_ref();
let (index, _, _, Ident { name, pos }) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
AccessMode::ReadWrite => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
}
AccessMode::ReadWrite => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// Constant values cannot be assigned to
AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
}
}
}
// xxx[???] = rhs, xxx.??? = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs {
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
}
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => {
let (
index,
_,
_,
IdentX {
name,
pos: name_pos,
},
) = x.as_ref();
let (index, _, _, Ident { name, pos }) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
AccessMode::ReadWrite => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
}
AccessMode::ReadWrite => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// Constant values cannot be assigned to
AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
}
}
}
@ -1378,7 +1383,7 @@ fn make_assignment_stmt<'a>(
"=".to_string(),
"Possibly a typo of '=='?".to_string(),
)
.into_err(pos)),
.into_err(op_pos)),
// expr = rhs
_ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())),
}
@ -1883,11 +1888,11 @@ fn parse_custom_syntax(
match required_token.as_str() {
MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => {
let ident = state.get_interned_string(s);
let var_name_def = IdentX::new(ident.clone(), pos);
keywords.push(Expr::Variable(Box::new((None, None, 0, var_name_def))));
segments.push(ident);
let name = state.get_interned_string(s);
segments.push(name.clone());
tokens.push(state.get_interned_string(MARKER_IDENT));
let var_name_def = Ident { name, pos };
keywords.push(Expr::Variable(Box::new((None, None, 0, var_name_def))));
}
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
@ -2208,16 +2213,16 @@ fn parse_let(
match var_type {
// let name = expr
AccessMode::ReadWrite => {
let var_name = state.get_interned_string(name.clone());
state.stack.push((var_name, AccessMode::ReadWrite));
let var_def = IdentX::new(name, pos);
let name = state.get_interned_string(name);
state.stack.push((name.clone(), AccessMode::ReadWrite));
let var_def = Ident { name, pos };
Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos))
}
// const name = { expr:constant }
AccessMode::ReadOnly => {
let var_name = state.get_interned_string(name.clone());
state.stack.push((var_name, AccessMode::ReadOnly));
let var_def = IdentX::new(name, pos);
let name = state.get_interned_string(name);
state.stack.push((name.clone(), AccessMode::ReadOnly));
let var_def = Ident { name, pos };
Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos))
}
}
@ -2261,7 +2266,10 @@ fn parse_import(
Ok(Stmt::Import(
expr,
Some(Box::new(IdentX::new(name, settings.pos))),
Some(Box::new(Ident {
name,
pos: settings.pos,
})),
token_pos,
))
}
@ -2310,7 +2318,10 @@ fn parse_export(
let rename = if match_token(input, Token::As).0 {
match input.next().unwrap() {
(Token::Identifier(s), pos) => Some(IdentX::new(state.get_interned_string(s), pos)),
(Token::Identifier(s), pos) => Some(Ident {
name: state.get_interned_string(s),
pos,
}),
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
@ -2321,7 +2332,13 @@ fn parse_export(
None
};
exports.push((IdentX::new(state.get_interned_string(id), id_pos), rename));
exports.push((
Ident {
name: state.get_interned_string(id),
pos: id_pos,
},
rename,
));
match input.peek().unwrap() {
(Token::Comma, _) => {
@ -2651,7 +2668,10 @@ fn parse_try_catch(
// try { body } catch (
let var_def = if match_token(input, Token::LeftParen).0 {
let id = match input.next().unwrap() {
(Token::Identifier(s), pos) => Ident::new(s, pos),
(Token::Identifier(s), pos) => Ident {
name: state.get_interned_string(s),
pos,
},
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
@ -2780,7 +2800,7 @@ fn parse_fn(
/// Creates a curried expression from a list of external variables
#[cfg(not(feature = "no_function"))]
fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<IdentX>, pos: Position) -> Expr {
fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Position) -> Expr {
if externals.is_empty() {
return fn_expr;
}
@ -2889,13 +2909,16 @@ fn parse_anon_fn(
// External variables may need to be processed in a consistent order,
// so extract them into a list.
let externals: StaticVec<IdentX> = {
let externals: StaticVec<Ident> = {
#[cfg(not(feature = "no_closure"))]
{
state
.externals
.iter()
.map(|(k, &v)| IdentX::new(k.clone(), v))
.map(|(name, &pos)| Ident {
name: name.clone(),
pos,
})
.collect()
}
#[cfg(feature = "no_closure")]
@ -3114,7 +3137,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
Union::Map(map, _) => {
let items: Vec<_> = map
.into_iter()
.map(|(k, v)| (IdentX::new(k, pos), map_dynamic_to_expr(v, pos)))
.map(|(name, value)| (Ident { name, pos }, map_dynamic_to_expr(value, pos)))
.collect();
if items.iter().all(|(_, expr)| expr.is_some()) {

View File

@ -219,17 +219,17 @@ impl Engine {
/// Functions from the following sources are included:
/// 1) Functions defined in an [`AST`][crate::AST]
/// 2) Functions registered into the global namespace
/// 3) Functions in registered sub-modules
/// 4) Functions in packages (optional)
/// 3) Functions in static modules
/// 4) Functions in global modules (optional)
pub fn gen_fn_metadata_with_ast_to_json(
&self,
ast: &AST,
include_packages: bool,
include_global: bool,
) -> serde_json::Result<String> {
let mut global: ModuleMetadata = Default::default();
if include_packages {
self.packages
if include_global {
self.global_modules
.iter()
.flat_map(|m| m.iter_fn().map(|f| f.into()))
.for_each(|info| global.functions.push(info));
@ -260,9 +260,9 @@ impl Engine {
///
/// Functions from the following sources are included:
/// 1) Functions registered into the global namespace
/// 2) Functions in registered sub-modules
/// 3) Functions in packages (optional)
pub fn gen_fn_metadata_to_json(&self, include_packages: bool) -> serde_json::Result<String> {
self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_packages)
/// 2) Functions in static modules
/// 3) Functions in global modules (optional)
pub fn gen_fn_metadata_to_json(&self, include_global: bool) -> serde_json::Result<String> {
self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_global)
}
}

View File

@ -101,7 +101,7 @@ fn test_for_module_iterator() -> Result<(), Box<EvalAltResult>> {
let mut module = Module::new();
module.set_sub_module("inner", sub_module);
engine.register_module("testing", module);
engine.register_static_module("testing", module.into());
let script = r#"
let item = testing::inner::new_ts();

View File

@ -62,7 +62,7 @@ fn test_functions_namespaces() -> Result<(), Box<EvalAltResult>> {
let hash = m.set_fn_0("test", || Ok(999 as INT));
m.update_fn_namespace(hash, FnNamespace::Global);
engine.register_module("hello", m);
engine.register_static_module("hello", m.into());
assert_eq!(engine.eval::<INT>("test()")?, 999);
assert_eq!(engine.eval::<INT>("fn test() { 123 } test()")?, 123);

View File

@ -44,7 +44,7 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
let mut engine = Engine::new();
engine.register_module("question", module);
engine.register_static_module("question", module.into());
assert_eq!(engine.eval::<INT>("question::MYSTIC_NUMBER")?, 42);
assert!(engine.eval::<INT>("MYSTIC_NUMBER").is_err());

View File

@ -55,7 +55,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(IdentX { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#));
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(Ident { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#));
let ast = engine.compile("if 1 == 2 { 42 }")?;

View File

@ -11,8 +11,8 @@ fn test_packages() -> Result<(), Box<EvalAltResult>> {
// Create a raw Engine - extremely cheap.
let mut engine = Engine::new_raw();
// Load packages - cheap.
engine.load_package(std_pkg.get());
// Register packages - cheap.
engine.register_global_module(std_pkg.as_shared_module());
// Create custom scope - cheap.
let mut scope = Scope::new();
@ -37,7 +37,7 @@ fn test_packages_with_script() -> Result<(), Box<EvalAltResult>> {
let ast = engine.compile("fn foo(x) { x + 1 } fn bar(x) { foo(x) + 1 }")?;
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
engine.load_package(module);
engine.register_global_module(module.into());
assert_eq!(engine.eval::<INT>("foo(41)")?, 42);
assert_eq!(engine.eval::<INT>("bar(40)")?, 42);

View File

@ -77,7 +77,7 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
let mut m = Module::new();
combine_with_exported_module!(&mut m, "test", test::special_array_package);
engine.load_package(m);
engine.register_global_module(m.into());
reg_functions!(engine += greet::single(INT, bool, char));
@ -95,7 +95,7 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
"6 kitties"
);
engine.register_module("test", exported_module!(test::special_array_package));
engine.register_static_module("test", exported_module!(test::special_array_package).into());
assert_eq!(engine.eval::<INT>("test::MYSTIC_NUMBER")?, 42);

View File

@ -52,7 +52,7 @@ fn test_generated_ops() -> Result<(), Box<EvalAltResult>> {
register_in_bulk!(m, add, i8, i16, i32, i64);
register_in_bulk!(m, mul, i8, i16, i32, i64);
engine.load_package(m);
engine.register_global_module(m.into());
#[cfg(feature = "only_i32")]
assert_eq!(engine.eval::<INT>("let a = 0; add_i32(a, 1)")?, 1);