Edit documentation.
This commit is contained in:
@@ -13,7 +13,8 @@ print(x * 2); // prints 84
|
||||
x = 123; // <- syntax error: cannot assign to constant
|
||||
```
|
||||
|
||||
Constants must be assigned a _value_, not an expression.
|
||||
Unlike variables which need not have initial values (default to [`()`]),
|
||||
constants must be assigned one, and it must be a constant _value_, not an expression.
|
||||
|
||||
```rust
|
||||
const x = 40 + 2; // <- syntax error: cannot assign expression to constant
|
||||
|
@@ -6,29 +6,29 @@
|
||||
Or "How to Shoot Yourself in the Foot even Easier"
|
||||
------------------------------------------------
|
||||
|
||||
Saving the best for last: in addition to script optimizations, there is the ever-dreaded... `eval` function!
|
||||
Saving the best for last, there is the ever-dreaded... `eval` function!
|
||||
|
||||
```rust
|
||||
let x = 10;
|
||||
|
||||
fn foo(x) { x += 12; x }
|
||||
|
||||
let script = "let y = x;"; // build a script
|
||||
let script = "let y = x;"; // build a script
|
||||
script += "y += foo(y);";
|
||||
script += "x + y";
|
||||
|
||||
let result = eval(script); // <- look, JS, we can also do this!
|
||||
let result = eval(script); // <- look, JS, we can also do this!
|
||||
|
||||
print("Answer: " + result); // prints 42
|
||||
print("Answer: " + result); // prints 42
|
||||
|
||||
print("x = " + x); // prints 10: functions call arguments are passed by value
|
||||
print("y = " + y); // prints 32: variables defined in 'eval' persist!
|
||||
print("x = " + x); // prints 10: functions call arguments are passed by value
|
||||
print("y = " + y); // prints 32: variables defined in 'eval' persist!
|
||||
|
||||
eval("{ let z = y }"); // to keep a variable local, use a statement block
|
||||
eval("{ let z = y }"); // to keep a variable local, use a statement block
|
||||
|
||||
print("z = " + z); // <- error: variable 'z' not found
|
||||
print("z = " + z); // <- error: variable 'z' not found
|
||||
|
||||
"print(42)".eval(); // <- nope... method-call style doesn't work
|
||||
"print(42)".eval(); // <- nope... method-call style doesn't work
|
||||
```
|
||||
|
||||
Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_,
|
||||
@@ -45,8 +45,8 @@ not inside another function call!
|
||||
```rust
|
||||
let script = "x += 32";
|
||||
let x = 10;
|
||||
eval(script); // variable 'x' in the current scope is visible!
|
||||
print(x); // prints 42
|
||||
eval(script); // variable 'x' in the current scope is visible!
|
||||
print(x); // prints 42
|
||||
|
||||
// The above is equivalent to:
|
||||
let script = "x += 32";
|
||||
@@ -65,7 +65,7 @@ disable `eval` by overloading it, probably with something that throws.
|
||||
```rust
|
||||
fn eval(script) { throw "eval is evil! I refuse to run " + script }
|
||||
|
||||
let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
|
||||
let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2"
|
||||
```
|
||||
|
||||
Or overload it from Rust:
|
||||
@@ -86,11 +86,11 @@ There is even a package named [`EvalPackage`]({{rootUrl}}/rust/packages.md) whic
|
||||
|
||||
```rust
|
||||
use rhai::Engine;
|
||||
use rhai::packages::Package // load the 'Package' trait to use packages
|
||||
use rhai::packages::EvalPackage; // the 'eval' package disables 'eval'
|
||||
use rhai::packages::Package // load the 'Package' trait to use packages
|
||||
use rhai::packages::EvalPackage; // the 'eval' package disables 'eval'
|
||||
|
||||
let mut engine = Engine::new();
|
||||
let package = EvalPackage::new(); // create the package
|
||||
let package = EvalPackage::new(); // create the package
|
||||
|
||||
engine.load_package(package.get()); // load the package
|
||||
engine.load_package(package.get()); // load the package
|
||||
```
|
||||
|
@@ -5,50 +5,53 @@
|
||||
|
||||
Iterating through a range or an [array] is provided by the `for` ... `in` loop.
|
||||
|
||||
Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
|
||||
`break` can be used to break out of the loop unconditionally.
|
||||
|
||||
```rust
|
||||
// Iterate through string, yielding characters
|
||||
let s = "hello, world!";
|
||||
|
||||
for ch in s {
|
||||
if ch > 'z' { continue; } // skip to the next iteration
|
||||
if ch > 'z' { continue; } // skip to the next iteration
|
||||
print(ch);
|
||||
if x == '@' { break; } // break out of for loop
|
||||
if x == '@' { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// Iterate through array
|
||||
let array = [1, 3, 5, 7, 9, 42];
|
||||
|
||||
for x in array {
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// The 'range' function allows iterating from first to last-1
|
||||
for x in range(0, 50) {
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// The 'range' function also takes a step
|
||||
for x in range(0, 50, 3) { // step by 3
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
for x in range(0, 50, 3) { // step by 3
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// Iterate through object map
|
||||
let map = #{a:1, b:3, c:5, d:7, e:9};
|
||||
|
||||
// Property names are returned in random order
|
||||
// Property names are returned in unsorted, random order
|
||||
for x in keys(map) {
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// Property values are returned in random order
|
||||
// Property values are returned in unsorted, random order
|
||||
for val in values(map) {
|
||||
print(val);
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ print(add(2, 3)); // prints 5
|
||||
print(sub(2, 3,)); // prints -1 - trailing comma in arguments list is OK
|
||||
```
|
||||
|
||||
|
||||
Implicit Return
|
||||
---------------
|
||||
|
||||
@@ -38,6 +39,7 @@ print(add(2, 3)); // prints 5
|
||||
print(add2(42)); // prints 44
|
||||
```
|
||||
|
||||
|
||||
No Access to External Scope
|
||||
--------------------------
|
||||
|
||||
@@ -50,13 +52,15 @@ let x = 42;
|
||||
fn foo() { x } // <- syntax error: variable 'x' doesn't exist
|
||||
```
|
||||
|
||||
Passing Arguments by Value
|
||||
-------------------------
|
||||
|
||||
Arguments Passed by Value
|
||||
------------------------
|
||||
|
||||
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
|
||||
It is important to remember that all arguments are passed by _value_, so all functions are _pure_
|
||||
(i.e. they never modify their arguments).
|
||||
Therefore, functions with the same name and same _number_ of parameters are equivalent.
|
||||
|
||||
It is important to remember that all arguments are passed by _value_, so all Rhai script-defined functions
|
||||
are _pure_ (i.e. they never modify their arguments).
|
||||
Any update to an argument will **not** be reflected back to the caller.
|
||||
|
||||
This can introduce subtle bugs, if not careful, especially when using the _method-call_ style.
|
||||
@@ -71,6 +75,7 @@ x.change(); // de-sugars to 'change(x)'
|
||||
x == 500; // 'x' is NOT changed!
|
||||
```
|
||||
|
||||
|
||||
Global Definitions Only
|
||||
----------------------
|
||||
|
||||
@@ -92,6 +97,12 @@ fn do_addition(x) {
|
||||
}
|
||||
```
|
||||
|
||||
Unlike C/C++, functions can be defined _anywhere_ within the global level. A function does not need to be defined
|
||||
prior to being used in a script; a statement in the script can freely call a function defined afterwards.
|
||||
This is similar to Rust and many other modern languages.
|
||||
|
||||
Use Before Definition
|
||||
--------------------
|
||||
|
||||
Unlike C/C++, functions in Rhai can be defined _anywhere_ at global level.
|
||||
A function does not need to be defined prior to being used in a script;
|
||||
a statement in the script can freely call a function defined afterwards.
|
||||
|
||||
This is similar to Rust and many other modern languages, such as JS's `function` keyword.
|
||||
|
@@ -3,24 +3,30 @@
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
`if` statements follow C syntax:
|
||||
|
||||
```rust
|
||||
if foo(x) {
|
||||
print("It's true!");
|
||||
} else if bar == baz {
|
||||
print("It's true again!");
|
||||
} else if ... {
|
||||
:
|
||||
} else if ... {
|
||||
:
|
||||
} else if baz.is_foo() {
|
||||
print("Yet again true.");
|
||||
} else if foo(bar - baz) {
|
||||
print("True again... this is getting boring.");
|
||||
} else {
|
||||
print("It's finally false!");
|
||||
}
|
||||
```
|
||||
|
||||
All branches of an `if` statement must be enclosed within braces '`{`' .. '`}`', even when there is only one statement.
|
||||
Like Rust, there is no ambiguity regarding which `if` clause a statement belongs to.
|
||||
Unlike C, the condition expression does _not_ need to be enclosed in parentheses '`(`' .. '`)`', but
|
||||
all branches of the `if` statement must be enclosed within braces '`{`' .. '`}`',
|
||||
even when there is only one statement inside the branch.
|
||||
|
||||
Like Rust, there is no ambiguity regarding which `if` clause a branch belongs to.
|
||||
|
||||
```rust
|
||||
// Rhai is not C!
|
||||
if (decision) print("I've decided!");
|
||||
// ^ syntax error, expecting '{' in statement block
|
||||
```
|
||||
|
@@ -3,6 +3,11 @@ Infinite `loop`
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Infinite loops follow C syntax.
|
||||
|
||||
Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
|
||||
`break` can be used to break out of the loop unconditionally.
|
||||
|
||||
```rust
|
||||
let x = 10;
|
||||
|
||||
@@ -13,3 +18,6 @@ loop {
|
||||
if x == 0 { break; } // break out of loop
|
||||
}
|
||||
```
|
||||
|
||||
Beware: a `loop` statement without a `break` statement inside its loop block is infinite -
|
||||
there is no way for the loop to stop iterating.
|
||||
|
@@ -3,11 +3,16 @@ Call Method as Function
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Properties and methods in a Rust custom type registered with the [`Engine`] can be called just like a regular function in Rust.
|
||||
Property getters/setters and methods in a Rust custom type registered with the [`Engine`] can be called
|
||||
just like a regular function. In fact, like Rust, property getters/setters and object methods
|
||||
are registered as regular functions in Rhai that take a first `&mut` parameter.
|
||||
|
||||
Unlike functions defined in script (for which all arguments are passed by _value_),
|
||||
native Rust functions may mutate the object (or the first argument if called in normal function call style).
|
||||
|
||||
However, sometimes it is not as straight-forward, and methods called in function-call style may end up
|
||||
not muting the object - see the example below. Therefore, it is best to always use method-call style.
|
||||
|
||||
Custom types, properties and methods can be disabled via the [`no_object`] feature.
|
||||
|
||||
```rust
|
||||
@@ -21,8 +26,8 @@ update(a); // <- this de-sugars to 'a.update()' thus if 'a' is a simple
|
||||
let array = [ a ];
|
||||
|
||||
update(array[0]); // <- 'array[0]' is an expression returning a calculated value,
|
||||
// a transient (i.e. a copy) so this statement has no effect
|
||||
// a transient (i.e. a copy), so this statement has no effect
|
||||
// except waste a lot of time cloning
|
||||
|
||||
array[0].update(); // <- call this method-call style will update 'a'
|
||||
array[0].update(); // <- call in method-call style will update 'a'
|
||||
```
|
||||
|
@@ -1,18 +1 @@
|
||||
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 is of the type `Module` and encapsulates a Rhai script together with the functions defined
|
||||
by that script.
|
||||
|
||||
The script text is run, variables are then selectively exposed via the [`export`] statement.
|
||||
Functions defined by the script are automatically exported.
|
||||
|
||||
Modules loaded within this module at the global level become _sub-modules_ and are also automatically exported.
|
||||
|
||||
Other scripts can then load this module and use the variables and functions exported
|
||||
as if they were defined inside the same script.
|
||||
# Modules
|
||||
|
@@ -7,8 +7,8 @@ For many applications in which Rhai is embedded, it is necessary to customize th
|
||||
are resolved. For instance, modules may need to be loaded from script texts stored in a database,
|
||||
not in the file system.
|
||||
|
||||
A module resolver must implement the trait `rhai::ModuleResolver`, which contains only one function:
|
||||
`resolve`.
|
||||
A module resolver must implement the trait [`rhai::ModuleResolver`]({{rootUrl}}/rust/traits.md),
|
||||
which contains only one function: `resolve`.
|
||||
|
||||
When Rhai prepares to load a module, `ModuleResolver::resolve` is called with the name
|
||||
of the _module path_ (i.e. the path specified in the [`import`] statement). Upon success, it should
|
||||
|
@@ -3,6 +3,9 @@ Import a Module
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
`import` Statement
|
||||
-----------------
|
||||
|
||||
A module can be _imported_ via the `import` statement, and its members are accessed via '`::`' similar to C++.
|
||||
|
||||
```rust
|
||||
@@ -17,10 +20,14 @@ print(lock::status); // module variables are constants
|
||||
lock::status = "off"; // <- runtime error - cannot modify a constant
|
||||
```
|
||||
|
||||
|
||||
Scoped Imports
|
||||
--------------
|
||||
|
||||
`import` statements are _scoped_, meaning that they are only accessible inside the scope that they're imported.
|
||||
|
||||
They can appear anywhere a normal statement can be, but in the vast majority of cases `import` statements are
|
||||
group at the beginning of a script. It is, however, not advised to deviate from this common practice unless
|
||||
group at the beginning of a script. 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
|
||||
@@ -44,3 +51,32 @@ for x in range(0, 1000) {
|
||||
c.encrypt(something);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Recursive Imports
|
||||
----------------
|
||||
|
||||
Beware of _import cycles_ - 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:
|
||||
|
||||
```rust
|
||||
// This file is 'hello.rhai'
|
||||
|
||||
import "hello" as foo; // import itself - infinite recursion!
|
||||
|
||||
foo::do_something();
|
||||
```
|
||||
|
||||
Modules cross-referencing also cause infinite recursion:
|
||||
|
||||
```rust
|
||||
// This file is 'hello.rhai' - references 'world.rhai'
|
||||
import "world" as foo;
|
||||
foo::do_something();
|
||||
|
||||
// This file is 'world.rhai' - references 'hello.rhai'
|
||||
import "hello" as bar;
|
||||
bar::do_something_else();
|
||||
```
|
||||
|
18
doc/src/language/modules/index.md
Normal file
18
doc/src/language/modules/index.md
Normal file
@@ -0,0 +1,18 @@
|
||||
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 is of the type `Module` and encapsulates a Rhai script together with the functions defined
|
||||
by that script.
|
||||
|
||||
The script text is run, variables are then selectively exposed via the [`export`] statement.
|
||||
Functions defined by the script are automatically exported.
|
||||
|
||||
Modules loaded within this module at the global level become _sub-modules_ and are also automatically exported.
|
||||
|
||||
Other scripts can then load this module and use the variables and functions exported
|
||||
as if they were defined inside the same script.
|
@@ -3,8 +3,14 @@ Return Values
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
The `return` statement is used to immediately stop evaluation and exist the current context
|
||||
(typically a function call) yielding a _return value_.
|
||||
|
||||
```rust
|
||||
return; // equivalent to return ();
|
||||
|
||||
return 123 + 456; // returns 579
|
||||
```
|
||||
|
||||
A `return` statement at _global_ level stop the entire script evaluation,
|
||||
the return value is taken as the result of the script evaluation.
|
||||
|
@@ -3,13 +3,14 @@ Statements
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Terminated by '`;`'
|
||||
------------------
|
||||
|
||||
Statements are terminated by semicolons '`;`' and they are mandatory,
|
||||
except for the _last_ statement in a _block_ (enclosed by '`{`' .. '`}`' pairs) where it can be omitted.
|
||||
|
||||
A statement can be used anywhere where an expression is expected. These are called, for lack of a more
|
||||
creative name, "statement expressions." The _last_ statement of a statement block is _always_ the block's
|
||||
return value when used as a statement.
|
||||
If the last statement has no return value (e.g. variable definitions, assignments) then it is assumed to be [`()`].
|
||||
Semicolons can also be omitted if the statement contains a block itself
|
||||
(e.g. the `if`, `while`, `for` and `loop` statements).
|
||||
|
||||
```rust
|
||||
let a = 42; // normal assignment statement
|
||||
@@ -20,6 +21,20 @@ let a = { 40 + 2 }; // 'a' is set to the value of the statement block, which
|
||||
// ^ the last statement does not require a terminating semicolon (although it also works with it)
|
||||
// ^ semicolon required here to terminate the assignment statement; it is a syntax error without it
|
||||
|
||||
4 * 10 + 2 // a statement which is just one expression; no ending semicolon is OK
|
||||
if foo { a = 42 }
|
||||
// ^ there is no need to terminate an if-statement with a semicolon
|
||||
|
||||
4 * 10 + 2 // a statement which is just one expression - no ending semicolon is OK
|
||||
// because it is the last statement of the whole block
|
||||
```
|
||||
|
||||
|
||||
Statement Expression
|
||||
--------------------
|
||||
|
||||
A statement can be used anywhere where an expression is expected. These are called, for lack of a more
|
||||
creative name, "statement expressions."
|
||||
|
||||
The _last_ statement of a statement block is _always_ the block's return value when used as a statement.
|
||||
|
||||
If the last statement has no return value (e.g. variable definitions, assignments) then it is assumed to be [`()`].
|
||||
|
@@ -3,6 +3,9 @@ Variables
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Valid Names
|
||||
-----------
|
||||
|
||||
Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
|
||||
|
||||
Variable names must start with an ASCII letter or an underscore '`_`', must contain at least one ASCII letter,
|
||||
@@ -11,9 +14,21 @@ and must start with an ASCII letter before a digit.
|
||||
Therefore, names like '`_`', '`_42`', '`3a`' etc. are not legal variable names, but '`_c3po`' and '`r2d2`' are.
|
||||
Variable names are also case _sensitive_.
|
||||
|
||||
Variables are defined using the `let` keyword. A variable defined within a statement block is _local_ to that block.
|
||||
Variable names cannot be the same as a [keyword].
|
||||
|
||||
|
||||
Declare a Variable
|
||||
------------------
|
||||
|
||||
Variables are declared using the `let` keyword.
|
||||
|
||||
Variables do not have to be given an initial value.
|
||||
If none is provided, then it defaults to [`()`].
|
||||
|
||||
A variable defined within a statement block is _local_ to that block.
|
||||
|
||||
```rust
|
||||
let x; // ok - value is '()'
|
||||
let x = 3; // ok
|
||||
let _x = 42; // ok
|
||||
let x_ = 42; // also ok
|
||||
|
@@ -3,6 +3,11 @@
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
`while` loops follow C syntax.
|
||||
|
||||
Like C, `continue` can be used to skip to the next iteration, by-passing all following statements;
|
||||
`break` can be used to break out of the loop unconditionally.
|
||||
|
||||
```rust
|
||||
let x = 10;
|
||||
|
||||
|
Reference in New Issue
Block a user