Add is_def_var and is_def_fn.

This commit is contained in:
Stephen Chung
2020-10-03 16:25:58 +08:00
parent eec3f4e1bf
commit fbfb7677c1
10 changed files with 220 additions and 56 deletions

View File

@@ -14,7 +14,9 @@ Versions
This Book is for version **{{version}}** of Rhai.
{% if rootUrl != "" and not rootUrl is ending_with("vnext") %}
For the latest development version, see [here]({{rootUrl}}/vnext/).
{% endif %}
Etymology of the name "Rhai"

View File

@@ -3,36 +3,38 @@ Keywords List
{{#include ../links.md}}
| Keyword | Description | Inactive under | Overloadable |
| :-------------------: | ------------------------------------------- | :-------------: | :----------: |
| `true` | boolean true literal | | no |
| `false` | boolean false literal | | no |
| `let` | variable declaration | | no |
| `const` | constant declaration | | no |
| `is_shared` | is a value shared? | | no |
| `if` | if statement | | no |
| `else` | else block of if statement | | no |
| `while` | while loop | | no |
| `loop` | infinite loop | | no |
| `for` | for loop | | no |
| `in` | 1) containment test<br/>2) part of for loop | | no |
| `continue` | continue a loop at the next iteration | | no |
| `break` | break out of loop iteration | | no |
| `return` | return value | | no |
| `throw` | throw exception | | no |
| `import` | import module | [`no_module`] | no |
| `export` | export variable | [`no_module`] | no |
| `as` | alias for variable export | [`no_module`] | no |
| `private` | mark function private | [`no_function`] | no |
| `fn` (lower-case `f`) | function definition | [`no_function`] | no |
| `Fn` (capital `F`) | create a [function pointer] | | yes |
| `call` | call a [function pointer] | | no |
| `curry` | curry a [function pointer] | | no |
| `this` | reference to base object for method call | [`no_function`] | no |
| `type_of` | get type name of value | | yes |
| `print` | print value | | yes |
| `debug` | print value in debug format | | yes |
| `eval` | evaluate script | | yes |
| Keyword | Description | Inactive under | Is function? | Overloadable |
| :-------------------: | ------------------------------------------- | :-------------: | :----------: | :----------: |
| `true` | boolean true literal | | no | |
| `false` | boolean false literal | | no | |
| `let` | variable declaration | | no | |
| `const` | constant declaration | | no | |
| `is_def_var` | is a variable declared? | | yes | yes |
| `is_shared` | is a value shared? | [`no_closure`] | yes | no |
| `if` | if statement | | no | |
| `else` | else block of if statement | | no | |
| `while` | while loop | | no | |
| `loop` | infinite loop | | no | |
| `for` | for loop | | no | |
| `in` | 1) containment test<br/>2) part of for loop | | no | |
| `continue` | continue a loop at the next iteration | | no | |
| `break` | break out of loop iteration | | no | |
| `return` | return value | | no | |
| `throw` | throw exception | | no | |
| `import` | import module | [`no_module`] | no | |
| `export` | export variable | [`no_module`] | no | |
| `as` | alias for variable export | [`no_module`] | no | |
| `private` | mark function private | [`no_function`] | no | |
| `fn` (lower-case `f`) | function definition | [`no_function`] | no | |
| `Fn` (capital `F`) | create a [function pointer] | | yes | yes |
| `call` | call a [function pointer] | | yes | no |
| `curry` | curry a [function pointer] | | yes | no |
| `this` | reference to base object for method call | [`no_function`] | no | |
| `is_def_fn` | is a scripted function defined? | [`no_function`] | yes | yes |
| `type_of` | get type name of value | | yes | yes |
| `print` | print value | | yes | yes |
| `debug` | print value in debug format | | yes | yes |
| `eval` | evaluate script | | yes | yes |
Reserved Keywords

View File

@@ -101,6 +101,21 @@ a statement in the script can freely call a function defined afterwards.
This is similar to Rust and many other modern languages, such as JavaScript's `function` keyword.
`is_def_fn`
-----------
Use `is_def_fn` to detect if a function is defined (and therefore callable), based on its name
and the number of parameters.
```rust
fn foo(x) { x + 1 }
is_def_fn("foo", 1) == true;
is_def_fn("bar", 1) == false;
```
Arguments are Passed by Value
----------------------------

View File

@@ -37,6 +37,8 @@ If none is provided, it defaults to [`()`].
A variable defined within a statement block is _local_ to that block.
Use `is_def_var` to detect if a variable is defined.
```rust
let x; // ok - value is '()'
let x = 3; // ok
@@ -57,4 +59,10 @@ X == 123;
x == 999; // access to local 'x'
}
x == 42; // the parent block's 'x' is not changed
is_def_var("x") == true;
is_def_var("_x") == true;
is_def_var("y") == false;
```

View File

@@ -32,28 +32,97 @@ are _merged_ into a _unified_ namespace.
| my_module.rhai |
------------------
// This function overrides any in the main script.
private fn inner_message() { "hello! from module!" }
fn greet(callback) { print(callback.call()); }
fn greet() {
print(inner_message()); // call function in module script
}
fn greet_main() {
print(main_message()); // call function not in module script
}
-------------
| main.rhai |
-------------
fn main_message() { "hi! from main!" }
// This function is overridden by the module script.
fn inner_message() { "hi! from main!" }
// This function is found by the module script.
fn main_message() { "main here!" }
import "my_module" as m;
m::greet(|| "hello, " + "world!"); // works - anonymous function in global
m::greet(); // prints "hello! from module!"
m::greet(|| inner_message()); // works - function in module
m::greet(|| main_message()); // works - function in global
m::greet_main(); // prints "main here!"
```
### Simulating virtual functions
When calling a namespace-qualified function defined within a module, other functions defined within
the same module script override any similar-named functions (with the same number of parameters)
defined in the global namespace. This is to ensure that a module acts as a self-contained unit and
functions defined in the calling script do not override module code.
In some situations, however, it is actually beneficial to do it in reverse: have module code call functions
defined in the calling script (i.e. in the global namespace) if they exist, and only call those defined
in the module script if none are found.
One such situation is the need to provide a _default implementation_ to a simulated _virtual_ function:
```rust
------------------
| my_module.rhai |
------------------
// Do not do this (it will override the main script):
// fn message() { "hello! from module!" }
// This function acts as the default implementation.
private fn default_message() { "hello! from module!" }
// This function depends on a 'virtual' function 'message'
// which is not defined in the module script.
fn greet() {
if is_def_fn("message", 0) { // 'is_def_fn' detects if 'message' is defined.
print(message());
} else {
print(default_message());
}
}
-------------
| main.rhai |
-------------
// The main script defines 'message' which is needed by the module script.
fn message() { "hi! from main!" }
import "my_module" as m;
m::greet(); // prints "hi! from main!"
--------------
| main2.rhai |
--------------
// The main script does not define 'message' which is needed by the module script.
import "my_module" as m;
m::greet(); // prints "hello! from module!"
```
### Changing the base directory
The base directory can be changed via the `FileModuleResolver::new_with_path` constructor function.
`FileModuleResolver::create_module` loads a script file and returns a module.
### Returning a module instead
`FileModuleResolver::create_module` loads a script file and returns a module with the standard behavior.
`StaticModuleResolver`