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

@@ -0,0 +1,113 @@
Export Variables, Functions and Sub-Modules From a Script
=========================================================
The easiest way to expose a collection of [functions](../functions.md) as a self-contained [module](index.md)
is to do it via a Rhai script itself.
The script text is evaluated.
[Variables](../variables.md) are then selectively exposed via the `export` statement.
[Functions](../functions.md) defined by the script are automatically exported, unless marked as `private`.
Modules loaded within this [module](index.md) at the global level become _sub-modules_ and are also
automatically exported.
Export Global Constants
-----------------------
The `export` statement, which can only be at global level, exposes a selected
[variable](../variables.md) as member of a [module](index.md).
[Variables](../variables.md) not exported are _private_ and hidden. They are merely used to
initialize the [module](index.md), but cannot be accessed from outside.
Everything exported from a [module](index.md) is **[constant](../constants.md)** (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.wide "Private functions"
`private` [functions](../functions.md) are commonly called within the [module](index.md) only.
They cannot be accessed otherwise.
```
All [functions](../functions.md) are automatically exported, _unless_ it is explicitly opt-out with
the `private` prefix.
[Functions](../functions.md) 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](index.md) are automatically exported as sub-modules.
~~~admonish tip.small "Tip: Skip exporting a module"
To prevent a [module](index.md) 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

@@ -0,0 +1,121 @@
Import a Module
===============
`import` Statement
------------------
```admonish tip.side.wide "Tip"
A [module](index.md) 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](index.md) 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](index.md) 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](index.md) during every iteration of the loop!
~~~
~~~admonish danger "Recursive imports"
Beware of _import cycles_ &ndash; i.e. recursively loading the same [module](index.md).
This is a sure-fire way to cause a stack overflow error.
For instance, importing itself always causes an infinite recursion:
```js
┌────────────┐
│ hello.rhai │
└────────────┘
import "hello" as foo; // import itself - infinite recursion!
foo::do_something();
```
[Modules](index.md) 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

@@ -0,0 +1,12 @@
Modules
=======
Rhai allows organizing code into _modules_.
A module holds a collection of [functions](../functions.md), [constants](../constants.md) and sub-modules.
It may encapsulates a Rhai script together with the [functions](../functions.md) and
[constants](../constants.md) defined by that script.
Other scripts can then load this module and use the [functions](../functions.md) and
[constants](../constants.md) exported as if they were defined inside the same script.