diff --git a/RELEASES.md b/RELEASES.md index ba43f472..bd6357b2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -33,6 +33,7 @@ Enhancements * `Engine::register_static_module` now supports sub-module paths (e.g. `foo::bar::baz`). * `Engine::register_custom_operator` now accepts reserved symbols. * `Engine::register_custom_operator` now returns an error if given a precedence of zero. +* The examples `repl` and `rhai_runner` are moved into `bin` and renamed `rhai-repl` and `rhai-run` respectively. Version 0.19.8 diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 8cd109f3..97d80523 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "0.3.0" +version = "0.3.1" edition = "2018" authors = ["jhwgh1968"] description = "Procedural macro support package for Rhai, a scripting language for Rust" diff --git a/codegen/README.md b/codegen/README.md index 8f276264..275c26eb 100644 --- a/codegen/README.md +++ b/codegen/README.md @@ -1,5 +1,5 @@ Procedural Macros for Plugins -============================ +============================= This crate holds procedural macros for code generation, supporting the plugins system for [Rhai](https://github.com/jonathandturner/rhai). diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 4b3e7e30..0f16501f 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -16,17 +16,18 @@ The Rhai Scripting Language 2. [Minimal](start/builds/minimal.md) 3. [no-std](start/builds/no-std.md) 4. [WebAssembly (WASM)](start/builds/wasm.md) - 5. [Examples](start/examples/index.md) + 5. [Packaged Utilities](start/bin.md) + 6. [Examples](start/examples/index.md) 1. [Rust](start/examples/rust.md) 2. [Scripts](start/examples/scripts.md) 3. [Using the `Engine`](engine/index.md) - 1. [Hello World in Rhai - Evaluate a Script](engine/hello-world.md) + 1. [Hello World in Rhai – Evaluate a Script](engine/hello-world.md) 2. [Compile to AST for Repeated Evaluations](engine/compile.md) 3. [Call a Rhai Function from Rust](engine/call-fn.md) 4. [Create a Rust Closure from a Rhai Function](engine/func.md) 5. [Evaluate Expressions Only](engine/expressions.md) 6. [Raw Engine](engine/raw.md) - 7. [Scope - Initializing and Maintaining State](engine/scope.md) + 7. [Scope – Initializing and Maintaining State](engine/scope.md) 8. [Engine Configuration Options](engine/options.md) 4. [Extend Rhai with Rust](rust/index.md) 1. [Traits](rust/traits.md) @@ -34,22 +35,22 @@ The Rhai Scripting Language 1. [String Parameters in Rust Functions](rust/strings.md) 3. [Register a Generic Rust Function](rust/generic.md) 4. [Register a Fallible Rust Function](rust/fallible.md) - 6. [Override a Built-in Function](rust/override.md) - 7. [Operator Overloading](rust/operators.md) - 8. [Register any Rust Type and its Methods](rust/custom.md) + 5. [Override a Built-in Function](rust/override.md) + 6. [Operator Overloading](rust/operators.md) + 7. [Register any Rust Type and its Methods](rust/custom.md) 1. [Property Getters and Setters](rust/getters-setters.md) 2. [Indexers](rust/indexers.md) 3. [Disable Custom Types](rust/disable-custom.md) 4. [Printing Custom Types](rust/print-custom.md) - 9. [Modules](rust/modules/index.md) + 8. [Modules](rust/modules/index.md) 1. [Create from Rust](rust/modules/create.md) 2. [Create from AST](rust/modules/ast.md) 3. [Module Resolvers](rust/modules/resolvers.md) 1. [Custom Module Resolvers](rust/modules/imp-resolver.md) - 10. [Plugins](plugins/index.md) + 9. [Plugins](plugins/index.md) 1. [Export a Rust Module](plugins/module.md) 2. [Export a Rust Function](plugins/function.md) - 11. [Packages](rust/packages/index.md) + 10. [Packages](rust/packages/index.md) 1. [Built-in Packages](rust/packages/builtin.md) 2. [Custom Packages](rust/packages/create.md) 5. [Rhai Language Reference](language/index.md) diff --git a/doc/src/about/features.md b/doc/src/about/features.md index 82bfc65a..597f6677 100644 --- a/doc/src/about/features.md +++ b/doc/src/about/features.md @@ -11,12 +11,12 @@ Easy * Tight integration with native Rust [functions] and [types][custom types] including [getters/setters], [methods][custom type] and [indexers]. -* Freely pass Rust variables/constants into a script via an external [`Scope`] - all clonable Rust types are supported seamlessly +* Freely pass Rust variables/constants into a script via an external [`Scope`] – all clonable Rust types are supported seamlessly without the need to implement any special trait. * Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust. -* Very few additional dependencies - right now only [`smallvec`](https://crates.io/crates/smallvec/) plus crates for procedural macros; +* Very few additional dependencies – right now only [`smallvec`](https://crates.io/crates/smallvec/) plus crates for procedural macros; for [`no-std`] and `WASM` builds, a number of additional dependencies are pulled in to provide for missing functionalities. * [Plugins] system powered by procedural macros simplifies custom API development. @@ -52,7 +52,7 @@ Safe * Relatively little `unsafe` code (yes there are some for performance reasons). -* Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless +* Sand-boxed – the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless [explicitly permitted]({{rootUrl}}/patterns/control.md). Rugged diff --git a/doc/src/about/index.md b/doc/src/about/index.md index 5932e802..f316698d 100644 --- a/doc/src/about/index.md +++ b/doc/src/about/index.md @@ -29,7 +29,7 @@ which is an embedded scripting language for C++. Originally it was intended to be a scripting language similar to **JavaScript**. With java being a kind of hot beverage, the new language was named after -another hot beverage - **Chai**, which is the word for "tea" in many world languages +another hot beverage – **Chai**, which is the word for "tea" in many world languages and, in particular, a popular kind of milk tea consumed in India. Later, when the novel implementation technique behind ChaiScript was ported from C++ to Rust, diff --git a/doc/src/about/non-design.md b/doc/src/about/non-design.md index b0c0cbe9..aa39f465 100644 --- a/doc/src/about/non-design.md +++ b/doc/src/about/non-design.md @@ -10,24 +10,24 @@ It doesn't attempt to be a new language. For example: * **No traits**... so it is also not Rust. Do your Rusty stuff in Rust. -* **No structures/records/tuples** - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_. +* **No structures/records/tuples** – define your types in Rust instead; Rhai can seamlessly work with _any Rust type_. There is, however, a built-in [object map] type which is adequate for most uses. It is possible to simulate [object-oriented programming (OOP)][OOP] by storing [function pointers] or [closures] in [object map] properties, turning them into _methods_. -* **No first-class functions** - Code your functions in Rust instead, and register them with Rhai. +* **No first-class functions** – Code your functions in Rust instead, and register them with Rhai. There is, however, support for simple [function pointers] to allow runtime dispatch by function name. -* **No garbage collection** - this should be expected, so... +* **No garbage collection** – this should be expected, so... -* **No first-class closures** - do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md). +* **No first-class closures** – do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md). There is, however, support for simulated [closures] via [currying] a [function pointer] with captured shared variables. -* **No byte-codes/JIT** - Rhai has an optimized AST-walking interpreter which is fast enough for most casual +* **No byte-codes/JIT** – Rhai has an optimized AST-walking interpreter which is fast enough for most casual usage scenarios. Essential AST data structures are packed and kept together to maximize cache friendliness. Functions are dispatched based on pre-calculated hashes and accessing variables are mostly through pre-calculated @@ -41,7 +41,7 @@ It doesn't attempt to be a new language. For example: Still, the purpose of Rhai is not to be super _fast_, but to make it as easy and versatile as possible to integrate with native Rust applications. -* **No formal language grammar** - Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser +* **No formal language grammar** – Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser for statements, and a hand-coded Pratt parser for expressions. This lack of formalism allows the _tokenizer_ and _parser_ themselves to be exposed as services in order diff --git a/doc/src/about/related.md b/doc/src/about/related.md index 0f29cb16..84af9824 100644 --- a/doc/src/about/related.md +++ b/doc/src/about/related.md @@ -7,30 +7,30 @@ Related Resources Online Resources for Rhai ------------------------- -* [GitHub](https://github.com/jonathandturner/rhai) - Home repository +* [GitHub](https://github.com/jonathandturner/rhai) – Home repository -* [`crates.io`](https://crates.io/crates/rhai) - Rhai crate +* [`crates.io`](https://crates.io/crates/rhai) – Rhai crate -* [`DOCS.RS`](https://docs.rs/rhai) - Rhai API documentation +* [`DOCS.RS`](https://docs.rs/rhai) – Rhai API documentation -* [`LIB.RS`](https://lib.rs/crates/rhai) - Rhai library info +* [`LIB.RS`](https://lib.rs/crates/rhai) – Rhai library info -* [Discord Chat](https://discord.gg/HquqbYFcZ9) - Rhai channel +* [Discord Chat](https://discord.gg/HquqbYFcZ9) – Rhai channel -* [Reddit](https://www.reddit.com/r/Rhai) - Rhai community +* [Reddit](https://www.reddit.com/r/Rhai) – Rhai community External Tools -------------- -* [Online Playground][playground] - Run Rhai scripts directly from an editor in the browser +* [Online Playground][playground] – Run Rhai scripts directly from an editor in the browser -* [`rhai-doc`] - Rhai script documentation tool +* [`rhai-doc`] – Rhai script documentation tool Other Cool Projects ------------------- -* [ChaiScript](http://chaiscript.com) - A strong inspiration for Rhai. An embedded scripting language for C++. +* [ChaiScript](http://chaiscript.com) – A strong inspiration for Rhai. An embedded scripting language for C++. * Check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust) diff --git a/doc/src/engine/call-fn.md b/doc/src/engine/call-fn.md index 466ec1ee..548b058a 100644 --- a/doc/src/engine/call-fn.md +++ b/doc/src/engine/call-fn.md @@ -3,7 +3,7 @@ Calling Rhai Functions from Rust {{#include ../links.md}} -Rhai also allows working _backwards_ from the other direction - i.e. calling a Rhai-scripted function +Rhai also allows working _backwards_ from the other direction – i.e. calling a Rhai-scripted function from Rust via `Engine::call_fn`. Functions declared with `private` are hidden and cannot be called from Rust (see also [modules]). @@ -56,8 +56,8 @@ let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?; ``` -Low-Level API - `Engine::call_fn_dynamic` ----------------------------------------- +Low-Level API – `Engine::call_fn_dynamic` +---------------------------------------------- For more control, construct all arguments as `Dynamic` values and use `Engine::call_fn_dynamic`, passing it anything that implements `AsMut` (such as a simple array or a `Vec`): diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index 99e02cf5..97747072 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -33,8 +33,8 @@ Where This Might Be Useful * Where you just want to confuse your user and make their lives miserable, because you can. -Step One - Design The Syntax ---------------------------- +Step One – Design The Syntax +--------------------------------- A custom syntax is simply a list of symbols. @@ -48,11 +48,11 @@ These symbol types can be used: * Identifiers following the [variable] naming rules. -* `$expr$` - any valid expression, statement or statement block. +* `$expr$` – any valid expression, statement or statement block. -* `$block$` - any valid statement block (i.e. must be enclosed by `'{'` .. `'}'`). +* `$block$` – any valid statement block (i.e. must be enclosed by `'{'` .. `'}'`). -* `$ident$` - any [variable] name. +* `$ident$` – any [variable] name. ### The First Symbol Must be an Identifier @@ -103,8 +103,8 @@ print(hello); // variable declared by a custom syntax persists! ``` -Step Two - Implementation -------------------------- +Step Two – Implementation +------------------------------ Any custom syntax must include an _implementation_ of it. @@ -176,8 +176,8 @@ let result = context.eval_expression_tree(expression)?; ``` -Step Three - Register the Custom Syntax --------------------------------------- +Step Three – Register the Custom Syntax +-------------------------------------------- Use `Engine::register_custom_syntax` to register a custom syntax. @@ -244,8 +244,8 @@ exec |x| -> { x += 1 } while x < 0; ``` -Step Four - Disable Unneeded Statement Types -------------------------------------------- +Step Four – Disable Unneeded Statement Types +------------------------------------------------- When a DSL needs a custom syntax, most likely than not it is extremely specialized. Therefore, many statement types actually may not make sense under the same usage scenario. @@ -258,24 +258,24 @@ the scenario. A keyword or operator that is disabled can still be used in a custom syntax. In an extreme case, it is possible to disable _every_ keyword in the language, leaving only -custom syntax (plus possibly expressions). But again, Don't Do It™ - unless you are certain +custom syntax (plus possibly expressions). But again, Don't Do It™ – unless you are certain of what you're doing. -Step Five - Document --------------------- +Step Five – Document +------------------------- For custom syntax, documentation is crucial. Make sure there are _lots_ of examples for users to follow. -Step Six - Profit! ------------------- +Step Six – Profit! +------------------------ -Really Advanced - Custom Parsers -------------------------------- +Really Advanced – Custom Parsers +------------------------------------- Sometimes it is desirable to have multiple custom syntax starting with the same symbol. This is especially common for _command-style_ syntax where the @@ -395,8 +395,8 @@ Most strings are [`ImmutableString`][string]'s so it is usually more efficient t The return value is `Result, ParseError>` where: -| Value | Description | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Ok(None)` | parsing complete and there are no more symbols to match | -| `Ok(Some(symbol))` | the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$` | -| `Err(ParseError)` | error that is reflected back to the [`Engine`] - normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate that there is a syntax error, but it can be any `ParseError`. | +| Value | Description | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Ok(None)` | parsing complete and there are no more symbols to match | +| `Ok(Some(symbol))` | the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$` | +| `Err(ParseError)` | error that is reflected back to the [`Engine`] – normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate that there is a syntax error, but it can be any `ParseError`. | diff --git a/doc/src/engine/expressions.md b/doc/src/engine/expressions.md index a87a901c..cb3ea9b2 100644 --- a/doc/src/engine/expressions.md +++ b/doc/src/engine/expressions.md @@ -11,7 +11,7 @@ In these cases, use the `Engine::compile_expression` and `Engine::eval_expressio let result = engine.eval_expression::("2 + (10 + 10) * 2")?; ``` -When evaluating _expressions_, no full-blown statement (e.g. `if`, `while`, `for`, `fn`) - not even variable assignment - +When evaluating _expressions_, no full-blown statement (e.g. `if`, `while`, `for`, `fn`) – not even variable assignment – is supported and will be considered parse errors when encountered. [Closures] and [anonymous functions] are also not supported because in the background they compile to functions. diff --git a/doc/src/engine/hello-world.md b/doc/src/engine/hello-world.md index 908a8cf8..241d282b 100644 --- a/doc/src/engine/hello-world.md +++ b/doc/src/engine/hello-world.md @@ -43,7 +43,7 @@ Return Type The type parameter for `Engine::eval` is used to specify the type of the return value, which _must_ match the actual type or an error is returned. Rhai is very strict here. -There are two ways to specify the return type - _turbofish_ notation, or type inference. +There are two ways to specify the return type – _turbofish_ notation, or type inference. Use [`Dynamic`] for uncertain return types. diff --git a/doc/src/engine/metadata/gen_fn_sig.md b/doc/src/engine/metadata/gen_fn_sig.md index 8eab0d0c..fe1df178 100644 --- a/doc/src/engine/metadata/gen_fn_sig.md +++ b/doc/src/engine/metadata/gen_fn_sig.md @@ -37,12 +37,12 @@ A function registered under the name `foo` with three parameters and unknown ret > `foo(_, _, _)` -An operator function - again, unknown parameters and return type. +An operator function – again, unknown parameters and return type. Notice that function names do not need to be valid identifiers. > `+(_, _)` -A [property setter][getters/setters] - again, unknown parameters and return type. +A [property setter][getters/setters] – again, unknown parameters and return type. Notice that function names do not need to be valid identifiers. In this case, the first parameter should be `&mut T` of the custom type and the return value is `()`: diff --git a/doc/src/engine/optimize/optimize-levels.md b/doc/src/engine/optimize/optimize-levels.md index a1f867c1..c9e3015f 100644 --- a/doc/src/engine/optimize/optimize-levels.md +++ b/doc/src/engine/optimize/optimize-levels.md @@ -5,7 +5,7 @@ Optimization Levels There are three levels of optimization: `None`, `Simple` and `Full`. -* `None` is obvious - no optimization on the AST is performed. +* `None` is obvious – no optimization on the AST is performed. * `Simple` (default) performs only relatively _safe_ optimizations without causing side-effects (i.e. it only relies on static analysis and [built-in operators] for constant [standard types], diff --git a/doc/src/engine/optimize/semantics.md b/doc/src/engine/optimize/semantics.md index db014688..86aa51b9 100644 --- a/doc/src/engine/optimize/semantics.md +++ b/doc/src/engine/optimize/semantics.md @@ -17,7 +17,7 @@ if true { // condition always true foo(42) // <- the above optimizes to this ``` -If the original script were evaluated instead, it would have been an error - the variable `hello` does not exist, +If the original script were evaluated instead, it would have been an error – the variable `hello` does not exist, so the script would have been terminated at that point with an error return. In fact, any errors inside a statement that has been eliminated will silently _disappear_: diff --git a/doc/src/engine/optimize/volatility.md b/doc/src/engine/optimize/volatility.md index 26287aaf..dcd1f487 100644 --- a/doc/src/engine/optimize/volatility.md +++ b/doc/src/engine/optimize/volatility.md @@ -6,7 +6,7 @@ Volatility Considerations for Full Optimization Level Even if a custom function does not mutate state nor cause side-effects, it may still be _volatile_, i.e. it _depends_ on the external environment and is not _pure_. -A perfect example is a function that gets the current time - obviously each run will return a different value! +A perfect example is a function that gets the current time – obviously each run will return a different value! The optimizer, when using [`OptimizationLevel::Full`], will _merrily assume_ that all functions are _pure_, so when it finds constant arguments (or none) it eagerly executes the function call and replaces it with the result. diff --git a/doc/src/engine/scope.md b/doc/src/engine/scope.md index 27f5d9b8..067fe077 100644 --- a/doc/src/engine/scope.md +++ b/doc/src/engine/scope.md @@ -1,4 +1,4 @@ -`Scope` - Initializing and Maintaining State +`Scope` – Initializing and Maintaining State ================================================= {{#include ../links.md}} @@ -37,7 +37,7 @@ scope // First invocation engine.eval_with_scope::<()>(&mut scope, r" - let x = 4 + 5 - y + z + MY_NUMBER + s.len; + let x = 4 + 5 – y + z + MY_NUMBER + s.len; y = 1; ")?; @@ -46,7 +46,7 @@ let result = engine.eval_with_scope::(&mut scope, "x")?; println!("result: {}", result); // prints 1102 -// Variable y is changed in the script - read it with 'get_value' +// 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' diff --git a/doc/src/language/constants.md b/doc/src/language/constants.md index edda3d4e..31398f42 100644 --- a/doc/src/language/constants.md +++ b/doc/src/language/constants.md @@ -61,11 +61,11 @@ r" ``` -Caveat - Constants Can be Modified via Rust ------------------------------------------- +Caveat – Constants Can be Modified via Rust +------------------------------------------------ A custom type stored as a constant cannot be modified via script, but _can_ be modified via -a registered Rust function that takes a first `&mut` parameter - because there is no way for +a registered Rust function that takes a first `&mut` parameter – because there is no way for Rhai to know whether the Rust function modifies its argument! ```rust diff --git a/doc/src/language/dynamic.md b/doc/src/language/dynamic.md index a143d519..8c10d9f8 100644 --- a/doc/src/language/dynamic.md +++ b/doc/src/language/dynamic.md @@ -32,7 +32,7 @@ switch type_of(mystery) { Functions Returning `Dynamic` ---------------------------- -In Rust, sometimes a `Dynamic` forms part of a returned value - a good example is an [array] +In Rust, sometimes a `Dynamic` forms part of a returned value – a good example is an [array] which contains `Dynamic` elements, or an [object map] which contains `Dynamic` property values. To get the _real_ values, the actual value types _must_ be known in advance. diff --git a/doc/src/language/fn-anon.md b/doc/src/language/fn-anon.md index 49ee13c2..a2e45706 100644 --- a/doc/src/language/fn-anon.md +++ b/doc/src/language/fn-anon.md @@ -50,8 +50,8 @@ fn anon_fn_1002() { print this.data; } ``` -WARNING - NOT Real Closures --------------------------- +WARNING – NOT Real Closures +-------------------------------- Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves **not** real closures. diff --git a/doc/src/language/fn-capture.md b/doc/src/language/fn-capture.md index 400241ca..599b211d 100644 --- a/doc/src/language/fn-capture.md +++ b/doc/src/language/fn-capture.md @@ -13,8 +13,8 @@ access to the calling environment. When a function accesses a variable that is not defined within that function's scope, it raises an evaluation error. -It is possible, through a special syntax, to capture the calling scope - i.e. the scope -that makes the function call - and access variables defined there. +It is possible, through a special syntax, to capture the calling scope – i.e. the scope +that makes the function call – and access variables defined there. ```rust fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined diff --git a/doc/src/language/fn-closure.md b/doc/src/language/fn-closure.md index e4f59fe7..985a67f3 100644 --- a/doc/src/language/fn-closure.md +++ b/doc/src/language/fn-closure.md @@ -10,7 +10,7 @@ Since [anonymous functions] de-sugar to standard function definitions, they reta Rhai functions, including being _pure_, having no access to external variables. The anonymous function syntax, however, automatically _captures_ variables that are not defined within -the current scope, but are defined in the external scope - i.e. the scope where the anonymous function +the current scope, but are defined in the external scope – i.e. the scope where the anonymous function is created. Variables that are accessible during the time the [anonymous function] is created can be captured, @@ -83,14 +83,14 @@ for f in funcs { ``` -Therefore - Be Careful to Prevent Data Races -------------------------------------------- +Therefore – Be Careful to Prevent Data Races +------------------------------------------------- Rust does not have data races, but that doesn't mean Rhai doesn't. Avoid performing a method call on a captured shared variable (which essentially takes a mutable reference to the shared object) while using that same variable as a parameter -in the method call - this is a sure-fire way to generate a data race error. +in the method call – this is a sure-fire way to generate a data race error. If a shared value is used as the `this` pointer in a method call to a closure function, then the same shared value _must not_ be captured inside that function, or a data race @@ -143,7 +143,7 @@ The actual implementation of closures de-sugars to: 1. Keeping track of what variables are accessed inside the anonymous function, 2. If a variable is not defined within the anonymous function's scope, it is looked up _outside_ the function and - in the current execution scope - where the anonymous function is created. + in the current execution scope – where the anonymous function is created. 3. The variable is added to the parameters list of the anonymous function, at the front. @@ -158,13 +158,13 @@ The actual implementation of closures de-sugars to: ### Q: Why are closures implemented as automatic currying? -In concept, a closure _closes_ over captured variables from the outer scope - that's why +In concept, a closure _closes_ over captured variables from the outer scope – that's why they are called _closures_. When this happen, a typical language implementation hoists those variables that are captured away from the stack frame and into heap-allocated storage. This is because those variables may be needed after the stack frame goes away. These heap-allocated captured variables only go away when all the closures that need them -are finished with them. A garbage collector makes this trivial to implement - they are +are finished with them. A garbage collector makes this trivial to implement – they are automatically collected as soon as all closures needing them are destroyed. In Rust, this can be done by reference counting instead, with the potential pitfall of creating diff --git a/doc/src/language/fn-namespaces.md b/doc/src/language/fn-namespaces.md index 95bcfb41..47489acf 100644 --- a/doc/src/language/fn-namespaces.md +++ b/doc/src/language/fn-namespaces.md @@ -63,7 +63,7 @@ There is one _global_ namespace for every [`Engine`], which includes (in the fol Anywhere in a Rhai script, when a function call is made, the function is searched within the global namespace, in the above search order. -Therefore, function calls in Rhai are _late_ bound - meaning that the function called cannot be +Therefore, function calls in Rhai are _late_ bound – meaning that the function called cannot be determined or guaranteed and there is no way to _lock down_ the function being called. This aspect is very similar to JavaScript before ES6 modules. diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index 1a896346..980c4799 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -125,8 +125,8 @@ x == 500; // 'x' is NOT changed! ``` -`this` - Simulating an Object Method ------------------------------------ +`this` – Simulating an Object Method +----------------------------------------- Script-defined functions can also be called in method-call style. When this happens, the keyword '`this`' binds to the object in the method call and can be changed. diff --git a/doc/src/language/json.md b/doc/src/language/json.md index 0d82b64a..26e9f81f 100644 --- a/doc/src/language/json.md +++ b/doc/src/language/json.md @@ -6,7 +6,7 @@ Parse an Object Map from JSON The syntax for an [object map] is extremely similar to the JSON representation of a object hash, with the exception of `null` values which can technically be mapped to [`()`]. -A valid JSON string does not start with a hash character `#` while a Rhai [object map] does - that's the major difference! +A valid JSON string does not start with a hash character `#` while a Rhai [object map] does – that's the major difference! Use the `Engine::parse_json` method to parse a piece of JSON into an object map. The JSON text must represent a single object hash (i.e. must be wrapped within "`{ .. }`") diff --git a/doc/src/language/logic.md b/doc/src/language/logic.md index edb1ae41..9f6ebbcf 100644 --- a/doc/src/language/logic.md +++ b/doc/src/language/logic.md @@ -60,7 +60,7 @@ Boolean operators | \|\| | boolean _OR_ | yes | | \| | boolean _OR_ | no | -Double boolean operators `&&` and `||` _short-circuit_ - meaning that the second operand will not be evaluated +Double boolean operators `&&` and `||` _short-circuit_ – meaning that the second operand will not be evaluated if the first one already proves the condition wrong. Single boolean operators `&` and `|` always evaluate both operands. diff --git a/doc/src/language/method.md b/doc/src/language/method.md index 6ca91a99..5843faef 100644 --- a/doc/src/language/method.md +++ b/doc/src/language/method.md @@ -15,7 +15,7 @@ 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. +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. @@ -59,7 +59,7 @@ The following table illustrates the differences: Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone, even when the intention is not to mutate that argument, because it avoids cloning that argument value. -Even when a function is never intended to be a method - for example an operator, +Even when a function is never intended to be a method – for example an operator, it is still sometimes beneficial to make it method-like (i.e. with a first `&mut` parameter) if the first parameter is not modified. diff --git a/doc/src/language/modules/import.md b/doc/src/language/modules/import.md index ed8918da..d335beeb 100644 --- a/doc/src/language/modules/import.md +++ b/doc/src/language/modules/import.md @@ -78,7 +78,7 @@ for x in range(0, 1000) { Recursive Imports ---------------- -Beware of _import cycles_ - i.e. recursively loading the same module. This is a sure-fire way to +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: diff --git a/doc/src/language/numbers.md b/doc/src/language/numbers.md index 33c3c930..78a4f236 100644 --- a/doc/src/language/numbers.md +++ b/doc/src/language/numbers.md @@ -10,7 +10,7 @@ The default system integer type (also aliased to `INT`) is `i64`. It can be turn Floating-point numbers are also supported if not disabled with [`no_float`]. The default system floating-point type is `i64` (also aliased to `FLOAT`). It can be turned into `f32` via the [`f32_float`] feature. -'`_`' separators can be added freely and are ignored within a number - except at the very beginning or right after +'`_`' separators can be added freely and are ignored within a number – except at the very beginning or right after a decimal point ('`.`'). | Format | Type | diff --git a/doc/src/language/object-maps.md b/doc/src/language/object-maps.md index 3d24f7f5..8dcdbef5 100644 --- a/doc/src/language/object-maps.md +++ b/doc/src/language/object-maps.md @@ -150,7 +150,7 @@ In order not to affect the speed of accessing properties in an object map, new p property access. A property [getter][getters/setters] function registered via `Engine::register_get`, for example, -for a `Map` will never be found - instead, the property will be looked up in the object map. +for a `Map` will never be found – instead, the property will be looked up in the object map. Therefore, _method-call_ notation must be used for built-in properties: diff --git a/doc/src/language/overload.md b/doc/src/language/overload.md index 0b6b4ea8..2ec2fd4e 100644 --- a/doc/src/language/overload.md +++ b/doc/src/language/overload.md @@ -4,7 +4,7 @@ Function Overloading {{#include ../links.md}} [Functions] defined in script can be _overloaded_ by _arity_ (i.e. they are resolved purely upon the function's _name_ -and _number_ of parameters, but not parameter _types_ since all parameters are the same type - [`Dynamic`]). +and _number_ of parameters, but not parameter _types_ since all parameters are the same type – [`Dynamic`]). New definitions _overwrite_ previous definitions of the same name and number of parameters. diff --git a/doc/src/language/strings-chars.md b/doc/src/language/strings-chars.md index e9f5e074..f814288e 100644 --- a/doc/src/language/strings-chars.md +++ b/doc/src/language/strings-chars.md @@ -24,7 +24,7 @@ An `ImmutableString` does not change and can be shared. Modifying an `ImmutableString` causes it first to be cloned, and then the modification made to the copy. -### **IMPORTANT** - Avoid `String` Parameters +### **IMPORTANT** – Avoid `String` Parameters `ImmutableString` should be used in place of `String` for function parameters because using `String` is very inefficient (the `String` argument is cloned during every call). diff --git a/doc/src/language/switch.md b/doc/src/language/switch.md index 2d05787c..08d380ee 100644 --- a/doc/src/language/switch.md +++ b/doc/src/language/switch.md @@ -75,8 +75,8 @@ Switching on [arrays] is very useful when working with Rust enums (see [this cha for more details). -Difference From `if` - `else if` Chain -------------------------------------- +Difference From `if`-`else if` Chain +----------------------------------- Although a `switch` expression looks _almost_ the same as an `if`-`else if` chain, there are subtle differences between the two. @@ -98,8 +98,8 @@ efficient, but it also means that [overloading][operator overloading] the `==` operator will have no effect. Therefore, in environments where it is desirable to [overload][operator overloading] -the `==` operator - though it is difficult to think of valid scenarios where you'd want -`1 == 1` to return something other than `true` - avoid using the `switch` expression. +the `==` operator – though it is difficult to think of valid scenarios where you'd want +`1 == 1` to return something other than `true` – avoid using the `switch` expression. ### Efficiency diff --git a/doc/src/language/try-catch.md b/doc/src/language/try-catch.md index 73d4da2f..a132fc61 100644 --- a/doc/src/language/try-catch.md +++ b/doc/src/language/try-catch.md @@ -102,7 +102,7 @@ Non-Catchable Exceptions Some exceptions _cannot_ be caught: * Syntax error during parsing -* System error - e.g. script file not found +* System error – e.g. script file not found * Script evaluation metrics over [safety limits]({{rootUrl}}/safety/index.md) * Function calls nesting exceeding [maximum call stack depth] * Script evaluation manually terminated diff --git a/doc/src/language/type-of.md b/doc/src/language/type-of.md index 87171d06..45e3d0b0 100644 --- a/doc/src/language/type-of.md +++ b/doc/src/language/type-of.md @@ -31,9 +31,9 @@ Custom Types `type_of()` a [custom type] returns: -* if registered via `Engine::register_type_with_name` - the registered name +* if registered via `Engine::register_type_with_name` – the registered name -* if registered via `Engine::register_type` - the full Rust path name +* if registered via `Engine::register_type` – the full Rust path name ```rust struct TestStruct1; diff --git a/doc/src/language/variables.md b/doc/src/language/variables.md index fd211e19..d2582906 100644 --- a/doc/src/language/variables.md +++ b/doc/src/language/variables.md @@ -6,7 +6,7 @@ Variables Valid Names ----------- -Variables in Rhai follow normal C naming rules - must contain only ASCII letters, digits and underscores '`_`', +Variables in Rhai follow normal C naming rules – must contain only ASCII letters, digits and underscores '`_`', and cannot start with a digit. For example: '`_c3po`' and '`r2d2`' are valid variable names, but '`3abc`' is not. diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md index 31d1c6f9..d76e62f9 100644 --- a/doc/src/patterns/enums.md +++ b/doc/src/patterns/enums.md @@ -228,7 +228,7 @@ let x = switch [value.type, value.field_0, value.field_1] { ``` Usually, a helper method returns an array of values that can uniquely determine -the switch case based on actual usage requirements - which means that it probably +the switch case based on actual usage requirements – which means that it probably skips fields that contain data instead of discriminants. Then `switch` is used to very quickly match through a large number of array shapes diff --git a/doc/src/patterns/multiple.md b/doc/src/patterns/multiple.md index 1abbe942..e7806265 100644 --- a/doc/src/patterns/multiple.md +++ b/doc/src/patterns/multiple.md @@ -45,9 +45,9 @@ Multiple Instantiations of Rhai Within The Same Project The trick is to differentiate between multiple identical copies of Rhai, each having a different [features] set, by their _sources_: -* Different versions from [`crates.io`](https://crates.io/crates/rhai/) - The official crate. +* Different versions from [`crates.io`](https://crates.io/crates/rhai/) – The official crate. -* Different releases from [`GitHub`](https://github.com/jonathandturner/rhai) - Crate source on GitHub. +* Different releases from [`GitHub`](https://github.com/jonathandturner/rhai) – Crate source on GitHub. * Forked copy of [https://github.com/jonathandturner/rhai](https://github.com/jonathandturner/rhai) on GitHub. @@ -73,14 +73,14 @@ If more than four different instantiations of Rhai is necessary (why?), create m or GitHub forks or branches. -Caveat - No Way To Avoid Dependency Conflicts --------------------------------------------- +Caveat – No Way To Avoid Dependency Conflicts +-------------------------------------------------- Unfortunately, pulling in Rhai from different sources do not resolve the problem of [features] conflict between dependencies. Even overriding `crates.io` via the `[patch]` manifest -section doesn't work - all dependencies will eventually find the only one copy. +section doesn't work – all dependencies will eventually find the only one copy. -What is necessary - multiple copies of Rhai, one for each dependent crate that requires it, +What is necessary – multiple copies of Rhai, one for each dependent crate that requires it, together with their _unique_ [features] set intact. In other words, turning off Cargo's crate merging feature _just for Rhai_. diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md index bab38bc5..fad53a03 100644 --- a/doc/src/plugins/function.md +++ b/doc/src/plugins/function.md @@ -24,7 +24,7 @@ Macros Apply `#[export_fn]` onto a function defined at _module level_ to convert it into a Rhai plugin function. -The function cannot be nested inside another function - it can only be defined directly under a module. +The function cannot be nested inside another function – it can only be defined directly under a module. To register the plugin function, simply call `register_exported_fn!`. The name of the function can be any text string, so it is possible to register _overloaded_ functions as well as operators. diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index c6ea73a0..d8ebbd41 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -32,7 +32,7 @@ This Rust module can then be registered into an [`Engine`] as a normal [module]. This is done via the `exported_module!` macro. The macro `combine_with_exported_module!` can be used to _combine_ all the functions -and variables into an existing [module], _flattening_ the namespace - i.e. all sub-modules +and variables into an existing [module], _flattening_ the namespace – i.e. all sub-modules are eliminated and their contents promoted to the top level. This is typical for developing [custom packages]. diff --git a/doc/src/rust/modules/ast.md b/doc/src/rust/modules/ast.md index 80dd7c5a..0b2bb8b8 100644 --- a/doc/src/rust/modules/ast.md +++ b/doc/src/rust/modules/ast.md @@ -16,7 +16,7 @@ prepare a Rhai script for this purpose as well as to control which functions/var When given an [`AST`], it is first evaluated, then the following items are exposed as members of the new [module]: -* Global variables - all variables exported via the `export` statement (those not exported remain hidden). +* Global variables – all variables exported via the `export` statement (those not exported remain hidden). * Functions not specifically marked `private`. diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index d6d3496a..f5e10e97 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -19,8 +19,8 @@ Manually creating a [module] is possible via the `Module` API. For the complete `Module` API, refer to the [documentation](https://docs.rs/rhai/{{version}}/rhai/struct.Module.html) online. -Use Case 1 - Make the `Module` Globally Available ------------------------------------------------- +Use Case 1 – Make the `Module` Globally Available +------------------------------------------------------ `Engine::register_global_module` registers a shared [module] into the _global_ namespace. @@ -62,8 +62,8 @@ engine.register_fn("inc", |x: i64| x + 1); engine.eval::("inc(41)")? == 42; // no need to import module ``` -Use Case 2 - Make the `Module` a Static Module ---------------------------------------------- +Use Case 2 – Make the `Module` a Static Module +--------------------------------------------------- `Engine::register_static_module` registers a [module] and under a specific module namespace. @@ -122,8 +122,8 @@ engine.eval::("let x = 41; inc(x)")? == 42; ``` -Use Case 3 - Make the `Module` Dynamically Loadable --------------------------------------------------- +Use Case 3 – Make the `Module` Dynamically Loadable +-------------------------------------------------------- In order to dynamically load a custom module, there must be a [module resolver] which serves the module when loaded via `import` statements. diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index d51e129d..c2b094e7 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -100,8 +100,8 @@ When there is a mutable reference to the `this` object (i.e. the first argument) there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain. -Example - Passing a Callback to a Rust Function ----------------------------------------------- +Example – Passing a Callback to a Rust Function +---------------------------------------------------- The low-level API is useful when there is a need to interact with the scripting [`Engine`] within a function. @@ -147,8 +147,8 @@ let result = engine.eval::( ``` -TL;DR - Why `read_lock` and `write_lock` ---------------------------------------- +TL;DR – Why `read_lock` and `write_lock` +--------------------------------------------- The `Dynamic` API that casts it to a reference to a particular data type is `read_lock` (for an immutable reference) and `write_lock` (for a mutable reference). diff --git a/doc/src/rust/serde.md b/doc/src/rust/serde.md index a1a01ae2..38ef690a 100644 --- a/doc/src/rust/serde.md +++ b/doc/src/rust/serde.md @@ -108,7 +108,7 @@ let x: MyStruct = from_dynamic(&result)?; Cannot Deserialize Shared Values ------------------------------- -A [`Dynamic`] containing a _shared_ value cannot be deserialized - i.e. it will give a type error. +A [`Dynamic`] containing a _shared_ value cannot be deserialized – i.e. it will give a type error. Use `Dynamic::flatten` to obtain a cloned copy before deserialization (if the value is not shared, it is simply returned and not cloned). diff --git a/doc/src/rust/strings.md b/doc/src/rust/strings.md index 197501e4..737bb207 100644 --- a/doc/src/rust/strings.md +++ b/doc/src/rust/strings.md @@ -9,7 +9,7 @@ Avoid `String` As must as possible, avoid using `String` parameters in functions. -Each `String` argument is cloned during every single call to that function - and the copy +Each `String` argument is cloned during every single call to that function – and the copy immediately thrown away right after the call. Needless to say, it is _extremely_ inefficient to use `String` parameters. diff --git a/doc/src/safety/max-operations.md b/doc/src/safety/max-operations.md index fe42647d..86d85fc1 100644 --- a/doc/src/safety/max-operations.md +++ b/doc/src/safety/max-operations.md @@ -29,7 +29,7 @@ engine.set_max_operations(0); // allow unlimited operations What Does One _Operation_ Mean ----------------------------- -The concept of one single _operation_ in Rhai is volatile - it roughly equals one expression node, +The concept of one single _operation_ in Rhai is volatile – it roughly equals one expression node, loading one variable/constant, one operator call, one iteration of a loop, or one function call etc. with sub-expressions, statements and function calls executed inside these contexts accumulated on top. diff --git a/doc/src/safety/sandbox.md b/doc/src/safety/sandbox.md index 60e35534..798a73ac 100644 --- a/doc/src/safety/sandbox.md +++ b/doc/src/safety/sandbox.md @@ -1,5 +1,5 @@ -Sand-Boxing - Block Access to External Data -========================================== +Sand-Boxing – Block Access to External Data +================================================ {{#include ../links.md}} diff --git a/doc/src/start/bin.md b/doc/src/start/bin.md new file mode 100644 index 00000000..9e180a37 --- /dev/null +++ b/doc/src/start/bin.md @@ -0,0 +1,24 @@ +Packaged Utilities +================== + +{{#include ../links.md}} + +A number of Rhai-driven utility programs can be found in the `src/bin` directory: + +| Utility program | Description | +| :-----------------------------------------------: | ----------------------------------------------------------- | +| [`rhai-repl`]({{repoTree}}/examples/rhai-repl.rs) | a simple REPL, interactively evaluate statements from stdin | +| [`rhai-run`]({{repoTree}}/examples/rhai-run.rs) | runs each filename passed to it as a Rhai script | + +`rhai-repl` is particularly useful – it allows one to interactively try out Rhai's +language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop). + + +Running a Utility Program +------------------------- + +Utilities can be run with the following command: + +```bash +cargo run --bin {program_name} +``` diff --git a/doc/src/start/builds/minimal.md b/doc/src/start/builds/minimal.md index 45882382..c916baea 100644 --- a/doc/src/start/builds/minimal.md +++ b/doc/src/start/builds/minimal.md @@ -6,7 +6,7 @@ Minimal Build Configuration ------------- -In order to compile a _minimal_ build - i.e. a build optimized for size - perhaps for `no-std` embedded targets or for +In order to compile a _minimal_ build – i.e. a build optimized for size – perhaps for `no-std` embedded targets or for compiling to [WASM], it is essential that the correct linker flags are used in `cargo.toml`: ```toml diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md index 35ee122b..859e1782 100644 --- a/doc/src/start/builds/performance.md +++ b/doc/src/start/builds/performance.md @@ -9,7 +9,7 @@ Some features are for performance. For example, using [`only_i32`] or [`only_i6 Use Only One Integer Type ------------------------ -If only a single integer type is needed in scripts - most of the time this is the case - it is best to avoid registering +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, [`Engine`] creation will be faster because fewer functions need to be loaded. @@ -19,7 +19,7 @@ The [`only_i32`] and [`only_i64`] features disable all integer types except `i32 Use Only 32-Bit Numbers ---------------------- -If only 32-bit integers are needed - again, most of the time this is the case - turn on [`only_i32`]. +If only 32-bit integers are needed – again, most of the time this is the case – turn on [`only_i32`]. Under this feature, only `i32` is supported as a built-in integer type and no others. On 64-bit targets this may not gain much, but on certain 32-bit targets this improves performance diff --git a/doc/src/start/builds/wasm.md b/doc/src/start/builds/wasm.md index 0875a778..27b85e77 100644 --- a/doc/src/start/builds/wasm.md +++ b/doc/src/start/builds/wasm.md @@ -7,7 +7,7 @@ It is possible to use Rhai when compiling to WebAssembly (WASM). This yields a scripting engine (and language) that can be run in a standard web browser. Why you would _want_ to is another matter... as there is already a nice, fast, complete scripting language -for the the common WASM environment (i.e. a browser) - and it is called JavaScript. +for the the common WASM environment (i.e. a browser) – and it is called JavaScript. But anyhow, do it because you _can_! @@ -39,12 +39,12 @@ 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`] | WASM supports 32-bit and 64-bit integers, but most scripts will only need 32-bit. | -| [`f32_float`] | WASM supports 32-bit single-precision and 64-bit double-precision floating-point numbers, but single-precision is usually fine for most uses. | -| [`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. | +| 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`] | WASM supports 32-bit and 64-bit integers, but most scripts will only need 32-bit. | +| [`f32_float`] | WASM supports 32-bit single-precision and 64-bit double-precision floating-point numbers, but single-precision is usually fine for most uses. | +| [`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: diff --git a/doc/src/start/examples/rust.md b/doc/src/start/examples/rust.md index 0b343a52..b46d7249 100644 --- a/doc/src/start/examples/rust.md +++ b/doc/src/start/examples/rust.md @@ -11,15 +11,10 @@ A number of examples can be found in the `examples` directory: | [`custom_types_and_methods`]({{repoTree}}/examples/custom_types_and_methods.rs) | shows how to register a custom Rust type and methods for it | | [`hello`]({{repoTree}}/examples/hello.rs) | simple example that evaluates an expression and prints the result | | [`reuse_scope`]({{repoTree}}/examples/reuse_scope.rs) | evaluates two pieces of code in separate runs, but using a common [`Scope`] | -| [`rhai-repl`]({{repoTree}}/examples/rhai-repl.rs) | a simple REPL, interactively evaluate statements from stdin | -| [`rhai-run`]({{repoTree}}/examples/rhai-run.rs) | runs each filename passed to it as a Rhai script | | [`serde`]({{repoTree}}/examples/serde.rs) | example to serialize and deserialize Rust types with [`serde`](https://crates.io/crates/serde).
The [`serde`] feature is required to run | | [`simple_fn`]({{repoTree}}/examples/simple_fn.rs) | shows how to register a simple function | | [`strings`]({{repoTree}}/examples/strings.rs) | shows different ways to register functions taking string arguments | -The `rhai-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). - Running Examples ---------------- diff --git a/doc/src/start/examples/scripts.md b/doc/src/start/examples/scripts.md index 8de664a5..80fe4908 100644 --- a/doc/src/start/examples/scripts.md +++ b/doc/src/start/examples/scripts.md @@ -46,8 +46,8 @@ The following scripts are for benchmarking the speed of Rhai: Running Example Scripts ---------------------- -The [`rhai-run`](../examples/rust.md) example can be used to run the scripts: +The [`rhai-run`](../bin.md) utility can be used to run Rhai scripts: ```bash -cargo run --example rhai-run scripts/any_script.rhai +cargo run --bin rhai-run scripts/any_script.rhai ``` diff --git a/doc/src/start/features.md b/doc/src/start/features.md index 06e868db..d1db695b 100644 --- a/doc/src/start/features.md +++ b/doc/src/start/features.md @@ -38,7 +38,7 @@ Example The `Cargo.toml` configuration below turns on these six features: * `sync` (everything `Send + Sync`) -* `unchecked` (disable all checking - 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]) @@ -56,12 +56,12 @@ nor loading external [modules]. This configuration is perfect for an expression parser in a 32-bit embedded system without floating-point hardware. -Caveat - Features Are Not Additive ---------------------------------- +Caveat – Features Are Not Additive +--------------------------------------- -Most Rhai features are not strictly _additive_ - i.e. they do not only add optional functionalities. +Most Rhai features are not strictly _additive_ – i.e. they do not only add optional functionalities. -In fact, most features are _subtractive_ - i.e. they _remove_ functionalities. +In fact, most features are _subtractive_ – i.e. they _remove_ functionalities. There is a reason for this design, because the _lack_ of a language feature by itself is a feature. diff --git a/examples/README.md b/examples/README.md index 610f388e..a3468bcb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,7 +1,7 @@ Sample Applications =================== -Sample applications written in Rhai. +Sample applications that use the Rhai scripting engine. How to Run diff --git a/src/bin/README.md b/src/bin/README.md new file mode 100644 index 00000000..1ba7e865 --- /dev/null +++ b/src/bin/README.md @@ -0,0 +1,12 @@ +Rhai Tools +========== + +Tools written in Rhai. + + +How to Run +---------- + +```bash +cargo run --bin sample_app_to_run +``` diff --git a/examples/rhai-repl.rs b/src/bin/rhai-repl.rs similarity index 76% rename from examples/rhai-repl.rs rename to src/bin/rhai-repl.rs index f60a0a81..88a372d5 100644 --- a/examples/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -1,10 +1,16 @@ -use rhai::{Dynamic, Engine, EvalAltResult, Scope, AST}; +use rhai::{Dynamic, Engine, EvalAltResult, Module, Scope, AST}; #[cfg(not(feature = "no_optimize"))] use rhai::OptimizationLevel; -use std::io::{stdin, stdout, Write}; +use std::{ + env, + fs::File, + io::{stdin, stdout, Read, Write}, + process::exit, +}; +/// Pretty-print error. fn print_error(input: &str, err: EvalAltResult) { let lines: Vec<_> = input.trim().split('\n').collect(); let pos = err.position(); @@ -19,16 +25,17 @@ fn print_error(input: &str, err: EvalAltResult) { "".to_string() }; - // Print error + // Print error position let pos_text = format!(" ({})", pos); if pos.is_none() { // No position println!("{}", err); } else { - // Specific position + // Specific position - print line text println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); + // Display position marker println!( "{0:>1$} {2}", "^", @@ -38,6 +45,7 @@ fn print_error(input: &str, err: EvalAltResult) { } } +/// Print help text. fn print_help() { println!("help => print this help"); println!("quit, exit => quit"); @@ -52,6 +60,63 @@ fn print_help() { fn main() { let mut engine = Engine::new(); + println!("Rhai REPL tool"); + println!("=============="); + print_help(); + + // Load init scripts + + let mut contents = String::new(); + let mut has_init_scripts = false; + + for filename in env::args().skip(1) { + { + contents.clear(); + + let mut f = match File::open(&filename) { + Err(err) => { + eprintln!("Error reading script file: {}\n{}", filename, err); + exit(1); + } + Ok(f) => f, + }; + + if let Err(err) = f.read_to_string(&mut contents) { + println!("Error reading script file: {}\n{}", filename, err); + exit(1); + } + } + + let module = match engine + .compile(&contents) + .map_err(|err| err.into()) + .and_then(|ast| Module::eval_ast_as_new(Default::default(), &ast, &engine)) + { + Err(err) => { + eprintln!("{:=<1$}", "", filename.len()); + eprintln!("{}", filename); + eprintln!("{:=<1$}", "", filename.len()); + eprintln!(""); + + print_error(&contents, *err); + exit(1); + } + Ok(m) => m, + }; + + engine.register_global_module(module.into()); + + has_init_scripts = true; + + println!("Script '{}' loaded.", filename); + } + + if has_init_scripts { + println!(); + } + + // Setup Engine + #[cfg(not(feature = "no_optimize"))] engine.set_optimization_level(OptimizationLevel::None); @@ -62,9 +127,7 @@ fn main() { let mut ast_u: AST = Default::default(); let mut ast: AST = Default::default(); - println!("Rhai REPL tool"); - println!("=============="); - print_help(); + // REPL loop 'main_loop: loop { print!("rhai-repl> "); diff --git a/examples/rhai-run.rs b/src/bin/rhai-run.rs similarity index 96% rename from examples/rhai-run.rs rename to src/bin/rhai-run.rs index 8c202396..4453f84b 100644 --- a/examples/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -37,6 +37,8 @@ fn eprint_error(input: &str, err: EvalAltResult) { } fn main() { + let mut contents = String::new(); + for filename in env::args().skip(1) { let mut engine = Engine::new(); @@ -51,7 +53,7 @@ fn main() { Ok(f) => f, }; - let mut contents = String::new(); + contents.clear(); if let Err(err) = f.read_to_string(&mut contents) { eprintln!("Error reading script file: {}\n{}", filename, err); diff --git a/src/fn_native.rs b/src/fn_native.rs index a1d32a37..6ca51e43 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -109,13 +109,13 @@ impl<'e, 's, 'a, 'm, 'pm> NativeCallContext<'e, 's, 'a, 'm, 'pm> { pub fn new_with_all_fields( engine: &'e Engine, source: &'s Option, - mods: &'a mut Imports, + imports: &'a mut Imports, lib: &'m impl AsRef<[&'pm Module]>, ) -> Self { Self { engine, source: source.as_ref().map(|s| s.as_str()), - mods: Some(mods), + mods: Some(imports), lib: lib.as_ref(), } } diff --git a/src/parse_error.rs b/src/parse_error.rs index a9ce4e7f..376631fe 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -295,13 +295,27 @@ impl fmt::Display for ParseError { impl From for Box { #[inline(always)] fn from(err: ParseErrorType) -> Self { - Box::new(EvalAltResult::ErrorParsing(err, Position::NONE)) + Box::new(err.into()) + } +} + +impl From for EvalAltResult { + #[inline(always)] + fn from(err: ParseErrorType) -> Self { + EvalAltResult::ErrorParsing(err, Position::NONE) } } impl From for Box { #[inline(always)] fn from(err: ParseError) -> Self { - Box::new(EvalAltResult::ErrorParsing(*err.0, err.1)) + Box::new(err.into()) + } +} + +impl From for EvalAltResult { + #[inline(always)] + fn from(err: ParseError) -> Self { + EvalAltResult::ErrorParsing(*err.0, err.1) } } diff --git a/src/token.rs b/src/token.rs index 54e1f1cc..c4f8d74f 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1685,18 +1685,18 @@ pub struct TokenIterator<'a, 'e> { /// Input character stream. stream: MultiInputsStream<'a>, /// A processor function that maps a token to another. - map: fn(Token) -> Token, + map: Option Token>, } impl<'a> Iterator for TokenIterator<'a, '_> { type Item = (Token, Position); fn next(&mut self) -> Option { - let token = match get_next_token(&mut self.stream, &mut self.state, &mut self.pos) { + let (token, pos) = match get_next_token(&mut self.stream, &mut self.state, &mut self.pos) { // {EOF} - None => None, + None => return None, // Reserved keyword/symbol - Some((Token::Reserved(s), pos)) => Some((match + Some((Token::Reserved(s), pos)) => (match (s.as_str(), self.engine.custom_keywords.contains_key(&s)) { ("===", false) => Token::LexError(LERR::ImproperSymbol(s, @@ -1736,16 +1736,16 @@ impl<'a> Iterator for TokenIterator<'a, '_> { }, // Reserved keyword/operator that is not custom. (_, false) => Token::Reserved(s), - }, pos)), + }, pos), // Custom keyword Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&s) => { - Some((Token::Custom(s), pos)) + (Token::Custom(s), pos) } // Custom standard keyword/symbol - must be disabled Some((token, pos)) if self.engine.custom_keywords.contains_key(token.syntax().as_ref()) => { if self.engine.disabled_symbols.contains(token.syntax().as_ref()) { // Disabled standard keyword/symbol - Some((Token::Custom(token.syntax().into()), pos)) + (Token::Custom(token.syntax().into()), pos) } else { // Active standard keyword - should never be a custom keyword! unreachable!("{:?} is an active keyword", token) @@ -1753,16 +1753,20 @@ impl<'a> Iterator for TokenIterator<'a, '_> { } // Disabled symbol Some((token, pos)) if self.engine.disabled_symbols.contains(token.syntax().as_ref()) => { - Some((Token::Reserved(token.syntax().into()), pos)) + (Token::Reserved(token.syntax().into()), pos) } // Normal symbol - r => r, + Some(r) => r, }; - match token { - None => None, - Some((token, pos)) => Some(((self.map)(token), pos)), - } + // Run the mapper, if any + let token = if let Some(map) = self.map { + map(token) + } else { + token + }; + + Some((token, pos)) } } @@ -1773,14 +1777,23 @@ impl Engine { &'e self, input: impl IntoIterator, ) -> TokenIterator<'a, 'e> { - self.lex_with_map(input, |f| f) + self.lex_raw(input, None) } /// Tokenize an input text stream with a mapping function. - #[inline] + #[inline(always)] pub fn lex_with_map<'a, 'e>( &'e self, input: impl IntoIterator, map: fn(Token) -> Token, + ) -> TokenIterator<'a, 'e> { + self.lex_raw(input, Some(map)) + } + /// Tokenize an input text stream with an optional mapping function. + #[inline] + fn lex_raw<'a, 'e>( + &'e self, + input: impl IntoIterator, + map: Option Token>, ) -> TokenIterator<'a, 'e> { TokenIterator { engine: self,