diff --git a/README.md b/README.md index ac9a79a1..7d55bdbe 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Supported targets and builds Features -------- -* Easy-to-use language similar to JS+Rust with dynamic typing. +* Easy-to-use language similar to JavaScript+Rust with dynamic typing. * Tight integration with native Rust [functions](https://schungx.github.io/rhai/rust/functions.html) and [types]([#custom-types-and-methods](https://schungx.github.io/rhai/rust/custom.html)), including [getters/setters](https://schungx.github.io/rhai/rust/getters-setters.html), [methods](https://schungx.github.io/rhai/rust/custom.html) and [indexers](https://schungx.github.io/rhai/rust/indexers.html). * Freely pass Rust variables/constants into a script via an external [`Scope`](https://schungx.github.io/rhai/rust/scope.html). * Easily [call a script-defined function](https://schungx.github.io/rhai/engine/call-fn.html) from Rust. diff --git a/doc/src/about/features.md b/doc/src/about/features.md index 16910c63..bce5bd69 100644 --- a/doc/src/about/features.md +++ b/doc/src/about/features.md @@ -6,7 +6,7 @@ Features Easy ---- -* Easy-to-use language similar to JS+Rust with dynamic typing. +* Easy-to-use language similar to JavaScript+Rust with dynamic typing. * Tight integration with native Rust [functions]({{rootUrl}}/rust/functions.md) and [types]({{rootUrl}}/rust/custom.md), including [getters/setters]({{rootUrl}}/rust/getters-setters.md), [methods]({{rootUrl}}/rust/custom.md) and [indexers]({{rootUrl}}/rust/indexers.md). diff --git a/doc/src/about/non-design.md b/doc/src/about/non-design.md index 2b07946e..22b70f7d 100644 --- a/doc/src/about/non-design.md +++ b/doc/src/about/non-design.md @@ -26,7 +26,7 @@ Due to this intended usage, Rhai deliberately keeps the language simple and smal such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc. Avoid the temptation to write full-fledge program logic entirely in Rhai - that use case is best fulfilled by -more complete languages such as JS or Lua. +more complete languages such as JavaScript or Lua. Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call. All your core functionalities should be in Rust. diff --git a/doc/src/appendix/literals.md b/doc/src/appendix/literals.md index 1f14b9fb..a308e951 100644 --- a/doc/src/appendix/literals.md +++ b/doc/src/appendix/literals.md @@ -3,14 +3,14 @@ Literals Syntax {{#include ../links.md}} -| Type | Literal syntax | -| :--------------------------------: | :---------------------------------------: | -| `INT` | `42`, `-123`, `0` | -| `FLOAT` | `42.0`, `-123.456`, `0.0` | -| [String] | `"... \x?? \u???? \U???????? ..."` | -| Character | `"... \x?? \u???? \U???????? ..."` | -| [`Array`] | `[ ???, ???, ??? ]` | -| [Object map] | `#{ a: ???, b: ???, c: ???, "def": ??? }` | -| Boolean true | `true` | -| Boolean false | `false` | -| `Nothing`/`null`/`nil`/`void`/Unit | `()` | +| Type | Literal syntax | +| :--------------------------------: | :------------------------------------------------------------------------------: | +| `INT` | `42`, `-123`, `0`,
`0x????..` (hex), `0b????..` (binary), `0o????..` (octal) | +| `FLOAT` | `42.0`, `-123.456`, `0.0` | +| [String] | `"... \x?? \u???? \U???????? ..."` | +| Character | `"... \x?? \u???? \U???????? ..."` | +| [`Array`] | `[ ???, ???, ??? ]` | +| [Object map] | `#{ a: ???, b: ???, c: ???, "def": ??? }` | +| Boolean true | `true` | +| Boolean false | `false` | +| `Nothing`/`null`/`nil`/`void`/Unit | `()` | diff --git a/doc/src/appendix/operators.md b/doc/src/appendix/operators.md index 4a1f4a2f..56782b95 100644 --- a/doc/src/appendix/operators.md +++ b/doc/src/appendix/operators.md @@ -3,28 +3,28 @@ Operators {{#include ../links.md}} -| Operator | Description | Binary? | -| :---------------: | ---------------------------- | :-----: | -| `+` | Add | Yes | -| `-` | Subtract, Minus | Yes/No | -| `*` | Multiply | Yes | -| `/` | Divide | Yes | -| `%` | Modulo | Yes | -| `~` | Power | Yes | -| `>>` | Right bit-shift | Yes | -| `<<` | Left bit-shift | Yes | -| `&` | Bit-wise AND, Boolean AND | Yes | -| \| | Bit-wise OR, Boolean OR | Yes | -| `^` | Bit-wise XOR | Yes | -| `==` | Equals to | Yes | -| `~=` | Not equals to | Yes | -| `>` | Greater than | Yes | -| `>=` | Greater than or equals to | Yes | -| `<` | Less than | Yes | -| `<=` | Less than or equals to | Yes | -| `>=` | Greater than or equals to | Yes | -| `&&` | Boolean AND (short-circuits) | Yes | -| \|\| | Boolean OR (short-circuits) | Yes | -| `~` | Boolean NOT | No | -| `[` .. `]` | Indexing | Yes | -| `.` | Property access, Method call | Yes | +| Operator | Description | Binary? | +| :---------------: | ------------------------------ | :-----: | +| `+` | Add | Yes | +| `-` | Subtract, Minus | Yes/No | +| `*` | Multiply | Yes | +| `/` | Divide | Yes | +| `%` | Modulo | Yes | +| `~` | Power | Yes | +| `>>` | Right bit-shift | Yes | +| `<<` | Left bit-shift | Yes | +| `&` | Bit-wise _And_, Boolean _And_ | Yes | +| \| | Bit-wise _Or_, Boolean _Or_ | Yes | +| `^` | Bit-wise _Xor_ | Yes | +| `==` | Equals to | Yes | +| `~=` | Not equals to | Yes | +| `>` | Greater than | Yes | +| `>=` | Greater than or equals to | Yes | +| `<` | Less than | Yes | +| `<=` | Less than or equals to | Yes | +| `>=` | Greater than or equals to | Yes | +| `&&` | Boolean _And_ (short-circuits) | Yes | +| \|\| | Boolean _Or_ (short-circuits) | Yes | +| `!` | Boolean _Not_ | No | +| `[` .. `]` | Indexing | Yes | +| `.` | Property access, Method call | Yes | diff --git a/doc/src/language/arrays.md b/doc/src/language/arrays.md index 5730d862..89870f5f 100644 --- a/doc/src/language/arrays.md +++ b/doc/src/language/arrays.md @@ -38,6 +38,7 @@ The following methods (mostly defined in the [`BasicArrayPackage`]({{rootUrl}}/r | `clear` | _none_ | empties the array | | `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) | + Examples -------- diff --git a/doc/src/language/eval.md b/doc/src/language/eval.md index f7f9965b..babb9242 100644 --- a/doc/src/language/eval.md +++ b/doc/src/language/eval.md @@ -17,7 +17,7 @@ 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, JavaScript, we can also do this! print("Answer: " + result); // prints 42 diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index 30604a53..47a6c717 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -105,4 +105,4 @@ 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. +This is similar to Rust and many other modern languages, such as JavaScript's `function` keyword. diff --git a/doc/src/language/modules/imp-resolver.md b/doc/src/language/modules/imp-resolver.md index a36f930b..9634f633 100644 --- a/doc/src/language/modules/imp-resolver.md +++ b/doc/src/language/modules/imp-resolver.md @@ -43,18 +43,14 @@ impl ModuleResolver for MyModuleResolver { } } -fn main() -> Result<(), Box> { - let mut engine = Engine::new(); +let mut engine = Engine::new(); - // Set the custom module resolver into the 'Engine'. - engine.set_module_resolver(Some(MyModuleResolver {})); +// Set the custom module resolver into the 'Engine'. +engine.set_module_resolver(Some(MyModuleResolver {})); - engine.consume(r#" - import "hello" as foo; // this 'import' statement will call - // 'MyModuleResolver::resolve' with "hello" as path - foo:bar(); - "#)?; - - Ok(()) -} +engine.consume(r#" + import "hello" as foo; // this 'import' statement will call + // 'MyModuleResolver::resolve' with "hello" as path + foo:bar(); +"#)?; ``` diff --git a/doc/src/language/num-op.md b/doc/src/language/num-op.md index a7bd524d..fa96f1e9 100644 --- a/doc/src/language/num-op.md +++ b/doc/src/language/num-op.md @@ -22,19 +22,19 @@ number = -5 - +5; Binary Operators ---------------- -| Operator | Description | Integers only | -| -------- | ---------------------------------------------------- | :-----------: | -| `+` | Plus | | -| `-` | Minus | | -| `*` | Multiply | | -| `/` | Divide (integer division if acting on integer types) | | -| `%` | Modulo (remainder) | | -| `~` | Power | | -| `&` | Binary _And_ bit-mask | Yes | -| `|` | Binary _Or_ bit-mask | Yes | -| `^` | Binary _Xor_ bit-mask | Yes | -| `<<` | Left bit-shift | Yes | -| `>>` | Right bit-shift | Yes | +| Operator | Description | Integers only | +| --------------- | ---------------------------------------------------- | :-----------: | +| `+` | Plus | | +| `-` | Minus | | +| `*` | Multiply | | +| `/` | Divide (integer division if acting on integer types) | | +| `%` | Modulo (remainder) | | +| `~` | Power | | +| `&` | Bit-wise _And_ | Yes | +| \| | Bit-wise _Or_ | Yes | +| `^` | Bit-wise _Xor_ | Yes | +| `<<` | Left bit-shift | Yes | +| `>>` | Right bit-shift | Yes | ```rust let x = (1 + 2) * (6 - 4) / 2; // arithmetic, with parentheses diff --git a/doc/src/language/object-maps.md b/doc/src/language/object-maps.md index 16d6b6f0..dc1adc45 100644 --- a/doc/src/language/object-maps.md +++ b/doc/src/language/object-maps.md @@ -61,7 +61,7 @@ Examples let y = #{ // object map literal with 3 properties a: 1, bar: "hello", - "baz!$@": 123.456, // like JS, you can use any string as property names... + "baz!$@": 123.456, // like JavaScript, you can use any string as property names... "": false, // even the empty string! a: 42 // <- syntax error: duplicated property name diff --git a/doc/src/language/values-and-types.md b/doc/src/language/values-and-types.md index 2aef02c3..b3e55cb4 100644 --- a/doc/src/language/values-and-types.md +++ b/doc/src/language/values-and-types.md @@ -14,7 +14,7 @@ The following primitive types are supported natively: | **Immutable Unicode string** | `rhai::ImmutableString` (implemented as `Rc` or `Arc`) | `"string"` | `"hello"` etc. | | **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` | | **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` | -| **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([instant::Instant](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ | +| **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ | | **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ | | **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. | | **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. | diff --git a/doc/src/links.md b/doc/src/links.md index ba4e57af..e59a8d45 100644 --- a/doc/src/links.md +++ b/doc/src/links.md @@ -69,6 +69,7 @@ [`Module`]: {{rootUrl}}/language/modules/index.md [module]: {{rootUrl}}/language/modules/index.md [modules]: {{rootUrl}}/language/modules/index.md +[module resolver]: {{rootUrl}}/language/modules/imp-resolver.md [`export`]: {{rootUrl}}/language/modules/export.md [`import`]: {{rootUrl}}/language/modules/import.md diff --git a/doc/src/rust/builtin-packages.md b/doc/src/rust/builtin-packages.md deleted file mode 100644 index f77cc900..00000000 --- a/doc/src/rust/builtin-packages.md +++ /dev/null @@ -1 +0,0 @@ -# Built-in Packages diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index cd3998b6..da9af45d 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -26,21 +26,16 @@ impl TestStruct { } } -fn main() -> Result<(), Box> -{ - let engine = Engine::new(); +let mut engine = Engine::new(); - engine.register_type::(); +engine.register_type::(); - engine.register_fn("update", TestStruct::update); - engine.register_fn("new_ts", TestStruct::new); +engine.register_fn("update", TestStruct::update); +engine.register_fn("new_ts", TestStruct::new); - let result = engine.eval::("let x = new_ts(); x.update(); x")?; +let result = engine.eval::("let x = new_ts(); x.update(); x")?; - println!("result: {}", result.field); // prints 42 - - Ok(()) -} +println!("result: {}", result.field); // prints 42 ``` Register a Custom Type @@ -66,7 +61,7 @@ impl TestStruct { } } -let engine = Engine::new(); +let mut engine = Engine::new(); engine.register_type::(); ``` @@ -102,8 +97,9 @@ println!("result: {}", result.field); // prints 42 Method-Call Style vs. Function-Call Style ---------------------------------------- -In fact, any function with a first argument that is a `&mut` reference can be used as method calls because -internally they are the same thing: methods on a type is implemented as a functions taking a `&mut` first argument. +In fact, any function with a first argument that is a `&mut` reference can be used +as method calls because internally they are the same thing: methods on a type is +implemented as a functions taking a `&mut` first argument. ```rust fn foo(ts: &mut TestStruct) -> i64 { @@ -119,8 +115,8 @@ let result = engine.eval::( println!("result: {}", result); // prints 1 ``` -Under [`no_object`], however, the _method_ style of function calls (i.e. calling a function as an object-method) -is no longer supported. +Under [`no_object`], however, the _method_ style of function calls +(i.e. calling a function as an object-method) is no longer supported. ```rust // Below is a syntax error under 'no_object' because 'clear' cannot be called in method style. diff --git a/doc/src/rust/fallible.md b/doc/src/rust/fallible.md index 6c20bf94..16c16826 100644 --- a/doc/src/rust/fallible.md +++ b/doc/src/rust/fallible.md @@ -8,11 +8,6 @@ If a function is _fallible_ (i.e. it returns a `Result<_, Error>`), it can be re The function must return `Result>`. -`Box` implements `From<&str>` and `From` etc. -and the error text gets converted into `Box`. - -The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely. - ```rust use rhai::{Engine, EvalAltResult, Position}; use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn' @@ -27,15 +22,20 @@ fn safe_divide(x: i64, y: i64) -> Result> { } } -fn main() -{ - let engine = Engine::new(); +let mut engine = Engine::new(); - // Fallible functions that return Result values must use register_result_fn() - engine.register_result_fn("divide", safe_divide); +// Fallible functions that return Result values must use register_result_fn() +engine.register_result_fn("divide", safe_divide); - if let Err(error) = engine.eval::("divide(40, 0)") { - println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)") - } +if let Err(error) = engine.eval::("divide(40, 0)") { + println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)") } ``` + +Create a `Box` +---------------------------- + +`Box` implements `From<&str>` and `From` etc. +and the error text gets converted into `Box`. + +The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely. diff --git a/doc/src/rust/functions.md b/doc/src/rust/functions.md index de60646f..aca6fe8e 100644 --- a/doc/src/rust/functions.md +++ b/doc/src/rust/functions.md @@ -29,30 +29,25 @@ fn get_any_value() -> Result> { Ok((42_i64).into()) // standard types can use 'into()' } -fn main() -> Result<(), Box> -{ - let engine = Engine::new(); +let mut engine = Engine::new(); - engine.register_fn("add", add_len); - engine.register_fn("add_str", add_len_str); +engine.register_fn("add", add_len); +engine.register_fn("add_str", add_len_str); - let result = engine.eval::(r#"add(40, "xx")"#)?; +let result = engine.eval::(r#"add(40, "xx")"#)?; - println!("Answer: {}", result); // prints 42 +println!("Answer: {}", result); // prints 42 - let result = engine.eval::(r#"add_str(40, "xx")"#)?; +let result = engine.eval::(r#"add_str(40, "xx")"#)?; - println!("Answer: {}", result); // prints 42 +println!("Answer: {}", result); // prints 42 - // Functions that return Dynamic values must use register_result_fn() - engine.register_result_fn("get_any_value", get_any_value); +// Functions that return Dynamic values must use register_result_fn() +engine.register_result_fn("get_any_value", get_any_value); - let result = engine.eval::("get_any_value()")?; +let result = engine.eval::("get_any_value()")?; - println!("Answer: {}", result); // prints 42 - - Ok(()) -} +println!("Answer: {}", result); // prints 42 ``` To create a [`Dynamic`] value, use the `Dynamic::from` method. diff --git a/doc/src/rust/generic.md b/doc/src/rust/generic.md index 84527951..c68bf562 100644 --- a/doc/src/rust/generic.md +++ b/doc/src/rust/generic.md @@ -17,14 +17,11 @@ fn show_it(x: &mut T) { println!("put up a good show: {}!", x) } -fn main() -{ - let engine = Engine::new(); +let mut engine = Engine::new(); - engine.register_fn("print", show_it::); - engine.register_fn("print", show_it::); - engine.register_fn("print", show_it::); -} +engine.register_fn("print", show_it::); +engine.register_fn("print", show_it::); +engine.register_fn("print", show_it::); ``` The above example shows how to register multiple functions diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md index 18cf868e..92c659b9 100644 --- a/doc/src/rust/getters-setters.md +++ b/doc/src/rust/getters-setters.md @@ -28,7 +28,7 @@ impl TestStruct { } } -let engine = Engine::new(); +let mut engine = Engine::new(); engine.register_type::(); diff --git a/doc/src/rust/indexers.md b/doc/src/rust/indexers.md index 9c85e9a5..800c43b8 100644 --- a/doc/src/rust/indexers.md +++ b/doc/src/rust/indexers.md @@ -9,6 +9,9 @@ A custom type with an indexer function defined can use the bracket '`[]`' notati Indexers are disabled when the [`no_index`] feature is used. +For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for +[arrays] and [object maps]. + ```rust #[derive(Clone)] struct TestStruct { @@ -28,7 +31,7 @@ impl TestStruct { } } -let engine = Engine::new(); +let mut engine = Engine::new(); engine.register_type::(); @@ -42,6 +45,3 @@ let result = engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?; println!("Answer: {}", result); // prints 42 ``` - -For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for -[arrays] and [object maps]. diff --git a/doc/src/rust/operators.md b/doc/src/rust/operators.md index b91b1cdc..8374bfb4 100644 --- a/doc/src/rust/operators.md +++ b/doc/src/rust/operators.md @@ -17,6 +17,10 @@ Similarly, comparison operators including `==`, `!=` etc. are all implemented as with the stark exception of `&&` and `||`. Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators), `&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all. + +Overload Operator via Rust Function +---------------------------------- + Operator functions cannot be defined as a script function (because operators syntax are not valid function names). However, operator functions _can_ be registered to the [`Engine`] via the methods @@ -48,6 +52,10 @@ engine.register_fn("+", mixed_add); // register '+' operator for let result: i64 = engine.eval("1 + 1.0"); // prints 2.0 (normally an error) ``` + +Considerations +-------------- + Normally, use operator overloading for [custom types] only. Be very careful when overriding built-in operators because script authors expect standard operators to behave in a diff --git a/doc/src/rust/print-custom.md b/doc/src/rust/print-custom.md index 53996fe0..7205e1c3 100644 --- a/doc/src/rust/print-custom.md +++ b/doc/src/rust/print-custom.md @@ -3,8 +3,9 @@ Printing for Custom Types {{#include ../links.md}} -To use custom types for [`print`] and [`debug`], or convert its value into a [string], it is necessary that the following -functions be registered (assuming the custom type is `T : Display + Debug`): +To use custom types for [`print`] and [`debug`], or convert its value into a [string], +it is necessary that the following functions be registered (assuming the custom type +is `T : Display + Debug`): | Function | Signature | Typical implementation | Usage | | ----------- | ------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- | diff --git a/doc/src/rust/scope.md b/doc/src/rust/scope.md index d5ad8fa5..a33e4b4c 100644 --- a/doc/src/rust/scope.md +++ b/doc/src/rust/scope.md @@ -20,40 +20,35 @@ then the same state is threaded through multiple invocations: ```rust use rhai::{Engine, Scope, EvalAltResult}; -fn main() -> Result<(), Box> -{ - let engine = Engine::new(); +let engine = Engine::new(); - // First create the state - let mut scope = Scope::new(); +// First create the state +let mut scope = Scope::new(); - // Then push (i.e. add) some initialized variables into the state. - // Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64. - // Better stick to them or it gets hard working with the script. - scope.push("y", 42_i64); - scope.push("z", 999_i64); +// Then push (i.e. add) some initialized variables into the state. +// Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64. +// Better stick to them or it gets hard working with the script. +scope.push("y", 42_i64); +scope.push("z", 999_i64); - // 'set_value' adds a variable when one doesn't exist - scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str' +// 'set_value' adds a variable when one doesn't exist +scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str' - // First invocation - engine.eval_with_scope::<()>(&mut scope, r" - let x = 4 + 5 - y + z + s.len; - y = 1; - ")?; +// First invocation +engine.eval_with_scope::<()>(&mut scope, r" + let x = 4 + 5 - y + z + s.len; + y = 1; +")?; - // Second invocation using the same state - let result = engine.eval_with_scope::(&mut scope, "x")?; +// Second invocation using the same state +let result = engine.eval_with_scope::(&mut scope, "x")?; - println!("result: {}", result); // prints 979 +println!("result: {}", result); // prints 979 - // Variable y is changed in the script - read it with 'get_value' - assert_eq!(scope.get_value::("y").expect("variable y should exist"), 1); +// Variable y is changed in the script - read it with 'get_value' +assert_eq!(scope.get_value::("y").expect("variable y should exist"), 1); - // We can modify scope variables directly with 'set_value' - scope.set_value("y", 42_i64); - assert_eq!(scope.get_value::("y").expect("variable y should exist"), 42); - - Ok(()) -} +// We can modify scope variables directly with 'set_value' +scope.set_value("y", 42_i64); +assert_eq!(scope.get_value::("y").expect("variable y should exist"), 42); ``` diff --git a/doc/src/safety/index.md b/doc/src/safety/index.md index d49768e4..2a06c1f4 100644 --- a/doc/src/safety/index.md +++ b/doc/src/safety/index.md @@ -8,28 +8,28 @@ resources used by a script so that it does not consume more resources that it is The most important resources to watch out for are: -* **Memory**: A malicous script may continuously grow a [string], an [array] or [object map] until all memory is consumed. +* **Memory**: A malicious script may continuously grow a [string], an [array] or [object map] until all memory is consumed. It may also create a large [array] or [object map] literal that exhausts all memory during parsing. -* **CPU**: A malicous script may run an infinite tight loop that consumes all CPU cycles. +* **CPU**: A malicious script may run an infinite tight loop that consumes all CPU cycles. -* **Time**: A malicous script may run indefinitely, thereby blocking the calling system which is waiting for a result. +* **Time**: A malicious script may run indefinitely, thereby blocking the calling system which is waiting for a result. -* **Stack**: A malicous script may attempt an infinite recursive call that exhausts the call stack. +* **Stack**: A malicious script may attempt an infinite recursive call that exhausts the call stack. Alternatively, it may create a degenerated deep expression with so many levels that the parser exhausts the call stack when parsing the expression; or even deeply-nested statement blocks, if nested deep enough. Another way to cause a stack overflow is to load a [self-referencing module]({{rootUrl}}/language/modules/import.md). -* **Overflows**: A malicous script may deliberately cause numeric over-flows and/or under-flows, divide by zero, and/or +* **Overflows**: A malicious script may deliberately cause numeric over-flows and/or under-flows, divide by zero, and/or create bad floating-point representations, in order to crash the system. -* **Files**: A malicous script may continuously [`import`] an external module within an infinite loop, +* **Files**: A malicious script may continuously [`import`] an external module within an infinite loop, thereby putting heavy load on the file-system (or even the network if the file is not local). Even when modules are not created from files, they still typically consume a lot of resources to load. -* **Data**: A malicous script may attempt to read from and/or write to data that it does not own. If this happens, +* **Data**: A malicious script may attempt to read from and/or write to data that it does not own. If this happens, it is a severe security breach and may put the entire system at risk. diff --git a/doc/src/start/builds/minimal.md b/doc/src/start/builds/minimal.md index 57ad59c7..a76075e6 100644 --- a/doc/src/start/builds/minimal.md +++ b/doc/src/start/builds/minimal.md @@ -17,6 +17,16 @@ opt-level = "z" # optimize for size ``` +Use `i32` Only +-------------- + +For embedded systems that must optimize for code size, the architecture is commonly 32-bit. +Use [`only_i32`] to prune away large sections of code implementing functions for other numeric types +(including `i64`). + +If, for some reason, 64-bit long integers must be supported, use [`only_i64`] instead of [`only_i32`]. + + Opt-Out of Features ------------------ @@ -28,13 +38,17 @@ Omitting arrays ([`no_index`]) yields the most code-size savings, followed by fl ([`no_float`]), checked arithmetic/script resource limits ([`unchecked`]) and finally object maps and custom types ([`no_object`]). Where the usage scenario does not call for loading externally-defined modules, use [`no_module`] to save some bytes. -Disable script-defined functions ([`no_function`]) only when the feature is not needed because code size savings is minimal. +Disable script-defined functions ([`no_function`]) when the feature is not needed. +Both of these have little code size savings. Use a Raw [`Engine`] ------------------- [`Engine::new_raw`](#raw-engine) creates a _raw_ engine. -A _raw_ engine supports, out of the box, only a very [restricted set](#built-in-operators) of basic arithmetic and logical 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. + Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint. + Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once. diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md index 65099707..5db1edc1 100644 --- a/doc/src/start/builds/performance.md +++ b/doc/src/start/builds/performance.md @@ -7,14 +7,17 @@ Use Only One Integer Type ------------------------ Some features are for performance. For example, using [`only_i32`] or [`only_i64`] disables all other integer types (such as `u16`). + If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering -lots of functions related to other integer types that will never be used. As a result, performance should improve. +lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster +because fewer functions need to be loaded. Use Only 32-Bit Numbers ---------------------- If only 32-bit integers are needed - again, most of the time this is the case - using [`only_i32`] disables also `i64`. + On 64-bit targets this may not gain much, but on some 32-bit targets this improves performance due to 64-bit arithmetic requiring more CPU cycles to complete. @@ -24,4 +27,5 @@ Minimize Size of [`Dynamic`] Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`. + Making [`Dynamic`] small helps performance due to better cache efficiency. diff --git a/doc/src/start/builds/wasm.md b/doc/src/start/builds/wasm.md index ca2b0dbb..858e4d6e 100644 --- a/doc/src/start/builds/wasm.md +++ b/doc/src/start/builds/wasm.md @@ -14,8 +14,40 @@ But anyhow, do it because you _can_! When building for WASM, certain features will not be available, such as the script file API's and loading modules from external script files. -Also look into [minimal builds] to reduce generated WASM size. As of this version, a typical, full-featured -Rhai scripting engine compiles to a single WASM file less than 200KB gzipped. When excluding features that are -marginal in WASM environment, the gzipped payload can be further shrunk to 160KB. + +Size +---- + +Also look into [minimal builds] to reduce generated WASM size. + +As of this version, a typical, full-featured Rhai scripting engine compiles to a single WASM file +less than 200KB gzipped. + +When excluding features that are marginal in WASM environment, the gzipped payload can be +further shrunk to 160KB. + + +Speed +----- In benchmark tests, a WASM build runs scripts roughly 1.7-2.2x slower than a native optimized release build. + + +Common Features +--------------- + +Some Rhai functionalities are not necessary in a WASM environment, so the following features +are typically used for a WASM build: + +| Feature | Description | +| :-----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`unchecked`] | When a WASM module panics, it doesn't crash the entire web app; however this also disables [maximum number of operations] and [progress] tracking so a script can still run indefinitely - the web app must terminate it itself. | +| [`only_i32`] | JavaScript has only one `number` type and we're only supporting `wasm32` here (so far). | +| [`no_module`] | A WASM module cannot load modules from the file system, so usually this is not needed, but the savings are minimal; alternatively, a custom [module resolver] can be provided that loads other Rhai scripts. | + +The following features are typically _not_ used because they don't make sense in a WASM build: + +| Feature | Why unnecessary | +| :--------: | ------------------------------- | +| [`sync`] | WASM is single-threaded. | +| [`no_std`] | `std` lib works fine with WASM. | diff --git a/doc/src/start/examples/rust.md b/doc/src/start/examples/rust.md index d7a28271..1803c49b 100644 --- a/doc/src/start/examples/rust.md +++ b/doc/src/start/examples/rust.md @@ -5,17 +5,17 @@ Rust Examples A number of examples can be found in the `examples` folder: -| Example | Description | -| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | shows how to register a custom Rust type and using [arrays] on it | -| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | shows how to register a custom Rust type and methods for it | -| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | simple example that evaluates an expression and prints the result | -| [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | example to test out `no-std` builds | -| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | evaluates two pieces of code in separate runs, but using a common [`Scope`] | -| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | runs each filename passed to it as a Rhai script | -| [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | shows how to register a simple function | -| [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | shows different ways to register functions taking string arguments | -| [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | a simple REPL, interactively evaluate statements from stdin | +| Example | Description | +| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. | +| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. | +| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | Simple example that evaluates an expression and prints the result. | +| [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | Example to test out `no-std` builds. | +| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. | +| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. | +| [`simple_fn`](https://github.com/jonathandturner/rhai/tree/master/examples/simple_fn.rs) | Shows how to register a simple function. | +| [`strings`](https://github.com/jonathandturner/rhai/tree/master/examples/strings.rs) | Shows different ways to register functions taking string arguments. | +| [`repl`](https://github.com/jonathandturner/rhai/tree/master/examples/repl.rs) | A simple REPL, interactively evaluate statements from stdin. | The `repl` example is a particularly good one as it allows one to interactively try out Rhai's language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop). diff --git a/doc/src/start/examples/scripts.md b/doc/src/start/examples/scripts.md index 50cfba4e..a4c8584c 100644 --- a/doc/src/start/examples/scripts.md +++ b/doc/src/start/examples/scripts.md @@ -10,21 +10,21 @@ There are also a number of examples scripts that showcase Rhai's features, all i | Script | Description | | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [arrays] in Rhai | -| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | variable declarations | -| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | just comments | +| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] | +| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations | +| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments | | [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`](#for-loop) loops | | [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] | -| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | a [function] without parameters | -| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | a [function] with two parameters | -| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | a [function] with many parameters | +| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | A [function] without parameters | +| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | A [function] with two parameters | +| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | A [function] with many parameters | | [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`](#if-statement) example | -| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop | -| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | just simple addition | -| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | simple addition and multiplication | -| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | change evaluation order with parenthesis | -| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [string] operations | -| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [string] and [object map] operations | +| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | Count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop | +| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition | +| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication | +| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis | +| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations | +| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations | | [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`](#while-loop) loop | @@ -33,12 +33,12 @@ Benchmark Scripts The following scripts are for benchmarking the speed of Rhai: -| Scripts | Description | -| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | -| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | a simple program to measure the speed of Rhai's interpreter (1 million iterations) | -| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | use Sieve of Eratosthenes to find all primes smaller than a limit | -| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | calculate the n-th Fibonacci number using a really dumb algorithm | -| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | matrix multiplication test to measure the speed of multi-dimensional array access | +| Scripts | Description | +| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | +| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | A simple program to measure the speed of Rhai's interpreter (1 million iterations). | +| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. | +| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. | +| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. | Running Example Scripts diff --git a/doc/src/start/features.md b/doc/src/start/features.md index 1f67fb28..57a8a44a 100644 --- a/doc/src/start/features.md +++ b/doc/src/start/features.md @@ -11,19 +11,19 @@ Notice that this deviates from Rust norm where features are _additive_. Excluding unneeded functionalities can result in smaller, faster builds as well as more control over what a script can (or cannot) do. -| Feature | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `unchecked` | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit. Beware that a bad script may panic the entire system! | -| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and `AST`, are all `Send + Sync`. | -| `no_optimize` | Disable the script optimizer. | -| `no_float` | Disable floating-point numbers and math. | -| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. | -| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. | -| `no_index` | Disable [arrays] and indexing features. | -| `no_object` | Disable support for custom types and [object maps]. | -| `no_function` | Disable script-defined functions. | -| `no_module` | Disable loading external modules. | -| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | +| Feature | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `unchecked` | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit.
Beware that a bad script may panic the entire system! | +| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and `AST`, are all `Send + Sync`. | +| `no_optimize` | Disable [script optimization]. | +| `no_float` | Disable floating-point numbers and math. | +| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. | +| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. | +| `no_index` | Disable [arrays] and indexing features. | +| `no_object` | Disable support for [custom types] and [object maps]. | +| `no_function` | Disable script-defined [functions]. | +| `no_module` | Disable loading external [modules]. | +| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | Example @@ -32,19 +32,19 @@ Example The `Cargo.toml` configuration below turns on these six features: * `sync` (everything `Send + Sync`) -* `unchecked` (no checked arithmetic - should not be used with untrusted user scripts) +* `unchecked` (disable all checking - should not be used with untrusted user scripts) * `only_i32` (only 32-bit signed integers) * `no_float` (no floating point numbers) -* `no_module` (no loading external modules) -* `no_function` (no defining functions) +* `no_module` (no loading external [modules]) +* `no_function` (no defining [functions]) ```toml [dependencies] rhai = { version = "{{version}}", features = [ "sync", "unchecked", "only_i32", "no_float", "no_module", "no_function" ] } ``` -The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32` or `i16`), -no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining functions -nor loading external modules. +The resulting scripting engine supports only the `i32` integer numeral type (and no others like `u32`, `i16` or `i64`), +no floating-point, is `Send + Sync` (so it can be safely used across threads), does not support defining [functions] +nor loading external [modules]. This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware. diff --git a/src/lib.rs b/src/lib.rs index 648a8dce..26cba976 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! Rhai is a tiny, simple and very fast embedded scripting language for Rust //! that gives you a safe and easy way to add scripting to your applications. -//! It provides a familiar syntax based on JS and Rust and a simple Rust interface. +//! It provides a familiar syntax based on JavaScript and Rust and a simple Rust interface. //! Here is a quick example. //! //! First, the contents of `my_script.rhai`: diff --git a/src/token.rs b/src/token.rs index d5f84902..5c3d8969 100644 --- a/src/token.rs +++ b/src/token.rs @@ -677,12 +677,12 @@ impl<'a> TokenIterator<'a> { _ => unreachable!(), }); - while let Some(next_char_in_hex) = self.peek_next() { - if !valid.contains(&next_char_in_hex) { + while let Some(next_char_in_escape_seq) = self.peek_next() { + if !valid.contains(&next_char_in_escape_seq) { break; } - result.push(next_char_in_hex); + result.push(next_char_in_escape_seq); self.eat_next(); } }