Merge branch 'min-build'
This commit is contained in:
commit
dbfc38763a
17
Cargo.toml
17
Cargo.toml
@ -18,15 +18,18 @@ include = [
|
|||||||
num-traits = "*"
|
num-traits = "*"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
#default = ["no_function", "no_index", "no_float", "only_i32", "no_stdlib", "unchecked"]
|
||||||
default = []
|
default = []
|
||||||
debug_msgs = []
|
debug_msgs = [] # print debug messages on function registrations and calls
|
||||||
unchecked = []
|
unchecked = [] # unchecked arithmetic
|
||||||
no_stdlib = []
|
no_stdlib = [] # no standard library of utility functions
|
||||||
no_index = []
|
no_index = [] # no arrays and indexing
|
||||||
no_float = []
|
no_float = [] # no floating-point
|
||||||
only_i32 = []
|
no_function = [] # no script-defined functions
|
||||||
only_i64 = []
|
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||||
|
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
#opt-level = "z" # optimize for size
|
||||||
|
99
README.md
99
README.md
@ -10,7 +10,7 @@ Rhai's current feature set:
|
|||||||
* Low compile-time overhead (~0.6 sec debug/~3 sec release for script runner app)
|
* Low compile-time overhead (~0.6 sec debug/~3 sec release for script runner app)
|
||||||
* Easy-to-use language similar to JS+Rust
|
* Easy-to-use language similar to JS+Rust
|
||||||
* Support for overloaded functions
|
* Support for overloaded functions
|
||||||
* Very few additional dependencies (right now only `num-traits` to do checked arithmetic operations)
|
* Very few additional dependencies (right now only [`num-traits`] to do checked arithmetic operations)
|
||||||
|
|
||||||
**Note:** Currently, the version is 0.10.2, so the language and API's may change before they stabilize.
|
**Note:** Currently, the version is 0.10.2, so the language and API's may change before they stabilize.
|
||||||
|
|
||||||
@ -38,15 +38,16 @@ Beware that in order to use pre-releases (alpha and beta) you need to specify th
|
|||||||
Optional features
|
Optional features
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
| Feature | Description |
|
| Feature | Description |
|
||||||
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `debug_msgs` | Print debug messages to stdout related to function registrations and calls. |
|
| `debug_msgs` | Print debug messages to stdout related to function registrations and calls. |
|
||||||
| `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. Standard types are not affected. |
|
| `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. Standard types are not affected. |
|
||||||
| `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
| `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
||||||
| `no_index` | Disable arrays and indexing features if you don't need them. |
|
| `no_function` | Disable script-defined functions if you don't need them. |
|
||||||
| `no_float` | Disable floating-point numbers and math if you don't need them. |
|
| `no_index` | Disable arrays and indexing features if you don't need them. |
|
||||||
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. |
|
| `no_float` | Disable floating-point numbers and math if you don't need them. |
|
||||||
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. |
|
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. |
|
||||||
|
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. |
|
||||||
|
|
||||||
By default, Rhai includes all the standard functionalities in a small, tight package. Most features are here for you to opt-**out** of certain functionalities that you do not need.
|
By default, Rhai includes all the standard functionalities in a small, tight package. Most features are here for you to opt-**out** of certain functionalities that you do not need.
|
||||||
Excluding unneeded functionalities can result in smaller, faster builds as well as less bugs due to a more restricted language.
|
Excluding unneeded functionalities can result in smaller, faster builds as well as less bugs due to a more restricted language.
|
||||||
@ -56,8 +57,8 @@ Related
|
|||||||
|
|
||||||
Other cool projects to check out:
|
Other cool projects to check out:
|
||||||
|
|
||||||
* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being lead by my cousin.
|
* [ChaiScript] - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being lead by my cousin.
|
||||||
* You can also check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust)
|
* You can also check out the list of [scripting languages for Rust] on [awesome-rust].
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
@ -195,24 +196,24 @@ Values and types
|
|||||||
|
|
||||||
The following primitive types are supported natively:
|
The following primitive types are supported natively:
|
||||||
|
|
||||||
| Category | Types |
|
| Category | Types |
|
||||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
| **Integer** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`](#optional-features)),<br/>`u64`, `i64` _(default)_ |
|
| **Integer** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ |
|
||||||
| **Floating-point** (disabled with [`no_float`](#optional-features)) | `f32`, `f64` _(default)_ |
|
| **Floating-point** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ |
|
||||||
| **Character** | `char` |
|
| **Character** | `char` |
|
||||||
| **Boolean** | `bool` |
|
| **Boolean** | `bool` |
|
||||||
| **Array** (disabled with [`no_index`](#optional-features)) | `rhai::Array` |
|
| **Array** (disabled with [`no_index`]) | `rhai::Array` |
|
||||||
| **Dynamic** (i.e. can be anything) | `rhai::Dynamic` |
|
| **Dynamic** (i.e. can be anything) | `rhai::Dynamic` |
|
||||||
| **System** (current configuration) | `rhai::INT` (`i32` or `i64`),<br/>`rhai::FLOAT` (`f32` or `f64`) |
|
| **System** (current configuration) | `rhai::INT` (`i32` or `i64`),<br/>`rhai::FLOAT` (`f32` or `f64`) |
|
||||||
|
|
||||||
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different; you cannot even add them together.
|
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different; you cannot even add them together.
|
||||||
|
|
||||||
The default integer type is `i64`. If you do not need any other integer type, you can enable the [`only_i64`](#optional-features) feature.
|
The default integer type is `i64`. If you do not need any other integer type, you can enable the [`only_i64`] feature.
|
||||||
|
|
||||||
If you only need 32-bit integers, you can enable the [`only_i32`](#optional-features) feature and remove support for all integer types other than `i32` including `i64`.
|
If you only need 32-bit integers, you can enable the [`only_i32`] feature and remove support for all integer types other than `i32` including `i64`.
|
||||||
This is useful on 32-bit systems where using 64-bit integers incurs a performance penalty.
|
This is useful on 32-bit systems where using 64-bit integers incurs a performance penalty.
|
||||||
|
|
||||||
If you do not need floating-point, enable the [`no_float`](#optional-features) feature to remove support.
|
If you do not need floating-point, enable the [`no_float`] feature to remove support.
|
||||||
|
|
||||||
Value conversions
|
Value conversions
|
||||||
-----------------
|
-----------------
|
||||||
@ -303,17 +304,17 @@ use std::fmt::Display;
|
|||||||
|
|
||||||
use rhai::{Engine, RegisterFn};
|
use rhai::{Engine, RegisterFn};
|
||||||
|
|
||||||
fn showit<T: Display>(x: &mut T) -> () {
|
fn show_it<T: Display>(x: &mut T) -> () {
|
||||||
println!("{}", x)
|
println!("put up a good show: {}!", x)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main()
|
fn main()
|
||||||
{
|
{
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut i64)->());
|
engine.register_fn("print", show_it as fn(x: &mut i64)->());
|
||||||
engine.register_fn("print", showit as fn(x: &mut bool)->());
|
engine.register_fn("print", show_it as fn(x: &mut bool)->());
|
||||||
engine.register_fn("print", showit as fn(x: &mut String)->());
|
engine.register_fn("print", show_it as fn(x: &mut String)->());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -615,24 +616,23 @@ Unary operators
|
|||||||
```rust
|
```rust
|
||||||
let number = -5;
|
let number = -5;
|
||||||
number = -5 - +5;
|
number = -5 - +5;
|
||||||
let booly = !true;
|
let boolean = !true;
|
||||||
```
|
```
|
||||||
|
|
||||||
Numeric functions
|
Numeric functions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
The following standard functions (defined in the standard library but excluded if [`no_stdlib`]) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||||
|
|
||||||
| Function | Description |
|
| Function | Description |
|
||||||
| ---------- | ----------------------------------- |
|
| ---------- | --------------------------------- |
|
||||||
| `abs` | absolute value |
|
| `abs` | absolute value |
|
||||||
| `to_int` | converts an `f32` or `f64` to `i64` |
|
| `to_float` | converts an integer type to `f64` |
|
||||||
| `to_float` | converts an integer type to `f64` |
|
|
||||||
|
|
||||||
Floating-point functions
|
Floating-point functions
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `f64` only:
|
The following standard functions (defined in the standard library but excluded if [`no_stdlib`]) operate on `f64` only:
|
||||||
|
|
||||||
| Category | Functions |
|
| Category | Functions |
|
||||||
| ---------------- | ------------------------------------------------------------ |
|
| ---------------- | ------------------------------------------------------------ |
|
||||||
@ -642,7 +642,8 @@ The following standard functions (defined in the standard library but excluded i
|
|||||||
| Exponential | `exp` (base _e_) |
|
| Exponential | `exp` (base _e_) |
|
||||||
| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) |
|
| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) |
|
||||||
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` |
|
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` |
|
||||||
| Tests | `is_nan`, `is_finite`, `is_infinite` |
|
| Conversion | `to_int` |
|
||||||
|
| Testing | `is_nan`, `is_finite`, `is_infinite` |
|
||||||
|
|
||||||
Strings and Chars
|
Strings and Chars
|
||||||
-----------------
|
-----------------
|
||||||
@ -684,7 +685,7 @@ record[4] = '\x58'; // 0x58 = 'X'
|
|||||||
record == "Bob X. Davis: age 42 ❤\n";
|
record == "Bob X. Davis: age 42 ❤\n";
|
||||||
```
|
```
|
||||||
|
|
||||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on strings:
|
The following standard functions (defined in the standard library but excluded if [`no_stdlib`]) operate on strings:
|
||||||
|
|
||||||
| Function | Description |
|
| Function | Description |
|
||||||
| ---------- | ------------------------------------------------------------------------ |
|
| ---------- | ------------------------------------------------------------------------ |
|
||||||
@ -731,7 +732,7 @@ Arrays
|
|||||||
|
|
||||||
You can create arrays of values, and then access them with numeric indices.
|
You can create arrays of values, and then access them with numeric indices.
|
||||||
|
|
||||||
The following functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on arrays:
|
The following functions (defined in the standard library but excluded if [`no_stdlib`]) operate on arrays:
|
||||||
|
|
||||||
| Function | Description |
|
| Function | Description |
|
||||||
| ---------- | ------------------------------------------------------------------------------------- |
|
| ---------- | ------------------------------------------------------------------------------------- |
|
||||||
@ -803,7 +804,7 @@ engine.register_fn("push",
|
|||||||
|
|
||||||
The type of a Rhai array is `rhai::Array`. `type_of()` returns `"array"`.
|
The type of a Rhai array is `rhai::Array`. `type_of()` returns `"array"`.
|
||||||
|
|
||||||
Arrays are disabled via the [`no_index`](#optional-features) feature.
|
Arrays are disabled via the [`no_index`] feature.
|
||||||
|
|
||||||
Comparison operators
|
Comparison operators
|
||||||
--------------------
|
--------------------
|
||||||
@ -954,7 +955,7 @@ println!(result); // prints "Runtime error: 42 is too large! (line 5, position
|
|||||||
Functions
|
Functions
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Rhai supports defining functions in script:
|
Rhai supports defining functions in script (unless disabled with [`no_function`]):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn add(x, y) {
|
fn add(x, y) {
|
||||||
@ -1048,3 +1049,17 @@ for entry in log {
|
|||||||
println!("{}", entry);
|
println!("{}", entry);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[ChaiScript]: http://chaiscript.com/
|
||||||
|
[scripting languages for Rust]: https://github.com/rust-unofficial/awesome-rust#scripting
|
||||||
|
[awesome-rust]: https://github.com/rust-unofficial/awesome-rust
|
||||||
|
|
||||||
|
[`num-traits`]: https://crates.io/crates/num-traits/
|
||||||
|
[`debug_msgs`]: #optional-features
|
||||||
|
[`unchecked`]: #optional-features
|
||||||
|
[`no_stdlib`]: #optional-features
|
||||||
|
[`no_index`]: #optional-features
|
||||||
|
[`no_float`]: #optional-features
|
||||||
|
[`no_function`]: #optional-features
|
||||||
|
[`only_i32`]: #optional-features
|
||||||
|
[`only_i64`]: #optional-features
|
||||||
|
60
src/api.rs
60
src/api.rs
@ -168,17 +168,25 @@ impl<'e> Engine<'e> {
|
|||||||
retain_functions: bool,
|
retain_functions: bool,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
let AST(statements, functions) = ast;
|
#[cfg(feature = "no_function")]
|
||||||
|
let AST(statements) = ast;
|
||||||
|
|
||||||
functions.iter().for_each(|f| {
|
#[cfg(not(feature = "no_function"))]
|
||||||
engine.script_functions.insert(
|
let statements = {
|
||||||
FnSpec {
|
let AST(statements, functions) = ast;
|
||||||
name: f.name.clone().into(),
|
|
||||||
args: None,
|
functions.iter().for_each(|f| {
|
||||||
},
|
engine.script_functions.insert(
|
||||||
Arc::new(FnIntExt::Int(f.clone())),
|
FnSpec {
|
||||||
);
|
name: f.name.clone().into(),
|
||||||
});
|
args: None,
|
||||||
|
},
|
||||||
|
Arc::new(FnIntExt::Int(f.clone())),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
statements
|
||||||
|
};
|
||||||
|
|
||||||
let result = statements
|
let result = statements
|
||||||
.iter()
|
.iter()
|
||||||
@ -244,16 +252,26 @@ impl<'e> Engine<'e> {
|
|||||||
|
|
||||||
parse(&mut tokens.peekable(), self.optimize)
|
parse(&mut tokens.peekable(), self.optimize)
|
||||||
.map_err(|err| EvalAltResult::ErrorParsing(err))
|
.map_err(|err| EvalAltResult::ErrorParsing(err))
|
||||||
.and_then(|AST(ref statements, ref functions)| {
|
.and_then(|ast| {
|
||||||
for f in functions {
|
#[cfg(feature = "no_function")]
|
||||||
self.script_functions.insert(
|
let AST(statements) = ast;
|
||||||
FnSpec {
|
|
||||||
name: f.name.clone().into(),
|
#[cfg(not(feature = "no_function"))]
|
||||||
args: None,
|
let statements = {
|
||||||
},
|
let AST(ref statements, ref functions) = ast;
|
||||||
Arc::new(FnIntExt::Int(f.clone())),
|
|
||||||
);
|
functions.iter().for_each(|f| {
|
||||||
}
|
self.script_functions.insert(
|
||||||
|
FnSpec {
|
||||||
|
name: f.name.clone().into(),
|
||||||
|
args: None,
|
||||||
|
},
|
||||||
|
Arc::new(FnIntExt::Int(f.clone())),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
statements
|
||||||
|
};
|
||||||
|
|
||||||
let val = statements
|
let val = statements
|
||||||
.iter()
|
.iter()
|
||||||
@ -275,6 +293,7 @@ impl<'e> Engine<'e> {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||||
/// # #[cfg(not(feature = "no_stdlib"))]
|
/// # #[cfg(not(feature = "no_stdlib"))]
|
||||||
|
/// # #[cfg(not(feature = "no_function"))]
|
||||||
/// # {
|
/// # {
|
||||||
/// use rhai::Engine;
|
/// use rhai::Engine;
|
||||||
///
|
///
|
||||||
@ -289,6 +308,7 @@ impl<'e> Engine<'e> {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub fn call_fn<A: FuncArgs, T: Any + Clone>(
|
pub fn call_fn<A: FuncArgs, T: Any + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -2,45 +2,23 @@
|
|||||||
//! _standard library_ of utility functions.
|
//! _standard library_ of utility functions.
|
||||||
|
|
||||||
use crate::any::Any;
|
use crate::any::Any;
|
||||||
use crate::engine::Engine;
|
|
||||||
use crate::fn_register::RegisterFn;
|
|
||||||
use crate::parser::INT;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
use crate::{parser::Position, result::EvalAltResult, RegisterResultFn};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
|
use crate::engine::Engine;
|
||||||
#[cfg(not(feature = "no_float"))]
|
use crate::fn_register::{RegisterFn, RegisterResultFn};
|
||||||
|
use crate::parser::{Position, INT};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
|
|
||||||
|
use num_traits::{
|
||||||
|
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
|
||||||
|
CheckedShr, CheckedSub,
|
||||||
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
ops::{BitAnd, BitOr, BitXor, Range},
|
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Shl, Shr, Sub},
|
||||||
};
|
{i32, i64, u32},
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
use std::ops::{Shl, Shr};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
use std::{i32, i64};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
use std::u32;
|
|
||||||
|
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
use {
|
|
||||||
num_traits::{
|
|
||||||
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr,
|
|
||||||
CheckedSub,
|
|
||||||
},
|
|
||||||
std::convert::TryFrom,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! reg_op {
|
macro_rules! reg_op {
|
||||||
@ -162,12 +140,9 @@ impl Engine<'_> {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
fn div<T>(x: T, y: T) -> Result<T, EvalAltResult>
|
fn div<T>(x: T, y: T) -> Result<T, EvalAltResult>
|
||||||
where
|
where
|
||||||
T: Display + CheckedDiv + PartialEq + TryFrom<i8>,
|
T: Display + CheckedDiv + PartialEq + Zero,
|
||||||
{
|
{
|
||||||
if y == <T as TryFrom<i8>>::try_from(0)
|
if y == T::zero() {
|
||||||
.map_err(|_| ())
|
|
||||||
.expect("zero should always succeed")
|
|
||||||
{
|
|
||||||
return Err(EvalAltResult::ErrorArithmetic(
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Division by zero: {} / {}", x, y),
|
format!("Division by zero: {} / {}", x, y),
|
||||||
Position::none(),
|
Position::none(),
|
||||||
@ -191,8 +166,10 @@ impl Engine<'_> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
fn abs<T: Display + CheckedNeg + PartialOrd + From<i8>>(x: T) -> Result<T, EvalAltResult> {
|
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> Result<T, EvalAltResult> {
|
||||||
if x >= 0.into() {
|
// 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)
|
Ok(x)
|
||||||
} else {
|
} else {
|
||||||
x.checked_neg().ok_or_else(|| {
|
x.checked_neg().ok_or_else(|| {
|
||||||
@ -224,14 +201,15 @@ impl Engine<'_> {
|
|||||||
-x
|
-x
|
||||||
}
|
}
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
||||||
fn abs_u<T: Neg + PartialOrd + From<i8>>(x: T) -> T
|
fn abs_u<T>(x: T) -> <T as Neg>::Output
|
||||||
where
|
where
|
||||||
<T as Neg>::Output: Into<T>,
|
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
|
||||||
{
|
{
|
||||||
if x < 0.into() {
|
// Numbers should default to zero
|
||||||
(-x).into()
|
if x < Default::default() {
|
||||||
|
-x
|
||||||
} else {
|
} else {
|
||||||
x
|
x.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
@ -29,6 +29,7 @@ type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
|||||||
|
|
||||||
pub(crate) const KEYWORD_PRINT: &'static str = "print";
|
pub(crate) const KEYWORD_PRINT: &'static str = "print";
|
||||||
pub(crate) const KEYWORD_DEBUG: &'static str = "debug";
|
pub(crate) const KEYWORD_DEBUG: &'static str = "debug";
|
||||||
|
pub(crate) const KEYWORD_DUMP_AST: &'static str = "dump_ast";
|
||||||
pub(crate) const KEYWORD_TYPE_OF: &'static str = "type_of";
|
pub(crate) const KEYWORD_TYPE_OF: &'static str = "type_of";
|
||||||
pub(crate) const FUNC_GETTER: &'static str = "get$";
|
pub(crate) const FUNC_GETTER: &'static str = "get$";
|
||||||
pub(crate) const FUNC_SETTER: &'static str = "set$";
|
pub(crate) const FUNC_SETTER: &'static str = "set$";
|
||||||
@ -788,6 +789,9 @@ impl Engine<'_> {
|
|||||||
.eval_index_expr(scope, lhs, idx_expr, *idx_pos)
|
.eval_index_expr(scope, lhs, idx_expr, *idx_pos)
|
||||||
.map(|(_, _, _, x)| x),
|
.map(|(_, _, _, x)| x),
|
||||||
|
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
Expr::Index(_, _, _) => panic!("encountered an index expression during no_index!"),
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt),
|
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt),
|
||||||
|
|
||||||
@ -855,7 +859,34 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
Ok(Box::new(arr))
|
Ok(Box::new(arr))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
Expr::Array(_, _) => panic!("encountered an array during no_index!"),
|
||||||
|
|
||||||
|
// Dump AST
|
||||||
|
Expr::FunctionCall(fn_name, args, _, pos) if fn_name == KEYWORD_DUMP_AST => {
|
||||||
|
let pos = if args.len() == 0 {
|
||||||
|
*pos
|
||||||
|
} else {
|
||||||
|
args[0].position()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Change the argument to a debug dump of the expressions
|
||||||
|
let result = args
|
||||||
|
.into_iter()
|
||||||
|
.map(|expr| format!("{:#?}", expr))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
// Redirect call to `print`
|
||||||
|
self.call_fn_raw(
|
||||||
|
KEYWORD_PRINT,
|
||||||
|
vec![result.into_dynamic().as_mut()],
|
||||||
|
None,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal function call
|
||||||
Expr::FunctionCall(fn_name, args, def_val, pos) => {
|
Expr::FunctionCall(fn_name, args, def_val, pos) => {
|
||||||
let mut args = args
|
let mut args = args
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -120,20 +120,20 @@ macro_rules! def_register {
|
|||||||
const NUM_ARGS: usize = count_args!($($par)*);
|
const NUM_ARGS: usize = count_args!($($par)*);
|
||||||
|
|
||||||
if args.len() != NUM_ARGS {
|
if args.len() != NUM_ARGS {
|
||||||
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
|
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos));
|
||||||
} else {
|
|
||||||
#[allow(unused_variables, unused_mut)]
|
|
||||||
let mut drain = args.drain(..);
|
|
||||||
$(
|
|
||||||
// Downcast every element, return in case of a type mismatch
|
|
||||||
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
|
|
||||||
)*
|
|
||||||
|
|
||||||
// Call the user-supplied function using ($clone) to
|
|
||||||
// potentially clone the value, otherwise pass the reference.
|
|
||||||
let r = f($(($clone)($par)),*);
|
|
||||||
Ok(Box::new(r) as Dynamic)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables, unused_mut)]
|
||||||
|
let mut drain = args.drain(..);
|
||||||
|
$(
|
||||||
|
// Downcast every element, return in case of a type mismatch
|
||||||
|
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
|
||||||
|
)*
|
||||||
|
|
||||||
|
// Call the user-supplied function using ($clone) to
|
||||||
|
// potentially clone the value, otherwise pass the reference.
|
||||||
|
let r = f($(($clone)($par)),*);
|
||||||
|
Ok(Box::new(r) as Dynamic)
|
||||||
};
|
};
|
||||||
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
||||||
}
|
}
|
||||||
@ -152,19 +152,19 @@ macro_rules! def_register {
|
|||||||
const NUM_ARGS: usize = count_args!($($par)*);
|
const NUM_ARGS: usize = count_args!($($par)*);
|
||||||
|
|
||||||
if args.len() != NUM_ARGS {
|
if args.len() != NUM_ARGS {
|
||||||
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
|
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos));
|
||||||
} else {
|
|
||||||
#[allow(unused_variables, unused_mut)]
|
|
||||||
let mut drain = args.drain(..);
|
|
||||||
$(
|
|
||||||
// Downcast every element, return in case of a type mismatch
|
|
||||||
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
|
|
||||||
)*
|
|
||||||
|
|
||||||
// Call the user-supplied function using ($clone) to
|
|
||||||
// potentially clone the value, otherwise pass the reference.
|
|
||||||
Ok(f($(($clone)($par)),*))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables, unused_mut)]
|
||||||
|
let mut drain = args.drain(..);
|
||||||
|
$(
|
||||||
|
// Downcast every element, return in case of a type mismatch
|
||||||
|
let $par = drain.next().unwrap().downcast_mut::<$par>().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, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
||||||
}
|
}
|
||||||
@ -184,23 +184,23 @@ macro_rules! def_register {
|
|||||||
const NUM_ARGS: usize = count_args!($($par)*);
|
const NUM_ARGS: usize = count_args!($($par)*);
|
||||||
|
|
||||||
if args.len() != NUM_ARGS {
|
if args.len() != NUM_ARGS {
|
||||||
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
|
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos));
|
||||||
} else {
|
}
|
||||||
#[allow(unused_variables, unused_mut)]
|
|
||||||
let mut drain = args.drain(..);
|
|
||||||
$(
|
|
||||||
// Downcast every element, return in case of a type mismatch
|
|
||||||
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
|
|
||||||
)*
|
|
||||||
|
|
||||||
// Call the user-supplied function using ($clone) to
|
#[allow(unused_variables, unused_mut)]
|
||||||
// potentially clone the value, otherwise pass the reference.
|
let mut drain = args.drain(..);
|
||||||
match f($(($clone)($par)),*) {
|
$(
|
||||||
Ok(r) => Ok(Box::new(r) as Dynamic),
|
// Downcast every element, return in case of a type mismatch
|
||||||
Err(mut err) => {
|
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
|
||||||
err.set_position(pos);
|
)*
|
||||||
Err(err)
|
|
||||||
}
|
// Call the user-supplied function using ($clone) to
|
||||||
|
// potentially clone the value, otherwise pass the reference.
|
||||||
|
match f($(($clone)($par)),*) {
|
||||||
|
Ok(r) => Ok(Box::new(r) as Dynamic),
|
||||||
|
Err(mut err) => {
|
||||||
|
err.set_position(pos);
|
||||||
|
Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -141,6 +141,7 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
|
|||||||
Box::new(optimize_expr(*rhs, changed)),
|
Box::new(optimize_expr(*rhs, changed)),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
|
||||||
(Expr::Array(mut items, _), Expr::IntegerConstant(i, _))
|
(Expr::Array(mut items, _), Expr::IntegerConstant(i, _))
|
||||||
@ -158,6 +159,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
|
|||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
Expr::Index(_, _, _) => panic!("encountered an index expression during no_index!"),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(items, pos) => {
|
Expr::Array(items, pos) => {
|
||||||
let original_len = items.len();
|
let original_len = items.len();
|
||||||
@ -172,6 +176,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
|
|||||||
Expr::Array(items, pos)
|
Expr::Array(items, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
Expr::Array(_, _) => panic!("encountered an array during no_index!"),
|
||||||
|
|
||||||
Expr::And(lhs, rhs) => match (*lhs, *rhs) {
|
Expr::And(lhs, rhs) => match (*lhs, *rhs) {
|
||||||
(Expr::True(_), rhs) => {
|
(Expr::True(_), rhs) => {
|
||||||
*changed = true;
|
*changed = true;
|
||||||
@ -208,7 +215,6 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
|
|||||||
Box::new(optimize_expr(rhs, changed)),
|
Box::new(optimize_expr(rhs, changed)),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
Expr::FunctionCall(id, args, def_value, pos) => {
|
Expr::FunctionCall(id, args, def_value, pos) => {
|
||||||
let original_len = args.len();
|
let original_len = args.len();
|
||||||
|
|
||||||
|
@ -3,13 +3,18 @@
|
|||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::optimize::optimize;
|
use crate::optimize::optimize;
|
||||||
|
|
||||||
use std::{borrow::Cow, char, fmt, iter::Peekable, str::Chars, str::FromStr, usize};
|
use std::{borrow::Cow, char, fmt, iter::Peekable, str::Chars, str::FromStr, usize};
|
||||||
|
|
||||||
/// The system integer type
|
/// The system integer type.
|
||||||
|
///
|
||||||
|
/// If the `only_i32` feature is enabled, this will be `i32` instead.
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
pub type INT = i64;
|
pub type INT = i64;
|
||||||
|
|
||||||
/// The system integer type
|
/// The system integer type
|
||||||
|
///
|
||||||
|
/// If the `only_i32` feature is not enabled, this will be `i64` instead.
|
||||||
#[cfg(feature = "only_i32")]
|
#[cfg(feature = "only_i32")]
|
||||||
pub type INT = i32;
|
pub type INT = i32;
|
||||||
|
|
||||||
@ -134,7 +139,10 @@ impl fmt::Debug for Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||||
pub struct AST(pub(crate) Vec<Stmt>, pub(crate) Vec<FnDef<'static>>);
|
pub struct AST(
|
||||||
|
pub(crate) Vec<Stmt>,
|
||||||
|
#[cfg(not(feature = "no_function"))] pub(crate) Vec<FnDef<'static>>,
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FnDef<'a> {
|
pub struct FnDef<'a> {
|
||||||
@ -179,9 +187,7 @@ pub enum Expr {
|
|||||||
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
|
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
|
||||||
Assignment(Box<Expr>, Box<Expr>, Position),
|
Assignment(Box<Expr>, Box<Expr>, Position),
|
||||||
Dot(Box<Expr>, Box<Expr>, Position),
|
Dot(Box<Expr>, Box<Expr>, Position),
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Index(Box<Expr>, Box<Expr>, Position),
|
Index(Box<Expr>, Box<Expr>, Position),
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Array(Vec<Expr>, Position),
|
Array(Vec<Expr>, Position),
|
||||||
And(Box<Expr>, Box<Expr>),
|
And(Box<Expr>, Box<Expr>),
|
||||||
Or(Box<Expr>, Box<Expr>),
|
Or(Box<Expr>, Box<Expr>),
|
||||||
@ -197,24 +203,21 @@ impl Expr {
|
|||||||
| Expr::Identifier(_, pos)
|
| Expr::Identifier(_, pos)
|
||||||
| Expr::CharConstant(_, pos)
|
| Expr::CharConstant(_, pos)
|
||||||
| Expr::StringConstant(_, pos)
|
| Expr::StringConstant(_, pos)
|
||||||
| Expr::FunctionCall(_, _, _, pos)
|
|
||||||
| Expr::Stmt(_, pos)
|
| Expr::Stmt(_, pos)
|
||||||
|
| Expr::FunctionCall(_, _, _, pos)
|
||||||
|
| Expr::Array(_, pos)
|
||||||
| Expr::True(pos)
|
| Expr::True(pos)
|
||||||
| Expr::False(pos)
|
| Expr::False(pos)
|
||||||
| Expr::Unit(pos) => *pos,
|
| Expr::Unit(pos) => *pos,
|
||||||
|
|
||||||
Expr::Assignment(e, _, _) | Expr::Dot(e, _, _) | Expr::And(e, _) | Expr::Or(e, _) => {
|
Expr::Assignment(e, _, _)
|
||||||
e.position()
|
| Expr::Dot(e, _, _)
|
||||||
}
|
| Expr::Index(e, _, _)
|
||||||
|
| Expr::And(e, _)
|
||||||
|
| Expr::Or(e, _) => e.position(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(_, pos) => *pos,
|
Expr::FloatConstant(_, pos) => *pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(e, _, _) => e.position(),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Array(_, pos) => *pos,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1820,6 +1823,7 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static>, ParseError> {
|
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static>, ParseError> {
|
||||||
let pos = match input.next() {
|
let pos = match input.next() {
|
||||||
Some((_, tok_pos)) => tok_pos,
|
Some((_, tok_pos)) => tok_pos,
|
||||||
@ -1892,11 +1896,14 @@ fn parse_top_level<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
optimize_ast: bool,
|
optimize_ast: bool,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let mut statements = Vec::new();
|
let mut statements = Vec::<Stmt>::new();
|
||||||
let mut functions = Vec::new();
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let mut functions = Vec::<FnDef>::new();
|
||||||
|
|
||||||
while input.peek().is_some() {
|
while input.peek().is_some() {
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some(&(Token::Fn, _)) => functions.push(parse_fn(input)?),
|
Some(&(Token::Fn, _)) => functions.push(parse_fn(input)?),
|
||||||
_ => statements.push(parse_stmt(input)?),
|
_ => statements.push(parse_stmt(input)?),
|
||||||
}
|
}
|
||||||
@ -1910,6 +1917,7 @@ fn parse_top_level<'a>(
|
|||||||
return Ok(if optimize_ast {
|
return Ok(if optimize_ast {
|
||||||
AST(
|
AST(
|
||||||
optimize(statements),
|
optimize(statements),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions
|
functions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut fn_def| {
|
.map(|mut fn_def| {
|
||||||
@ -1920,7 +1928,11 @@ fn parse_top_level<'a>(
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AST(statements, functions)
|
AST(
|
||||||
|
statements,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
functions,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::parser::{Position, INT};
|
use crate::parser::{Position, INT};
|
||||||
|
|
||||||
use std::{error::Error, fmt};
|
use std::{error::Error, fmt};
|
||||||
|
|
||||||
/// Evaluation result.
|
/// Evaluation result.
|
||||||
@ -75,12 +76,12 @@ impl Error for EvalAltResult {
|
|||||||
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
||||||
"Array access expects non-negative index"
|
"Array access expects non-negative index"
|
||||||
}
|
}
|
||||||
Self::ErrorArrayBounds(max, _, _) if *max == 0 => "Access of empty array",
|
Self::ErrorArrayBounds(0, _, _) => "Access of empty array",
|
||||||
Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds",
|
Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds",
|
||||||
Self::ErrorStringBounds(_, index, _) if *index < 0 => {
|
Self::ErrorStringBounds(_, index, _) if *index < 0 => {
|
||||||
"Indexing a string expects a non-negative index"
|
"Indexing a string expects a non-negative index"
|
||||||
}
|
}
|
||||||
Self::ErrorStringBounds(max, _, _) if *max == 0 => "Indexing of empty string",
|
Self::ErrorStringBounds(0, _, _) => "Indexing of empty string",
|
||||||
Self::ErrorStringBounds(_, _, _) => "String index out of bounds",
|
Self::ErrorStringBounds(_, _, _) => "String index out of bounds",
|
||||||
Self::ErrorIfGuard(_) => "If guard expects boolean expression",
|
Self::ErrorIfGuard(_) => "If guard expects boolean expression",
|
||||||
Self::ErrorFor(_) => "For loop expects array or range",
|
Self::ErrorFor(_) => "For loop expects array or range",
|
||||||
@ -128,6 +129,16 @@ impl fmt::Display for EvalAltResult {
|
|||||||
write!(f, "{} '{}': {}", desc, filename, err)
|
write!(f, "{} '{}': {}", desc, filename, err)
|
||||||
}
|
}
|
||||||
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
|
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
|
||||||
|
Self::ErrorFunctionArgsMismatch(fun, 0, n, pos) => write!(
|
||||||
|
f,
|
||||||
|
"Function '{}' expects no argument but {} found ({})",
|
||||||
|
fun, n, pos
|
||||||
|
),
|
||||||
|
Self::ErrorFunctionArgsMismatch(fun, 1, n, pos) => write!(
|
||||||
|
f,
|
||||||
|
"Function '{}' expects one argument but {} found ({})",
|
||||||
|
fun, n, pos
|
||||||
|
),
|
||||||
Self::ErrorFunctionArgsMismatch(fun, need, n, pos) => write!(
|
Self::ErrorFunctionArgsMismatch(fun, need, n, pos) => write!(
|
||||||
f,
|
f,
|
||||||
"Function '{}' expects {} argument(s) but {} found ({})",
|
"Function '{}' expects {} argument(s) but {} found ({})",
|
||||||
@ -142,26 +153,30 @@ impl fmt::Display for EvalAltResult {
|
|||||||
Self::ErrorArrayBounds(_, index, pos) if *index < 0 => {
|
Self::ErrorArrayBounds(_, index, pos) if *index < 0 => {
|
||||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
||||||
}
|
}
|
||||||
Self::ErrorArrayBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
|
Self::ErrorArrayBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
|
||||||
|
Self::ErrorArrayBounds(1, index, pos) => write!(
|
||||||
|
f,
|
||||||
|
"Array index {} is out of bounds: only one element in the array ({})",
|
||||||
|
index, pos
|
||||||
|
),
|
||||||
Self::ErrorArrayBounds(max, index, pos) => write!(
|
Self::ErrorArrayBounds(max, index, pos) => write!(
|
||||||
f,
|
f,
|
||||||
"Array index {} is out of bounds: only {} element{} in the array ({})",
|
"Array index {} is out of bounds: only {} elements in the array ({})",
|
||||||
index,
|
index, max, pos
|
||||||
max,
|
|
||||||
if *max > 1 { "s" } else { "" },
|
|
||||||
pos
|
|
||||||
),
|
),
|
||||||
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
|
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
|
||||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
||||||
}
|
}
|
||||||
Self::ErrorStringBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
|
Self::ErrorStringBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
|
||||||
|
Self::ErrorStringBounds(1, index, pos) => write!(
|
||||||
|
f,
|
||||||
|
"String index {} is out of bounds: only one character in the string ({})",
|
||||||
|
index, pos
|
||||||
|
),
|
||||||
Self::ErrorStringBounds(max, index, pos) => write!(
|
Self::ErrorStringBounds(max, index, pos) => write!(
|
||||||
f,
|
f,
|
||||||
"String index {} is out of bounds: only {} character{} in the string ({})",
|
"String index {} is out of bounds: only {} characters in the string ({})",
|
||||||
index,
|
index, max, pos
|
||||||
max,
|
|
||||||
if *max > 1 { "s" } else { "" },
|
|
||||||
pos
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||||
|
|
||||||
use crate::any::{Any, Dynamic};
|
use crate::any::{Any, Dynamic};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// A type containing information about current scope.
|
/// A type containing information about current scope.
|
||||||
|
@ -39,10 +39,9 @@ fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>(
|
engine.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
fn this() { true }
|
let this = true;
|
||||||
fn that() { 9/0 }
|
|
||||||
|
|
||||||
this() || that();
|
this || { throw; };
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
true
|
true
|
||||||
@ -51,10 +50,9 @@ fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>(
|
engine.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
fn this() { false }
|
let this = false;
|
||||||
fn that() { 9/0 }
|
|
||||||
|
|
||||||
this() && that();
|
this && { throw; };
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
false
|
false
|
||||||
@ -72,10 +70,9 @@ fn test_bool_op_no_short_circuit1() {
|
|||||||
engine
|
engine
|
||||||
.eval::<bool>(
|
.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
fn this() { false }
|
let this = true;
|
||||||
fn that() { 9/0 }
|
|
||||||
|
|
||||||
this() | that();
|
this | { throw; }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -92,10 +89,9 @@ fn test_bool_op_no_short_circuit2() {
|
|||||||
engine
|
engine
|
||||||
.eval::<bool>(
|
.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
fn this() { false }
|
let this = false;
|
||||||
fn that() { 9/0 }
|
|
||||||
|
|
||||||
this() & that();
|
this & { throw; }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_stdlib"))]
|
#![cfg(not(feature = "no_stdlib"))]
|
||||||
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#![cfg(not(feature = "no_function"))]
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -24,6 +24,10 @@ fn test_math() -> Result<(), EvalAltResult> {
|
|||||||
{
|
{
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
{
|
{
|
||||||
|
match engine.eval::<INT>("(-9223372036854775808).abs()") {
|
||||||
|
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
|
||||||
|
r => panic!("should return overflow error: {:?}", r),
|
||||||
|
}
|
||||||
match engine.eval::<INT>("9223372036854775807 + 1") {
|
match engine.eval::<INT>("9223372036854775807 + 1") {
|
||||||
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
|
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
|
||||||
r => panic!("should return overflow error: {:?}", r),
|
r => panic!("should return overflow error: {:?}", r),
|
||||||
|
@ -9,6 +9,7 @@ fn test_not() -> Result<(), EvalAltResult> {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert_eq!(engine.eval::<bool>("fn not(x) { !x } not(false)")?, true);
|
assert_eq!(engine.eval::<bool>("fn not(x) { !x } not(false)")?, true);
|
||||||
|
|
||||||
// TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true'
|
// TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true'
|
||||||
|
@ -5,7 +5,10 @@ fn test_unary_minus() -> Result<(), EvalAltResult> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5);
|
assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert_eq!(engine.eval::<INT>("fn neg(x) { -x } neg(5)")?, -5);
|
assert_eq!(engine.eval::<INT>("fn neg(x) { -x } neg(5)")?, -5);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("5 - -+++--+-5")?, 0);
|
assert_eq!(engine.eval::<INT>("5 - -+++--+-5")?, 0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
Reference in New Issue
Block a user