Merge pull request #144 from schungx/master
Packages and general speed increases (esp for arrays and maps)
This commit is contained in:
commit
ec4b01d40d
@ -33,7 +33,7 @@ only_i64 = [] # set INT=i64 (default) and disable support for all other in
|
||||
sync = [] # restrict to only types that implement Send + Sync
|
||||
|
||||
# compiling for no-std
|
||||
no_std = [ "num-traits/libm", "hashbrown", "core-error", "libm" ]
|
||||
no_std = [ "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ]
|
||||
|
||||
# other developer features
|
||||
no_stdlib = [] # do not register the standard library
|
||||
@ -63,4 +63,5 @@ optional = true
|
||||
[dependencies.ahash]
|
||||
version = "0.3.2"
|
||||
default-features = false
|
||||
features = ["compile-time-rng"]
|
||||
optional = true
|
||||
|
170
README.md
170
README.md
@ -13,19 +13,20 @@ to add scripting to any application.
|
||||
|
||||
Rhai's current features set:
|
||||
|
||||
* `no-std` support
|
||||
* Easy integration with Rust native functions and types, including getter/setter/methods
|
||||
* Easily call a script-defined function from Rust
|
||||
* Easy-to-use language similar to JS+Rust
|
||||
* Easy integration with Rust [native functions](#working-with-functions) and [types](#custom-types-and-methods),
|
||||
including [getter/setter](#getters-and-setters)/[methods](#members-and-methods)
|
||||
* Easily [call a script-defined function](#calling-rhai-functions-from-rust) from Rust
|
||||
* Freely pass variables/constants into a script via an external [`Scope`]
|
||||
* Fairly efficient (1 million iterations in 0.75 sec on my 5 year old laptop)
|
||||
* Low compile-time overhead (~0.6 sec debug/~3 sec release for script runner app)
|
||||
* Easy-to-use language similar to JS+Rust
|
||||
* Support for function overloading
|
||||
* Support for operator overloading
|
||||
* Compiled script is optimized for repeat evaluations
|
||||
* Support for minimal builds by excluding unneeded language features
|
||||
* [`no-std`](#optional-features) support
|
||||
* Support for [function overloading](#function-overloading)
|
||||
* Support for [operator overloading](#operator-overloading)
|
||||
* Compiled script is [optimized](#script-optimization) for repeat evaluations
|
||||
* Support for [minimal builds](#minimal-builds) by excluding unneeded language [features](#optional-features)
|
||||
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/)
|
||||
to do checked arithmetic operations); for [`no_std`] builds, a number of additional dependencies are
|
||||
to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are
|
||||
pulled in to provide for functionalities that used to be in `std`.
|
||||
|
||||
**Note:** Currently, the version is 0.13.0, so the language and API's may change before they stabilize.
|
||||
@ -116,10 +117,13 @@ Opt out of as many features as possible, if they are not needed, to reduce code
|
||||
all code is compiled in as what a script requires cannot be predicted. If a language feature is not needed,
|
||||
omitting them via special features is a prudent strategy to optimize the build for size.
|
||||
|
||||
Start by using [`Engine::new_raw`](#raw-engine) to create a _raw_ engine which does not register the standard library of utility
|
||||
functions. Secondly, omitting arrays (`no_index`) yields the most code-size savings, followed by floating-point support
|
||||
(`no_float`), checked arithmetic (`unchecked`) and finally object maps and custom types (`no_object`). Disable script-defined
|
||||
functions (`no_function`) only when the feature is not needed because code size savings is minimal.
|
||||
Omitting arrays (`no_index`) yields the most code-size savings, followed by floating-point support
|
||||
(`no_float`), checked arithmetic (`unchecked`) and finally object maps and custom types (`no_object`).
|
||||
Disable script-defined functions (`no_function`) only when the feature is not needed because code size savings is minimal.
|
||||
|
||||
[`Engine::new_raw`](#raw-engine) creates a _raw_ engine which does not register _any_ utility functions.
|
||||
This makes the scripting language quite useless as even basic arithmetic operators are not supported.
|
||||
Selectively include the necessary operators by loading specific [packages](#packages) while minimizing the code footprint.
|
||||
|
||||
Related
|
||||
-------
|
||||
@ -199,7 +203,7 @@ To get going with Rhai, create an instance of the scripting engine via `Engine::
|
||||
```rust
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
fn main() -> Result<(), EvalAltResult>
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
@ -320,7 +324,7 @@ let script = "fn calc(x, y) { x + y.len() < 42 }";
|
||||
// 1) a tuple made up of the types of the script function's parameters
|
||||
// 2) the return type of the script function
|
||||
//
|
||||
// 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, EvalAltResult>> and is callable!
|
||||
// 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
let func = Func::<(i64, String), bool>::create_from_script(
|
||||
// ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
|
||||
@ -336,7 +340,7 @@ schedule_callback(func); // pass it as a callback to anot
|
||||
// Although there is nothing you can't do by manually writing out the closure yourself...
|
||||
let engine = Engine::new();
|
||||
let ast = engine.compile(script)?;
|
||||
schedule_callback(Box::new(move |x: i64, y: String| -> Result<bool, EvalAltResult> {
|
||||
schedule_callback(Box::new(move |x: i64, y: String| -> Result<bool, Box<EvalAltResult>> {
|
||||
engine.call_fn(&mut Scope::new(), &ast, "calc", (x, y))
|
||||
}));
|
||||
```
|
||||
@ -349,17 +353,41 @@ Raw `Engine`
|
||||
`Engine::new` creates a scripting [`Engine`] with common functionalities (e.g. printing to the console via `print`).
|
||||
In many controlled embedded environments, however, these are not needed.
|
||||
|
||||
Use `Engine::new_raw` to create a _raw_ `Engine`, in which:
|
||||
Use `Engine::new_raw` to create a _raw_ `Engine`, in which _nothing_ is added, not even basic arithmetic and logic operators!
|
||||
|
||||
* the `print` and `debug` statements do nothing instead of displaying to the console (see [`print` and `debug`](#print-and-debug) below)
|
||||
* the _standard library_ of utility functions is _not_ loaded by default (load it using the `register_stdlib` method).
|
||||
### Packages
|
||||
|
||||
Rhai functional features are provided in different _packages_ that can be loaded via a call to `load_package`.
|
||||
Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` must be imported in order for
|
||||
packages to be used.
|
||||
|
||||
```rust
|
||||
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
||||
use rhai::Engine;
|
||||
use rhai::packages::Package // load the 'Package' trait to use packages
|
||||
use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic)
|
||||
|
||||
engine.register_stdlib(); // register the standard library manually
|
||||
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
||||
let package = CorePackage::new(); // create a package - can be shared among multiple `Engine` instances
|
||||
|
||||
engine.load_package(package.get()); // load the package manually
|
||||
```
|
||||
|
||||
The follow packages are available:
|
||||
|
||||
| Package | Description | In `CorePackage` | In `StandardPackage` |
|
||||
| ------------------------ | ----------------------------------------------- | :--------------: | :------------------: |
|
||||
| `BasicArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) | Yes | Yes |
|
||||
| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes |
|
||||
| `LogicPackage` | Logic and comparison operators (e.g. `==`, `>`) | Yes | Yes |
|
||||
| `BasicStringPackage` | Basic string functions | Yes | Yes |
|
||||
| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes |
|
||||
| `MoreStringPackage` | Additional string functions | No | Yes |
|
||||
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
||||
| `BasicArrayPackage` | Basic [array] functions | No | Yes |
|
||||
| `BasicMapPackage` | Basic [object map] functions | No | Yes |
|
||||
| `CorePackage` | Basic essentials | | |
|
||||
| `StandardPackage` | Standard library | | |
|
||||
|
||||
Evaluate expressions only
|
||||
-------------------------
|
||||
|
||||
@ -401,7 +429,7 @@ The following primitive types are supported natively:
|
||||
| **Unicode string** | `String` (_not_ `&str`) | `"string"` | `"hello"` etc. |
|
||||
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ? ? ? ]"` |
|
||||
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
|
||||
| **Timestamp** (implemented in standard library) | `std::time::Instant` | `"timestamp"` | _not supported_ |
|
||||
| **Timestamp** (implemented in the [`BasicTimePackage`](#packages)) | `std::time::Instant` | `"timestamp"` | _not supported_ |
|
||||
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
||||
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
||||
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
||||
@ -428,7 +456,7 @@ type_of('c') == "char";
|
||||
type_of(42) == "i64";
|
||||
|
||||
let x = 123;
|
||||
x.type_of(); // <- error: 'type_of' cannot use method-call style
|
||||
x.type_of() == "i64"; // method-call style is also OK
|
||||
type_of(x) == "i64";
|
||||
|
||||
x = 99.999;
|
||||
@ -507,6 +535,16 @@ match item.type_name() { // 'type_name' returns the name
|
||||
}
|
||||
```
|
||||
|
||||
The following conversion traits are implemented for `Dynamic`:
|
||||
|
||||
* `From<i64>` (`i32` if [`only_i32`])
|
||||
* `From<f64>` (if not [`no_float`])
|
||||
* `From<bool>`
|
||||
* `From<String>`
|
||||
* `From<char>`
|
||||
* `From<Vec<T>>` (into an [array])
|
||||
* `From<HashMap<String, T>>` (into an [object map]).
|
||||
|
||||
Value conversions
|
||||
-----------------
|
||||
|
||||
@ -532,12 +570,12 @@ Traits
|
||||
|
||||
A number of traits, under the `rhai::` module namespace, provide additional functionalities.
|
||||
|
||||
| Trait | Description | Methods |
|
||||
| ------------------- | --------------------------------------------------------------------------------- | --------------------------------------- |
|
||||
| `RegisterFn` | Trait for registering functions | `register_fn` |
|
||||
| `RegisterDynamicFn` | Trait for registering functions returning [`Dynamic`] | `register_dynamic_fn` |
|
||||
| `RegisterResultFn` | Trait for registering fallible functions returning `Result<`_T_`, EvalAltResult>` | `register_result_fn` |
|
||||
| `Func` | Trait for creating anonymous functions from script | `create_from_ast`, `create_from_script` |
|
||||
| Trait | Description | Methods |
|
||||
| ------------------- | -------------------------------------------------------------------------------------- | --------------------------------------- |
|
||||
| `RegisterFn` | Trait for registering functions | `register_fn` |
|
||||
| `RegisterDynamicFn` | Trait for registering functions returning [`Dynamic`] | `register_dynamic_fn` |
|
||||
| `RegisterResultFn` | Trait for registering fallible functions returning `Result<`_T_`, Box<EvalAltResult>>` | `register_result_fn` |
|
||||
| `Func` | Trait for creating anonymous functions from script | `create_from_ast`, `create_from_script` |
|
||||
|
||||
Working with functions
|
||||
----------------------
|
||||
@ -560,7 +598,7 @@ fn get_an_any() -> Dynamic {
|
||||
Dynamic::from(42_i64)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), EvalAltResult>
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
@ -629,18 +667,20 @@ Fallible functions
|
||||
If a function is _fallible_ (i.e. it returns a `Result<_, Error>`), it can be registered with `register_result_fn`
|
||||
(using the `RegisterResultFn` trait).
|
||||
|
||||
The function must return `Result<_, EvalAltResult>`. `EvalAltResult` implements `From<&str>` and `From<String>` etc.
|
||||
and the error text gets converted into `EvalAltResult::ErrorRuntime`.
|
||||
The function must return `Result<_, Box<EvalAltResult>>`. `Box<EvalAltResult>` implements `From<&str>` and `From<String>` etc.
|
||||
and the error text gets converted into `Box<EvalAltResult::ErrorRuntime>`.
|
||||
|
||||
The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely.
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, EvalAltResult, Position};
|
||||
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
||||
|
||||
// Function that may fail
|
||||
fn safe_divide(x: i64, y: i64) -> Result<i64, EvalAltResult> {
|
||||
fn safe_divide(x: i64, y: i64) -> Result<i64, Box<EvalAltResult>> {
|
||||
if y == 0 {
|
||||
// Return an error if y is zero
|
||||
Err("Division by zero!".into()) // short-cut to create EvalAltResult
|
||||
Err("Division by zero!".into()) // short-cut to create Box<EvalAltResult::ErrorRuntime>
|
||||
} else {
|
||||
Ok(x / y)
|
||||
}
|
||||
@ -654,7 +694,7 @@ fn main()
|
||||
engine.register_result_fn("divide", safe_divide);
|
||||
|
||||
if let Err(error) = engine.eval::<i64>("divide(40, 0)") {
|
||||
println!("Error: {:?}", error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
|
||||
println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -741,7 +781,7 @@ impl TestStruct {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), EvalAltResult>
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
@ -838,12 +878,12 @@ with a special "pretty-print" name, [`type_of()`] will return that name instead.
|
||||
engine.register_type::<TestStruct>();
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
let x = new_ts();
|
||||
print(type_of(x)); // prints "path::to::module::TestStruct"
|
||||
print(x.type_of()); // prints "path::to::module::TestStruct"
|
||||
|
||||
engine.register_type_with_name::<TestStruct>("Hello");
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
let x = new_ts();
|
||||
print(type_of(x)); // prints "Hello"
|
||||
print(x.type_of()); // prints "Hello"
|
||||
```
|
||||
|
||||
Getters and setters
|
||||
@ -905,7 +945,7 @@ threaded through multiple invocations:
|
||||
```rust
|
||||
use rhai::{Engine, Scope, EvalAltResult};
|
||||
|
||||
fn main() -> Result<(), EvalAltResult>
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
@ -1116,7 +1156,7 @@ number = -5 - +5;
|
||||
Numeric functions
|
||||
-----------------
|
||||
|
||||
The following standard functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on
|
||||
The following standard functions (defined in the [`BasicMathPackage`] but excluded if using a [raw `Engine`]) operate on
|
||||
`i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||
|
||||
| Function | Description |
|
||||
@ -1127,7 +1167,7 @@ The following standard functions (defined in the standard library but excluded i
|
||||
Floating-point functions
|
||||
------------------------
|
||||
|
||||
The following standard functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on `f64` only:
|
||||
The following standard functions (defined in the [`BasicMathPackage`](#packages) but excluded if using a [raw `Engine`]) operate on `f64` only:
|
||||
|
||||
| Category | Functions |
|
||||
| ---------------- | ------------------------------------------------------------ |
|
||||
@ -1174,8 +1214,8 @@ Unicode characters.
|
||||
Individual characters within a Rhai string can also be replaced just as if the string is an array of Unicode characters.
|
||||
In Rhai, there is also no separate concepts of `String` and `&str` as in Rust.
|
||||
|
||||
Strings can be built up from other strings and types via the `+` operator (provided by the standard library but excluded
|
||||
if using a [raw `Engine`]). This is particularly useful when printing output.
|
||||
Strings can be built up from other strings and types via the `+` operator (provided by the [`MoreStringPackage`](#packages)
|
||||
but excluded if using a [raw `Engine`]). This is particularly useful when printing output.
|
||||
|
||||
[`type_of()`] a string returns `"string"`.
|
||||
|
||||
@ -1225,7 +1265,7 @@ record == "Bob X. Davis: age 42 ❤\n";
|
||||
|
||||
### Built-in functions
|
||||
|
||||
The following standard methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on strings:
|
||||
The following standard methods (defined in the [`MoreStringPackage`](#packages) but excluded if using a [raw `Engine`]) operate on strings:
|
||||
|
||||
| Function | Parameter(s) | Description |
|
||||
| ------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
||||
@ -1300,7 +1340,7 @@ Arrays are disabled via the [`no_index`] feature.
|
||||
|
||||
### Built-in functions
|
||||
|
||||
The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on arrays:
|
||||
The following methods (defined in the [`BasicArrayPackage`](#packages) but excluded if using a [raw `Engine`]) operate on arrays:
|
||||
|
||||
| Function | Parameter(s) | Description |
|
||||
| ------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
@ -1400,6 +1440,7 @@ engine.register_fn("push", |list: &mut Array, item: MyType| list.push(Box::new(i
|
||||
Object maps
|
||||
-----------
|
||||
|
||||
[`Map`]: #object-maps
|
||||
[object map]: #object-maps
|
||||
[object maps]: #object-maps
|
||||
|
||||
@ -1420,7 +1461,7 @@ Object maps are disabled via the [`no_object`] feature.
|
||||
|
||||
### Built-in functions
|
||||
|
||||
The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on object maps:
|
||||
The following methods (defined in the [`BasicMapPackage`](#packages) but excluded if using a [raw `Engine`]) operate on object maps:
|
||||
|
||||
| Function | Parameter(s) | Description |
|
||||
| ------------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@ -1430,8 +1471,8 @@ The following methods (defined in the standard library but excluded if using a [
|
||||
| `remove` | property name | removes a certain property and returns it ([`()`] if the property does not exist) |
|
||||
| `mixin` | second object map | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
|
||||
| `+` operator | first object map, second object map | merges the first object map with the second |
|
||||
| `keys` | _none_ | returns an [array] of all the property names (in random order) |
|
||||
| `values` | _none_ | returns an [array] of all the property values (in random order) |
|
||||
| `keys` | _none_ | returns an [array] of all the property names (in random order), not available under [`no_index`] |
|
||||
| `values` | _none_ | returns an [array] of all the property values (in random order), not available under [`no_index`] |
|
||||
|
||||
### Examples
|
||||
|
||||
@ -1530,7 +1571,7 @@ let json = r#"{
|
||||
// Set the second boolean parameter to true in order to map 'null' to '()'
|
||||
let map = engine.parse_json(json, true)?;
|
||||
|
||||
map.len() == 6; // 'map' contains all properties int the JSON string
|
||||
map.len() == 6; // 'map' contains all properties in the JSON string
|
||||
|
||||
// Put the object map into a 'Scope'
|
||||
let mut scope = Scope::new();
|
||||
@ -1543,16 +1584,19 @@ result == 3; // the object map is successfully used i
|
||||
|
||||
`timestamp`'s
|
||||
-------------
|
||||
[`timestamp`]: #timestamp-s
|
||||
|
||||
Timestamps are provided by the standard library (excluded if using a [raw `Engine`]) via the `timestamp`
|
||||
[`timestamp`]: #timestamps
|
||||
[timestamp]: #timestamps
|
||||
[timestamps]: #timestamps
|
||||
|
||||
Timestamps are provided by the [`BasicTimePackage`](#packages) (excluded if using a [raw `Engine`]) via the `timestamp`
|
||||
function.
|
||||
|
||||
The Rust type of a timestamp is `std::time::Instant`. [`type_of()`] a timestamp returns `"timestamp"`.
|
||||
|
||||
### Built-in functions
|
||||
|
||||
The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on timestamps:
|
||||
The following methods (defined in the [`BasicTimePackage`](#packages) but excluded if using a [raw `Engine`]) operate on timestamps:
|
||||
|
||||
| Function | Parameter(s) | Description |
|
||||
| ------------ | ---------------------------------- | -------------------------------------------------------- |
|
||||
@ -1600,13 +1644,13 @@ ts == 42; // false - types are not the same
|
||||
Boolean operators
|
||||
-----------------
|
||||
|
||||
| Operator | Description |
|
||||
| -------- | ------------------------------- |
|
||||
| `!` | Boolean _Not_ |
|
||||
| `&&` | Boolean _And_ (short-circuits) |
|
||||
| `\|\|` | Boolean _Or_ (short-circuits) |
|
||||
| `&` | Boolean _And_ (full evaluation) |
|
||||
| `\|` | Boolean _Or_ (full evaluation) |
|
||||
| Operator | Description |
|
||||
| -------- | ------------------------------------- |
|
||||
| `!` | Boolean _Not_ |
|
||||
| `&&` | Boolean _And_ (short-circuits) |
|
||||
| `\|\|` | Boolean _Or_ (short-circuits) |
|
||||
| `&` | Boolean _And_ (doesn't short-circuit) |
|
||||
| `\|` | Boolean _Or_ (doesn't short-circuit) |
|
||||
|
||||
Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated
|
||||
if the first one already proves the condition wrong.
|
||||
@ -1766,7 +1810,7 @@ return 123 + 456; // returns 579
|
||||
Errors and `throw`-ing exceptions
|
||||
--------------------------------
|
||||
|
||||
All of [`Engine`]'s evaluation/consuming methods return `Result<T, rhai::EvalAltResult>` with `EvalAltResult`
|
||||
All of [`Engine`]'s evaluation/consuming methods return `Result<T, Box<rhai::EvalAltResult>>` with `EvalAltResult`
|
||||
holding error information. To deliberately return an error during an evaluation, use the `throw` keyword.
|
||||
|
||||
```rust
|
||||
@ -1876,7 +1920,7 @@ Unlike C/C++, functions can be defined _anywhere_ within the global level. A fun
|
||||
prior to being used in a script; a statement in the script can freely call a function defined afterwards.
|
||||
This is similar to Rust and many other modern languages.
|
||||
|
||||
### Functions overloading
|
||||
### Function overloading
|
||||
|
||||
Functions can be _overloaded_ and are resolved purely upon the function's _name_ and the _number_ of parameters
|
||||
(but not parameter _types_, since all parameters are the same type - [`Dynamic`]).
|
||||
@ -1903,7 +1947,7 @@ Properties and methods in a Rust custom type registered with the [`Engine`] can
|
||||
```rust
|
||||
let a = new_ts(); // constructor function
|
||||
a.field = 500; // property access
|
||||
a.update(); // method call
|
||||
a.update(); // method call, 'a' can be changed
|
||||
|
||||
update(a); // this works, but 'a' is unchanged because only
|
||||
// a COPY of 'a' is passed to 'update' by VALUE
|
||||
@ -2205,7 +2249,7 @@ eval("{ let z = y }"); // to keep a variable local, use a statement block
|
||||
|
||||
print("z = " + z); // <- error: variable 'z' not found
|
||||
|
||||
"print(42)".eval(); // <- nope... just like 'type_of', method-call style doesn't work
|
||||
"print(42)".eval(); // <- nope... method-call style doesn't work
|
||||
```
|
||||
|
||||
Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_,
|
||||
|
@ -16,6 +16,17 @@ fn bench_engine_new_raw(bench: &mut Bencher) {
|
||||
bench.iter(|| Engine::new_raw());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_engine_new_raw_core(bench: &mut Bencher) {
|
||||
use rhai::packages::*;
|
||||
let package = CorePackage::new();
|
||||
|
||||
bench.iter(|| {
|
||||
let mut engine = Engine::new_raw();
|
||||
engine.load_package(package.get());
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_engine_register_fn(bench: &mut Bencher) {
|
||||
fn hello(a: INT, b: Array, c: Map) -> bool {
|
||||
@ -23,7 +34,7 @@ fn bench_engine_register_fn(bench: &mut Bencher) {
|
||||
}
|
||||
|
||||
bench.iter(|| {
|
||||
let mut engine = Engine::new();
|
||||
let mut engine = Engine::new_raw();
|
||||
engine.register_fn("hello", hello);
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ fn bench_eval_array_large_set(bench: &mut Bencher) {
|
||||
let script = r#"let x = [ 1, 2.345, "hello", true,
|
||||
[ 1, 2, 3, [ "hey", [ "deeply", "nested" ], "jude" ] ]
|
||||
];
|
||||
x[4] = 42
|
||||
x[4][3][1][1] = 42
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
@ -3,7 +3,7 @@
|
||||
///! Test 1,000 iterations
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel, Scope, INT};
|
||||
use rhai::{Engine, OptimizationLevel, INT};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
@ -34,6 +34,8 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) {
|
||||
fibonacci(n-1) + fibonacci(n-2)
|
||||
}
|
||||
}
|
||||
|
||||
fibonacci(20)
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
@ -41,9 +43,5 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
engine
|
||||
.call_fn::<_, INT>(&mut Scope::new(), &ast, "fibonacci", (20 as INT,))
|
||||
.unwrap()
|
||||
});
|
||||
bench.iter(|| engine.eval_ast::<INT>(&ast).unwrap());
|
||||
}
|
||||
|
@ -74,7 +74,8 @@ fn bench_parse_primes(bench: &mut Bencher) {
|
||||
let prime_mask = [];
|
||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
||||
|
||||
prime_mask[0] = prime_mask[1] = false;
|
||||
prime_mask[0] = false;
|
||||
prime_mask[1] = false;
|
||||
|
||||
let total_primes_found = 0;
|
||||
|
||||
|
@ -9,14 +9,13 @@ use test::Bencher;
|
||||
// This script uses the Sieve of Eratosthenes to calculate prime numbers.
|
||||
|
||||
const SCRIPT: &str = r#"
|
||||
let now = timestamp();
|
||||
|
||||
const MAX_NUMBER_TO_CHECK = 1_000; // 168 primes <= 1000
|
||||
|
||||
let prime_mask = [];
|
||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
||||
|
||||
prime_mask[0] = prime_mask[1] = false;
|
||||
prime_mask[0] = false;
|
||||
prime_mask[1] = false;
|
||||
|
||||
let total_primes_found = 0;
|
||||
|
||||
|
@ -16,7 +16,7 @@ impl TestStruct {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn main() -> Result<(), EvalAltResult> {
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
@ -1,7 +1,9 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
use rhai::{packages::*, Engine, EvalAltResult, INT};
|
||||
use std::rc::Rc;
|
||||
|
||||
fn main() -> Result<(), EvalAltResult> {
|
||||
let engine = Engine::new();
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new_raw();
|
||||
engine.load_package(ArithmeticPackage::new().get());
|
||||
|
||||
let result = engine.eval::<INT>("40 + 2")?;
|
||||
|
||||
|
@ -2,7 +2,13 @@
|
||||
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
fn main() -> Result<(), EvalAltResult> {
|
||||
#[cfg(feature = "no_std")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let result = engine.eval::<INT>("40 + 2")?;
|
||||
|
@ -157,7 +157,7 @@ fn main() {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
println!();
|
||||
print_error(&input, err);
|
||||
print_error(&input, *err);
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
|
||||
fn main() -> Result<(), EvalAltResult> {
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
|
@ -72,7 +72,7 @@ fn main() {
|
||||
eprintln!("{:=<1$}", "", filename.len());
|
||||
eprintln!("");
|
||||
|
||||
eprint_error(&contents, err);
|
||||
eprint_error(&contents, *err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
|
||||
fn main() -> Result<(), EvalAltResult> {
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
fn add(x: INT, y: INT) -> INT {
|
||||
|
@ -16,9 +16,7 @@ fn mat_gen(n) {
|
||||
|
||||
for i in range(0, n) {
|
||||
for j in range(0, n) {
|
||||
let foo = m[i];
|
||||
foo[j] = tmp * (i.to_float() - j.to_float()) * (i.to_float() + j.to_float());
|
||||
m[i] = foo;
|
||||
m[i][j] = tmp * (i.to_float() - j.to_float()) * (i.to_float() + j.to_float());
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,9 +32,7 @@ fn mat_mul(a, b) {
|
||||
|
||||
for i in range(0, n) {
|
||||
for j in range(0, p) {
|
||||
let foo = b2[j];
|
||||
foo[i] = b[i][j];
|
||||
b2[j] = foo;
|
||||
b2[j][i] = b[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,13 @@
|
||||
|
||||
let now = timestamp();
|
||||
|
||||
const MAX_NUMBER_TO_CHECK = 10_000; // 1229 primes <= 10000
|
||||
const MAX_NUMBER_TO_CHECK = 100_000; // 9592 primes <= 100000
|
||||
|
||||
let prime_mask = [];
|
||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
||||
|
||||
prime_mask[0] = prime_mask[1] = false;
|
||||
prime_mask[0] = false;
|
||||
prime_mask[1] = false;
|
||||
|
||||
let total_primes_found = 0;
|
||||
|
||||
|
74
src/any.rs
74
src/any.rs
@ -9,8 +9,10 @@ use crate::parser::FLOAT;
|
||||
use crate::stdlib::{
|
||||
any::{type_name, Any, TypeId},
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
@ -65,6 +67,9 @@ impl<T: Any + Clone> Variant for T {
|
||||
}
|
||||
|
||||
/// A trait to represent any type.
|
||||
///
|
||||
/// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`),
|
||||
/// `bool`, `String`, `char`, `Vec<T>` (into `Array`) and `HashMap<String, T>` (into `Map`).
|
||||
#[cfg(feature = "sync")]
|
||||
pub trait Variant: Any + Send + Sync {
|
||||
/// Convert this `Variant` trait object to `&dyn Any`.
|
||||
@ -207,7 +212,7 @@ impl fmt::Display for Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value) => write!(f, "{}", value),
|
||||
Union::Array(value) => write!(f, "{:?}", value),
|
||||
Union::Map(value) => write!(f, "{:?}", value),
|
||||
Union::Map(value) => write!(f, "#{:?}", value),
|
||||
Union::Variant(_) => write!(f, "?"),
|
||||
}
|
||||
}
|
||||
@ -224,7 +229,7 @@ impl fmt::Debug for Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value) => write!(f, "{:?}", value),
|
||||
Union::Array(value) => write!(f, "{:?}", value),
|
||||
Union::Map(value) => write!(f, "{:?}", value),
|
||||
Union::Map(value) => write!(f, "#{:?}", value),
|
||||
Union::Variant(_) => write!(f, "<dynamic>"),
|
||||
}
|
||||
}
|
||||
@ -263,16 +268,6 @@ fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<T, Box<X>> {
|
||||
}
|
||||
|
||||
impl Dynamic {
|
||||
/// Get a reference to the inner `Union`.
|
||||
pub(crate) fn get_ref(&self) -> &Union {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the inner `Union`.
|
||||
pub(crate) fn get_mut(&mut self) -> &mut Union {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
||||
///
|
||||
/// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`.
|
||||
@ -450,7 +445,7 @@ impl Dynamic {
|
||||
|
||||
/// Cast the `Dynamic` as the system integer type `INT` and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
pub(crate) fn as_int(&self) -> Result<INT, &'static str> {
|
||||
pub fn as_int(&self) -> Result<INT, &'static str> {
|
||||
match self.0 {
|
||||
Union::Int(n) => Ok(n),
|
||||
_ => Err(self.type_name()),
|
||||
@ -459,7 +454,7 @@ impl Dynamic {
|
||||
|
||||
/// Cast the `Dynamic` as a `bool` and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
pub(crate) fn as_bool(&self) -> Result<bool, &'static str> {
|
||||
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
||||
match self.0 {
|
||||
Union::Bool(b) => Ok(b),
|
||||
_ => Err(self.type_name()),
|
||||
@ -468,7 +463,7 @@ impl Dynamic {
|
||||
|
||||
/// Cast the `Dynamic` as a `char` and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
pub(crate) fn as_char(&self) -> Result<char, &'static str> {
|
||||
pub fn as_char(&self) -> Result<char, &'static str> {
|
||||
match self.0 {
|
||||
Union::Char(n) => Ok(n),
|
||||
_ => Err(self.type_name()),
|
||||
@ -477,7 +472,7 @@ impl Dynamic {
|
||||
|
||||
/// Cast the `Dynamic` as a string and return the string slice.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
pub(crate) fn as_str(&self) -> Result<&str, &'static str> {
|
||||
pub fn as_str(&self) -> Result<&str, &'static str> {
|
||||
match &self.0 {
|
||||
Union::Str(s) => Ok(s),
|
||||
_ => Err(self.type_name()),
|
||||
@ -486,33 +481,62 @@ impl Dynamic {
|
||||
|
||||
/// Convert the `Dynamic` into `String` and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
pub(crate) fn take_string(self) -> Result<String, &'static str> {
|
||||
pub fn take_string(self) -> Result<String, &'static str> {
|
||||
match self.0 {
|
||||
Union::Str(s) => Ok(*s),
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_unit() -> Self {
|
||||
Self(Union::Unit(()))
|
||||
impl From<()> for Dynamic {
|
||||
fn from(value: ()) -> Self {
|
||||
Self(Union::Unit(value))
|
||||
}
|
||||
pub(crate) fn from_bool(value: bool) -> Self {
|
||||
}
|
||||
impl From<bool> for Dynamic {
|
||||
fn from(value: bool) -> Self {
|
||||
Self(Union::Bool(value))
|
||||
}
|
||||
pub(crate) fn from_int(value: INT) -> Self {
|
||||
}
|
||||
impl From<INT> for Dynamic {
|
||||
fn from(value: INT) -> Self {
|
||||
Self(Union::Int(value))
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub(crate) fn from_float(value: FLOAT) -> Self {
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl From<FLOAT> for Dynamic {
|
||||
fn from(value: FLOAT) -> Self {
|
||||
Self(Union::Float(value))
|
||||
}
|
||||
pub(crate) fn from_char(value: char) -> Self {
|
||||
}
|
||||
impl From<char> for Dynamic {
|
||||
fn from(value: char) -> Self {
|
||||
Self(Union::Char(value))
|
||||
}
|
||||
pub(crate) fn from_string(value: String) -> Self {
|
||||
}
|
||||
impl From<String> for Dynamic {
|
||||
fn from(value: String) -> Self {
|
||||
Self(Union::Str(Box::new(value)))
|
||||
}
|
||||
}
|
||||
impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
Self(Union::Array(Box::new(
|
||||
value.into_iter().map(Dynamic::from).collect(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic {
|
||||
fn from(value: HashMap<String, T>) -> Self {
|
||||
Self(Union::Map(Box::new(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Dynamic::from(v)))
|
||||
.collect(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only
|
||||
/// be implemented by this crate.
|
||||
|
184
src/api.rs
184
src/api.rs
@ -1,7 +1,7 @@
|
||||
//! Module that defines the extern API of `Engine`.
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::engine::{calc_fn_spec, make_getter, make_setter, Engine, FnAny, Map};
|
||||
use crate::engine::{make_getter, make_setter, Engine, Map, State};
|
||||
use crate::error::ParseError;
|
||||
use crate::fn_call::FuncArgs;
|
||||
use crate::fn_register::RegisterFn;
|
||||
@ -44,28 +44,22 @@ impl<F: Fn(&mut T, U) + 'static, T, U> ObjectSetCallback<T, U> for F {}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
pub trait IteratorCallback:
|
||||
Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync + 'static
|
||||
Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync + 'static
|
||||
{
|
||||
}
|
||||
#[cfg(feature = "sync")]
|
||||
impl<F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync + 'static> IteratorCallback
|
||||
impl<F: Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync + 'static> IteratorCallback
|
||||
for F
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub trait IteratorCallback: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static {}
|
||||
pub trait IteratorCallback: Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static {}
|
||||
#[cfg(not(feature = "sync"))]
|
||||
impl<F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static> IteratorCallback for F {}
|
||||
impl<F: Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static> IteratorCallback for F {}
|
||||
|
||||
/// Engine public API
|
||||
impl Engine {
|
||||
/// Register a custom function.
|
||||
pub(crate) fn register_fn_raw(&mut self, fn_name: &str, args: Vec<TypeId>, f: Box<FnAny>) {
|
||||
self.functions
|
||||
.insert(calc_fn_spec(fn_name, args.into_iter()), f);
|
||||
}
|
||||
|
||||
/// Register a custom type for use with the `Engine`.
|
||||
/// The type must implement `Clone`.
|
||||
///
|
||||
@ -82,7 +76,7 @@ impl Engine {
|
||||
/// fn update(&mut self, offset: i64) { self.field += offset; }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
@ -122,7 +116,7 @@ impl Engine {
|
||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
@ -152,14 +146,8 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) {
|
||||
if self.type_names.is_none() {
|
||||
self.type_names = Some(HashMap::new());
|
||||
}
|
||||
|
||||
// Add the pretty-print type name into the map
|
||||
self.type_names
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(type_name::<T>().to_string(), name.to_string());
|
||||
}
|
||||
|
||||
@ -188,7 +176,7 @@ impl Engine {
|
||||
/// fn get_field(&mut self) -> i64 { self.field }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
@ -230,7 +218,7 @@ impl Engine {
|
||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
@ -281,7 +269,7 @@ impl Engine {
|
||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
@ -316,7 +304,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -330,7 +318,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
||||
pub fn compile(&self, script: &str) -> Result<AST, Box<ParseError>> {
|
||||
self.compile_with_scope(&Scope::new(), script)
|
||||
}
|
||||
|
||||
@ -341,7 +329,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_optimize"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope, OptimizationLevel};
|
||||
@ -371,7 +359,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
||||
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, Box<ParseError>> {
|
||||
self.compile_with_scope_and_optimization_level(scope, script, self.optimization_level)
|
||||
}
|
||||
|
||||
@ -381,22 +369,22 @@ impl Engine {
|
||||
scope: &Scope,
|
||||
script: &str,
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> Result<AST, ParseError> {
|
||||
) -> Result<AST, Box<ParseError>> {
|
||||
let scripts = [script];
|
||||
let stream = lex(&scripts);
|
||||
parse(&mut stream.peekable(), self, scope, optimization_level).map_err(|err| *err)
|
||||
parse(&mut stream.peekable(), self, scope, optimization_level)
|
||||
}
|
||||
|
||||
/// Read the contents of a file into a string.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
fn read_file(path: PathBuf) -> Result<String, EvalAltResult> {
|
||||
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
|
||||
let mut f = File::open(path.clone())
|
||||
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(path.clone(), err))?;
|
||||
.map_err(|err| Box::new(EvalAltResult::ErrorReadingScriptFile(path.clone(), err)))?;
|
||||
|
||||
let mut contents = String::new();
|
||||
|
||||
f.read_to_string(&mut contents)
|
||||
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(path.clone(), err))?;
|
||||
.map_err(|err| Box::new(EvalAltResult::ErrorReadingScriptFile(path.clone(), err)))?;
|
||||
|
||||
Ok(contents)
|
||||
}
|
||||
@ -406,7 +394,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -422,7 +410,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub fn compile_file(&self, path: PathBuf) -> Result<AST, EvalAltResult> {
|
||||
pub fn compile_file(&self, path: PathBuf) -> Result<AST, Box<EvalAltResult>> {
|
||||
self.compile_file_with_scope(&Scope::new(), path)
|
||||
}
|
||||
|
||||
@ -433,7 +421,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_optimize"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope, OptimizationLevel};
|
||||
@ -461,7 +449,7 @@ impl Engine {
|
||||
&self,
|
||||
scope: &Scope,
|
||||
path: PathBuf,
|
||||
) -> Result<AST, EvalAltResult> {
|
||||
) -> Result<AST, Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
|
||||
}
|
||||
|
||||
@ -473,7 +461,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -489,7 +477,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, EvalAltResult> {
|
||||
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> {
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Trims the JSON string and add a '#' in front
|
||||
@ -516,7 +504,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -530,7 +518,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
||||
pub fn compile_expression(&self, script: &str) -> Result<AST, Box<ParseError>> {
|
||||
self.compile_expression_with_scope(&Scope::new(), script)
|
||||
}
|
||||
|
||||
@ -543,7 +531,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_optimize"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope, OptimizationLevel};
|
||||
@ -577,12 +565,11 @@ impl Engine {
|
||||
&self,
|
||||
scope: &Scope,
|
||||
script: &str,
|
||||
) -> Result<AST, ParseError> {
|
||||
) -> Result<AST, Box<ParseError>> {
|
||||
let scripts = [script];
|
||||
let stream = lex(&scripts);
|
||||
|
||||
parse_global_expr(&mut stream.peekable(), self, scope, self.optimization_level)
|
||||
.map_err(|err| *err)
|
||||
}
|
||||
|
||||
/// Evaluate a script file.
|
||||
@ -590,7 +577,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -601,7 +588,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub fn eval_file<T: Variant + Clone>(&self, path: PathBuf) -> Result<T, EvalAltResult> {
|
||||
pub fn eval_file<T: Variant + Clone>(&self, path: PathBuf) -> Result<T, Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
|
||||
}
|
||||
|
||||
@ -610,7 +597,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -629,7 +616,7 @@ impl Engine {
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
path: PathBuf,
|
||||
) -> Result<T, EvalAltResult> {
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.eval_with_scope::<T>(scope, &contents))
|
||||
}
|
||||
|
||||
@ -638,7 +625,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -647,7 +634,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, EvalAltResult> {
|
||||
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
|
||||
self.eval_with_scope(&mut Scope::new(), script)
|
||||
}
|
||||
|
||||
@ -656,7 +643,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -677,7 +664,7 @@ impl Engine {
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
script: &str,
|
||||
) -> Result<T, EvalAltResult> {
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
// Since the AST will be thrown away afterwards, don't bother to optimize it
|
||||
let ast =
|
||||
self.compile_with_scope_and_optimization_level(scope, script, OptimizationLevel::None)?;
|
||||
@ -689,7 +676,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -698,7 +685,10 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn eval_expression<T: Variant + Clone>(&self, script: &str) -> Result<T, EvalAltResult> {
|
||||
pub fn eval_expression<T: Variant + Clone>(
|
||||
&self,
|
||||
script: &str,
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
self.eval_expression_with_scope(&mut Scope::new(), script)
|
||||
}
|
||||
|
||||
@ -707,7 +697,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -724,7 +714,7 @@ impl Engine {
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
script: &str,
|
||||
) -> Result<T, EvalAltResult> {
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let scripts = [script];
|
||||
let stream = lex(&scripts);
|
||||
// Since the AST will be thrown away afterwards, don't bother to optimize it
|
||||
@ -737,7 +727,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -750,7 +740,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, EvalAltResult> {
|
||||
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
||||
self.eval_ast_with_scope(&mut Scope::new(), ast)
|
||||
}
|
||||
|
||||
@ -759,7 +749,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -787,15 +777,16 @@ impl Engine {
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<T, EvalAltResult> {
|
||||
let result = self
|
||||
.eval_ast_with_scope_raw(scope, ast)
|
||||
.map_err(|err| *err)?;
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let result = self.eval_ast_with_scope_raw(scope, ast)?;
|
||||
|
||||
let return_type = self.map_type_name(result.type_name());
|
||||
|
||||
return result.try_cast::<T>().ok_or_else(|| {
|
||||
EvalAltResult::ErrorMismatchOutputType(return_type.to_string(), Position::none())
|
||||
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||
return_type.to_string(),
|
||||
Position::none(),
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
@ -804,10 +795,12 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut state = State::new();
|
||||
|
||||
ast.0
|
||||
.iter()
|
||||
.try_fold(Dynamic::from_unit(), |_, stmt| {
|
||||
self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0)
|
||||
.try_fold(().into(), |_, stmt| {
|
||||
self.eval_stmt(scope, &mut state, ast.1.as_ref(), stmt, 0)
|
||||
})
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::Return(out, _) => Ok(out),
|
||||
@ -818,7 +811,7 @@ impl Engine {
|
||||
/// Evaluate a file, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub fn consume_file(&self, path: PathBuf) -> Result<(), EvalAltResult> {
|
||||
pub fn consume_file(&self, path: PathBuf) -> Result<(), Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.consume(&contents))
|
||||
}
|
||||
|
||||
@ -829,19 +822,23 @@ impl Engine {
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
path: PathBuf,
|
||||
) -> Result<(), EvalAltResult> {
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.consume_with_scope(scope, &contents))
|
||||
}
|
||||
|
||||
/// Evaluate a string, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
pub fn consume(&self, script: &str) -> Result<(), EvalAltResult> {
|
||||
pub fn consume(&self, script: &str) -> Result<(), Box<EvalAltResult>> {
|
||||
self.consume_with_scope(&mut Scope::new(), script)
|
||||
}
|
||||
|
||||
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
pub fn consume_with_scope(&self, scope: &mut Scope, script: &str) -> Result<(), EvalAltResult> {
|
||||
pub fn consume_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
script: &str,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let scripts = [script];
|
||||
let stream = lex(&scripts);
|
||||
|
||||
@ -852,7 +849,7 @@ impl Engine {
|
||||
|
||||
/// Evaluate an AST, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
pub fn consume_ast(&self, ast: &AST) -> Result<(), EvalAltResult> {
|
||||
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
||||
self.consume_ast_with_scope(&mut Scope::new(), ast)
|
||||
}
|
||||
|
||||
@ -862,16 +859,18 @@ impl Engine {
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<(), EvalAltResult> {
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let mut state = State::new();
|
||||
|
||||
ast.0
|
||||
.iter()
|
||||
.try_fold(Dynamic::from_unit(), |_, stmt| {
|
||||
self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0)
|
||||
.try_fold(().into(), |_, stmt| {
|
||||
self.eval_stmt(scope, &mut state, ast.1.as_ref(), stmt, 0)
|
||||
})
|
||||
.map_or_else(
|
||||
|err| match *err {
|
||||
EvalAltResult::Return(_, _) => Ok(()),
|
||||
err => Err(err),
|
||||
err => Err(Box::new(err)),
|
||||
},
|
||||
|_| Ok(()),
|
||||
)
|
||||
@ -882,7 +881,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_function"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope};
|
||||
@ -919,21 +918,26 @@ impl Engine {
|
||||
ast: &AST,
|
||||
name: &str,
|
||||
args: A,
|
||||
) -> Result<T, EvalAltResult> {
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let mut arg_values = args.into_vec();
|
||||
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
||||
let fn_lib = Some(ast.1.as_ref());
|
||||
let fn_lib = ast.1.as_ref();
|
||||
let pos = Position::none();
|
||||
|
||||
let result = self
|
||||
.call_fn_raw(Some(scope), fn_lib, name, &mut args, None, pos, 0)
|
||||
.map_err(|err| *err)?;
|
||||
let fn_def = fn_lib
|
||||
.get_function(name, args.len())
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
||||
|
||||
let result = self.call_fn_from_lib(Some(scope), fn_lib, fn_def, &mut args, pos, 0)?;
|
||||
|
||||
let return_type = self.map_type_name(result.type_name());
|
||||
|
||||
return result
|
||||
.try_cast()
|
||||
.ok_or_else(|| EvalAltResult::ErrorMismatchOutputType(return_type.into(), pos));
|
||||
return result.try_cast().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||
return_type.into(),
|
||||
pos,
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
/// Optimize the `AST` with constants defined in an external Scope.
|
||||
@ -967,7 +971,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # use std::sync::RwLock;
|
||||
/// # use std::sync::Arc;
|
||||
/// use rhai::Engine;
|
||||
@ -988,14 +992,14 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(feature = "sync")]
|
||||
pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
||||
self.on_print = Some(Box::new(callback));
|
||||
self.print = Box::new(callback);
|
||||
}
|
||||
/// Override default action of `print` (print to stdout using `println!`)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # use std::sync::RwLock;
|
||||
/// # use std::sync::Arc;
|
||||
/// use rhai::Engine;
|
||||
@ -1016,7 +1020,7 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
||||
self.on_print = Some(Box::new(callback));
|
||||
self.print = Box::new(callback);
|
||||
}
|
||||
|
||||
/// Override default action of `debug` (print to stdout using `println!`)
|
||||
@ -1024,7 +1028,7 @@ impl Engine {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # use std::sync::RwLock;
|
||||
/// # use std::sync::Arc;
|
||||
/// use rhai::Engine;
|
||||
@ -1045,14 +1049,14 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(feature = "sync")]
|
||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
||||
self.on_debug = Some(Box::new(callback));
|
||||
self.debug = Box::new(callback);
|
||||
}
|
||||
/// Override default action of `debug` (print to stdout using `println!`)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # use std::sync::RwLock;
|
||||
/// # use std::sync::Arc;
|
||||
/// use rhai::Engine;
|
||||
@ -1073,6 +1077,6 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
||||
self.on_debug = Some(Box::new(callback));
|
||||
self.debug = Box::new(callback);
|
||||
}
|
||||
}
|
||||
|
1238
src/builtin.rs
1238
src/builtin.rs
File diff suppressed because it is too large
Load Diff
1662
src/engine.rs
1662
src/engine.rs
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{char, error::Error, fmt, string::String};
|
||||
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
||||
|
||||
/// Error when tokenizing the script text.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||
@ -19,6 +19,8 @@ pub enum LexError {
|
||||
MalformedChar(String),
|
||||
/// An identifier is in an invalid format.
|
||||
MalformedIdentifier(String),
|
||||
/// Bad keyword encountered when tokenizing the script text.
|
||||
ImproperKeyword(String),
|
||||
}
|
||||
|
||||
impl Error for LexError {}
|
||||
@ -32,6 +34,7 @@ impl fmt::Display for LexError {
|
||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
|
||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s),
|
||||
Self::UnterminatedString => write!(f, "Open string is not terminated"),
|
||||
Self::ImproperKeyword(s) => write!(f, "{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -97,8 +100,6 @@ pub enum ParseErrorType {
|
||||
FnMissingBody(String),
|
||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
||||
AssignmentToInvalidLHS,
|
||||
/// Assignment to a copy of a value.
|
||||
AssignmentToCopy,
|
||||
/// Assignment to an a constant variable.
|
||||
AssignmentToConstant(String),
|
||||
/// Break statement not inside a loop.
|
||||
@ -147,7 +148,6 @@ impl ParseError {
|
||||
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
|
||||
ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value",
|
||||
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant variable.",
|
||||
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
||||
}
|
||||
|
@ -5,15 +5,15 @@
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::stdlib::vec::Vec;
|
||||
|
||||
/// Trait that represent arguments to a function call.
|
||||
/// Any data type that can be converted into a `Vec` of `Dynamic` values can be used
|
||||
/// Trait that represents arguments to a function call.
|
||||
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
||||
/// as arguments to a function call.
|
||||
pub trait FuncArgs {
|
||||
/// Convert to a `Vec` of `Dynamic` arguments.
|
||||
/// Convert to a `Vec<Dynamic>` of the function call arguments.
|
||||
fn into_vec(self) -> Vec<Dynamic>;
|
||||
}
|
||||
|
||||
// Macro to implement `FuncArgs` for tuples of standard types (each can be
|
||||
/// Macro to implement `FuncArgs` for tuples of standard types (each can be
|
||||
/// converted into `Dynamic`).
|
||||
macro_rules! impl_args {
|
||||
($($p:ident),*) => {
|
||||
|
@ -21,18 +21,18 @@ pub trait Func<ARGS, RET> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Func}; // use 'Func' for 'create_from_ast'
|
||||
///
|
||||
/// let engine = Engine::new(); // create a new 'Engine' just for this
|
||||
///
|
||||
/// let ast = engine.compile("fn calc(x, y) { x + y.len() < 42 }")?;
|
||||
/// let ast = engine.compile("fn calc(x, y) { x + len(y) < 42 }")?;
|
||||
///
|
||||
/// // Func takes two type parameters:
|
||||
/// // 1) a tuple made up of the types of the script function's parameters
|
||||
/// // 2) the return type of the script function
|
||||
/// //
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, EvalAltResult>> and is callable!
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
/// let func = Func::<(i64, String), bool>::create_from_ast(
|
||||
/// // ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
///
|
||||
@ -52,18 +52,18 @@ pub trait Func<ARGS, RET> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Func}; // use 'Func' for 'create_from_script'
|
||||
///
|
||||
/// let engine = Engine::new(); // create a new 'Engine' just for this
|
||||
///
|
||||
/// let script = "fn calc(x, y) { x + y.len() < 42 }";
|
||||
/// let script = "fn calc(x, y) { x + len(y) < 42 }";
|
||||
///
|
||||
/// // Func takes two type parameters:
|
||||
/// // 1) a tuple made up of the types of the script function's parameters
|
||||
/// // 2) the return type of the script function
|
||||
/// //
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, EvalAltResult>> and is callable!
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
/// let func = Func::<(i64, String), bool>::create_from_script(
|
||||
/// // ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
///
|
||||
@ -80,7 +80,7 @@ pub trait Func<ARGS, RET> {
|
||||
self,
|
||||
script: &str,
|
||||
entry_point: &str,
|
||||
) -> Result<Self::Output, ParseError>;
|
||||
) -> Result<Self::Output, Box<ParseError>>;
|
||||
}
|
||||
|
||||
macro_rules! def_anonymous_fn {
|
||||
@ -91,10 +91,10 @@ macro_rules! def_anonymous_fn {
|
||||
impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine
|
||||
{
|
||||
#[cfg(feature = "sync")]
|
||||
type Output = Box<dyn Fn($($par),*) -> Result<RET, EvalAltResult> + Send + Sync>;
|
||||
type Output = Box<dyn Fn($($par),*) -> Result<RET, Box<EvalAltResult>> + Send + Sync>;
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
type Output = Box<dyn Fn($($par),*) -> Result<RET, EvalAltResult>>;
|
||||
type Output = Box<dyn Fn($($par),*) -> Result<RET, Box<EvalAltResult>>>;
|
||||
|
||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
|
||||
let name = entry_point.to_string();
|
||||
@ -104,7 +104,7 @@ macro_rules! def_anonymous_fn {
|
||||
})
|
||||
}
|
||||
|
||||
fn create_from_script(self, script: &str, entry_point: &str) -> Result<Self::Output, ParseError> {
|
||||
fn create_from_script(self, script: &str, entry_point: &str) -> Result<Self::Output, Box<ParseError>> {
|
||||
let ast = self.compile(script)?;
|
||||
Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point))
|
||||
}
|
||||
|
@ -3,11 +3,11 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::engine::{Engine, FnCallArgs};
|
||||
use crate::engine::{calc_fn_spec, Engine, FnCallArgs};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, string::ToString, vec};
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, string::ToString};
|
||||
|
||||
/// A trait to register custom functions with the `Engine`.
|
||||
pub trait RegisterFn<FN, ARGS, RET> {
|
||||
@ -16,7 +16,7 @@ pub trait RegisterFn<FN, ARGS, RET> {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, RegisterFn};
|
||||
///
|
||||
/// // Normal function
|
||||
@ -48,7 +48,7 @@ pub trait RegisterDynamicFn<FN, ARGS> {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Dynamic, RegisterDynamicFn};
|
||||
///
|
||||
/// // Function that returns a Dynamic value
|
||||
@ -68,7 +68,7 @@ pub trait RegisterDynamicFn<FN, ARGS> {
|
||||
fn register_dynamic_fn(&mut self, name: &str, f: FN);
|
||||
}
|
||||
|
||||
/// A trait to register fallible custom functions returning Result<_, EvalAltResult> with the `Engine`.
|
||||
/// A trait to register fallible custom functions returning `Result<_, Box<EvalAltResult>>` with the `Engine`.
|
||||
pub trait RegisterResultFn<FN, ARGS, RET> {
|
||||
/// Register a custom fallible function with the `Engine`.
|
||||
///
|
||||
@ -78,9 +78,9 @@ pub trait RegisterResultFn<FN, ARGS, RET> {
|
||||
/// use rhai::{Engine, RegisterResultFn, EvalAltResult};
|
||||
///
|
||||
/// // Normal function
|
||||
/// fn div(x: i64, y: i64) -> Result<i64, EvalAltResult> {
|
||||
/// fn div(x: i64, y: i64) -> Result<i64, Box<EvalAltResult>> {
|
||||
/// if y == 0 {
|
||||
/// // '.into()' automatically converts to 'EvalAltResult::ErrorRuntime'
|
||||
/// // '.into()' automatically converts to 'Box<EvalAltResult::ErrorRuntime>'
|
||||
/// Err("division by zero!".into())
|
||||
/// } else {
|
||||
/// Ok(x / y)
|
||||
@ -117,16 +117,79 @@ pub struct Mut<T>(T);
|
||||
|
||||
/// Identity dereferencing function.
|
||||
#[inline]
|
||||
fn identity<T>(data: T) -> T {
|
||||
pub fn identity<T>(data: &mut T) -> &mut T {
|
||||
data
|
||||
}
|
||||
|
||||
/// Clone dereferencing function.
|
||||
#[inline]
|
||||
pub fn cloned<T: Clone>(data: &mut T) -> T {
|
||||
data.clone()
|
||||
}
|
||||
|
||||
/// This macro counts the number of arguments via recursion.
|
||||
macro_rules! count_args {
|
||||
() => { 0_usize };
|
||||
( $head:ident $($tail:ident)* ) => { 1_usize + count_args!($($tail)*) };
|
||||
}
|
||||
|
||||
/// This macro creates a closure wrapping a registered function.
|
||||
macro_rules! make_func {
|
||||
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $clone:expr),*) => {
|
||||
// ^ function name
|
||||
// ^ function pointer
|
||||
// ^ result mapping function
|
||||
// ^ function parameter generic type name (A, B, C etc.)
|
||||
// ^ dereferencing function
|
||||
|
||||
move |args: &mut FnCallArgs, pos: Position| {
|
||||
// Check for length at the beginning to avoid per-element bound checks.
|
||||
const NUM_ARGS: usize = count_args!($($par)*);
|
||||
|
||||
if args.len() != NUM_ARGS {
|
||||
return Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch($fn_name.clone(), NUM_ARGS, args.len(), pos)));
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
let mut drain = args.iter_mut();
|
||||
$(
|
||||
// Downcast every element, return in case of a type mismatch
|
||||
let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap();
|
||||
)*
|
||||
|
||||
// Call the user-supplied function using ($clone) to
|
||||
// potentially clone the value, otherwise pass the reference.
|
||||
let r = $fn($(($clone)($par)),*);
|
||||
$map(r, pos)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// To Dynamic mapping function.
|
||||
#[inline]
|
||||
pub fn map_dynamic<T: Variant + Clone>(
|
||||
data: T,
|
||||
_pos: Position,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(data.into_dynamic())
|
||||
}
|
||||
|
||||
/// To Dynamic mapping function.
|
||||
#[inline]
|
||||
pub fn map_identity(data: Dynamic, _pos: Position) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// To `Result<Dynamic, Box<EvalAltResult>>` mapping function.
|
||||
#[inline]
|
||||
pub fn map_result<T: Variant + Clone>(
|
||||
data: Result<T, Box<EvalAltResult>>,
|
||||
pos: Position,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
data.map(|v| v.into_dynamic())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
}
|
||||
|
||||
macro_rules! def_register {
|
||||
() => {
|
||||
def_register!(imp);
|
||||
@ -150,29 +213,9 @@ macro_rules! def_register {
|
||||
{
|
||||
fn register_fn(&mut self, name: &str, f: FN) {
|
||||
let fn_name = name.to_string();
|
||||
|
||||
let func = move |args: &mut FnCallArgs, pos: Position| {
|
||||
// Check for length at the beginning to avoid per-element bound checks.
|
||||
const NUM_ARGS: usize = count_args!($($par)*);
|
||||
|
||||
if args.len() != NUM_ARGS {
|
||||
return Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos)));
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
let mut drain = args.iter_mut();
|
||||
$(
|
||||
// Downcast every element, return in case of a type mismatch
|
||||
let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap();
|
||||
)*
|
||||
|
||||
// Call the user-supplied function using ($clone) to
|
||||
// potentially clone the value, otherwise pass the reference.
|
||||
let r = f($(($clone)($par)),*);
|
||||
Ok(r.into_dynamic())
|
||||
};
|
||||
|
||||
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
|
||||
let func = make_func!(fn_name : f : map_dynamic ; $($par => $clone),*);
|
||||
let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||
self.functions.insert(hash, Box::new(func));
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,27 +231,9 @@ macro_rules! def_register {
|
||||
{
|
||||
fn register_dynamic_fn(&mut self, name: &str, f: FN) {
|
||||
let fn_name = name.to_string();
|
||||
|
||||
let func = move |args: &mut FnCallArgs, pos: Position| {
|
||||
// Check for length at the beginning to avoid per-element bound checks.
|
||||
const NUM_ARGS: usize = count_args!($($par)*);
|
||||
|
||||
if args.len() != NUM_ARGS {
|
||||
return Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos)));
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
let mut drain = args.iter_mut();
|
||||
$(
|
||||
// Downcast every element, return in case of a type mismatch
|
||||
let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap();
|
||||
)*
|
||||
|
||||
// Call the user-supplied function using ($clone) to
|
||||
// potentially clone the value, otherwise pass the reference.
|
||||
Ok(f($(($clone)($par)),*))
|
||||
};
|
||||
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
|
||||
let func = make_func!(fn_name : f : map_identity ; $($par => $clone),*);
|
||||
let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||
self.functions.insert(hash, Box::new(func));
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,45 +241,26 @@ macro_rules! def_register {
|
||||
$($par: Variant + Clone,)*
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
FN: Fn($($param),*) -> Result<RET, EvalAltResult> + Send + Sync + 'static,
|
||||
FN: Fn($($param),*) -> Result<RET, Box<EvalAltResult>> + Send + Sync + 'static,
|
||||
#[cfg(not(feature = "sync"))]
|
||||
FN: Fn($($param),*) -> Result<RET, EvalAltResult> + 'static,
|
||||
FN: Fn($($param),*) -> Result<RET, Box<EvalAltResult>> + 'static,
|
||||
|
||||
RET: Variant + Clone
|
||||
> RegisterResultFn<FN, ($($mark,)*), RET> for Engine
|
||||
{
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) {
|
||||
let fn_name = name.to_string();
|
||||
|
||||
let func = move |args: &mut FnCallArgs, pos: Position| {
|
||||
// Check for length at the beginning to avoid per-element bound checks.
|
||||
const NUM_ARGS: usize = count_args!($($par)*);
|
||||
|
||||
if args.len() != NUM_ARGS {
|
||||
return Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos)));
|
||||
}
|
||||
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
let mut drain = args.iter_mut();
|
||||
$(
|
||||
// Downcast every element, return in case of a type mismatch
|
||||
let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap();
|
||||
)*
|
||||
|
||||
// Call the user-supplied function using ($clone) to
|
||||
// potentially clone the value, otherwise pass the reference.
|
||||
f($(($clone)($par)),*).map(|r| r.into_dynamic())
|
||||
.map_err(|err| Box::new(err.set_position(pos)))
|
||||
};
|
||||
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
|
||||
let func = make_func!(fn_name : f : map_result ; $($par => $clone),*);
|
||||
let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||
self.functions.insert(hash, Box::new(func));
|
||||
}
|
||||
}
|
||||
|
||||
//def_register!(imp_pop $($par => $mark => $param),*);
|
||||
};
|
||||
($p0:ident $(, $p:ident)*) => {
|
||||
def_register!(imp $p0 => $p0 => $p0 => Clone::clone $(, $p => $p => $p => Clone::clone)*);
|
||||
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => Clone::clone)*);
|
||||
def_register!(imp $p0 => $p0 => $p0 => cloned $(, $p => $p => $p => cloned)*);
|
||||
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => cloned)*);
|
||||
// handle the first parameter ^ first parameter passed through
|
||||
// ^ others passed by value (cloned)
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
//! ```,no_run
|
||||
//! use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||
//!
|
||||
//! fn main() -> Result<(), EvalAltResult>
|
||||
//! fn main() -> Result<(), Box<EvalAltResult>>
|
||||
//! {
|
||||
//! // Define external function
|
||||
//! fn compute_something(x: i64) -> bool {
|
||||
@ -71,13 +71,13 @@ extern crate alloc;
|
||||
|
||||
mod any;
|
||||
mod api;
|
||||
mod builtin;
|
||||
mod engine;
|
||||
mod error;
|
||||
mod fn_call;
|
||||
mod fn_func;
|
||||
mod fn_register;
|
||||
mod optimize;
|
||||
pub mod packages;
|
||||
mod parser;
|
||||
mod result;
|
||||
mod scope;
|
||||
@ -85,7 +85,7 @@ mod stdlib;
|
||||
mod token;
|
||||
|
||||
pub use any::Dynamic;
|
||||
pub use engine::Engine;
|
||||
pub use engine::{calc_fn_spec as calc_fn_hash, Engine};
|
||||
pub use error::{ParseError, ParseErrorType};
|
||||
pub use fn_call::FuncArgs;
|
||||
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{
|
||||
calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL,
|
||||
KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||
Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
|
||||
KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::packages::PackageLibrary;
|
||||
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
@ -110,14 +112,23 @@ impl<'a> State<'a> {
|
||||
|
||||
/// Call a registered function
|
||||
fn call_fn(
|
||||
packages: &Vec<PackageLibrary>,
|
||||
functions: &HashMap<u64, Box<FnAny>>,
|
||||
fn_name: &str,
|
||||
args: &mut FnCallArgs,
|
||||
pos: Position,
|
||||
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
||||
// Search built-in's and external functions
|
||||
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||
|
||||
functions
|
||||
.get(&calc_fn_spec(fn_name, args.iter().map(|a| a.type_id())))
|
||||
.get(&hash)
|
||||
.or_else(|| {
|
||||
packages
|
||||
.iter()
|
||||
.find(|p| p.functions.contains_key(&hash))
|
||||
.and_then(|p| p.functions.get(&hash))
|
||||
})
|
||||
.map(|func| func(args, pos))
|
||||
.transpose()
|
||||
}
|
||||
@ -167,7 +178,7 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
||||
Box::new(optimize_expr(expr, state)),
|
||||
Box::new(optimize_stmt(*if_block, state, true)),
|
||||
match optimize_stmt(*else_block, state, true) {
|
||||
stmt if matches!(stmt, Stmt::Noop(_)) => None, // Noop -> no else block
|
||||
Stmt::Noop(_) => None, // Noop -> no else block
|
||||
stmt => Some(Box::new(stmt)),
|
||||
},
|
||||
),
|
||||
@ -357,12 +368,12 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
//id = id2 = expr2
|
||||
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) {
|
||||
// var = var = expr2 -> var = expr2
|
||||
(Expr::Variable(var, _), Expr::Variable(var2, _)) if var == var2 => {
|
||||
(Expr::Variable(var, sp, _), Expr::Variable(var2, sp2, _)) if var == var2 && sp == sp2 => {
|
||||
// Assignment to the same variable - fold
|
||||
state.set_dirty();
|
||||
|
||||
Expr::Assignment(
|
||||
Box::new(Expr::Variable(var, pos)),
|
||||
Box::new(Expr::Variable(var, sp, pos)),
|
||||
Box::new(optimize_expr(*expr2, state)),
|
||||
pos,
|
||||
)
|
||||
@ -386,13 +397,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(lhs, rhs, pos) => match (*lhs, *rhs) {
|
||||
// map.string
|
||||
(Expr::Map(items, pos), Expr::Property(s, _))
|
||||
if items.iter().all(|(_, x, _)| x.is_pure()) =>
|
||||
{
|
||||
(Expr::Map(items, pos), Expr::Property(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => {
|
||||
// Map literal where everything is pure - promote the indexed item.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
items.into_iter().find(|(name, _, _)| name == s.as_ref())
|
||||
items.into_iter().find(|(name, _, _)| name == &s)
|
||||
.map(|(_, expr, _)| expr.set_position(pos))
|
||||
.unwrap_or_else(|| Expr::Unit(pos))
|
||||
}
|
||||
@ -417,13 +426,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
items.remove(i as usize).set_position(pos)
|
||||
}
|
||||
// map[string]
|
||||
(Expr::Map(items, pos), Expr::StringConstant(s, _))
|
||||
if items.iter().all(|(_, x, _)| x.is_pure()) =>
|
||||
{
|
||||
(Expr::Map(items, pos), Expr::StringConstant(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => {
|
||||
// Map literal where everything is pure - promote the indexed item.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
items.into_iter().find(|(name, _, _)| name == s.as_ref())
|
||||
items.into_iter().find(|(name, _, _)| name == &s)
|
||||
.map(|(_, expr, _)| expr.set_position(pos))
|
||||
.unwrap_or_else(|| Expr::Unit(pos))
|
||||
}
|
||||
@ -459,7 +466,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
// "xxx" in "xxxxx"
|
||||
(Expr::StringConstant(lhs, pos), Expr::StringConstant(rhs, _)) => {
|
||||
state.set_dirty();
|
||||
if rhs.contains(lhs.as_ref()) {
|
||||
if rhs.contains(&lhs) {
|
||||
Expr::True(pos)
|
||||
} else {
|
||||
Expr::False(pos)
|
||||
@ -552,7 +559,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
|
||||
// Do not call some special keywords
|
||||
Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref())=>
|
||||
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
|
||||
Expr::FunctionCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||
|
||||
// Eagerly call functions
|
||||
Expr::FunctionCall(id, args, def_value, pos)
|
||||
@ -562,7 +569,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
// First search in script-defined functions (can override built-in)
|
||||
if state.fn_lib.iter().find(|(name, len)| name == &id && *len == args.len()).is_some() {
|
||||
// A script-defined function overrides the built-in function - do not make the call
|
||||
return Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos);
|
||||
return Expr::FunctionCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos);
|
||||
}
|
||||
|
||||
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||
@ -576,15 +583,15 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
""
|
||||
};
|
||||
|
||||
call_fn(&state.engine.functions, &id, &mut call_args, pos).ok()
|
||||
call_fn(&state.engine.packages, &state.engine.functions, &id, &mut call_args, pos).ok()
|
||||
.and_then(|result|
|
||||
result.or_else(|| {
|
||||
if !arg_for_type_of.is_empty() {
|
||||
// Handle `type_of()`
|
||||
Some(Dynamic::from_string(arg_for_type_of.to_string()))
|
||||
Some(arg_for_type_of.to_string().into())
|
||||
} else {
|
||||
// Otherwise use the default value, if any
|
||||
def_value.clone()
|
||||
def_value.clone().map(|v| *v)
|
||||
}
|
||||
}).and_then(|result| map_dynamic_to_expr(result, pos))
|
||||
.map(|expr| {
|
||||
@ -593,16 +600,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
})
|
||||
).unwrap_or_else(||
|
||||
// Optimize function call arguments
|
||||
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos)
|
||||
Expr::FunctionCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos)
|
||||
)
|
||||
}
|
||||
|
||||
// id(args ..) -> optimize function call arguments
|
||||
Expr::FunctionCall(id, args, def_value, pos) =>
|
||||
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
|
||||
Expr::FunctionCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||
|
||||
// constant-name
|
||||
Expr::Variable(name, pos) if state.contains_constant(&name) => {
|
||||
Expr::Variable(name, _, pos) if state.contains_constant(&name) => {
|
||||
state.set_dirty();
|
||||
|
||||
// Replace constant with value
|
||||
@ -635,12 +642,12 @@ fn optimize<'a>(
|
||||
.filter(|ScopeEntry { typ, expr, .. }| {
|
||||
// Get all the constants with definite constant expressions
|
||||
*typ == ScopeEntryType::Constant
|
||||
&& expr.as_ref().map(Expr::is_constant).unwrap_or(false)
|
||||
&& expr.as_ref().map(|v| v.is_constant()).unwrap_or(false)
|
||||
})
|
||||
.for_each(|ScopeEntry { name, expr, .. }| {
|
||||
state.push_constant(
|
||||
name.as_ref(),
|
||||
expr.as_ref().expect("should be Some(expr)").clone(),
|
||||
(**expr.as_ref().expect("should be Some(expr)")).clone(),
|
||||
)
|
||||
});
|
||||
|
||||
@ -722,10 +729,10 @@ pub fn optimize_into_ast(
|
||||
|
||||
// Optimize the function body
|
||||
let mut body =
|
||||
optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
||||
optimize(vec![*fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
||||
|
||||
// {} -> Noop
|
||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||
fn_def.body = Box::new(match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||
// { return val; } -> val
|
||||
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val),
|
||||
// { return; } -> ()
|
||||
@ -734,7 +741,7 @@ pub fn optimize_into_ast(
|
||||
}
|
||||
// All others
|
||||
stmt => stmt,
|
||||
};
|
||||
});
|
||||
}
|
||||
fn_def
|
||||
})
|
||||
|
425
src/packages/arithmetic.rs
Normal file
425
src/packages/arithmetic.rs
Normal file
@ -0,0 +1,425 @@
|
||||
use super::{reg_binary, reg_unary};
|
||||
|
||||
use crate::def_package;
|
||||
use crate::fn_register::{map_dynamic as map, map_result as result};
|
||||
use crate::parser::INT;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
use num_traits::{
|
||||
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
|
||||
CheckedShr, CheckedSub,
|
||||
};
|
||||
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
fmt::Display,
|
||||
format,
|
||||
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub},
|
||||
};
|
||||
|
||||
// Checked add
|
||||
fn add<T: Display + CheckedAdd>(x: T, y: T) -> Result<T, Box<EvalAltResult>> {
|
||||
x.checked_add(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Addition overflow: {} + {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Checked subtract
|
||||
fn sub<T: Display + CheckedSub>(x: T, y: T) -> Result<T, Box<EvalAltResult>> {
|
||||
x.checked_sub(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Subtraction underflow: {} - {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Checked multiply
|
||||
fn mul<T: Display + CheckedMul>(x: T, y: T) -> Result<T, Box<EvalAltResult>> {
|
||||
x.checked_mul(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Multiplication overflow: {} * {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Checked divide
|
||||
fn div<T>(x: T, y: T) -> Result<T, Box<EvalAltResult>>
|
||||
where
|
||||
T: Display + CheckedDiv + PartialEq + Zero,
|
||||
{
|
||||
// Detect division by zero
|
||||
if y == T::zero() {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Division by zero: {} / {}", x, y),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
|
||||
x.checked_div(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Division overflow: {} / {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
|
||||
fn neg<T: Display + CheckedNeg>(x: T) -> Result<T, Box<EvalAltResult>> {
|
||||
x.checked_neg().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Negation overflow: -{}", x),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Checked absolute
|
||||
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> Result<T, Box<EvalAltResult>> {
|
||||
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
|
||||
// when the number is ::MIN instead of returning ::MIN itself.
|
||||
if x >= <T as Zero>::zero() {
|
||||
Ok(x)
|
||||
} else {
|
||||
x.checked_neg().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Negation overflow: -{}", x),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
// Unchecked add - may panic on overflow
|
||||
fn add_u<T: Add>(x: T, y: T) -> <T as Add>::Output {
|
||||
x + y
|
||||
}
|
||||
// Unchecked subtract - may panic on underflow
|
||||
fn sub_u<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
|
||||
x - y
|
||||
}
|
||||
// Unchecked multiply - may panic on overflow
|
||||
fn mul_u<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
|
||||
x * y
|
||||
}
|
||||
// Unchecked divide - may panic when dividing by zero
|
||||
fn div_u<T: Div>(x: T, y: T) -> <T as Div>::Output {
|
||||
x / y
|
||||
}
|
||||
// Unchecked negative - may panic on overflow
|
||||
fn neg_u<T: Neg>(x: T) -> <T as Neg>::Output {
|
||||
-x
|
||||
}
|
||||
// Unchecked absolute - may panic on overflow
|
||||
fn abs_u<T>(x: T) -> <T as Neg>::Output
|
||||
where
|
||||
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
|
||||
{
|
||||
// Numbers should default to zero
|
||||
if x < Default::default() {
|
||||
-x
|
||||
} else {
|
||||
x.into()
|
||||
}
|
||||
}
|
||||
// Bit operators
|
||||
fn binary_and<T: BitAnd>(x: T, y: T) -> <T as BitAnd>::Output {
|
||||
x & y
|
||||
}
|
||||
fn binary_or<T: BitOr>(x: T, y: T) -> <T as BitOr>::Output {
|
||||
x | y
|
||||
}
|
||||
fn binary_xor<T: BitXor>(x: T, y: T) -> <T as BitXor>::Output {
|
||||
x ^ y
|
||||
}
|
||||
// Checked left-shift
|
||||
fn shl<T: Display + CheckedShl>(x: T, y: INT) -> Result<T, Box<EvalAltResult>> {
|
||||
// Cannot shift by a negative number of bits
|
||||
if y < 0 {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Left-shift by a negative number: {} << {}", x, y),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
|
||||
CheckedShl::checked_shl(&x, y as u32).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Left-shift by too many bits: {} << {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Checked right-shift
|
||||
fn shr<T: Display + CheckedShr>(x: T, y: INT) -> Result<T, Box<EvalAltResult>> {
|
||||
// Cannot shift by a negative number of bits
|
||||
if y < 0 {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Right-shift by a negative number: {} >> {}", x, y),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
|
||||
CheckedShr::checked_shr(&x, y as u32).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Right-shift by too many bits: {} % {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Unchecked left-shift - may panic if shifting by a negative number of bits
|
||||
fn shl_u<T: Shl<T>>(x: T, y: T) -> <T as Shl<T>>::Output {
|
||||
x.shl(y)
|
||||
}
|
||||
// Unchecked right-shift - may panic if shifting by a negative number of bits
|
||||
fn shr_u<T: Shr<T>>(x: T, y: T) -> <T as Shr<T>>::Output {
|
||||
x.shr(y)
|
||||
}
|
||||
// Checked modulo
|
||||
fn modulo<T: Display + CheckedRem>(x: T, y: T) -> Result<T, Box<EvalAltResult>> {
|
||||
x.checked_rem(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Modulo division by zero or overflow: {} % {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
// Unchecked modulo - may panic if dividing by zero
|
||||
fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output {
|
||||
x % y
|
||||
}
|
||||
// Checked power
|
||||
fn pow_i_i(x: INT, y: INT) -> Result<INT, Box<EvalAltResult>> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
if y > (u32::MAX as INT) {
|
||||
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer raised to too large an index: {} ~ {}", x, y),
|
||||
Position::none(),
|
||||
)))
|
||||
} else if y < 0 {
|
||||
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer raised to a negative index: {} ~ {}", x, y),
|
||||
Position::none(),
|
||||
)))
|
||||
} else {
|
||||
x.checked_pow(y as u32).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Power overflow: {} ~ {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
{
|
||||
if y < 0 {
|
||||
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer raised to a negative index: {} ~ {}", x, y),
|
||||
Position::none(),
|
||||
)))
|
||||
} else {
|
||||
x.checked_pow(y as u32).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Power overflow: {} ~ {}", x, y),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX)
|
||||
fn pow_i_i_u(x: INT, y: INT) -> INT {
|
||||
x.pow(y as u32)
|
||||
}
|
||||
// Floating-point power - always well-defined
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn pow_f_f(x: FLOAT, y: FLOAT) -> FLOAT {
|
||||
x.powf(y)
|
||||
}
|
||||
// Checked power
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn pow_f_i(x: FLOAT, y: INT) -> Result<FLOAT, Box<EvalAltResult>> {
|
||||
// Raise to power that is larger than an i32
|
||||
if y > (i32::MAX as INT) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Number raised to too large an index: {} ~ {}", x, y),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(x.powi(y as i32))
|
||||
}
|
||||
// Unchecked power - may be incorrect if the power index is too high (> i32::MAX)
|
||||
#[cfg(feature = "unchecked")]
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn pow_f_i_u(x: FLOAT, y: INT) -> FLOAT {
|
||||
x.powi(y as i32)
|
||||
}
|
||||
|
||||
macro_rules! reg_unary_x { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_unary($lib, $op, $func::<$par>, result);)* };
|
||||
}
|
||||
macro_rules! reg_unary { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_unary($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
macro_rules! reg_op_x { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_binary($lib, $op, $func::<$par>, result);)* };
|
||||
}
|
||||
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_binary($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
|
||||
def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
// Checked basic arithmetic
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
reg_op_x!(lib, "+", add, INT);
|
||||
reg_op_x!(lib, "-", sub, INT);
|
||||
reg_op_x!(lib, "*", mul, INT);
|
||||
reg_op_x!(lib, "/", div, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op_x!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op_x!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op_x!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op_x!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Unchecked basic arithmetic
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
reg_op!(lib, "+", add_u, INT);
|
||||
reg_op!(lib, "-", sub_u, INT);
|
||||
reg_op!(lib, "*", mul_u, INT);
|
||||
reg_op!(lib, "/", div_u, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Basic arithmetic for floating-point - no need to check
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_op!(lib, "+", add_u, f32, f64);
|
||||
reg_op!(lib, "-", sub_u, f32, f64);
|
||||
reg_op!(lib, "*", mul_u, f32, f64);
|
||||
reg_op!(lib, "/", div_u, f32, f64);
|
||||
}
|
||||
|
||||
// Bit operations
|
||||
reg_op!(lib, "|", binary_or, INT);
|
||||
reg_op!(lib, "&", binary_and, INT);
|
||||
reg_op!(lib, "^", binary_xor, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
// Checked bit shifts
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
reg_op_x!(lib, "<<", shl, INT);
|
||||
reg_op_x!(lib, ">>", shr, INT);
|
||||
reg_op_x!(lib, "%", modulo, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op_x!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op_x!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op_x!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Unchecked bit shifts
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
reg_op!(lib, "<<", shl_u, INT, INT);
|
||||
reg_op!(lib, ">>", shr_u, INT, INT);
|
||||
reg_op!(lib, "%", modulo_u, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Checked power
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
reg_binary(lib, "~", pow_i_i, result);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
reg_binary(lib, "~", pow_f_i, result);
|
||||
}
|
||||
|
||||
// Unchecked power
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
reg_binary(lib, "~", pow_i_i_u, map);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
reg_binary(lib, "~", pow_f_i_u, map);
|
||||
}
|
||||
|
||||
// Floating-point modulo and power
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_op!(lib, "%", modulo_u, f32, f64);
|
||||
reg_binary(lib, "~", pow_f_f, map);
|
||||
}
|
||||
|
||||
// Checked unary
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
reg_unary_x!(lib, "-", neg, INT);
|
||||
reg_unary_x!(lib, "abs", abs, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_unary_x!(lib, "-", neg, i8, i16, i32, i64, i128);
|
||||
reg_unary_x!(lib, "abs", abs, i8, i16, i32, i64, i128);
|
||||
}
|
||||
}
|
||||
|
||||
// Unchecked unary
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
reg_unary!(lib, "-", neg_u, INT);
|
||||
reg_unary!(lib, "abs", abs_u, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_unary!(lib, "-", neg_u, i8, i16, i32, i64, i128);
|
||||
reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128);
|
||||
}
|
||||
}
|
||||
|
||||
// Floating-point unary
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_unary!(lib, "-", neg_u, f32, f64);
|
||||
reg_unary!(lib, "abs", abs_u, f32, f64);
|
||||
}
|
||||
});
|
124
src/packages/array_basic.rs
Normal file
124
src/packages/array_basic.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::def_package;
|
||||
use crate::engine::Array;
|
||||
use crate::fn_register::{map_dynamic as map, map_identity as pass};
|
||||
use crate::parser::INT;
|
||||
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, string::String};
|
||||
|
||||
// Register array utility functions
|
||||
fn push<T: Variant + Clone>(list: &mut Array, item: T) {
|
||||
list.push(Dynamic::from(item));
|
||||
}
|
||||
fn ins<T: Variant + Clone>(list: &mut Array, position: INT, item: T) {
|
||||
if position <= 0 {
|
||||
list.insert(0, Dynamic::from(item));
|
||||
} else if (position as usize) >= list.len() - 1 {
|
||||
push(list, item);
|
||||
} else {
|
||||
list.insert(position as usize, Dynamic::from(item));
|
||||
}
|
||||
}
|
||||
fn pad<T: Variant + Clone>(list: &mut Array, len: INT, item: T) {
|
||||
if len >= 0 {
|
||||
while list.len() < len as usize {
|
||||
push(list, item.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_binary_mut($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
macro_rules! reg_tri { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_trinary_mut($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
||||
reg_op!(lib, "push", push, INT, bool, char, String, Array, ());
|
||||
reg_tri!(lib, "pad", pad, INT, bool, char, String, Array, ());
|
||||
reg_tri!(lib, "insert", ins, INT, bool, char, String, Array, ());
|
||||
|
||||
reg_binary_mut(lib, "append", |x: &mut Array, y: Array| x.extend(y), map);
|
||||
reg_binary(
|
||||
lib,
|
||||
"+",
|
||||
|mut x: Array, y: Array| {
|
||||
x.extend(y);
|
||||
x
|
||||
},
|
||||
map,
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "push", push, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_tri!(lib, "pad", pad, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
|
||||
reg_tri!(lib, "insert", ins, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_op!(lib, "push", push, f32, f64);
|
||||
reg_tri!(lib, "pad", pad, f32, f64);
|
||||
reg_tri!(lib, "insert", ins, f32, f64);
|
||||
}
|
||||
|
||||
reg_unary_mut(
|
||||
lib,
|
||||
"pop",
|
||||
|list: &mut Array| list.pop().unwrap_or_else(|| ().into()),
|
||||
pass,
|
||||
);
|
||||
reg_unary_mut(
|
||||
lib,
|
||||
"shift",
|
||||
|list: &mut Array| {
|
||||
if list.is_empty() {
|
||||
().into()
|
||||
} else {
|
||||
list.remove(0)
|
||||
}
|
||||
},
|
||||
pass,
|
||||
);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"remove",
|
||||
|list: &mut Array, len: INT| {
|
||||
if len < 0 || (len as usize) >= list.len() {
|
||||
().into()
|
||||
} else {
|
||||
list.remove(len as usize)
|
||||
}
|
||||
},
|
||||
pass,
|
||||
);
|
||||
reg_unary_mut(lib, "len", |list: &mut Array| list.len() as INT, map);
|
||||
reg_unary_mut(lib, "clear", |list: &mut Array| list.clear(), map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"truncate",
|
||||
|list: &mut Array, len: INT| {
|
||||
if len >= 0 {
|
||||
list.truncate(len as usize);
|
||||
} else {
|
||||
list.clear();
|
||||
}
|
||||
},
|
||||
map,
|
||||
);
|
||||
|
||||
// Register array iterator
|
||||
lib.type_iterators.insert(
|
||||
TypeId::of::<Array>(),
|
||||
Box::new(|a: Dynamic| {
|
||||
Box::new(a.cast::<Array>().into_iter())
|
||||
as Box<dyn Iterator<Item = Dynamic>>
|
||||
}),
|
||||
);
|
||||
});
|
109
src/packages/iter_basic.rs
Normal file
109
src/packages/iter_basic.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use super::{reg_binary, reg_trinary, reg_unary_mut, PackageStore};
|
||||
|
||||
use crate::any::{Dynamic, Union, Variant};
|
||||
use crate::def_package;
|
||||
use crate::engine::{Array, Map};
|
||||
use crate::fn_register::map_dynamic as map;
|
||||
use crate::parser::INT;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
ops::{Add, Range},
|
||||
};
|
||||
|
||||
// Register range function
|
||||
fn reg_range<T: Variant + Clone>(lib: &mut PackageStore)
|
||||
where
|
||||
Range<T>: Iterator<Item = T>,
|
||||
{
|
||||
lib.type_iterators.insert(
|
||||
TypeId::of::<Range<T>>(),
|
||||
Box::new(|source: Dynamic| {
|
||||
Box::new(source.cast::<Range<T>>().map(|x| x.into_dynamic()))
|
||||
as Box<dyn Iterator<Item = Dynamic>>
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Register range function with step
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
struct StepRange<T>(T, T, T)
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd;
|
||||
|
||||
impl<T> Iterator for StepRange<T>
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
if self.0 < self.1 {
|
||||
let v = self.0.clone();
|
||||
self.0 = &v + &self.2;
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_step<T>(lib: &mut PackageStore)
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd,
|
||||
StepRange<T>: Iterator<Item = T>,
|
||||
{
|
||||
lib.type_iterators.insert(
|
||||
TypeId::of::<StepRange<T>>(),
|
||||
Box::new(|source: Dynamic| {
|
||||
Box::new(source.cast::<StepRange<T>>().map(|x| x.into_dynamic()))
|
||||
as Box<dyn Iterator<Item = Dynamic>>
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
fn get_range<T>(from: T, to: T) -> Range<T> {
|
||||
from..to
|
||||
}
|
||||
|
||||
reg_range::<INT>(lib);
|
||||
reg_binary(lib, "range", get_range::<INT>, map);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
macro_rules! reg_range {
|
||||
($self:expr, $x:expr, $( $y:ty ),*) => (
|
||||
$(
|
||||
reg_range::<$y>($self);
|
||||
reg_binary($self, $x, get_range::<$y>, map);
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
reg_range!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
reg_step::<INT>(lib);
|
||||
reg_trinary(lib, "range", StepRange::<INT>, map);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
macro_rules! reg_step {
|
||||
($self:expr, $x:expr, $( $y:ty ),*) => (
|
||||
$(
|
||||
reg_step::<$y>($self);
|
||||
reg_trinary($self, $x, StepRange::<$y>, map);
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
reg_step!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
});
|
91
src/packages/logic.rs
Normal file
91
src/packages/logic.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use super::{reg_binary, reg_binary_mut, reg_unary};
|
||||
|
||||
use crate::def_package;
|
||||
use crate::fn_register::map_dynamic as map;
|
||||
use crate::parser::INT;
|
||||
|
||||
use crate::stdlib::string::String;
|
||||
|
||||
// Comparison operators
|
||||
pub fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x < y
|
||||
}
|
||||
pub fn lte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x <= y
|
||||
}
|
||||
pub fn gt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x > y
|
||||
}
|
||||
pub fn gte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x >= y
|
||||
}
|
||||
pub fn eq<T: PartialEq>(x: T, y: T) -> bool {
|
||||
x == y
|
||||
}
|
||||
pub fn ne<T: PartialEq>(x: T, y: T) -> bool {
|
||||
x != y
|
||||
}
|
||||
|
||||
// Logic operators
|
||||
fn and(x: bool, y: bool) -> bool {
|
||||
x && y
|
||||
}
|
||||
fn or(x: bool, y: bool) -> bool {
|
||||
x || y
|
||||
}
|
||||
fn not(x: bool) -> bool {
|
||||
!x
|
||||
}
|
||||
|
||||
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_binary($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
|
||||
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||
reg_op!(lib, "<", lt, INT, char);
|
||||
reg_op!(lib, "<=", lte, INT, char);
|
||||
reg_op!(lib, ">", gt, INT, char);
|
||||
reg_op!(lib, ">=", gte, INT, char);
|
||||
reg_op!(lib, "==", eq, INT, char, bool, ());
|
||||
reg_op!(lib, "!=", ne, INT, char, bool, ());
|
||||
|
||||
// Special versions for strings - at least avoid copying the first string
|
||||
// use super::utils::reg_test;
|
||||
// reg_test(lib, "<", |x: &mut String, y: String| *x < y, |v| v, map);
|
||||
reg_binary_mut(lib, "<", |x: &mut String, y: String| *x < y, map);
|
||||
reg_binary_mut(lib, "<=", |x: &mut String, y: String| *x <= y, map);
|
||||
reg_binary_mut(lib, ">", |x: &mut String, y: String| *x > y, map);
|
||||
reg_binary_mut(lib, ">=", |x: &mut String, y: String| *x >= y, map);
|
||||
reg_binary_mut(lib, "==", |x: &mut String, y: String| *x == y, map);
|
||||
reg_binary_mut(lib, "!=", |x: &mut String, y: String| *x != y, map);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_op!(lib, "<", lt, f32, f64);
|
||||
reg_op!(lib, "<=", lte, f32, f64);
|
||||
reg_op!(lib, ">", gt, f32, f64);
|
||||
reg_op!(lib, ">=", gte, f32, f64);
|
||||
reg_op!(lib, "==", eq, f32, f64);
|
||||
reg_op!(lib, "!=", ne, f32, f64);
|
||||
}
|
||||
|
||||
// `&&` and `||` are treated specially as they short-circuit.
|
||||
// They are implemented as special `Expr` instances, not function calls.
|
||||
//reg_op!(lib, "||", or, bool);
|
||||
//reg_op!(lib, "&&", and, bool);
|
||||
|
||||
reg_binary(lib, "|", or, map);
|
||||
reg_binary(lib, "&", and, map);
|
||||
reg_unary(lib, "!", not, map);
|
||||
});
|
65
src/packages/map_basic.rs
Normal file
65
src/packages/map_basic.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use super::{reg_binary, reg_binary_mut, reg_unary_mut};
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::def_package;
|
||||
use crate::engine::Map;
|
||||
use crate::fn_register::map_dynamic as map;
|
||||
use crate::parser::INT;
|
||||
|
||||
use crate::stdlib::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
fn map_get_keys(map: &mut Map) -> Vec<Dynamic> {
|
||||
map.iter().map(|(k, _)| k.to_string().into()).collect()
|
||||
}
|
||||
fn map_get_values(map: &mut Map) -> Vec<Dynamic> {
|
||||
map.iter().map(|(_, v)| v.clone()).collect()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"has",
|
||||
|map: &mut Map, prop: String| map.contains_key(&prop),
|
||||
map,
|
||||
);
|
||||
reg_unary_mut(lib, "len", |map: &mut Map| map.len() as INT, map);
|
||||
reg_unary_mut(lib, "clear", |map: &mut Map| map.clear(), map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"remove",
|
||||
|x: &mut Map, name: String| x.remove(&name).unwrap_or_else(|| ().into()),
|
||||
map,
|
||||
);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"mixin",
|
||||
|map1: &mut Map, map2: Map| {
|
||||
map2.into_iter().for_each(|(key, value)| {
|
||||
map1.insert(key, value);
|
||||
});
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_binary(
|
||||
lib,
|
||||
"+",
|
||||
|mut map1: Map, map2: Map| {
|
||||
map2.into_iter().for_each(|(key, value)| {
|
||||
map1.insert(key, value);
|
||||
});
|
||||
map1
|
||||
},
|
||||
map,
|
||||
);
|
||||
|
||||
// Register map access functions
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
reg_unary_mut(lib, "keys", map_get_keys, map);
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
reg_unary_mut(lib, "values", map_get_values, map);
|
||||
});
|
131
src/packages/math_basic.rs
Normal file
131
src/packages/math_basic.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use super::{reg_binary, reg_unary};
|
||||
|
||||
use crate::def_package;
|
||||
use crate::fn_register::{map_dynamic as map, map_result as result};
|
||||
use crate::parser::INT;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
use crate::stdlib::{boxed::Box, format, i32, i64};
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
pub const MAX_INT: INT = i32::MAX;
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
pub const MAX_INT: INT = i64::MAX;
|
||||
|
||||
def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
// Advanced math functions
|
||||
reg_unary(lib, "sin", |x: FLOAT| x.to_radians().sin(), map);
|
||||
reg_unary(lib, "cos", |x: FLOAT| x.to_radians().cos(), map);
|
||||
reg_unary(lib, "tan", |x: FLOAT| x.to_radians().tan(), map);
|
||||
reg_unary(lib, "sinh", |x: FLOAT| x.to_radians().sinh(), map);
|
||||
reg_unary(lib, "cosh", |x: FLOAT| x.to_radians().cosh(), map);
|
||||
reg_unary(lib, "tanh", |x: FLOAT| x.to_radians().tanh(), map);
|
||||
reg_unary(lib, "asin", |x: FLOAT| x.asin().to_degrees(), map);
|
||||
reg_unary(lib, "acos", |x: FLOAT| x.acos().to_degrees(), map);
|
||||
reg_unary(lib, "atan", |x: FLOAT| x.atan().to_degrees(), map);
|
||||
reg_unary(lib, "asinh", |x: FLOAT| x.asinh().to_degrees(), map);
|
||||
reg_unary(lib, "acosh", |x: FLOAT| x.acosh().to_degrees(), map);
|
||||
reg_unary(lib, "atanh", |x: FLOAT| x.atanh().to_degrees(), map);
|
||||
reg_unary(lib, "sqrt", |x: FLOAT| x.sqrt(), map);
|
||||
reg_unary(lib, "exp", |x: FLOAT| x.exp(), map);
|
||||
reg_unary(lib, "ln", |x: FLOAT| x.ln(), map);
|
||||
reg_binary(lib, "log", |x: FLOAT, base: FLOAT| x.log(base), map);
|
||||
reg_unary(lib, "log10", |x: FLOAT| x.log10(), map);
|
||||
reg_unary(lib, "floor", |x: FLOAT| x.floor(), map);
|
||||
reg_unary(lib, "ceiling", |x: FLOAT| x.ceil(), map);
|
||||
reg_unary(lib, "round", |x: FLOAT| x.ceil(), map);
|
||||
reg_unary(lib, "int", |x: FLOAT| x.trunc(), map);
|
||||
reg_unary(lib, "fraction", |x: FLOAT| x.fract(), map);
|
||||
reg_unary(lib, "is_nan", |x: FLOAT| x.is_nan(), map);
|
||||
reg_unary(lib, "is_finite", |x: FLOAT| x.is_finite(), map);
|
||||
reg_unary(lib, "is_infinite", |x: FLOAT| x.is_infinite(), map);
|
||||
|
||||
// Register conversion functions
|
||||
reg_unary(lib, "to_float", |x: INT| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: f32| x as FLOAT, map);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_unary(lib, "to_float", |x: i8| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: u8| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: i16| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: u16| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: i32| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: u32| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: i64| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: u64| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: i128| x as FLOAT, map);
|
||||
reg_unary(lib, "to_float", |x: u128| x as FLOAT, map);
|
||||
}
|
||||
}
|
||||
|
||||
reg_unary(lib, "to_int", |ch: char| ch as INT, map);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_unary(lib, "to_int", |x: i8| x as INT, map);
|
||||
reg_unary(lib, "to_int", |x: u8| x as INT, map);
|
||||
reg_unary(lib, "to_int", |x: i16| x as INT, map);
|
||||
reg_unary(lib, "to_int", |x: u16| x as INT, map);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
reg_unary(lib, "to_int", |x: i32| x as INT, map);
|
||||
reg_unary(lib, "to_int", |x: u64| x as INT, map);
|
||||
|
||||
#[cfg(feature = "only_i64")]
|
||||
reg_unary(lib, "to_int", |x: u32| x as INT, map);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
reg_unary(
|
||||
lib,
|
||||
"to_int",
|
||||
|x: f32| {
|
||||
if x > (MAX_INT as f32) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer overflow: to_int({})", x),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(x.trunc() as INT)
|
||||
},
|
||||
result,
|
||||
);
|
||||
reg_unary(
|
||||
lib,
|
||||
"to_int",
|
||||
|x: FLOAT| {
|
||||
if x > (MAX_INT as FLOAT) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer overflow: to_int({})", x),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(x.trunc() as INT)
|
||||
},
|
||||
result,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
reg_unary(lib, "to_int", |x: f32| x as INT, map);
|
||||
reg_unary(lib, "to_int", |x: f64| x as INT, map);
|
||||
}
|
||||
}
|
||||
});
|
74
src/packages/mod.rs
Normal file
74
src/packages/mod.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! This module contains all built-in _packages_ available to Rhai, plus facilities to define custom packages.
|
||||
|
||||
use crate::engine::{FnAny, IteratorFn};
|
||||
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc};
|
||||
|
||||
mod arithmetic;
|
||||
mod array_basic;
|
||||
mod iter_basic;
|
||||
mod logic;
|
||||
mod map_basic;
|
||||
mod math_basic;
|
||||
mod pkg_core;
|
||||
mod pkg_std;
|
||||
mod string_basic;
|
||||
mod string_more;
|
||||
mod time_basic;
|
||||
mod utils;
|
||||
|
||||
pub use arithmetic::ArithmeticPackage;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub use array_basic::BasicArrayPackage;
|
||||
pub use iter_basic::BasicIteratorPackage;
|
||||
pub use logic::LogicPackage;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub use map_basic::BasicMapPackage;
|
||||
pub use math_basic::BasicMathPackage;
|
||||
pub use pkg_core::CorePackage;
|
||||
pub use pkg_std::StandardPackage;
|
||||
pub use string_basic::BasicStringPackage;
|
||||
pub use string_more::MoreStringPackage;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub use time_basic::BasicTimePackage;
|
||||
|
||||
pub use utils::*;
|
||||
|
||||
/// Trait that all packages must implement.
|
||||
pub trait Package {
|
||||
/// Create a new instance of a package.
|
||||
fn new() -> Self;
|
||||
|
||||
/// Register all the functions in a package into a store.
|
||||
fn init(lib: &mut PackageStore);
|
||||
|
||||
/// Retrieve the generic package library from this package.
|
||||
fn get(&self) -> PackageLibrary;
|
||||
}
|
||||
|
||||
/// Type to store all functions in the package.
|
||||
pub struct PackageStore {
|
||||
/// All functions, keyed by a hash created from the function name and parameter types.
|
||||
pub functions: HashMap<u64, Box<FnAny>>,
|
||||
|
||||
/// All iterator functions, keyed by the type producing the iterator.
|
||||
pub type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
||||
}
|
||||
|
||||
impl PackageStore {
|
||||
/// Create a new `PackageStore`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
functions: HashMap::new(),
|
||||
type_iterators: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type PackageLibrary = Rc<PackageStore>;
|
||||
|
||||
/// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type PackageLibrary = Arc<PackageStore>;
|
13
src/packages/pkg_core.rs
Normal file
13
src/packages/pkg_core.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use super::arithmetic::ArithmeticPackage;
|
||||
use super::iter_basic::BasicIteratorPackage;
|
||||
use super::logic::LogicPackage;
|
||||
use super::string_basic::BasicStringPackage;
|
||||
|
||||
use crate::def_package;
|
||||
|
||||
def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", lib, {
|
||||
ArithmeticPackage::init(lib);
|
||||
LogicPackage::init(lib);
|
||||
BasicStringPackage::init(lib);
|
||||
BasicIteratorPackage::init(lib);
|
||||
});
|
23
src/packages/pkg_std.rs
Normal file
23
src/packages/pkg_std.rs
Normal file
@ -0,0 +1,23 @@
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use super::array_basic::BasicArrayPackage;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use super::map_basic::BasicMapPackage;
|
||||
use super::math_basic::BasicMathPackage;
|
||||
use super::pkg_core::CorePackage;
|
||||
use super::string_more::MoreStringPackage;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use super::time_basic::BasicTimePackage;
|
||||
|
||||
use crate::def_package;
|
||||
|
||||
def_package!(crate:StandardPackage:"_Standard_ package containing all built-in features.", lib, {
|
||||
CorePackage::init(lib);
|
||||
BasicMathPackage::init(lib);
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
BasicArrayPackage::init(lib);
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
BasicMapPackage::init(lib);
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
BasicTimePackage::init(lib);
|
||||
MoreStringPackage::init(lib);
|
||||
});
|
99
src/packages/string_basic.rs
Normal file
99
src/packages/string_basic.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut};
|
||||
|
||||
use crate::def_package;
|
||||
use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
||||
use crate::fn_register::map_dynamic as map;
|
||||
use crate::parser::INT;
|
||||
|
||||
use crate::stdlib::{
|
||||
fmt::{Debug, Display},
|
||||
format,
|
||||
string::{String, ToString},
|
||||
};
|
||||
|
||||
// Register print and debug
|
||||
fn to_debug<T: Debug>(x: &mut T) -> String {
|
||||
format!("{:?}", x)
|
||||
}
|
||||
fn to_string<T: Display>(x: &mut T) -> String {
|
||||
format!("{}", x)
|
||||
}
|
||||
fn format_map(x: &mut Map) -> String {
|
||||
format!("#{:?}", x)
|
||||
}
|
||||
|
||||
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_unary_mut($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
|
||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||
reg_op!(lib, KEYWORD_PRINT, to_string, INT, bool, char);
|
||||
reg_op!(lib, FUNC_TO_STRING, to_string, INT, bool, char);
|
||||
|
||||
reg_none(lib, KEYWORD_PRINT, || "".to_string(), map);
|
||||
reg_unary(lib, KEYWORD_PRINT, |_: ()| "".to_string(), map);
|
||||
reg_unary(lib, FUNC_TO_STRING, |_: ()| "".to_string(), map);
|
||||
|
||||
reg_unary_mut(lib, KEYWORD_PRINT, |s: &mut String| s.clone(), map);
|
||||
reg_unary_mut(lib, FUNC_TO_STRING, |s: &mut String| s.clone(), map);
|
||||
|
||||
reg_op!(lib, KEYWORD_DEBUG, to_debug, INT, bool, (), char, String);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, KEYWORD_PRINT, to_string, i8, u8, i16, u16, i32, u32);
|
||||
reg_op!(lib, FUNC_TO_STRING, to_string, i8, u8, i16, u16, i32, u32);
|
||||
reg_op!(lib, KEYWORD_DEBUG, to_debug, i8, u8, i16, u16, i32, u32);
|
||||
reg_op!(lib, KEYWORD_PRINT, to_string, i64, u64, i128, u128);
|
||||
reg_op!(lib, FUNC_TO_STRING, to_string, i64, u64, i128, u128);
|
||||
reg_op!(lib, KEYWORD_DEBUG, to_debug, i64, u64, i128, u128);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_op!(lib, KEYWORD_PRINT, to_string, f32, f64);
|
||||
reg_op!(lib, FUNC_TO_STRING, to_string, f32, f64);
|
||||
reg_op!(lib, KEYWORD_DEBUG, to_debug, f32, f64);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
{
|
||||
reg_op!(lib, KEYWORD_PRINT, to_debug, Array);
|
||||
reg_op!(lib, FUNC_TO_STRING, to_debug, Array);
|
||||
reg_op!(lib, KEYWORD_DEBUG, to_debug, Array);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
reg_unary_mut(lib, KEYWORD_PRINT, format_map, map);
|
||||
reg_unary_mut(lib, FUNC_TO_STRING, format_map, map);
|
||||
reg_unary_mut(lib, KEYWORD_DEBUG, format_map, map);
|
||||
}
|
||||
|
||||
reg_binary(
|
||||
lib,
|
||||
"+",
|
||||
|mut s: String, ch: char| {
|
||||
s.push(ch);
|
||||
s
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_binary(
|
||||
lib,
|
||||
"+",
|
||||
|mut s: String, s2: String| {
|
||||
s.push_str(&s2);
|
||||
s
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"append",
|
||||
|s: &mut String, s2: String| s.push_str(&s2),
|
||||
map,
|
||||
);
|
||||
});
|
239
src/packages/string_more.rs
Normal file
239
src/packages/string_more.rs
Normal file
@ -0,0 +1,239 @@
|
||||
use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
|
||||
|
||||
use crate::def_package;
|
||||
use crate::engine::Array;
|
||||
use crate::fn_register::map_dynamic as map;
|
||||
use crate::parser::INT;
|
||||
|
||||
use crate::stdlib::{
|
||||
fmt::Display,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
fn prepend<T: Display>(x: T, y: String) -> String {
|
||||
format!("{}{}", x, y)
|
||||
}
|
||||
fn append<T: Display>(x: String, y: T) -> String {
|
||||
format!("{}{}", x, y)
|
||||
}
|
||||
fn sub_string(s: &mut String, start: INT, len: INT) -> String {
|
||||
let offset = if s.is_empty() || len <= 0 {
|
||||
return "".to_string();
|
||||
} else if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
return "".to_string();
|
||||
} else {
|
||||
start as usize
|
||||
};
|
||||
|
||||
let chars: Vec<_> = s.chars().collect();
|
||||
|
||||
let len = if offset + (len as usize) > chars.len() {
|
||||
chars.len() - offset
|
||||
} else {
|
||||
len as usize
|
||||
};
|
||||
|
||||
chars[offset..][..len].into_iter().collect()
|
||||
}
|
||||
fn crop_string(s: &mut String, start: INT, len: INT) {
|
||||
let offset = if s.is_empty() || len <= 0 {
|
||||
s.clear();
|
||||
return;
|
||||
} else if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
s.clear();
|
||||
return;
|
||||
} else {
|
||||
start as usize
|
||||
};
|
||||
|
||||
let chars: Vec<_> = s.chars().collect();
|
||||
|
||||
let len = if offset + (len as usize) > chars.len() {
|
||||
chars.len() - offset
|
||||
} else {
|
||||
len as usize
|
||||
};
|
||||
|
||||
s.clear();
|
||||
|
||||
chars[offset..][..len]
|
||||
.into_iter()
|
||||
.for_each(|&ch| s.push(ch));
|
||||
}
|
||||
|
||||
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||
$(reg_binary($lib, $op, $func::<$par>, map);)* };
|
||||
}
|
||||
|
||||
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
||||
reg_op!(lib, "+", append, INT, bool, char);
|
||||
reg_binary_mut(lib, "+", |x: &mut String, _: ()| x.clone(), map);
|
||||
|
||||
reg_op!(lib, "+", prepend, INT, bool, char);
|
||||
reg_binary(lib, "+", |_: (), y: String| y, map);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "+", append, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "+", prepend, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_op!(lib, "+", append, f32, f64);
|
||||
reg_op!(lib, "+", prepend, f32, f64);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
{
|
||||
reg_binary(lib, "+", |x: String, y: Array| format!("{}{:?}", x, y), map);
|
||||
reg_binary(lib, "+", |x: Array, y: String| format!("{:?}{}", x, y), map);
|
||||
}
|
||||
|
||||
reg_unary_mut(lib, "len", |s: &mut String| s.chars().count() as INT, map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"contains",
|
||||
|s: &mut String, ch: char| s.contains(ch),
|
||||
map,
|
||||
);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"contains",
|
||||
|s: &mut String, find: String| s.contains(&find),
|
||||
map,
|
||||
);
|
||||
reg_trinary_mut(
|
||||
lib,
|
||||
"index_of",
|
||||
|s: &mut String, ch: char, start: INT| {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
return -1 as INT;
|
||||
} else {
|
||||
s.chars().take(start as usize).collect::<String>().len()
|
||||
};
|
||||
|
||||
s[start..]
|
||||
.find(ch)
|
||||
.map(|index| s[0..start + index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"index_of",
|
||||
|s: &mut String, ch: char| {
|
||||
s.find(ch)
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_trinary_mut(
|
||||
lib,
|
||||
"index_of",
|
||||
|s: &mut String, find: String, start: INT| {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
return -1 as INT;
|
||||
} else {
|
||||
s.chars().take(start as usize).collect::<String>().len()
|
||||
};
|
||||
|
||||
s[start..]
|
||||
.find(&find)
|
||||
.map(|index| s[0..start + index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"index_of",
|
||||
|s: &mut String, find: String| {
|
||||
s.find(&find)
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_unary_mut(lib, "clear", |s: &mut String| s.clear(), map);
|
||||
reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"append",
|
||||
|s: &mut String, add: String| s.push_str(&add),
|
||||
map,
|
||||
);
|
||||
reg_trinary_mut(lib, "sub_string", sub_string, map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"sub_string",
|
||||
|s: &mut String, start: INT| sub_string(s, start, s.len() as INT),
|
||||
map,
|
||||
);
|
||||
reg_trinary_mut(lib, "crop", crop_string, map);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"crop",
|
||||
|s: &mut String, start: INT| crop_string(s, start, s.len() as INT),
|
||||
map,
|
||||
);
|
||||
reg_binary_mut(
|
||||
lib,
|
||||
"truncate",
|
||||
|s: &mut String, len: INT| {
|
||||
if len >= 0 {
|
||||
let chars: Vec<_> = s.chars().take(len as usize).collect();
|
||||
s.clear();
|
||||
chars.into_iter().for_each(|ch| s.push(ch));
|
||||
} else {
|
||||
s.clear();
|
||||
}
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_trinary_mut(
|
||||
lib,
|
||||
"pad",
|
||||
|s: &mut String, len: INT, ch: char| {
|
||||
for _ in 0..s.chars().count() - len as usize {
|
||||
s.push(ch);
|
||||
}
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_trinary_mut(
|
||||
lib,
|
||||
"replace",
|
||||
|s: &mut String, find: String, sub: String| {
|
||||
let new_str = s.replace(&find, &sub);
|
||||
s.clear();
|
||||
s.push_str(&new_str);
|
||||
},
|
||||
map,
|
||||
);
|
||||
reg_unary_mut(
|
||||
lib,
|
||||
"trim",
|
||||
|s: &mut String| {
|
||||
let trimmed = s.trim();
|
||||
|
||||
if trimmed.len() < s.len() {
|
||||
*s = trimmed.to_string();
|
||||
}
|
||||
},
|
||||
map,
|
||||
);
|
||||
});
|
101
src/packages/time_basic.rs
Normal file
101
src/packages/time_basic.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use super::logic::{eq, gt, gte, lt, lte, ne};
|
||||
use super::math_basic::MAX_INT;
|
||||
use super::{reg_binary, reg_none, reg_unary};
|
||||
|
||||
use crate::def_package;
|
||||
use crate::fn_register::{map_dynamic as map, map_result as result};
|
||||
use crate::parser::INT;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use crate::stdlib::time::Instant;
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
|
||||
// Register date/time functions
|
||||
reg_none(lib, "timestamp", || Instant::now(), map);
|
||||
|
||||
reg_binary(
|
||||
lib,
|
||||
"-",
|
||||
|ts1: Instant, ts2: Instant| {
|
||||
if ts2 > ts1 {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(-(ts2 - ts1).as_secs_f64());
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
{
|
||||
let seconds = (ts2 - ts1).as_secs();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
if seconds > (MAX_INT as u64) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Integer overflow for timestamp duration: {}",
|
||||
-(seconds as i64)
|
||||
),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
return Ok(-(seconds as INT));
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok((ts1 - ts2).as_secs_f64());
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
{
|
||||
let seconds = (ts1 - ts2).as_secs();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
if seconds > (MAX_INT as u64) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer overflow for timestamp duration: {}", seconds),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
return Ok(seconds as INT);
|
||||
}
|
||||
}
|
||||
},
|
||||
result,
|
||||
);
|
||||
|
||||
reg_binary(lib, "<", lt::<Instant>, map);
|
||||
reg_binary(lib, "<=", lte::<Instant>, map);
|
||||
reg_binary(lib, ">", gt::<Instant>, map);
|
||||
reg_binary(lib, ">=", gte::<Instant>, map);
|
||||
reg_binary(lib, "==", eq::<Instant>, map);
|
||||
reg_binary(lib, "!=", ne::<Instant>, map);
|
||||
|
||||
reg_unary(
|
||||
lib,
|
||||
"elapsed",
|
||||
|timestamp: Instant| {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(timestamp.elapsed().as_secs_f64());
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
{
|
||||
let seconds = timestamp.elapsed().as_secs();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
if seconds > (MAX_INT as u64) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer overflow for timestamp.elapsed(): {}", seconds),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
return Ok(seconds as INT);
|
||||
}
|
||||
},
|
||||
result,
|
||||
);
|
||||
});
|
481
src/packages/utils.rs
Normal file
481
src/packages/utils.rs
Normal file
@ -0,0 +1,481 @@
|
||||
use super::PackageStore;
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::FnCallArgs;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
};
|
||||
|
||||
/// This macro makes it easy to define a _package_ and register functions into it.
|
||||
///
|
||||
/// Functions can be added to the package using a number of helper functions under the `packages` module,
|
||||
/// such as `reg_unary`, `reg_binary_mut`, `reg_trinary_mut` etc.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Dynamic;
|
||||
/// use rhai::def_package;
|
||||
/// use rhai::packages::reg_binary;
|
||||
///
|
||||
/// fn add(x: i64, y: i64) -> i64 { x + y }
|
||||
///
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// reg_binary(lib, "my_add", add, |v, _| Ok(v.into()));
|
||||
/// // ^^^^^^^^^^^^^^^^^^^
|
||||
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The above defines a package named 'MyPackage' with a single function named 'my_add'.
|
||||
#[macro_export]
|
||||
macro_rules! def_package {
|
||||
($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => {
|
||||
#[doc=$comment]
|
||||
pub struct $package($root::packages::PackageLibrary);
|
||||
|
||||
impl $root::packages::Package for $package {
|
||||
fn new() -> Self {
|
||||
let mut pkg = $root::packages::PackageStore::new();
|
||||
Self::init(&mut pkg);
|
||||
Self(pkg.into())
|
||||
}
|
||||
|
||||
fn get(&self) -> $root::packages::PackageLibrary {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
fn init($lib: &mut $root::packages::PackageStore) {
|
||||
$block
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Check whether the correct number of arguments is passed to the function.
|
||||
fn check_num_args(
|
||||
name: &str,
|
||||
num_args: usize,
|
||||
args: &mut FnCallArgs,
|
||||
pos: Position,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
if args.len() != num_args {
|
||||
Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(
|
||||
name.to_string(),
|
||||
num_args,
|
||||
args.len(),
|
||||
pos,
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a function with no parameters to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Dynamic;
|
||||
/// use rhai::def_package;
|
||||
/// use rhai::packages::reg_none;
|
||||
///
|
||||
/// fn get_answer() -> i64 { 42 }
|
||||
///
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// reg_none(lib, "my_answer", get_answer, |v, _| Ok(v.into()));
|
||||
/// // ^^^^^^^^^^^^^^^^^^^
|
||||
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The above defines a package named 'MyPackage' with a single function named 'my_add_1'.
|
||||
pub fn reg_none<R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn() -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
let hash = calc_fn_hash(fn_name, ([] as [TypeId; 0]).iter().cloned());
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 0, args, pos)?;
|
||||
|
||||
let r = func();
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
/// Add a function with one parameter to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Dynamic;
|
||||
/// use rhai::def_package;
|
||||
/// use rhai::packages::reg_unary;
|
||||
///
|
||||
/// fn add_1(x: i64) -> i64 { x + 1 }
|
||||
///
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// reg_unary(lib, "my_add_1", add_1, |v, _| Ok(v.into()));
|
||||
/// // ^^^^^^^^^^^^^^^^^^^
|
||||
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The above defines a package named 'MyPackage' with a single function named 'my_add_1'.
|
||||
pub fn reg_unary<T: Variant + Clone, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(T) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(T) -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}({})", fn_name, crate::std::any::type_name::<T>());
|
||||
|
||||
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 1, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: &mut T = drain.next().unwrap().downcast_mut().unwrap();
|
||||
|
||||
let r = func(x.clone());
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
/// Add a function with one mutable reference parameter to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Dynamic, EvalAltResult};
|
||||
/// use rhai::def_package;
|
||||
/// use rhai::packages::reg_unary_mut;
|
||||
///
|
||||
/// fn inc(x: &mut i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// if *x == 0 {
|
||||
/// return Err("boo! zero cannot be incremented!".into())
|
||||
/// }
|
||||
/// *x += 1;
|
||||
/// Ok(().into())
|
||||
/// }
|
||||
///
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// reg_unary_mut(lib, "try_inc", inc, |r, _| r);
|
||||
/// // ^^^^^^^^
|
||||
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The above defines a package named 'MyPackage' with a single fallible function named 'try_inc'
|
||||
/// which takes a first argument of `&mut`, return a `Result<Dynamic, Box<EvalAltResult>>`.
|
||||
pub fn reg_unary_mut<T: Variant + Clone, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut T) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut T) -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}(&mut {})", fn_name, crate::std::any::type_name::<T>());
|
||||
|
||||
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 1, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: &mut T = drain.next().unwrap().downcast_mut().unwrap();
|
||||
|
||||
let r = func(x);
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub(crate) fn reg_test<'a, A: Variant + Clone, B: Variant + Clone, X, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(X, B) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(X, B) -> R + Send + Sync + 'static,
|
||||
|
||||
map: impl Fn(&mut A) -> X + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||
);
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 2, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: X = map(drain.next().unwrap().downcast_mut::<A>().unwrap());
|
||||
let y: B = drain.next().unwrap().downcast_mut::<B>().unwrap().clone();
|
||||
|
||||
let r = func(x, y);
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
/// Add a function with two parameters to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Dynamic;
|
||||
/// use rhai::def_package;
|
||||
/// use rhai::packages::reg_binary;
|
||||
///
|
||||
/// fn add(x: i64, y: i64) -> i64 { x + y }
|
||||
///
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// reg_binary(lib, "my_add", add, |v, _| Ok(v.into()));
|
||||
/// // ^^^^^^^^^^^^^^^^^^^
|
||||
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The above defines a package named 'MyPackage' with a single function named 'my_add'.
|
||||
pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||
);
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 2, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||
|
||||
let r = func(x.clone(), y.clone());
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
/// Add a function with two parameters (the first one being a mutable reference) to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Dynamic, EvalAltResult};
|
||||
/// use rhai::def_package;
|
||||
/// use rhai::packages::reg_binary_mut;
|
||||
///
|
||||
/// fn add(x: &mut i64, y: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// if y == 0 {
|
||||
/// return Err("boo! cannot add zero!".into())
|
||||
/// }
|
||||
/// *x += y;
|
||||
/// Ok(().into())
|
||||
/// }
|
||||
///
|
||||
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
||||
/// {
|
||||
/// reg_binary_mut(lib, "try_add", add, |r, _| r);
|
||||
/// // ^^^^^^^^
|
||||
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The above defines a package named 'MyPackage' with a single fallible function named 'try_add'
|
||||
/// which takes a first argument of `&mut`, return a `Result<Dynamic, Box<EvalAltResult>>`.
|
||||
pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||
);
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 2, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||
|
||||
let r = func(x, y.clone());
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
/// Add a function with three parameters to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 3, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||
let z: &mut C = drain.next().unwrap().downcast_mut().unwrap();
|
||||
|
||||
let r = func(x.clone(), y.clone(), z.clone());
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
||||
|
||||
/// Add a function with three parameters (the first one is a mutable reference) to the package.
|
||||
///
|
||||
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
||||
pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
|
||||
lib: &mut PackageStore,
|
||||
fn_name: &'static str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> R + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> R + Send + Sync + 'static,
|
||||
|
||||
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
//println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
|
||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||
check_num_args(fn_name, 3, args, pos)?;
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||
let z: &mut C = drain.next().unwrap().downcast_mut().unwrap();
|
||||
|
||||
let r = func(x, y.clone(), z.clone());
|
||||
map_result(r, pos)
|
||||
});
|
||||
|
||||
lib.functions.insert(hash, f);
|
||||
}
|
807
src/parser.rs
807
src/parser.rs
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ use crate::parser::INT;
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
error::Error,
|
||||
fmt,
|
||||
string::{String, ToString},
|
||||
@ -228,20 +229,23 @@ impl fmt::Display for EvalAltResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for EvalAltResult {
|
||||
impl From<ParseError> for Box<EvalAltResult> {
|
||||
fn from(err: ParseError) -> Self {
|
||||
Self::ErrorParsing(Box::new(err))
|
||||
Box::new(EvalAltResult::ErrorParsing(Box::new(err)))
|
||||
}
|
||||
}
|
||||
impl From<Box<ParseError>> for EvalAltResult {
|
||||
impl From<Box<ParseError>> for Box<EvalAltResult> {
|
||||
fn from(err: Box<ParseError>) -> Self {
|
||||
Self::ErrorParsing(err)
|
||||
Box::new(EvalAltResult::ErrorParsing(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||
fn from(err: T) -> Self {
|
||||
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
err.as_ref().to_string(),
|
||||
Position::none(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,8 +284,8 @@ impl EvalAltResult {
|
||||
|
||||
/// Consume the current `EvalAltResult` and return a new one
|
||||
/// with the specified `Position`.
|
||||
pub(crate) fn set_position(mut self, new_position: Position) -> Self {
|
||||
match &mut self {
|
||||
pub(crate) fn set_position(mut err: Box<Self>, new_position: Position) -> Box<Self> {
|
||||
match err.as_mut() {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Self::ErrorReadingScriptFile(_, _) => (),
|
||||
|
||||
@ -311,6 +315,6 @@ impl EvalAltResult {
|
||||
| Self::Return(_, pos) => *pos = new_position,
|
||||
}
|
||||
|
||||
self
|
||||
err
|
||||
}
|
||||
}
|
||||
|
97
src/scope.rs
97
src/scope.rs
@ -4,7 +4,7 @@ use crate::any::{Dynamic, Variant};
|
||||
use crate::parser::{map_dynamic_to_expr, Expr};
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{borrow::Cow, iter, vec::Vec};
|
||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
|
||||
|
||||
/// Type of an entry in the Scope.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||
@ -25,15 +25,7 @@ pub struct Entry<'a> {
|
||||
/// Current value of the entry.
|
||||
pub value: Dynamic,
|
||||
/// A constant expression if the initial value matches one of the recognized types.
|
||||
pub expr: Option<Expr>,
|
||||
}
|
||||
|
||||
/// Information about a particular entry in the Scope.
|
||||
#[derive(Debug, Hash, Copy, Clone)]
|
||||
pub(crate) struct EntryRef<'a> {
|
||||
pub name: &'a str,
|
||||
pub index: usize,
|
||||
pub typ: EntryType,
|
||||
pub expr: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
/// A type containing information about the current scope.
|
||||
@ -42,7 +34,7 @@ pub(crate) struct EntryRef<'a> {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
@ -226,15 +218,17 @@ impl<'a> Scope<'a> {
|
||||
value: Dynamic,
|
||||
map_expr: bool,
|
||||
) {
|
||||
let expr = if map_expr {
|
||||
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.0.push(Entry {
|
||||
name: name.into(),
|
||||
typ: entry_type,
|
||||
value: value.clone(),
|
||||
expr: if map_expr {
|
||||
map_dynamic_to_expr(value, Position::none())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
value: value.into(),
|
||||
expr,
|
||||
});
|
||||
}
|
||||
|
||||
@ -289,35 +283,18 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
|
||||
/// Find an entry in the Scope, starting from the last.
|
||||
pub(crate) fn get(&self, name: &str) -> Option<(EntryRef, Dynamic)> {
|
||||
pub(crate) fn get(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.find_map(
|
||||
|(
|
||||
index,
|
||||
Entry {
|
||||
name: key,
|
||||
typ,
|
||||
value,
|
||||
..
|
||||
},
|
||||
)| {
|
||||
if name == key {
|
||||
Some((
|
||||
EntryRef {
|
||||
name: key,
|
||||
index,
|
||||
typ: *typ,
|
||||
},
|
||||
value.clone(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.find_map(|(index, Entry { name: key, typ, .. })| {
|
||||
if name == key {
|
||||
Some((index, *typ))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the value of an entry in the Scope, starting from the last.
|
||||
@ -363,42 +340,29 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) {
|
||||
match self.get(name) {
|
||||
Some((
|
||||
EntryRef {
|
||||
typ: EntryType::Constant,
|
||||
..
|
||||
},
|
||||
_,
|
||||
)) => panic!("variable {} is constant", name),
|
||||
Some((
|
||||
EntryRef {
|
||||
index,
|
||||
typ: EntryType::Normal,
|
||||
..
|
||||
},
|
||||
_,
|
||||
)) => self.0.get_mut(index).unwrap().value = Dynamic::from(value),
|
||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||
Some((index, EntryType::Normal)) => {
|
||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||
}
|
||||
None => self.push(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an entry in the Scope.
|
||||
pub(crate) fn get_mut(&mut self, key: EntryRef) -> &mut Dynamic {
|
||||
let entry = self.0.get_mut(key.index).expect("invalid index in Scope");
|
||||
assert_eq!(entry.typ, key.typ, "entry type not matched");
|
||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||
|
||||
// assert_ne!(
|
||||
// entry.typ,
|
||||
// EntryType::Constant,
|
||||
// "get mut of constant entry"
|
||||
// );
|
||||
assert_eq!(entry.name, key.name, "incorrect key at Scope entry");
|
||||
|
||||
&mut entry.value
|
||||
(&mut entry.value, entry.typ)
|
||||
}
|
||||
|
||||
/// Get an iterator to entries in the Scope.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Entry> {
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &Entry> {
|
||||
self.0.iter().rev() // Always search a Scope in reverse order
|
||||
}
|
||||
}
|
||||
@ -409,16 +373,13 @@ impl Default for Scope<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a>
|
||||
where
|
||||
K: Into<Cow<'a, str>>,
|
||||
{
|
||||
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||
self.0
|
||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
||||
name: name.into(),
|
||||
typ,
|
||||
value,
|
||||
value: value.into(),
|
||||
expr: None,
|
||||
}));
|
||||
}
|
||||
|
69
src/token.rs
69
src/token.rs
@ -13,29 +13,28 @@ use crate::stdlib::{
|
||||
iter::Peekable,
|
||||
str::{Chars, FromStr},
|
||||
string::{String, ToString},
|
||||
usize,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
type LERR = LexError;
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
///
|
||||
/// In order to keep footprint small, both line number and character position have 16-bit resolution,
|
||||
/// meaning they go up to a maximum of 65,535 lines/characters per line.
|
||||
/// Advancing beyond the maximum line length or maximum number of lines is not an error but has no effect.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position {
|
||||
/// Line number - 0 = none
|
||||
line: usize,
|
||||
line: u16,
|
||||
/// Character position - 0 = BOL
|
||||
pos: usize,
|
||||
pos: u16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// Create a new `Position`.
|
||||
pub fn new(line: usize, position: usize) -> Self {
|
||||
pub fn new(line: u16, position: u16) -> Self {
|
||||
assert!(line != 0, "line cannot be zero");
|
||||
assert!(
|
||||
line != usize::MAX || position != usize::MAX,
|
||||
"invalid position"
|
||||
);
|
||||
|
||||
Self {
|
||||
line,
|
||||
@ -48,7 +47,7 @@ impl Position {
|
||||
if self.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(self.line)
|
||||
Some(self.line as usize)
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,13 +56,18 @@ impl Position {
|
||||
if self.is_none() || self.pos == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.pos)
|
||||
Some(self.pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Advance by one character position.
|
||||
pub(crate) fn advance(&mut self) {
|
||||
self.pos += 1;
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.pos < u16::MAX {
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Go backwards by one character position.
|
||||
@ -73,14 +77,20 @@ impl Position {
|
||||
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
||||
///
|
||||
pub(crate) fn rewind(&mut self) {
|
||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||
self.pos -= 1;
|
||||
}
|
||||
|
||||
/// Advance to the next line.
|
||||
pub(crate) fn new_line(&mut self) {
|
||||
self.line += 1;
|
||||
self.pos = 0;
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.line < u16::MAX {
|
||||
self.line += 1;
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Position` representing no position.
|
||||
@ -350,9 +360,10 @@ impl Token {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
// Assignments are not considered expressions - set to zero
|
||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
||||
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
||||
| PowerOfAssign => 10,
|
||||
| PowerOfAssign => 0,
|
||||
|
||||
Or | XOr | Pipe => 40,
|
||||
|
||||
@ -659,7 +670,7 @@ impl<'a> TokenIterator<'a> {
|
||||
.map(Token::IntegerConstant)
|
||||
.unwrap_or_else(|_| {
|
||||
Token::LexError(Box::new(LERR::MalformedNumber(
|
||||
result.iter().collect(),
|
||||
result.into_iter().collect(),
|
||||
)))
|
||||
}),
|
||||
pos,
|
||||
@ -675,7 +686,7 @@ impl<'a> TokenIterator<'a> {
|
||||
return Some((
|
||||
num.unwrap_or_else(|_| {
|
||||
Token::LexError(Box::new(LERR::MalformedNumber(
|
||||
result.iter().collect(),
|
||||
result.into_iter().collect(),
|
||||
)))
|
||||
}),
|
||||
pos,
|
||||
@ -869,6 +880,18 @@ impl<'a> TokenIterator<'a> {
|
||||
|
||||
('=', '=') => {
|
||||
self.eat_next();
|
||||
|
||||
// Warn against `===`
|
||||
if self.peek_next() == Some('=') {
|
||||
return Some((
|
||||
Token::LexError(Box::new(LERR::ImproperKeyword(
|
||||
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?"
|
||||
.to_string(),
|
||||
))),
|
||||
pos,
|
||||
));
|
||||
}
|
||||
|
||||
return Some((Token::EqualsTo, pos));
|
||||
}
|
||||
('=', _) => return Some((Token::Equals, pos)),
|
||||
@ -913,6 +936,18 @@ impl<'a> TokenIterator<'a> {
|
||||
|
||||
('!', '=') => {
|
||||
self.eat_next();
|
||||
|
||||
// Warn against `!==`
|
||||
if self.peek_next() == Some('=') {
|
||||
return Some((
|
||||
Token::LexError(Box::new(LERR::ImproperKeyword(
|
||||
"'!==' is not a valid operator. This is not JavaScript! Should it be '!='?"
|
||||
.to_string(),
|
||||
))),
|
||||
pos,
|
||||
));
|
||||
}
|
||||
|
||||
return Some((Token::NotEqualsTo, pos));
|
||||
}
|
||||
('!', _) => return Some((Token::Bang, pos)),
|
||||
@ -955,6 +990,8 @@ impl<'a> TokenIterator<'a> {
|
||||
}
|
||||
('~', _) => return Some((Token::PowerOf, pos)),
|
||||
|
||||
('\0', _) => panic!("should not be EOF"),
|
||||
|
||||
(ch, _) if ch.is_whitespace() => (),
|
||||
(ch, _) => return Some((Token::LexError(Box::new(LERR::UnexpectedChar(ch))), pos)),
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
use rhai::{Array, Engine, EvalAltResult, RegisterFn, INT};
|
||||
|
||||
#[test]
|
||||
fn test_arrays() -> Result<(), EvalAltResult> {
|
||||
fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2);
|
||||
@ -13,6 +13,7 @@ fn test_arrays() -> Result<(), EvalAltResult> {
|
||||
);
|
||||
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r"
|
||||
@ -35,7 +36,7 @@ fn test_arrays() -> Result<(), EvalAltResult> {
|
||||
r"
|
||||
let x = [1, 2, 3];
|
||||
x += [4, 5];
|
||||
x.len()
|
||||
len(x)
|
||||
"
|
||||
)?,
|
||||
5
|
||||
@ -58,7 +59,7 @@ fn test_arrays() -> Result<(), EvalAltResult> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn test_array_with_structs() -> Result<(), EvalAltResult> {
|
||||
fn test_array_with_structs() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
x: INT,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_binary_ops() -> Result<(), EvalAltResult> {
|
||||
fn test_binary_ops() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("10 % 4")?, 2);
|
||||
|
@ -1,14 +1,14 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_left_shift() -> Result<(), EvalAltResult> {
|
||||
fn test_left_shift() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("4 << 2")?, 16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_right_shift() -> Result<(), EvalAltResult> {
|
||||
fn test_right_shift() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("9 >> 1")?, 4);
|
||||
Ok(())
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
#[test]
|
||||
fn test_bool_op1() -> Result<(), EvalAltResult> {
|
||||
fn test_bool_op1() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<bool>("true && (false || true)")?, true);
|
||||
@ -11,7 +11,7 @@ fn test_bool_op1() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_op2() -> Result<(), EvalAltResult> {
|
||||
fn test_bool_op2() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<bool>("false && (false || true)")?, false);
|
||||
@ -21,7 +21,7 @@ fn test_bool_op2() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_op3() -> Result<(), EvalAltResult> {
|
||||
fn test_bool_op3() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert!(engine.eval::<bool>("true && (false || 123)").is_err());
|
||||
@ -33,7 +33,7 @@ fn test_bool_op3() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
|
||||
fn test_bool_op_short_circuit() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -2,7 +2,7 @@
|
||||
use rhai::{Engine, EvalAltResult, Func, ParseErrorType, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_fn() -> Result<(), EvalAltResult> {
|
||||
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
// Expect duplicated parameters error
|
||||
@ -19,7 +19,7 @@ fn test_fn() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_fn() -> Result<(), EvalAltResult> {
|
||||
fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
@ -61,7 +61,7 @@ fn test_call_fn() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anonymous_fn() -> Result<(), EvalAltResult> {
|
||||
fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
let calc_func = Func::<(INT, INT, INT), INT>::create_from_script(
|
||||
Engine::new(),
|
||||
"fn calc(x, y, z) { (x + y) * z }",
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
#[test]
|
||||
fn test_chars() -> Result<(), EvalAltResult> {
|
||||
fn test_chars() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<char>("'y'")?, 'y');
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_or_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_or_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 16; x |= 74; x")?, 90);
|
||||
@ -12,7 +12,7 @@ fn test_or_equals() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_and_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 16; x &= 31; x")?, 16);
|
||||
@ -24,42 +24,42 @@ fn test_and_equals() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_xor_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("let x = 90; x ^= 12; x")?, 86);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiply_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_multiply_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("let x = 2; x *= 3; x")?, 6);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_divide_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_divide_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("let x = 6; x /= 2; x")?, 3);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_left_shift_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_left_shift_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("let x = 9; x >>=1; x")?, 4);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_right_shift_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_right_shift_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("let x = 4; x<<= 2; x")?, 16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modulo_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_modulo_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2);
|
||||
Ok(())
|
||||
|
@ -1,21 +1,21 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_constant() -> Result<(), EvalAltResult> {
|
||||
fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("const x = 123; x")?, 123);
|
||||
|
||||
assert!(
|
||||
matches!(engine.eval::<INT>("const x = 123; x = 42;").expect_err("expects error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(var, _) if var == "x")
|
||||
);
|
||||
assert!(matches!(
|
||||
*engine.eval::<INT>("const x = 123; x = 42;").expect_err("expects error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(var, _) if var == "x"
|
||||
));
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
assert!(
|
||||
matches!(engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(var, _) if var == "x")
|
||||
);
|
||||
assert!(matches!(
|
||||
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(var, _) if var == "x"
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_decrement() -> Result<(), EvalAltResult> {
|
||||
fn test_decrement() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);
|
||||
|
||||
assert!(matches!(engine
|
||||
.eval::<String>(r#"let s = "test"; s -= "ing"; s"#)
|
||||
.expect_err("expects error"), EvalAltResult::ErrorFunctionNotFound(err, _) if err == "- (string, string)"));
|
||||
assert!(matches!(
|
||||
*engine.eval::<String>(r#"let s = "test"; s -= "ing"; s"#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorFunctionNotFound(err, _) if err == "- (string, string)"
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_eval() -> Result<(), EvalAltResult> {
|
||||
fn test_eval() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -18,7 +18,7 @@ fn test_eval() -> Result<(), EvalAltResult> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn test_eval_function() -> Result<(), EvalAltResult> {
|
||||
fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
@ -34,10 +34,10 @@ fn test_eval_function() -> Result<(), EvalAltResult> {
|
||||
script += "y += foo(y);";
|
||||
script += "x + y";
|
||||
|
||||
eval(script)
|
||||
eval(script) + x + y
|
||||
"#
|
||||
)?,
|
||||
42
|
||||
84
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -54,14 +54,15 @@ fn test_eval_function() -> Result<(), EvalAltResult> {
|
||||
32
|
||||
);
|
||||
|
||||
assert!(!scope.contains("z"));
|
||||
assert!(scope.contains("script"));
|
||||
assert_eq!(scope.len(), 3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn test_eval_override() -> Result<(), EvalAltResult> {
|
||||
fn test_eval_override() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_expressions() -> Result<(), EvalAltResult> {
|
||||
fn test_expressions() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
@ -28,7 +28,7 @@ fn test_expressions() -> Result<(), EvalAltResult> {
|
||||
/// This example taken from https://github.com/jonathandturner/rhai/issues/115
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn test_expressions_eval() -> Result<(), EvalAltResult> {
|
||||
fn test_expressions_eval() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Debug, Clone)]
|
||||
struct AGENT {
|
||||
pub gender: String,
|
||||
|
@ -4,7 +4,7 @@ use rhai::{Engine, EvalAltResult, RegisterFn, FLOAT};
|
||||
const EPSILON: FLOAT = 0.000_000_000_1;
|
||||
|
||||
#[test]
|
||||
fn test_float() -> Result<(), EvalAltResult> {
|
||||
fn test_float() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -22,7 +22,7 @@ fn test_float() -> Result<(), EvalAltResult> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn struct_with_float() -> Result<(), EvalAltResult> {
|
||||
fn struct_with_float() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
x: f64,
|
||||
|
@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[test]
|
||||
fn test_for_array() -> Result<(), EvalAltResult> {
|
||||
fn test_for_array() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let script = r"
|
||||
@ -31,8 +31,9 @@ fn test_for_array() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[test]
|
||||
fn test_for_object() -> Result<(), EvalAltResult> {
|
||||
fn test_for_object() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let script = r#"
|
||||
|
@ -3,7 +3,7 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
|
||||
#[test]
|
||||
fn test_get_set() -> Result<(), EvalAltResult> {
|
||||
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
x: INT,
|
||||
@ -36,7 +36,7 @@ fn test_get_set() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_big_get_set() -> Result<(), EvalAltResult> {
|
||||
fn test_big_get_set() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Clone)]
|
||||
struct TestChild {
|
||||
x: INT,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_if() -> Result<(), EvalAltResult> {
|
||||
fn test_if() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("if true { 55 }")?, 55);
|
||||
@ -29,7 +29,7 @@ fn test_if() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_expr() -> Result<(), EvalAltResult> {
|
||||
fn test_if_expr() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1,10 +1,11 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_increment() -> Result<(), EvalAltResult> {
|
||||
fn test_increment() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>("let s = \"test\"; s += \"ing\"; s")?,
|
||||
"testing"
|
||||
|
@ -3,7 +3,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_internal_fn() -> Result<(), EvalAltResult> {
|
||||
fn test_internal_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -16,7 +16,7 @@ fn test_internal_fn() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_big_internal_fn() -> Result<(), EvalAltResult> {
|
||||
fn test_big_internal_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -35,7 +35,7 @@ fn test_big_internal_fn() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_internal_fn_overloading() -> Result<(), EvalAltResult> {
|
||||
fn test_internal_fn_overloading() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_loop() -> Result<(), EvalAltResult> {
|
||||
fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -3,7 +3,7 @@
|
||||
use rhai::{Engine, EvalAltResult, Map, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_map_indexing() -> Result<(), EvalAltResult> {
|
||||
fn test_map_indexing() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -81,7 +81,7 @@ fn test_map_indexing() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_assign() -> Result<(), EvalAltResult> {
|
||||
fn test_map_assign() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let x = engine.eval::<Map>(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?;
|
||||
@ -112,7 +112,7 @@ fn test_map_assign() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_return() -> Result<(), EvalAltResult> {
|
||||
fn test_map_return() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let x = engine.eval::<Map>(r#"#{a: 1, b: true, "c$": "hello"}"#)?;
|
||||
@ -143,7 +143,8 @@ fn test_map_return() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_for() -> Result<(), EvalAltResult> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
fn test_map_for() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -170,7 +171,7 @@ fn test_map_for() -> Result<(), EvalAltResult> {
|
||||
#[test]
|
||||
/// Because a Rhai object map literal is almost the same as JSON,
|
||||
/// it is possible to convert from JSON into a Rhai object map.
|
||||
fn test_map_json() -> Result<(), EvalAltResult> {
|
||||
fn test_map_json() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let json = r#"{"a":1, "b":true, "c":42, "$d e f!":"hello", "z":null}"#;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_math() -> Result<(), EvalAltResult> {
|
||||
fn test_math() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("1 + 2")?, 3);
|
||||
@ -25,37 +25,37 @@ fn test_math() -> Result<(), EvalAltResult> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("abs(-9223372036854775808)")
|
||||
.expect_err("expects negation overflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("9223372036854775807 + 1")
|
||||
.expect_err("expects overflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("-9223372036854775808 - 1")
|
||||
.expect_err("expects underflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("9223372036854775807 * 9223372036854775807")
|
||||
.expect_err("expects overflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("9223372036854775807 / 0")
|
||||
.expect_err("expects division by zero"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("9223372036854775807 % 0")
|
||||
.expect_err("expects division by zero"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
@ -65,31 +65,31 @@ fn test_math() -> Result<(), EvalAltResult> {
|
||||
#[cfg(feature = "only_i32")]
|
||||
{
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("2147483647 + 1")
|
||||
.expect_err("expects overflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("-2147483648 - 1")
|
||||
.expect_err("expects underflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("2147483647 * 2147483647")
|
||||
.expect_err("expects overflow"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("2147483647 / 0")
|
||||
.expect_err("expects division by zero"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
));
|
||||
assert!(matches!(
|
||||
engine
|
||||
*engine
|
||||
.eval::<INT>("2147483647 % 0")
|
||||
.expect_err("expects division by zero"),
|
||||
EvalAltResult::ErrorArithmetic(_, _)
|
||||
|
@ -3,7 +3,7 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
|
||||
#[test]
|
||||
fn test_method_call() -> Result<(), EvalAltResult> {
|
||||
fn test_method_call() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
struct TestStruct {
|
||||
x: INT,
|
||||
|
@ -4,10 +4,10 @@ use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
fn test_mismatched_op() {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert!(
|
||||
matches!(engine.eval::<INT>(r#""hello, " + "world!""#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorMismatchOutputType(err, _) if err == "string")
|
||||
);
|
||||
assert!(matches!(
|
||||
*engine.eval::<INT>(r#""hello, " + "world!""#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorMismatchOutputType(err, _) if err == "string"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -33,12 +33,14 @@ fn test_mismatched_op_custom_type() {
|
||||
.expect_err("expects error");
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
assert!(
|
||||
matches!(r, EvalAltResult::ErrorFunctionNotFound(err, _) if err == "+ (i32, TestStruct)")
|
||||
);
|
||||
assert!(matches!(
|
||||
*r,
|
||||
EvalAltResult::ErrorFunctionNotFound(err, _) if err == "+ (i32, TestStruct)"
|
||||
));
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
assert!(
|
||||
matches!(r, EvalAltResult::ErrorFunctionNotFound(err, _) if err == "+ (i64, TestStruct)")
|
||||
);
|
||||
assert!(matches!(
|
||||
*r,
|
||||
EvalAltResult::ErrorFunctionNotFound(err, _) if err == "+ (i64, TestStruct)"
|
||||
));
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
#[test]
|
||||
fn test_not() -> Result<(), EvalAltResult> {
|
||||
fn test_not() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_number_literal() -> Result<(), EvalAltResult> {
|
||||
fn test_number_literal() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("65")?, 65);
|
||||
@ -10,7 +10,7 @@ fn test_number_literal() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_literal() -> Result<(), EvalAltResult> {
|
||||
fn test_hex_literal() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 0xf; x")?, 15);
|
||||
@ -20,7 +20,7 @@ fn test_hex_literal() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_octal_literal() -> Result<(), EvalAltResult> {
|
||||
fn test_octal_literal() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 0o77; x")?, 63);
|
||||
@ -30,7 +30,7 @@ fn test_octal_literal() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binary_literal() -> Result<(), EvalAltResult> {
|
||||
fn test_binary_literal() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 0b1111; x")?, 15);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_ops() -> Result<(), EvalAltResult> {
|
||||
fn test_ops() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("60 + 5")?, 65);
|
||||
@ -11,7 +11,7 @@ fn test_ops() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_op_precedence() -> Result<(), EvalAltResult> {
|
||||
fn test_op_precedence() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -3,8 +3,8 @@
|
||||
use rhai::{Engine, EvalAltResult, OptimizationLevel, INT};
|
||||
|
||||
#[test]
|
||||
fn test_optimizer() -> Result<(), EvalAltResult> {
|
||||
fn run_test(engine: &mut Engine) -> Result<(), EvalAltResult> {
|
||||
fn test_optimizer() -> Result<(), Box<EvalAltResult>> {
|
||||
fn run_test(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(engine.eval::<INT>(r"if true { 42 } else { 123 }")?, 42);
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||
|
@ -7,7 +7,7 @@ use rhai::FLOAT;
|
||||
const EPSILON: FLOAT = 0.000_000_000_1;
|
||||
|
||||
#[test]
|
||||
fn test_power_of() -> Result<(), EvalAltResult> {
|
||||
fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("2 ~ 3")?, 8);
|
||||
@ -28,7 +28,7 @@ fn test_power_of() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_power_of_equals() -> Result<(), EvalAltResult> {
|
||||
fn test_power_of_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 2; x ~= 3; x")?, 8);
|
||||
|
@ -40,7 +40,7 @@ impl CommandWrapper {
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[test]
|
||||
fn test_side_effects_command() -> Result<(), EvalAltResult> {
|
||||
fn test_side_effects_command() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
@ -80,7 +80,7 @@ fn test_side_effects_command() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_side_effects_print() -> Result<(), EvalAltResult> {
|
||||
fn test_side_effects_print() -> Result<(), Box<EvalAltResult>> {
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
#[test]
|
||||
fn test_stack_overflow() -> Result<(), EvalAltResult> {
|
||||
fn test_stack_overflow() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -22,8 +22,10 @@ fn test_stack_overflow() -> Result<(), EvalAltResult> {
|
||||
",
|
||||
) {
|
||||
Ok(_) => panic!("should be stack overflow"),
|
||||
Err(EvalAltResult::ErrorStackOverflow(_)) => (),
|
||||
Err(_) => panic!("should be stack overflow"),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::ErrorStackOverflow(_) => (),
|
||||
_ => panic!("should be stack overflow"),
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_string() -> Result<(), EvalAltResult> {
|
||||
fn test_string() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
@ -37,7 +37,7 @@ fn test_string() -> Result<(), EvalAltResult> {
|
||||
#[cfg(not(feature = "no_stdlib"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[test]
|
||||
fn test_string_substring() -> Result<(), EvalAltResult> {
|
||||
fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -5,10 +5,12 @@ fn test_throw() {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert!(matches!(
|
||||
engine.eval::<()>(r#"if true { throw "hello" }"#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorRuntime(s, _) if s == "hello"));
|
||||
*engine.eval::<()>(r#"if true { throw "hello" }"#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorRuntime(s, _) if s == "hello"
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
engine.eval::<()>(r#"throw"#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorRuntime(s, _) if s == ""));
|
||||
*engine.eval::<()>(r#"throw"#).expect_err("expects error"),
|
||||
EvalAltResult::ErrorRuntime(s, _) if s == ""
|
||||
));
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use rhai::{Engine, EvalAltResult, INT};
|
||||
use rhai::FLOAT;
|
||||
|
||||
#[test]
|
||||
fn test_timestamp() -> Result<(), EvalAltResult> {
|
||||
fn test_timestamp() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<String>("type_of(timestamp())")?, "timestamp");
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||
|
||||
#[test]
|
||||
fn test_type_of() -> Result<(), EvalAltResult> {
|
||||
fn test_type_of() -> Result<(), Box<EvalAltResult>> {
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
x: INT,
|
||||
|
@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, INT};
|
||||
#[test]
|
||||
// TODO also add test case for unary after compound
|
||||
// Hah, turns out unary + has a good use after all!
|
||||
fn test_unary_after_binary() -> Result<(), EvalAltResult> {
|
||||
fn test_unary_after_binary() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("10 % +4")?, 2);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_unary_minus() -> Result<(), EvalAltResult> {
|
||||
fn test_unary_minus() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5);
|
||||
|
@ -1,21 +1,21 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
|
||||
#[test]
|
||||
fn test_unit() -> Result<(), EvalAltResult> {
|
||||
fn test_unit() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
engine.eval::<()>("let x = (); x")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit_eq() -> Result<(), EvalAltResult> {
|
||||
fn test_unit_eq() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
assert_eq!(engine.eval::<bool>("let x = (); let y = (); x == y")?, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit_with_spaces() -> Result<(), EvalAltResult> {
|
||||
fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
engine.eval::<()>("let x = ( ); x")?;
|
||||
Ok(())
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_var_scope() -> Result<(), EvalAltResult> {
|
||||
fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
@ -20,7 +20,7 @@ fn test_var_scope() -> Result<(), EvalAltResult> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_eval() -> Result<(), EvalAltResult> {
|
||||
fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
// First create the state
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_while() -> Result<(), EvalAltResult> {
|
||||
fn test_while() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
|
Loading…
Reference in New Issue
Block a user