Merge pull request #263 from schungx/master

Refactor.
This commit is contained in:
Stephen Chung 2020-10-19 21:16:09 +08:00 committed by GitHub
commit 1f9fb34e75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 390 additions and 322 deletions

View File

@ -1,16 +1,19 @@
Rhai - Embedded Scripting for Rust
=================================
![GitHub last commit](https://img.shields.io/github/last-commit/jonathandturner/rhai)
![GitHub last commit](https://img.shields.io/github/last-commit/jonathandturner/rhai?logo=github)
[![Build Status](https://github.com/jonathandturner/rhai/workflows/Build/badge.svg)](https://github.com/jonathandturner/rhai/actions)
[![license](https://img.shields.io/github/license/jonathandturner/rhai)](https://github.com/license/jonathandturner/rhai)
[![crates.io](https://img.shields.io/crates/v/rhai.svg)](https://crates.io/crates/rhai/)
[![crates.io](https://img.shields.io/crates/d/rhai)](https://crates.io/crates/rhai/)
[![API Docs](https://docs.rs/rhai/badge.svg)](https://docs.rs/rhai/)
[![license](https://img.shields.io/crates/l/rhai)](https://github.com/license/jonathandturner/rhai)
[![crates.io](https://img.shields.io/crates/v/rhai?logo=rust)](https://crates.io/crates/rhai/)
[![crates.io](https://img.shields.io/crates/d/rhai?logo=rust)](https://crates.io/crates/rhai/)
[![API Docs](https://docs.rs/rhai/badge.svg?logo=docs.rs)](https://docs.rs/rhai/)
[![chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord)](https://discord.gg/yZMKAQ)
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/Rhai?logo=reddit)](https://www.reddit.com/r/Rhai)
Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way
to add scripting to any application.
Supported targets and builds
---------------------------
@ -19,6 +22,7 @@ Supported targets and builds
* `no-std`
* Minimum Rust version 1.45
Standard features
-----------------
@ -41,13 +45,15 @@ Standard features
* Serialization/deserialization support via [serde](https://crates.io/crates/serde) (requires the `serde` feature).
* Support for [minimal builds](https://schungx.github.io/rhai/start/builds/minimal.html) by excluding unneeded language [features](https://schungx.github.io/rhai/start/features.html).
Protection against attacks
--------------------------
Protected against attacks
-------------------------
* Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://schungx.github.io/rhai/patterns/control.html).
* Rugged - protected against malicious attacks (such as [stack-overflow](https://schungx.github.io/rhai/safety/max-call-stack.html), [over-sized data](https://schungx.github.io/rhai/safety/max-string-size.html), and [runaway scripts](https://schungx.github.io/rhai/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts.
* Track script evaluation [progress](https://schungx.github.io/rhai/safety/progress.html) and manually terminate a script run.
For those who actually want their own language
---------------------------------------------
@ -56,6 +62,7 @@ For those who actually want their own language
* Define [custom operators](https://schungx.github.io/rhai/engine/custom-op.html).
* Extend the language with [custom syntax](https://schungx.github.io/rhai/engine/custom-syntax.html).
Documentation
-------------
@ -65,12 +72,14 @@ To build _The Book_, first install [`mdbook`](https://github.com/rust-lang/mdBoo
and [`mdbook-tera`](https://github.com/avitex/mdbook-tera) (for templating).
Running `mdbook build` builds it.
Playground
----------
An [Online Playground](https://alvinhochun.github.io/rhai-demo/) is available with syntax-highlighting editor.
Scripts can be evaluated directly from the editor.
License
-------

View File

@ -11,6 +11,7 @@ Breaking changes
* `EvalAltResult::ErrorReadingScriptFile` is removed in favor of the new `EvalAltResult::ErrorSystem`.
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
* `Engine::register_raw_fn` and `FnPtr::call_dynamic` function signatures have changed.
* Callback signatures to `Engine::on_var` and `Engine::register_custom_syntax` have changed.
New features
------------

View File

@ -36,8 +36,8 @@ The Rhai Scripting Language
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 a Custom Type and its Methods](rust/custom.md)
1. [Getters and Setters](rust/getters-setters.md)
8. [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)

View File

@ -3,7 +3,9 @@ Related Resources
{{#include ../links.md}}
Other online documentation resources for Rhai:
Other Online Resources for Rhai
------------------------------
* [`crates.io`](https://crates.io/crates/rhai) - Rhai crate
@ -13,7 +15,13 @@ Other online documentation resources for Rhai:
* [Online Playground][playground] - Run scripts directly from editor
Other cool projects to check out:
* [Discord Chat](https://discord.gg/yZMKAQ) - Rhai channel
* [Reddit](https://www.reddit.com/r/Rhai) - Rhai community
Other Cool Projects
-------------------
* [ChaiScript](http://chaiscript.com) - A strong inspiration for Rhai. An embedded scripting language for C++.

View File

@ -114,25 +114,19 @@ Any custom syntax must include an _implementation_ of it.
The function signature of an implementation is:
> `Fn(scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression])`
> `-> Result<Dynamic, Box<EvalAltResult>>`
> `Fn(context: &mut EvalContext, inputs: &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>`
where:
* `scope: &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to it.
* `context: &mut EvalContext` - mutable reference to the current evaluation _context_ (**do not touch**) which exposes the following fields:
* `context: &mut EvalContext` - mutable reference to the current evaluation _context_, exposing the following:
* `context.scope: &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to/removed from it.
* `context.engine(): &Engine` - reference to the current [`Engine`].
* `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions.
* `context.this_ptr(): Option<&Dynamic>` - reference to the current bound [`this`] pointer, if any.
* `context.call_level(): usize` - the current nesting level of function calls.
* `inputs: &[Expression]` - a list of input expression trees.
#### WARNING - Lark's Vomit
The `context` parameter contains the evaluation _context_ and should not be touched or Bad Things Happen™.
It should simply be passed straight-through the the [`Engine`].
### Access Arguments
The most important argument is `inputs` where the matched identifiers (`$ident$`), expressions/statements (`$expr$`)
@ -152,8 +146,8 @@ Use the `EvalContext::eval_expression_tree` method to evaluate an arbitrary expr
within the current evaluation context.
```rust
let expr = inputs.get(0).unwrap();
let result = context.eval_expression_tree(scope, expr)?;
let expression = inputs.get(0).unwrap();
let result = context.eval_expression_tree(expression)?;
```
### Declare Variables
@ -163,15 +157,16 @@ New variables maybe declared (usually with a variable name that is passed in via
It can simply be pushed into the [`Scope`].
However, beware that all new variables must be declared _prior_ to evaluating any expression tree.
In other words, any `Scope::push` calls must come _before_ any `EvalContext::eval_expression_tree` calls.
In other words, any [`Scope`] calls that change the list of must come _before_ any
`EvalContext::eval_expression_tree` calls.
```rust
let var_name = inputs[0].get_variable_name().unwrap().to_string();
let expr = inputs.get(1).unwrap();
let var_name = inputs[0].get_variable_name().unwrap();
let expression = inputs.get(1).unwrap();
scope.push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'!
context.scope.push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'!
let result = context.eval_expression_tree(context, scope, expr)?;
let result = context.eval_expression_tree(expression)?;
```
@ -188,7 +183,6 @@ The syntax is passed simply as a slice of `&str`.
```rust
// Custom syntax implementation
fn implementation_func(
scope: &mut Scope,
context: &mut EvalContext,
inputs: &[Expression]
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -196,15 +190,15 @@ fn implementation_func(
let stmt = inputs.get(1).unwrap();
let condition = inputs.get(2).unwrap();
// Push one new variable into the 'scope' BEFORE 'context.eval_expression_tree'
scope.push(var_name, 0 as INT);
// Push one new variable into the scope BEFORE 'context.eval_expression_tree'
context.scope.push(var_name, 0 as INT);
loop {
// Evaluate the statement block
context.eval_expression_tree(scope, stmt)?;
context.eval_expression_tree(stmt)?;
// Evaluate the condition expression
let stop = !context.eval_expression_tree(scope, condition)?
let stop = !context.eval_expression_tree(condition)?
.as_bool().map_err(|err| Box::new(
EvalAltResult::ErrorMismatchDataType(
"bool".to_string(),

View File

@ -62,7 +62,7 @@ Function Signature
The function signature passed to `Engine::on_var` takes the following form:
> `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext)`
> `Fn(name: &str, index: usize, context: &EvalContext)`
> `-> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
where:
@ -74,11 +74,11 @@ where:
If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed.
* `scope: &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position.
* `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields:
* `context.scope: &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position.
* `context.engine(): &Engine` - reference to the current [`Engine`].
* `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions.
* `context.this_ptr(): Option<&Dynamic>` - reference to the current bound [`this`] pointer, if any.
* `context.call_level(): usize` - the current nesting level of function calls.
### Return Value

BIN
doc/src/images/rhai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -8,7 +8,7 @@ Prelude
-------
When using the plugins system, the entire `rhai::plugin` module must be imported as a prelude
because code generated will these imports.
because code generated will need these imports.
```rust
use rhai::plugin::*;

View File

@ -1,124 +1,118 @@
Register a Custom Type and its Methods
Register any Rust Type and its Methods
=====================================
{{#include ../links.md}}
Rhai works seamlessly with _any_ complex Rust type. The type can be registered with the `Engine`, as below.
Free Typing
-----------
Rhai works seamlessly with _any_ Rust type. The type can be _anything_; it does not
have any prerequisites other than being `Clone`. It does not need to implement
any other trait or use any custom `#[derive]`.
This allows Rhai to be integrated into an existing code base with as little plumbing
as possible, usually silently and seamlessly. External types that are not defined
within the same crate (and thus cannot implement special Rhai traits or
use special `#[derive]`) can also be used easily with Rhai.
The reason why it is termed a _custom_ type throughout this documentation is that
Rhai natively supports a number of data types with fast, internal treatment (see
the list of [standard types]). Any type outside of this list is considered _custom_.
Any type not supported natively by Rhai is stored as a Rust _trait object_, with no
restrictions other than being `Clone` (plus `Send + Sync` under the [`sync`] feature).
It runs slightly slower than natively-supported types as it does not have built-in,
optimized implementations for commonly-used functions, but for all other purposes has
no difference.
Support for custom types can be turned off via the [`no_object`] feature.
```rust
use rhai::{Engine, EvalAltResult};
use rhai::RegisterFn; // remember 'RegisterFn' is needed
#[derive(Clone)]
struct TestStruct {
field: i64
}
Register a Custom Type and its Methods
-------------------------------------
impl TestStruct {
fn update(&mut self) {
self.field += 41;
}
Any custom type must implement the `Clone` trait as this allows the [`Engine`] to pass by value.
fn new() -> Self {
TestStruct { field: 1 }
}
}
let mut engine = Engine::new();
engine
.register_type::<TestStruct>() // most API's can be chained up
.register_fn("update", TestStruct::update)
.register_fn("new_ts", TestStruct::new);
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
println!("result: {}", result.field); // prints 42
```
Register a Custom Type
---------------------
A custom type must implement `Clone` as this allows the [`Engine`] to pass by value.
If the [`sync`] feature is used, it must also be `Send + Sync`.
Notice that the custom type needs to be _registered_ using `Engine::register_type`
or `Engine::register_type_with_name`.
To use native methods on custom types in Rhai scripts, it is common to register an API
for the type using one of the `Engine::register_XXX` functions.
```rust
use rhai::{Engine, EvalAltResult};
use rhai::RegisterFn; // remember 'RegisterFn' is needed
#[derive(Clone)]
struct TestStruct {
field: i64
}
impl TestStruct {
fn update(&mut self) { // methods take &mut as first parameter
self.field += 41;
fn new() -> Self {
Self { field: 1 }
}
fn new() -> Self {
TestStruct { field: 1 }
fn update(&mut self, x: i64) { // methods take &mut as first parameter
self.field += x;
}
}
let mut engine = Engine::new();
engine.register_type::<TestStruct>();
```
Methods on the Custom Type
-------------------------
To use native custom types, methods and functions in Rhai scripts, simply register them
using one of the `Engine::register_XXX` API.
Below, the `update` and `new` methods are registered using `Engine::register_fn`.
```rust
// Most Engine API's can be chained up.
engine
.register_fn("update", TestStruct::update) // registers 'update(&mut TestStruct)'
.register_fn("new_ts", TestStruct::new); // registers 'new()'
.register_type::<TestStruct>() // register custom type
.register_fn("new_ts", TestStruct::new)
.register_fn("update", TestStruct::update);
// Cast result back to custom type.
let result = engine.eval::<TestStruct>(
r"
let x = new_ts(); // calls 'TestStruct::new'
x.update(41); // calls 'TestStruct::update'
x // 'x' holds a 'TestStruct'
"
)?;
println!("result: {}", result.field); // prints 42
```
***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter
so that invoking methods can update the types. All other parameters in Rhai are passed by value (i.e. clones).*
Rhai follows the convention that methods of custom types take a `&mut` first parameter
to that type, so that invoking methods can always update it.
All other parameters in Rhai are passed by value (i.e. clones).
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Use the Custom Type in Scripts
-----------------------------
The custom type is then ready for use in scripts. Scripts can see the functions and methods registered earlier.
Get the evaluation result back out just as before, this time casting to the custom type:
```rust
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
println!("result: {}", result.field); // prints 42
```
Method-Call Style vs. Function-Call Style
----------------------------------------
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.
This design is similar to Rust.
```rust
fn foo(ts: &mut TestStruct) -> i64 {
ts.field
impl TestStruct {
fn foo(&mut self) -> i64 {
self.field
}
}
engine.register_fn("foo", foo); // register a Rust native function
engine.register_fn("foo", TestStruct::foo);
let result = engine.eval::<i64>(
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
r"
let x = new_ts();
foo(x); // normal call to 'foo'
x.foo() // 'foo' can also be called like a method on 'x'
"
)?;
println!("result: {}", result); // prints 1
@ -128,8 +122,9 @@ 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.
let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?;
// Below is a syntax error under 'no_object'.
let result = engine.eval("let x = [1, 2, 3]; x.clear();")?;
// ^ cannot call in method style under 'no_object'
```
@ -143,18 +138,16 @@ with a special "pretty-print" name, [`type_of()`] will return that name instead.
```rust
engine
.register_type::<TestStruct>()
.register_fn("new_ts", TestStruct::new);
.register_type::<TestStruct1>()
.register_fn("new_ts1", TestStruct1::new)
.register_type_with_name::<TestStruct2>("MyType")
.register_fn("new_ts2", TestStruct2::new);
let x = new_ts();
x.type_of() == "path::to::module::TestStruct";
let ts1_type = engine.eval::<String>(r#"let x = new_ts1(); x.type_of()"#)?;
let ts2_type = engine.eval::<String>(r#"let x = new_ts2(); x.type_of()"#)?;
engine
.register_type_with_name::<TestStruct>("Hello")
.register_fn("new_ts", TestStruct::new);
let x = new_ts();
x.type_of() == "Hello";
println!("{}", ts1_type); // prints 'path::to::TestStruct'
println!("{}", ts1_type); // prints 'MyType'
```
@ -190,7 +183,9 @@ the `==` operator must be registered for the custom type:
```rust
// Assume 'TestStruct' implements `PartialEq`
engine.register_fn("==", |item1: &mut TestStruct, item2: TestStruct| item1 == item2);
engine.register_fn("==",
|item1: &mut TestStruct, item2: TestStruct| item1 == &item2
);
// Then this works in Rhai:
let item = new_ts(); // construct a new 'TestStruct'

View File

@ -1,29 +1,32 @@
Custom Type Getters and Setters
==============================
Custom Type Property Getters and Setters
=======================================
{{#include ../links.md}}
A custom type can also expose members by registering `get` and/or `set` functions.
A [custom type] can also expose properties by registering `get` and/or `set` functions.
Getters and setters each take a `&mut` reference to the first parameter.
Getters and setters are disabled when the [`no_object`] feature is used.
| `Engine` API | Description | Return Value of Function |
| --------------------- | ------------------------------------------------- | :-----------------------------------: |
| `register_get` | register a getter | _any_ `T: Clone` |
| `register_set` | register a setter | _none_ |
| `register_get_set` | short-hand to register both a getter and a setter | _none_ |
| `register_get_result` | register a getter | `Result<Dynamic, Box<EvalAltResult>>` |
| `register_set_result` | register a setter | `Result<(), Box<EvalAltResult>>` |
| `Engine` API | Function signature(s)<br/>(`T: Clone` = custom type,<br/>`V: Clone` = data type) | Can mutate `T`? |
| --------------------- | -------------------------------------------------------------------------------- | :----------------------------: |
| `register_get` | `Fn(&mut T) -> V` | yes, but not advised |
| `register_set` | `Fn(&mut T, V)` | yes |
| `register_get_set` | getter: `Fn(&mut T) -> V`</br>setter: `Fn(&mut T, V)` | yes, but not advised in getter |
| `register_get_result` | `Fn(&mut T) -> Result<Dynamic, Box<EvalAltResult>>` | yes, but not advised |
| `register_set_result` | `Fn(&mut T, V) -> Result<(), Box<EvalAltResult>>` | yes |
By convention, property getters are not supposed to mutate the [custom type], although there is nothing
that prevents this mutation.
Cannot Override Object Maps
--------------------------
Getters and setters are only intended for [custom types].
Property getters and setters are mainly intended for [custom types].
Any getter or setter function registered for [object maps] is simply ignored because
Any getter or setter function registered for [object maps] is simply _ignored_ because
the get/set calls will be interpreted as properties on the [object maps].
@ -47,7 +50,7 @@ impl TestStruct {
}
fn new() -> Self {
TestStruct { field: "hello" }
Self { field: "hello" }
}
}

View File

@ -3,23 +3,30 @@ Custom Type Indexers
{{#include ../links.md}}
A custom type can also expose an _indexer_ by registering an indexer function.
A [custom type] can also expose an _indexer_ by registering an indexer function.
A custom type with an indexer function defined can use the bracket notation to get a property value:
A [custom type] with an indexer function defined can use the bracket notation to get a property value:
> _object_ `[` _index_ `]`
Like getters and setters, indexers take a `&mut` reference to the first parameter.
Like property [getters/setters], indexers take a `&mut` reference to the first parameter.
They also take an additional parameter of any type that serves as the _index_ within brackets.
Indexers are disabled when the [`no_index`] feature is used.
| `Engine` API | Description | Return Value of Function |
| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: |
| `register_indexer_get` | register an index getter | _any_ `T: Clone` |
| `register_indexer_set` | register an index setter | _none_ |
| `register_indexer_get_set` | short-hand to register both an index getter and a setter | _none_ |
| `register_indexer_get_result` | register an index getter | `Result<Dynamic, Box<EvalAltResult>>` |
| `register_indexer_set_result` | register an index setter | `Result<(), Box<EvalAltResult>>` |
| `Engine` API | Function signature(s)<br/>(`T: Clone` = custom type,<br/>`X: Clone` = index type,<br/>`V: Clone` = data type) | Can mutate `T`? |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------- | :----------------------------: |
| `register_indexer_get` | `Fn(&mut T, X) -> V` | yes, but not advised |
| `register_indexer_set` | `Fn(&mut T, X, V)` | yes |
| `register_indexer_get_set` | getter: `Fn(&mut T, X) -> V`<br/>setter: `Fn(&mut T, X, V)` | yes, but not advised in getter |
| `register_indexer_get_result` | `Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>>` | yes, but not advised |
| `register_indexer_set_result` | `Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>>` | yes |
By convention, index getters are not supposed to mutate the [custom type], although there is nothing
that prevents this mutation.
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Cannot Override Arrays, Object Maps and Strings
@ -42,15 +49,15 @@ struct TestStruct {
impl TestStruct {
// Remember &mut must be used even for getters
fn get_field(&mut self, index: i64) -> i64 {
self.fields[index as usize]
fn get_field(&mut self, index: String) -> i64 {
self.fields[index.len()]
}
fn set_field(&mut self, index: i64, value: i64) {
self.fields[index as usize] = value
fn set_field(&mut self, index: String, value: i64) {
self.fields[index.len()] = value
}
fn new() -> Self {
TestStruct { fields: vec![1, 2, 3, 4, 5] }
Self { fields: vec![1, 2, 3, 4, 5] }
}
}
@ -63,9 +70,13 @@ engine
.register_indexer_get(TestStruct::get_field)
.register_indexer_set(TestStruct::set_field);
let result = engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?;
let result = engine.eval::<i64>(
r#"
let a = new_ts();
a["xyz"] = 42; // these indexers use strings
a["xyz"] // as the index type
"#
)?;
println!("Answer: {}", result); // prints 42
```
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**

View File

@ -65,7 +65,7 @@ The function signature passed to `Engine::register_raw_fn` takes the following f
where:
* `T: Variant + Clone` - return type of the function.
* `T: Clone` - return type of the function.
* `context: NativeCallContext` - the current _native call context_, which exposes the following:

View File

@ -11,7 +11,7 @@ impl TestStruct {
}
fn new() -> Self {
TestStruct { x: 1 }
Self { x: 1 }
}
}

View File

@ -11,7 +11,7 @@ impl TestStruct {
}
fn new() -> Self {
TestStruct { x: 1 }
Self { x: 1 }
}
}

View File

@ -3,12 +3,12 @@
use crate::any::{Dynamic, Variant};
use crate::engine::{Engine, EvalContext, Imports, State};
use crate::error::ParseError;
use crate::fn_native::{NativeCallContext, SendSync};
use crate::fn_native::{FnCallArgs, NativeCallContext, SendSync};
use crate::optimize::OptimizationLevel;
use crate::parser::AST;
use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{lex, Position};
use crate::token::Position;
#[cfg(not(feature = "no_index"))]
use crate::{
@ -68,7 +68,7 @@ impl Engine {
&mut self,
name: &str,
arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>>
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync
+ 'static,
) -> &mut Self {
@ -88,7 +88,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// fn update(&mut self, offset: i64) { self.field += offset; }
/// }
///
@ -130,7 +130,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// }
///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -200,7 +200,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self) -> i64 { self.field }
/// }
@ -252,7 +252,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
/// Ok(self.field.into())
@ -295,7 +295,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
/// }
///
@ -348,7 +348,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> {
/// self.field = new_val;
/// Ok(())
@ -386,8 +386,7 @@ impl Engine {
U: Variant + Clone,
{
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
callback(obj, value)?;
Ok(().into())
callback(obj, value).map(Into::into)
})
}
@ -405,7 +404,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn new() -> Self { Self { field: 1 } }
/// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self) -> i64 { self.field }
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
@ -462,7 +461,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
/// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
/// }
@ -534,7 +533,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
/// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self, index: i64) -> Result<Dynamic, Box<EvalAltResult>> {
/// Ok(self.fields[index as usize].into())
@ -600,7 +599,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
/// }
///
@ -672,7 +671,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> {
/// self.fields[index as usize] = value;
/// Ok(())
@ -724,8 +723,7 @@ impl Engine {
}
self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| {
callback(obj, index, value)?;
Ok(().into())
callback(obj, index, value).map(Into::into)
})
}
@ -745,7 +743,7 @@ impl Engine {
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
/// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
@ -915,7 +913,7 @@ impl Engine {
scripts: &[&str],
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let stream = lex(scripts, None, self);
let stream = self.lex(scripts, None);
self.parse(&mut stream.peekable(), scope, optimization_level)
}
@ -1069,7 +1067,7 @@ impl Engine {
.into());
};
let stream = lex(
let stream = self.lex(
&scripts,
if has_null {
Some(Box::new(|token| match token {
@ -1080,7 +1078,6 @@ impl Engine {
} else {
None
},
self,
);
let ast =
self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?;
@ -1164,7 +1161,7 @@ impl Engine {
script: &str,
) -> Result<AST, ParseError> {
let scripts = [script];
let stream = lex(&scripts, None, self);
let stream = self.lex(&scripts, None);
{
let mut peekable = stream.peekable();
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
@ -1325,7 +1322,7 @@ impl Engine {
script: &str,
) -> Result<T, Box<EvalAltResult>> {
let scripts = [script];
let stream = lex(&scripts, None, self);
let stream = self.lex(&scripts, None);
// No need to optimize a lone expression
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
@ -1423,6 +1420,7 @@ impl Engine {
})
.or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::LoopBreak(_, _) => unreachable!(),
_ => Err(err),
})
.map(|v| (v, state.operations))
@ -1466,7 +1464,7 @@ impl Engine {
script: &str,
) -> Result<(), Box<EvalAltResult>> {
let scripts = [script];
let stream = lex(&scripts, None, self);
let stream = self.lex(&scripts, None);
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
self.consume_ast_with_scope(scope, &ast)
}
@ -1497,7 +1495,8 @@ impl Engine {
.map_or_else(
|err| match *err {
EvalAltResult::Return(_, _) => Ok(()),
err => Err(Box::new(err)),
EvalAltResult::LoopBreak(_, _) => unreachable!(),
_ => Err(err),
},
|_| Ok(()),
)
@ -1645,7 +1644,7 @@ impl Engine {
lib: &Module,
name: &str,
this_ptr: &mut Option<&mut Dynamic>,
args: &mut [&mut Dynamic],
args: &mut FnCallArgs,
) -> Result<Dynamic, Box<EvalAltResult>> {
let fn_def = lib
.get_script_fn(name, args.len(), true)
@ -1715,7 +1714,7 @@ impl Engine {
/// let mut engine = Engine::new();
///
/// // Register a variable resolver.
/// engine.on_var(|name, _, _, _| {
/// engine.on_var(|name, _, _| {
/// match name {
/// "MYSTIC_NUMBER" => Ok(Some(42_i64.into())),
/// _ => Ok(None)
@ -1730,7 +1729,7 @@ impl Engine {
#[inline(always)]
pub fn on_var(
&mut self,
callback: impl Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
callback: impl Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ SendSync
+ 'static,
) -> &mut Self {

View File

@ -441,16 +441,17 @@ pub struct Limits {
/// Context of a script evaluation process.
#[derive(Debug)]
pub struct EvalContext<'e, 'a, 's, 'm, 't, 'd: 't> {
pub struct EvalContext<'e, 'x, 'px: 'x, 'a, 's, 'm, 't, 'pt: 't> {
engine: &'e Engine,
pub scope: &'x mut Scope<'px>,
pub(crate) mods: &'a mut Imports,
pub(crate) state: &'s mut State,
lib: &'m Module,
pub(crate) this_ptr: &'t mut Option<&'d mut Dynamic>,
pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>,
level: usize,
}
impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> {
impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> {
/// The current `Engine`.
#[inline(always)]
pub fn engine(&self) -> &'e Engine {
@ -469,6 +470,11 @@ impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> {
pub fn namespace(&self) -> &'m Module {
self.lib
}
/// The current bound `this` pointer, if any.
#[inline(always)]
pub fn this_ptr(&self) -> Option<&Dynamic> {
self.this_ptr.as_ref().map(|v| &**v)
}
/// The current nesting level of function calls.
#[inline(always)]
pub fn call_level(&self) -> usize {
@ -815,6 +821,7 @@ impl Engine {
if let Some(ref resolve_var) = self.resolve_var {
let context = EvalContext {
engine: self,
scope,
mods,
state,
lib,
@ -822,7 +829,7 @@ impl Engine {
level: 0,
};
if let Some(result) =
resolve_var(name, index, scope, &context).map_err(|err| err.fill_position(*pos))?
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
{
return Ok((result.into(), name, ScopeEntryType::Constant, *pos));
}
@ -934,7 +941,9 @@ impl Engine {
level,
)
.map_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(_, _) => {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.ends_with("]=") =>
{
EvalAltResult::ErrorIndexingType(
self.map_type_name(val_type_name).into(),
Position::none(),
@ -1381,9 +1390,12 @@ impl Engine {
)
.map(|(v, _)| v.into())
.map_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(_, _) => Box::new(
EvalAltResult::ErrorIndexingType(type_name.into(), Position::none()),
),
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
Box::new(EvalAltResult::ErrorIndexingType(
type_name.into(),
Position::none(),
))
}
_ => err,
})
}
@ -1737,17 +1749,22 @@ impl Engine {
Expr::Unit(_) => Ok(().into()),
Expr::Custom(x) => {
let func = (x.0).1.as_ref();
let ep = (x.0).0.iter().map(|e| e.into()).collect::<StaticVec<_>>();
let func = (x.0).func();
let expressions = (x.0)
.keywords()
.iter()
.map(Into::into)
.collect::<StaticVec<_>>();
let mut context = EvalContext {
engine: self,
scope,
mods,
state,
lib,
this_ptr,
level,
};
func(scope, &mut context, ep.as_ref())
func(&mut context, &expressions)
}
_ => unreachable!(),

View File

@ -435,9 +435,11 @@ impl Engine {
&self,
lib: &Module,
name: &str,
arg_types: &[TypeId],
arg_types: impl AsRef<[TypeId]>,
pub_only: bool,
) -> bool {
let arg_types = arg_types.as_ref();
let arg_len = if arg_types.is_empty() {
usize::MAX
} else {
@ -840,7 +842,7 @@ impl Engine {
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
name: &str,
args_expr: &[Expr],
args_expr: impl AsRef<[Expr]>,
def_val: &Option<Dynamic>,
mut hash_script: u64,
native: bool,
@ -848,6 +850,8 @@ impl Engine {
capture: bool,
level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
let args_expr = args_expr.as_ref();
// Handle Fn()
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
@ -1085,11 +1089,13 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>,
modules: &Option<Box<ModuleRef>>,
name: &str,
args_expr: &[Expr],
args_expr: impl AsRef<[Expr]>,
def_val: Option<bool>,
hash_script: u64,
level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
let args_expr = args_expr.as_ref();
let modules = modules.as_ref().unwrap();
let mut arg_values: StaticVec<_>;
let mut first_arg_value = None;

View File

@ -6,7 +6,6 @@ use crate::module::Module;
use crate::parser::{FnAccess, ScriptFnDef};
use crate::plugin::PluginFunction;
use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{is_valid_identifier, Position};
use crate::utils::ImmutableString;
use crate::{calc_fn_hash, StaticVec};
@ -265,14 +264,12 @@ pub type Callback<T, R> = Box<dyn Fn(&T) -> R + Send + Sync + 'static>;
/// A standard callback function.
#[cfg(not(feature = "sync"))]
pub type OnVarCallback = Box<
dyn Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ 'static,
>;
pub type OnVarCallback =
Box<dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static>;
/// A standard callback function.
#[cfg(feature = "sync")]
pub type OnVarCallback = Box<
dyn Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,

View File

@ -536,11 +536,11 @@ impl Module {
&mut self,
name: impl Into<String>,
arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>>
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync
+ 'static,
) -> u64 {
let f = move |context: NativeCallContext, args: &mut [&mut Dynamic]| {
let f = move |context: NativeCallContext, args: &mut FnCallArgs| {
func(context, args).map(Dynamic::from)
};
self.set_fn(

View File

@ -7,7 +7,7 @@ use crate::fn_native::{FnPtr, Shared};
use crate::module::{Module, ModuleRef};
use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::FnCustomSyntaxEval;
use crate::syntax::{CustomSyntax, FnCustomSyntaxEval};
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream};
use crate::utils::StraightHasherBuilder;
use crate::{calc_fn_hash, StaticVec};
@ -919,6 +919,19 @@ impl Hash for CustomExpr {
}
}
impl CustomExpr {
/// Get the keywords for this `CustomExpr`.
#[inline(always)]
pub fn keywords(&self) -> &[Expr] {
&self.0
}
/// Get the implementation function for this `CustomExpr`.
#[inline(always)]
pub fn func(&self) -> &FnCustomSyntaxEval {
self.1.as_ref()
}
}
/// _[INTERNALS]_ A type wrapping a floating-point number.
/// Exported under the `internals` feature only.
///
@ -2641,6 +2654,74 @@ fn parse_binary_op(
}
}
/// Parse a custom syntax.
fn parse_custom(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
key: &str,
syntax: &CustomSyntax,
pos: Position,
) -> Result<Expr, ParseError> {
let mut exprs: StaticVec<Expr> = Default::default();
// Adjust the variables stack
match syntax.scope_delta {
delta if delta > 0 => {
state.stack.resize(
state.stack.len() + delta as usize,
("".to_string(), ScopeEntryType::Normal),
);
}
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(),
delta if delta < 0 => state
.stack
.truncate(state.stack.len() - delta.abs() as usize),
_ => (),
}
for segment in syntax.segments.iter() {
settings.pos = input.peek().unwrap().1;
let settings = settings.level_up();
match segment.as_str() {
MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => {
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
}
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
},
MARKER_EXPR => exprs.push(parse_expr(input, state, lib, settings)?),
MARKER_BLOCK => {
let stmt = parse_block(input, state, lib, settings)?;
let pos = stmt.position();
exprs.push(Expr::Stmt(Box::new((stmt, pos))))
}
s => match input.peek().unwrap() {
(t, _) if t.syntax().as_ref() == s => {
input.next().unwrap();
}
(_, pos) => {
return Err(PERR::MissingToken(
s.to_string(),
format!("for '{}' expression", key),
)
.into_err(*pos))
}
},
}
}
Ok(Expr::Custom(Box::new((
CustomExpr(exprs, syntax.func.clone()),
pos,
))))
}
/// Parse an expression.
fn parse_expr(
input: &mut TokenStream,
@ -2662,64 +2743,8 @@ fn parse_expr(
Token::Custom(key) if custom.contains_key(key) => {
let custom = custom.get_key_value(key).unwrap();
let (key, syntax) = custom;
input.next().unwrap();
let mut exprs: StaticVec<Expr> = Default::default();
// Adjust the variables stack
match syntax.scope_delta {
delta if delta > 0 => {
state.stack.push(("".to_string(), ScopeEntryType::Normal))
}
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => {
state.stack.clear()
}
delta if delta < 0 => state
.stack
.truncate(state.stack.len() - delta.abs() as usize),
_ => (),
}
for segment in syntax.segments.iter() {
settings.pos = input.peek().unwrap().1;
let settings = settings.level_up();
match segment.as_str() {
MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => {
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
}
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
},
MARKER_EXPR => exprs.push(parse_expr(input, state, lib, settings)?),
MARKER_BLOCK => {
let stmt = parse_block(input, state, lib, settings)?;
let pos = stmt.position();
exprs.push(Expr::Stmt(Box::new((stmt, pos))))
}
s => match input.peek().unwrap() {
(t, _) if t.syntax().as_ref() == s => {
input.next().unwrap();
}
(_, pos) => {
return Err(PERR::MissingToken(
s.to_string(),
format!("for '{}' expression", key),
)
.into_err(*pos))
}
},
}
}
return Ok(Expr::Custom(Box::new((
CustomExpr(exprs, syntax.func.clone()),
token_pos,
))));
return parse_custom(input, state, lib, settings, key, syntax, token_pos);
}
_ => (),
}

View File

@ -2,7 +2,7 @@
pub use crate::any::Dynamic;
pub use crate::engine::Engine;
pub use crate::fn_native::{CallableFunction, NativeCallContext};
pub use crate::fn_native::{CallableFunction, FnCallArgs, NativeCallContext};
pub use crate::fn_register::{RegisterFn, RegisterResultFn};
pub use crate::module::Module;
pub use crate::parser::FnAccess;
@ -25,7 +25,7 @@ pub trait PluginFunction {
fn call(
&self,
context: NativeCallContext,
args: &mut [&mut Dynamic],
args: &mut FnCallArgs,
) -> Result<Dynamic, Box<EvalAltResult>>;
/// Is this plugin function a method?

View File

@ -6,7 +6,6 @@ use crate::error::{LexError, ParseError};
use crate::fn_native::{SendSync, Shared};
use crate::parser::Expr;
use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{is_valid_identifier, Position, Token};
use crate::StaticVec;
@ -19,12 +18,11 @@ use crate::stdlib::{
/// A general expression evaluation trait object.
#[cfg(not(feature = "sync"))]
pub type FnCustomSyntaxEval =
dyn Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>;
dyn Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>;
/// A general expression evaluation trait object.
#[cfg(feature = "sync")]
pub type FnCustomSyntaxEval = dyn Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync;
pub type FnCustomSyntaxEval =
dyn Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
/// An expression sub-tree in an AST.
#[derive(Debug, Clone, Hash)]
@ -55,7 +53,7 @@ impl Expression<'_> {
}
}
impl EvalContext<'_, '_, '_, '_, '_, '_> {
impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
/// Evaluate an expression tree.
///
/// ## WARNING - Low Level API
@ -64,11 +62,10 @@ impl EvalContext<'_, '_, '_, '_, '_, '_> {
#[inline(always)]
pub fn eval_expression_tree(
&mut self,
scope: &mut Scope,
expr: &Expression,
) -> Result<Dynamic, Box<EvalAltResult>> {
self.engine().eval_expr(
scope,
self.scope,
self.mods,
self.state,
self.namespace(),
@ -101,12 +98,14 @@ impl Engine {
/// * `func` is the implementation function.
pub fn register_custom_syntax<S: AsRef<str> + ToString>(
&mut self,
keywords: &[S],
keywords: impl AsRef<[S]>,
new_vars: isize,
func: impl Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
func: impl Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
+ SendSync
+ 'static,
) -> Result<&mut Self, ParseError> {
let keywords = keywords.as_ref();
let mut segments: StaticVec<_> = Default::default();
for s in keywords {

View File

@ -1736,31 +1736,33 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
}
}
/// Tokenize an input text stream.
#[inline]
pub fn lex<'a, 'e>(
input: &'a [&'a str],
map: Option<Box<dyn Fn(Token) -> Token>>,
engine: &'e Engine,
) -> TokenIterator<'a, 'e> {
TokenIterator {
engine,
state: TokenizeState {
#[cfg(not(feature = "unchecked"))]
max_string_size: engine.limits_set.max_string_size,
#[cfg(feature = "unchecked")]
max_string_size: 0,
non_unary: false,
comment_level: 0,
end_with_none: false,
include_comments: false,
},
pos: Position::new(1, 0),
stream: MultiInputsStream {
buf: None,
streams: input.iter().map(|s| s.chars().peekable()).collect(),
index: 0,
},
map,
impl Engine {
/// Tokenize an input text stream.
#[inline]
pub fn lex<'a, 'e>(
&'e self,
input: impl IntoIterator<Item = &'a &'a str>,
map: Option<Box<dyn Fn(Token) -> Token>>,
) -> TokenIterator<'a, 'e> {
TokenIterator {
engine: self,
state: TokenizeState {
#[cfg(not(feature = "unchecked"))]
max_string_size: self.limits_set.max_string_size,
#[cfg(feature = "unchecked")]
max_string_size: 0,
non_unary: false,
comment_level: 0,
end_with_none: false,
include_comments: false,
},
pos: Position::new(1, 0),
stream: MultiInputsStream {
buf: None,
streams: input.into_iter().map(|s| s.chars().peekable()).collect(),
index: 0,
},
map,
}
}
}

View File

@ -84,7 +84,7 @@ fn test_array_with_structs() -> Result<(), Box<EvalAltResult>> {
}
fn new() -> Self {
TestStruct { x: 1 }
Self { x: 1 }
}
}

View File

@ -1,7 +1,5 @@
#![cfg(not(feature = "no_function"))]
use rhai::{
Dynamic, Engine, EvalAltResult, FnPtr, Func, Module, ParseErrorType, RegisterFn, Scope, INT,
};
use rhai::{Engine, EvalAltResult, FnPtr, Func, ParseErrorType, RegisterFn, Scope, INT};
use std::any::TypeId;
#[test]

View File

@ -51,7 +51,7 @@ fn test_struct_with_float() -> Result<(), Box<EvalAltResult>> {
}
fn new() -> Self {
TestStruct { x: 1.0 }
Self { x: 1.0 }
}
}

View File

@ -25,7 +25,7 @@ fn test_get_set() -> Result<(), Box<EvalAltResult>> {
}
fn new() -> Self {
TestStruct {
Self {
x: 1,
y: 0,
array: vec![1, 2, 3, 4, 5],

View File

@ -15,7 +15,7 @@ fn test_method_call() -> Result<(), Box<EvalAltResult>> {
}
fn new() -> Self {
TestStruct { x: 1 }
Self { x: 1 }
}
}

View File

@ -20,7 +20,7 @@ fn test_mismatched_op_custom_type() {
impl TestStruct {
fn new() -> Self {
TestStruct { x: 1 }
Self { x: 1 }
}
}

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, EvalContext, Expression, ParseErrorType, Scope, INT};
use rhai::{Engine, EvalAltResult, EvalContext, Expression, ParseErrorType, INT};
#[test]
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
@ -22,18 +22,18 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
"do", "|", "$ident$", "|", "->", "$block$", "while", "$expr$",
],
1,
|scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression]| {
|context: &mut EvalContext, inputs: &[Expression]| {
let var_name = inputs[0].get_variable_name().unwrap().to_string();
let stmt = inputs.get(1).unwrap();
let condition = inputs.get(2).unwrap();
scope.push(var_name, 0 as INT);
context.scope.push(var_name, 0 as INT);
loop {
context.eval_expression_tree(scope, stmt)?;
context.eval_expression_tree(stmt)?;
let stop = !context
.eval_expression_tree(scope, condition)?
.eval_expression_tree(condition)?
.as_bool()
.map_err(|err| {
Box::new(EvalAltResult::ErrorMismatchDataType(
@ -68,7 +68,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
// The first symbol must be an identifier
assert_eq!(
*engine
.register_custom_syntax(&["!"], 0, |_, _, _| Ok(().into()))
.register_custom_syntax(&["!"], 0, |_, _| Ok(().into()))
.expect_err("should error")
.0,
ParseErrorType::BadInput("Improper symbol for custom syntax: '!'".to_string())

View File

@ -62,7 +62,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
scope.push("chameleon", 123 as INT);
scope.push("DO_NOT_USE", 999 as INT);
engine.on_var(|name, _, scope, _| {
engine.on_var(|name, _, context| {
match name {
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
// Override a variable - make it not found even if it exists!
@ -70,9 +70,13 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into())
}
// Silently maps 'chameleon' into 'innocent'.
"chameleon" => scope.get_value("innocent").map(Some).ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into()
}),
"chameleon" => context
.scope
.get_value("innocent")
.map(Some)
.ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into()
}),
// Return Ok(None) to continue with the normal variable resolution process.
_ => Ok(None),
}