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] [package]
name = "rhai" name = "rhai"
version = "0.19.8" version = "0.19.9"
edition = "2018" edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
description = "Embedded scripting for Rust" description = "Embedded scripting for Rust"

View File

@ -1,6 +1,20 @@
Rhai Release Notes 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 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 paired with the new `metadata` feature, exports the full list of functions metadata
(including those in an `AST`) as a JSON document. (including those in an `AST`) as a JSON document.
There are also a sizable number of bug fixes.
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. * 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]`. * 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 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`. * Generic type parameter of `Engine::register_iterator` is `IntoIterator` instead of `Iterator`.
* Fixes parsing of block comments ending with `**/` or inner blocks starting with `//*`. * 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 `/**`. * `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. * _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. * 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`. * `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. * `NativeCallContext` and `EvalContext` both expose `source()` which returns the current source, if any.
Enhancements Enhancements
@ -46,7 +62,6 @@ Enhancements
* A functions lookup cache is added to make function call resolution faster. * 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. * 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. * 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 `switch` statement.
* New `do ... while` and `do ... until` statements. * 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::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. * 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_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. * `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. * `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. * `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). * 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. * Error in `Engine::register_custom_syntax` is no longer `Box`-ed.
Housekeeping Housekeeping

View File

@ -23,7 +23,7 @@ fn bench_engine_new_raw_core(bench: &mut Bencher) {
bench.iter(|| { bench.iter(|| {
let mut engine = Engine::new_raw(); 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(); 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 let ast = engine
.compile( .compile(

View File

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

View File

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

View File

@ -41,18 +41,17 @@ The Rhai Scripting Language
2. [Indexers](rust/indexers.md) 2. [Indexers](rust/indexers.md)
3. [Disable Custom Types](rust/disable-custom.md) 3. [Disable Custom Types](rust/disable-custom.md)
4. [Printing Custom Types](rust/print-custom.md) 4. [Printing Custom Types](rust/print-custom.md)
9. [Packages](rust/packages/index.md) 9. [Modules](rust/modules/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)
1. [Create from Rust](rust/modules/create.md) 1. [Create from Rust](rust/modules/create.md)
2. [Create from AST](rust/modules/ast.md) 2. [Create from AST](rust/modules/ast.md)
3. [Module Resolvers](rust/modules/resolvers.md) 3. [Module Resolvers](rust/modules/resolvers.md)
1. [Custom Implementation](rust/modules/imp-resolver.md) 1. [Custom Module Resolvers](rust/modules/imp-resolver.md)
11. [Plugins](plugins/index.md) 10. [Plugins](plugins/index.md)
1. [Export a Rust Module](plugins/module.md) 1. [Export a Rust Module](plugins/module.md)
2. [Export a Rust Function](plugins/function.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) 5. [Rhai Language Reference](language/index.md)
1. [Comments](language/comments.md) 1. [Comments](language/comments.md)
1. [Doc-Comments](language/doc-comments.md) 1. [Doc-Comments](language/doc-comments.md)
@ -129,17 +128,17 @@ The Rhai Scripting Language
8. [Scriptable Event Handler with State](patterns/events.md) 8. [Scriptable Event Handler with State](patterns/events.md)
9. [Dynamic Constants Provider](patterns/dynamic-const.md) 9. [Dynamic Constants Provider](patterns/dynamic-const.md)
9. [Advanced Topics](advanced.md) 9. [Advanced Topics](advanced.md)
1. [Capture Scope for Function Call](language/fn-capture.md) 10. [Capture Scope for Function Call](language/fn-capture.md)
2. [Low-Level API](rust/register-raw.md) 11. [Low-Level API](rust/register-raw.md)
3. [Variable Resolver](engine/var.md) 12. [Variable Resolver](engine/var.md)
4. [Use as DSL](engine/dsl.md) 13. [Use as DSL](engine/dsl.md)
1. [Disable Keywords and/or Operators](engine/disable.md) 1. [Disable Keywords and/or Operators](engine/disable.md)
2. [Custom Operators](engine/custom-op.md) 2. [Custom Operators](engine/custom-op.md)
3. [Extending with Custom Syntax](engine/custom-syntax.md) 3. [Extending with Custom Syntax](engine/custom-syntax.md)
5. [Multiple Instantiation](patterns/multiple.md) 14. [Multiple Instantiation](patterns/multiple.md)
6. [Functions Metadata](engine/metadata/index.md) 15. [Functions Metadata](engine/metadata/index.md)
1. [Generate Function Signatures](engine/metadata/gen_fn_sig.md) 4. [Generate Function Signatures](engine/metadata/gen_fn_sig.md)
2. [Export Metadata to JSON](engine/metadata/export_to_json.md) 5. [Export Metadata to JSON](engine/metadata/export_to_json.md)
10. [Appendix](appendix/index.md) 10. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md) 1. [Keywords](appendix/keywords.md)
2. [Operators and Symbols](appendix/operators.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", "repoHome": "https://github.com/jonathandturner/rhai/blob/master",
"repoTree": "https://github.com/jonathandturner/rhai/tree/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master",
"rootUrl": "", "rootUrl": "",

View File

@ -4,24 +4,25 @@ Export Functions Metadata to JSON
{{#include ../../links.md}} {{#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 As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` and the corresponding
of [functions metadata] in JSON format. `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. the [`serde_json`](https://crates.io/crates/serde_json) crate.
### Sources ### Sources
Functions from the following sources are included: 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 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 3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in static modules
[`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) registered via `Engine::register_static_module`
4) Native Rust functions in registered [packages] (optional) 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 Notice that if a function has been [overloaded][function overloading], only the overriding function's
metadata is included. 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: Functions from the following sources are included, in order:
1) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API 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 2) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules
[`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) registered via `Engine::register_static_module`.
3) Native Rust functions in registered [packages] (optional) 3) Native Rust functions in global modules registered via `Engine::register_global_module` (optional)
Functions Metadata Functions Metadata

View File

@ -1,6 +1,8 @@
Doc-Comments Doc-Comments
============ ============
{{#include ../links.md}}
Similar to Rust, comments starting with `///` (three slashes) or `/**` (two asterisks) are Similar to Rust, comments starting with `///` (three slashes) or `/**` (two asterisks) are
_doc-comments_. _doc-comments_.
@ -56,7 +58,6 @@ This is consistent with popular comment block styles for C-like languages.
``` ```
Using Doc-Comments 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: In general, there are two types of _namespaces_ where functions are looked up:
| Namespace | How Many | Source | Lookup method | Sub-modules? | Variables? | | Namespace | How Many | Source | Lookup | 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 | | 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 | [`Module`] | namespace-qualified function name | yes | yes | | 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. [`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. 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. 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 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 * Functions defined in [modules] registered via `Engine::register_static_module` that are specifically
for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]). 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 Anywhere in a Rhai script, when a function call is made, the function is searched within the
global namespace, in the above search order. 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), 1) Encapsulated script environment (e.g. when loading a [module] from a script file),
2) Current script, 2) Current script,
3) [Modules] imported via the [`import`] statement (latest imports first), 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 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: [`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}} {{#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. 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" // Load the module as the module namespace "MyEnum"
engine engine
.register_type_with_name::<MyEnum>("MyEnum") .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: 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 // Register custom types and API's
engine engine
.register_type_with_name::<SomeType>("SomeType") .register_type_with_name::<SomeType>("SomeType")
.load_package(exported_module!(SomeTypeAPI)); .register_global_module(exported_module!(SomeTypeAPI));
// Create a custom 'Scope' to hold state // Create a custom 'Scope' to hold state
let mut scope = Scope::new(); let mut scope = Scope::new();

View File

@ -20,7 +20,8 @@ Usage Scenario
Key Concepts 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]. * 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. * 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. 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`. Registering the [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md) into a [raw `Engine`] via
But because packages are shared, loading an existing package is _much cheaper_ than registering all the functions one by one. `Engine::register_global_module` is essentially the same as `Engine::new`.
* Load the required packages into the [raw `Engine`] via `Engine::load_package`, using `Package::get` to obtain a shared copy. However, because packages are shared, using existing package is _much cheaper_ than
registering all the functions one by one.
* Register the required packages with the [raw `Engine`] via `Engine::register_global_module`,
using `Package::as_shared_module` to obtain a shared [module].
Examples Examples
@ -46,17 +51,17 @@ let std_pkg = StandardPackage::new();
let custom_pkg = MyCustomPackage::new(); let custom_pkg = MyCustomPackage::new();
let make_call = |x: i64| -> Result<(), Box<EvalAltResult>> { 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(); let mut engine = Engine::new_raw();
// Load packages - cheap. // Register packages as global modules - cheap
engine.load_package(std_pkg.get()); engine.register_global_module(std_pkg.as_shared_module());
engine.load_package(custom_pkg.get()); engine.register_global_module(custom_pkg.as_shared_module());
// Create custom scope - cheap. // Create custom scope - cheap
let mut scope = Scope::new(); let mut scope = Scope::new();
// Push variable into scope - relatively cheap. // Push variable into scope - relatively cheap
scope.push("x", x); scope.push("x", x);
// Evaluate script. // 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 ### 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, All `pub` functions become registered functions, all `pub` constants become [module] constant variables,
and all sub-modules become Rhai sub-modules. and all sub-modules become Rhai sub-modules.
This Rust module can then either be loaded into an [`Engine`] as a normal [module] or This Rust module can then be registered into an [`Engine`] as a normal [module].
registered as a [package]. This is done by using the `exported_module!` macro. This is done via the `exported_module!` macro.
The macro `combine_with_exported_module!` can also be used to _combine_ all the functions 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 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 are eliminated and their contents promoted to the top level. This is typical for
developing [custom packages]. developing [custom packages].
@ -42,7 +42,7 @@ use rhai::plugin::*; // a "prelude" import for macros
#[export_module] #[export_module]
mod my_module { mod my_module {
// This constant will be registered as the constant variable 'MY_NUMBER'. // 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; pub const MY_NUMBER: i64 = 42;
// This function will be registered as 'greet'. // This function will be registered as 'greet'.
@ -64,9 +64,9 @@ mod my_module {
42 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 { 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. // Otherwise it is a valid registered function under a sub-module.
pub fn get_info() -> String { pub fn get_info() -> String {
"hello".to_string() "hello".to_string()
@ -78,7 +78,7 @@ mod my_module {
// This is currently a limitation of the plugin procedural macros. // This is currently a limitation of the plugin procedural macros.
#[cfg(feature = "advanced_functions")] #[cfg(feature = "advanced_functions")]
pub mod advanced { 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 // Otherwise it is a valid registered function under a sub-module
// which only exists when the 'advanced_functions' feature is used. // which only exists when the 'advanced_functions' feature is used.
pub fn advanced_calc(input: i64) -> i64 { 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 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::load_package` method on it: to turn it into a normal Rhai [module], then use the `Engine::register_global_module` method on it:
```rust ```rust
fn main() { fn main() {
@ -100,13 +100,13 @@ fn main() {
// The macro call creates a Rhai module from the plugin module. // The macro call creates a Rhai module from the plugin module.
let module = exported_module!(my_module); let module = exported_module!(my_module);
// A module can simply be loaded, registering all public functions. // A module can simply be registered into the global namespace.
engine.load_package(module); engine.register_global_module(module.into());
} }
``` ```
The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`) 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 ```rust
let x = greet("world"); 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_ 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 ### Use `Engine::register_static_module`
to turn it into a normal Rhai [module], then use the `Engine::register_module` method on it:
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 ```rust
fn main() { fn main() {
@ -137,13 +139,13 @@ fn main() {
// The macro call creates a Rhai module from the plugin module. // The macro call creates a Rhai module from the plugin module.
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 registered as a static module namespace.
engine.register_module("service", module); engine.register_static_module("service", module.into());
} }
``` ```
The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`), 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 ```rust
let x = service::greet("world"); let x = service::greet("world");
@ -178,14 +180,14 @@ x.increment();
x == 43; x == 43;
``` ```
### Use as loadable `Module` ### Use Dynamically
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
[module resolver] must be used to serve the module, and the module is loaded via `import` statements. [module resolver] must be used to serve the module, and the module is loaded via `import` statements.
See the [module] section for more information. 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], Finally the plugin module can also be used to develop a [custom package],
using `combine_with_exported_module!`: 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. 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 All [functions] and [type iterators] can be accessed without _namespace qualifiers_.
and can be accessed without _namespace qualifiers_. This is by far the easiest way to expose
a module's functionalities to Rhai. Variables and sub-modules are **ignored**.
This is by far the easiest way to expose a module's functionalities to Rhai.
```rust ```rust
use rhai::{Engine, Module}; 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. // 'set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: i64", "i64"]); 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(); 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 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 ```rust
use rhai::{Engine, Module}; 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. // 'set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: i64", "i64"]); 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(); 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 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 ### Expose Functions to the Global Namespace
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. `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 ```rust
use rhai::{Engine, Module, FnNamespace}; 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. // 'set_fn_XXX' by default does not set function metadata.
module.update_fn_metadata(hash, ["x: &mut i64", "i64"]); 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(); 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 // The method 'inc' works as expected because it is exposed to the global namespace
engine.eval::<i64>("let x = 41; x.inc()")? == 42; 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 In order to dynamically load a custom module, there must be a [module resolver] which serves
the module when loaded via `import` statements. the module when loaded via `import` statements.

View File

@ -3,12 +3,27 @@ Modules
{{#include ../../links.md}} {{#include ../../links.md}}
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_. Rhai allows organizing functionalities (functions, both Rust-based or script-based, and variables)
Modules can be disabled via the [`no_module`] feature. 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. A module is of the type `Module` and holds a collection of functions, variables,
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions [type iterators] and sub-modules.
and variables defined by that script.
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 Other scripts can then load this module and use the functions and variables exported
as if they were defined inside the same script. 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 | | `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 ```rust
use rhai::Engine; use rhai::Engine;
@ -35,5 +35,6 @@ use rhai::packages::{Package, CorePackage};
let mut engine = Engine::new_raw(); let mut engine = Engine::new_raw();
let package = CorePackage::new(); 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}} {{#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. The macro `def_package!` can be used to create a custom [package].
An [`Engine`] only needs to `Engine::load_package` the custom package once to gain access
to the entire set of functions within.
Loading a package into an [`Engine`] is functionally equivalent to calling `Engine::register_fn` etc. A custom package can aggregate many other packages into a single self-contained unit.
on _each_ of the functions inside the package. But because packages are _shared_, loading an existing More functions can be added on top of others.
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.
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. Examples
--------
* `block` - a code block that initializes the package.
```rust ```rust
// Import necessary types and traits. // Import necessary types and traits.
@ -43,7 +41,7 @@ use rhai::{
// Define the package 'MyPackage'. // Define the package 'MyPackage'.
def_package!(rhai:MyPackage:"My own personal super package", module, { 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); ArithmeticPackage::init(module);
LogicPackage::init(module); LogicPackage::init(module);
BasicArrayPackage::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 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!` By far the easiest way to create a custom module is to call `plugin::combine_with_exported_module!`
from within `rhai::def_package!` which simply merges in all the functions defined within a [plugin 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. 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_ Due to specific requirements of a [package], `plugin::combine_with_exported_module!`
(i.e. all functions defined within sub-modules are pulled up and registered at the top level instead) _flattens_ all sub-modules (i.e. all functions and [type iterators] defined within sub-modules
and so there will not be any sub-modules added to the package. 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. Variables in the [plugin module] are ignored.
@ -107,7 +105,7 @@ mod my_module {
// Define the package 'MyPackage'. // Define the package 'MyPackage'.
def_package!(rhai:MyPackage:"My own personal super package", module, { 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); ArithmeticPackage::init(module);
LogicPackage::init(module); LogicPackage::init(module);
BasicArrayPackage::init(module); BasicArrayPackage::init(module);

View File

@ -3,61 +3,56 @@ Packages
{{#include ../../links.md}} {{#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 reside under `rhai::packages::*` and the trait `rhai::packages::Package`
packages to be used. 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. 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). (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`) Sub-modules and variables are ignored in packages.
among multiple instances of [`Engine`], even across threads (under [`sync`]).
Therefore, a package only has to be created _once_.
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 ```rust
use rhai::Engine; use rhai::Engine;
use rhai::packages::Package // load the 'Package' trait to use packages use rhai::packages::Package // load the 'Package' trait to use packages
use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic) use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic)
let mut engine = Engine::new_raw(); // create a 'raw' Engine // Create a package - can be shared among multiple 'Engine' instances
let package = CorePackage::new(); // 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) 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. 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. /// _(INTERNALS)_ Get the statements.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this method is volatile and may change")] #[deprecated = "this method is volatile and may change"]
#[inline(always)] #[inline(always)]
pub fn statements(&self) -> &[Stmt] { pub fn statements(&self) -> &[Stmt] {
&self.statements &self.statements
@ -255,7 +255,7 @@ impl AST {
/// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions. /// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this method is volatile and may change")] #[deprecated = "this method is volatile and may change"]
#[inline(always)] #[inline(always)]
pub fn lib(&self) -> &Module { pub fn lib(&self) -> &Module {
&self.functions &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]. /// _(INTERNALS)_ An identifier containing an [immutable string][ImmutableString] name and a [position][Position].
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -666,30 +647,13 @@ impl Ident {
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct IdentX { pub struct Ident {
/// Identifier name.
pub name: ImmutableString, pub name: ImmutableString,
/// Declaration position.
pub pos: 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. /// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -729,9 +693,9 @@ pub enum Stmt {
/// `for` id `in` expr `{` stmt `}` /// `for` id `in` expr `{` stmt `}`
For(Expr, Box<(String, Stmt)>, Position), For(Expr, Box<(String, Stmt)>, Position),
/// \[`export`\] `let` id `=` expr /// \[`export`\] `let` id `=` expr
Let(Box<IdentX>, Option<Expr>, bool, Position), Let(Box<Ident>, Option<Expr>, bool, Position),
/// \[`export`\] `const` id `=` expr /// \[`export`\] `const` id `=` expr
Const(Box<IdentX>, Option<Expr>, bool, Position), Const(Box<Ident>, Option<Expr>, bool, Position),
/// expr op`=` expr /// expr op`=` expr
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position), Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
/// `{` stmt`;` ... `}` /// `{` stmt`;` ... `}`
@ -748,13 +712,13 @@ pub enum Stmt {
Return((ReturnType, Position), Option<Expr>, Position), Return((ReturnType, Position), Option<Expr>, Position),
/// `import` expr `as` var /// `import` expr `as` var
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import(Expr, Option<Box<IdentX>>, Position), Import(Expr, Option<Box<Ident>>, Position),
/// `export` var `as` var `,` ... /// `export` var `as` var `,` ...
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export(Vec<(IdentX, Option<IdentX>)>, Position), Export(Vec<(Ident, Option<Ident>)>, Position),
/// Convert a variable to shared. /// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Share(IdentX), Share(Ident),
} }
impl Default for Stmt { impl Default for Stmt {
@ -996,13 +960,13 @@ pub enum Expr {
/// [ expr, ... ] /// [ expr, ... ]
Array(Box<StaticVec<Expr>>, Position), Array(Box<StaticVec<Expr>>, Position),
/// #{ name:expr, ... } /// #{ name:expr, ... }
Map(Box<StaticVec<(IdentX, Expr)>>, Position), Map(Box<StaticVec<(Ident, Expr)>>, Position),
/// () /// ()
Unit(Position), Unit(Position),
/// Variable access - (optional index, optional modules, hash, variable name) /// 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 access - (getter, setter), prop
Property(Box<((ImmutableString, ImmutableString), IdentX)>), Property(Box<((ImmutableString, ImmutableString), Ident)>),
/// { [statement][Stmt] } /// { [statement][Stmt] }
Stmt(Box<StaticVec<Stmt>>, Position), Stmt(Box<StaticVec<Stmt>>, Position),
/// Wrapped [expression][`Expr`] - should not be optimized away. /// Wrapped [expression][`Expr`] - should not be optimized away.

View File

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

View File

@ -1,6 +1,5 @@
//! Configuration settings for [`Engine`]. //! Configuration settings for [`Engine`].
use crate::packages::PackageLibrary;
use crate::stdlib::{format, string::String}; use crate::stdlib::{format, string::String};
use crate::token::{is_valid_identifier, Token}; use crate::token::{is_valid_identifier, Token};
use crate::Engine; use crate::Engine;
@ -9,16 +8,6 @@ use crate::Engine;
use crate::stdlib::boxed::Box; use crate::stdlib::boxed::Box;
impl Engine { 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. /// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation.
/// ///
/// Not available under the `no_optimize` feature. /// Not available under the `no_optimize` feature.

View File

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

View File

@ -174,17 +174,15 @@ pub use optimize::OptimizationLevel;
// Expose internal data structures. // Expose internal data structures.
#[cfg(feature = "internals")] #[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}; pub use token::{get_next_token, parse_string_literal, InputStream, Token, TokenizeState};
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[deprecated = "this type is volatile and may change"]
pub use ast::{ pub use ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt};
BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
};
#[cfg(feature = "internals")] #[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}; pub use engine::{Imports, State as EvalState};
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
@ -192,7 +190,7 @@ pub use engine::{Imports, State as EvalState};
pub use engine::Limits; pub use engine::Limits;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[deprecated = "this type is volatile and may change"]
pub use module::NamespaceRef; pub use module::NamespaceRef;
/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), /// _(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. //! Module defining external-loaded modules for Rhai.
use crate::ast::{FnAccess, IdentX}; use crate::ast::{FnAccess, Ident};
use crate::dynamic::Variant; use crate::dynamic::Variant;
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
use crate::fn_register::by_value as cast_arg; use crate::fn_register::by_value as cast_arg;
@ -1906,7 +1906,7 @@ impl Module {
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Clone, Eq, PartialEq, Default, Hash)] #[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 { impl fmt::Debug for NamespaceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1921,7 +1921,7 @@ impl fmt::Debug for NamespaceRef {
} }
impl Deref for NamespaceRef { impl Deref for NamespaceRef {
type Target = StaticVec<IdentX>; type Target = StaticVec<Ident>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -1936,15 +1936,15 @@ impl DerefMut for NamespaceRef {
impl fmt::Display for NamespaceRef { impl fmt::Display for NamespaceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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())?; write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
} }
Ok(()) Ok(())
} }
} }
impl From<StaticVec<IdentX>> for NamespaceRef { impl From<StaticVec<Ident>> for NamespaceRef {
fn from(modules: StaticVec<IdentX>) -> Self { fn from(modules: StaticVec<Ident>) -> Self {
Self(modules, None) Self(modules, None)
} }
} }

View File

@ -1,8 +1,6 @@
//! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages. //! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages.
use crate::fn_native::{CallableFunction, IteratorFn}; use crate::{Module, Shared};
use crate::stdlib::{any::TypeId, string::String};
use crate::{Module, Shared, StaticVec};
pub(crate) mod arithmetic; pub(crate) mod arithmetic;
mod array_basic; mod array_basic;
@ -39,70 +37,16 @@ pub trait Package {
fn init(lib: &mut Module); fn init(lib: &mut Module);
/// Retrieve the generic package library from this package. /// 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. /// Retrieve the generic package library from this package.
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`].
/// ///
/// Packages are searched in reverse order. /// ## Deprecated
pub fn add(&mut self, package: PackageLibrary) { ///
if self.0.is_none() { /// Use `as_shared_module` instead.
self.0 = Some(Default::default()); #[deprecated = "use `as_shared_module` instead"]
} fn get(&self) -> Shared<Module> {
// Later packages override previous ones. self.as_shared_module()
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())
} }
} }
@ -132,10 +76,10 @@ impl PackagesCollection {
macro_rules! def_package { macro_rules! def_package {
($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => { ($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => {
#[doc=$comment] #[doc=$comment]
pub struct $package($root::packages::PackageLibrary); pub struct $package($root::Shared<$root::Module>);
impl $root::packages::Package for $package { impl $root::packages::Package for $package {
fn get(&self) -> $root::packages::PackageLibrary { fn as_shared_module(&self) -> $root::Shared<$root::Module> {
self.0.clone() self.0.clone()
} }

View File

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

View File

@ -219,17 +219,17 @@ impl Engine {
/// Functions from the following sources are included: /// Functions from the following sources are included:
/// 1) Functions defined in an [`AST`][crate::AST] /// 1) Functions defined in an [`AST`][crate::AST]
/// 2) Functions registered into the global namespace /// 2) Functions registered into the global namespace
/// 3) Functions in registered sub-modules /// 3) Functions in static modules
/// 4) Functions in packages (optional) /// 4) Functions in global modules (optional)
pub fn gen_fn_metadata_with_ast_to_json( pub fn gen_fn_metadata_with_ast_to_json(
&self, &self,
ast: &AST, ast: &AST,
include_packages: bool, include_global: bool,
) -> serde_json::Result<String> { ) -> serde_json::Result<String> {
let mut global: ModuleMetadata = Default::default(); let mut global: ModuleMetadata = Default::default();
if include_packages { if include_global {
self.packages self.global_modules
.iter() .iter()
.flat_map(|m| m.iter_fn().map(|f| f.into())) .flat_map(|m| m.iter_fn().map(|f| f.into()))
.for_each(|info| global.functions.push(info)); .for_each(|info| global.functions.push(info));
@ -260,9 +260,9 @@ impl Engine {
/// ///
/// Functions from the following sources are included: /// Functions from the following sources are included:
/// 1) Functions registered into the global namespace /// 1) Functions registered into the global namespace
/// 2) Functions in registered sub-modules /// 2) Functions in static modules
/// 3) Functions in packages (optional) /// 3) Functions in global modules (optional)
pub fn gen_fn_metadata_to_json(&self, include_packages: bool) -> serde_json::Result<String> { 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_packages) 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(); let mut module = Module::new();
module.set_sub_module("inner", sub_module); module.set_sub_module("inner", sub_module);
engine.register_module("testing", module); engine.register_static_module("testing", module.into());
let script = r#" let script = r#"
let item = testing::inner::new_ts(); 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)); let hash = m.set_fn_0("test", || Ok(999 as INT));
m.update_fn_namespace(hash, FnNamespace::Global); 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>("test()")?, 999);
assert_eq!(engine.eval::<INT>("fn test() { 123 } test()")?, 123); 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); assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
let mut engine = Engine::new(); 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_eq!(engine.eval::<INT>("question::MYSTIC_NUMBER")?, 42);
assert!(engine.eval::<INT>("MYSTIC_NUMBER").is_err()); 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 } }")?; 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 }")?; 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. // Create a raw Engine - extremely cheap.
let mut engine = Engine::new_raw(); let mut engine = Engine::new_raw();
// Load packages - cheap. // Register packages - cheap.
engine.load_package(std_pkg.get()); engine.register_global_module(std_pkg.as_shared_module());
// Create custom scope - cheap. // Create custom scope - cheap.
let mut scope = Scope::new(); 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 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)?; 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>("foo(41)")?, 42);
assert_eq!(engine.eval::<INT>("bar(40)")?, 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(); let mut m = Module::new();
combine_with_exported_module!(&mut m, "test", test::special_array_package); 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)); reg_functions!(engine += greet::single(INT, bool, char));
@ -95,7 +95,7 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
"6 kitties" "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); 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, add, i8, i16, i32, i64);
register_in_bulk!(m, mul, 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")] #[cfg(feature = "only_i32")]
assert_eq!(engine.eval::<INT>("let a = 0; add_i32(a, 1)")?, 1); assert_eq!(engine.eval::<INT>("let a = 0; add_i32(a, 1)")?, 1);