reorganize module

This commit is contained in:
Timur Gordon
2025-04-04 08:28:07 +02:00
parent 1ea37e2e7f
commit 939b6b4e57
375 changed files with 7580 additions and 191 deletions

View File

@@ -1,116 +0,0 @@
Export Variables, Functions and Sub-Modules From a Script
=========================================================
{{#include ../../links.md}}
```admonish info.side "See also"
See [_Create a Module from AST_]({{rootUrl}}/rust/modules/ast.md) for more details.
```
The easiest way to expose a collection of [functions] as a self-contained [module] is to do it via a Rhai script itself.
The script text is evaluated.
[Variables] are then selectively exposed via the `export` statement.
[Functions] defined by the script are automatically exported, unless marked as `private`.
Modules loaded within this [module] at the global level become _sub-modules_ and are also automatically exported.
Export Global Variables
-----------------------
The `export` statement, which can only be at global level, exposes a selected [variable] as member of a [module].
[Variables] not exported are _private_ and hidden. They are merely used to initialize the [module],
but cannot be accessed from outside.
Everything exported from a [module] is **[constant]** (i.e. read-only).
```js
// This is a module script.
let hidden = 123; // variable not exported - default hidden
let x = 42; // this will be exported below
export x; // the variable 'x' is exported under its own name
export const x = 42; // convenient short-hand to declare a constant and export it
// under its own name
export let x = 123; // variables can be exported as well, though it'll still be constant
export x as answer; // the variable 'x' is exported under the alias 'answer'
// another script can load this module and access 'x' as 'module::answer'
{
let inner = 0; // local variable - it disappears when the statements block ends,
// therefore it is not 'global' and cannot be exported
export inner; // <- syntax error: cannot export a local variable
}
```
```admonish tip.small "Tip: Multiple exports"
[Variables] can be exported under multiple names.
For example, the following exports three [variables]:
* `x` as `x` and `hello`
* `y` as `foo` and `bar`
* `z` as `z`
~~~js
export x;
export x as hello;
export y as foo;
export x as world;
export y as bar;
export z;
~~~
```
Export Functions
----------------
```admonish info.side "Private functions"
`private` [functions] are commonly called within the [module] only.
They cannot be accessed otherwise.
```
All [functions] are automatically exported, _unless_ it is explicitly opt-out with the `private` prefix.
[Functions] declared `private` are hidden to the outside.
```rust
// This is a module script.
fn inc(x) { x + 1 } // script-defined function - default public
private fn foo() {} // private function - hidden
```
Sub-Modules
-----------
All loaded [modules] are automatically exported as sub-modules.
~~~admonish tip.small "Tip: Skip exporting a module"
To prevent a [module] from being exported, load it inside a block statement so that it goes away at the
end of the block.
```js
// This is a module script.
import "hello" as foo; // <- exported
{
import "world" as bar; // <- not exported
}
```
~~~

View File

@@ -1,132 +0,0 @@
Import a Module
===============
{{#include ../../links.md}}
```admonish info.side "See also"
See [_Module Resolvers_][module resolver] for more details.
```
Before a [module] can be used (via an `import` statement) in a script, there must be a
[module resolver] registered into the [`Engine`], the default being the `FileModuleResolver`.
`import` Statement
------------------
```admonish tip.side.wide "Tip"
A [module] that is only `import`-ed but not given any name is simply run.
This is a very simple way to run another script file from within a script.
```
A [module] can be _imported_ via the `import` statement, and be given a name.
Its members can be accessed via `::` similar to C++.
```js
import "crypto_banner"; // run the script file 'crypto_banner.rhai' without creating an imported module
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
const SECRET_NUMBER = 42;
let mod_file = `crypto_${SECRET_NUMBER}`;
import mod_file as my_mod; // load the script file "crypto_42.rhai" and import it as a module named 'my_mod'
// notice that module path names can be dynamically constructed!
// any expression that evaluates to a string is acceptable after the 'import' keyword
lock::encrypt(secret); // use functions defined under the module via '::'
lock::hash::sha256(key); // sub-modules are also supported
print(lock::status); // module variables are constants
lock::status = "off"; // <- runtime error: cannot modify a constant
```
```admonish info "Imports are _scoped_"
[Modules] imported via `import` statements are only accessible inside the relevant block scope.
~~~js
import "hacker" as h; // import module - visible globally
if secured { // <- new block scope
let mod = "crypt";
import mod + "o" as c; // import module (the path needs not be a constant string)
let x = c::encrypt(key); // use a function in the module
h::hack(x); // global module 'h' is visible here
} // <- module 'c' disappears at the end of the block scope
h::hack(something); // this works as 'h' is visible
c::encrypt(something); // <- this causes a run-time error because
// module 'c' is no longer available!
fn foo(something) {
h::hack(something); // <- this also works as 'h' is visible
}
for x in 0..1000 {
import "crypto" as c; // <- importing a module inside a loop is a Very Bad Idea™
c.encrypt(something);
}
~~~
```
~~~admonish note "Place `import` statements at the top"
`import` statements can appear anywhere a normal statement can be, but in the vast majority of cases they are
usually grouped at the top (beginning) of a script for manageability and visibility.
It is not advised to deviate from this common practice unless there is a _Very Good Reason™_.
Especially, do not place an `import` statement within a loop; doing so will repeatedly re-load the
same [module] during every iteration of the loop!
~~~
~~~admonish danger "Recursive imports"
Beware of _import cycles_ &ndash; i.e. recursively loading the same [module]. This is a sure-fire way to
cause a stack overflow in the [`Engine`], unless stopped by setting a limit for [maximum number of modules].
For instance, importing itself always causes an infinite recursion:
```js
┌────────────┐
│ hello.rhai │
└────────────┘
import "hello" as foo; // import itself - infinite recursion!
foo::do_something();
```
[Modules] cross-referencing also cause infinite recursion:
```js
┌────────────┐
│ hello.rhai │
└────────────┘
import "world" as foo;
foo::do_something();
┌────────────┐
│ world.rhai │
└────────────┘
import "hello" as bar;
bar::do_something_else();
```
~~~

View File

@@ -1,14 +0,0 @@
Modules
=======
{{#include ../../links.md}}
Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_.
Modules can be disabled via the [`no_module`] feature.
A module has the type `Module` and holds a collection of functions, variables, [type iterators] and sub-modules.
It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions
and variables defined by that script.
Other scripts can then load this module and use the functions and variables exported as if they were
defined inside the same script.