This commit is contained in:
Stephen Chung 2020-03-11 14:36:10 +08:00
commit fbb0808884
37 changed files with 949 additions and 536 deletions

View File

@ -18,6 +18,15 @@ include = [
num-traits = "*" num-traits = "*"
[features] [features]
default = []
debug_msgs = [] debug_msgs = []
no_stdlib = []
unchecked = [] unchecked = []
no_stdlib = []
no_index = []
no_float = []
only_i32 = []
only_i64 = []
[profile.release]
lto = "fat"
codegen-units = 1

View File

@ -38,11 +38,18 @@ 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 (using `println!`) related to function registrations and function 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. | | `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 in the standard library. 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_float` | Disable floating-point numbers and math if you don't need them. |
| `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.
Excluding unneeded functionalities can result in smaller, faster builds as well as less bugs due to a more restricted language.
Related Related
------- -------
@ -188,20 +195,28 @@ Values and types
The following primitive types are supported natively: The following primitive types are supported natively:
| Category | Types | | Category | Types |
| ------------------------------ | -------------------------------------- | | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Integer | `i32`, `u32`, `i64` _(default)_, `u64` | | **Integer** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`](#optional-features)),<br/>`u64`, `i64` _(default)_ |
| Floating-point | `f32`, `f64` _(default)_ | | **Floating-point** (disabled with [`no_float`](#optional-features)) | `f32`, `f64` _(default)_ |
| Character | `char` | | **Character** | `char` |
| Boolean | `bool` | | **Boolean** | `bool` |
| Array | `rhai::Array` | | **Array** (disabled with [`no_index`](#optional-features)) | `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`) |
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.
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`.
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.
Value conversions Value conversions
----------------- -----------------
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different; you cannot even add them together.
There is a `to_float` function to convert a supported number to an `f64`, and a `to_int` function to convert a supported number to `i64` and that's about it. For other conversions you can register your own conversion functions. There is a `to_float` function to convert a supported number to an `f64`, and a `to_int` function to convert a supported number to `i64` and that's about it. For other conversions you can register your own conversion functions.
There is also a `type_of` function to detect the type of a value. There is also a `type_of` function to detect the type of a value.
@ -606,7 +621,7 @@ let booly = !true;
Numeric functions Numeric functions
----------------- -----------------
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only: 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:
| Function | Description | | Function | Description |
| ---------- | ----------------------------------- | | ---------- | ----------------------------------- |
@ -617,7 +632,7 @@ The following standard functions (defined in the standard library but excluded i
Floating-point functions Floating-point functions
------------------------ ------------------------
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on `f64` only: The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `f64` only:
| Category | Functions | | Category | Functions |
| ---------------- | ------------------------------------------------------------ | | ---------------- | ------------------------------------------------------------ |
@ -645,7 +660,7 @@ let age = 42;
let record = full_name + ": age " + age; let record = full_name + ": age " + age;
record == "Bob C. Davis: age 42"; record == "Bob C. Davis: age 42";
// Strings can be indexed to get a character // Strings can be indexed to get a character (disabled with the 'no_index' feature)
let c = record[4]; let c = record[4];
c == 'C'; c == 'C';
@ -669,7 +684,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`) operate on strings: The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on strings:
| Function | Description | | Function | Description |
| ---------- | ------------------------------------------------------------------------ | | ---------- | ------------------------------------------------------------------------ |
@ -716,7 +731,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`) operate on arrays: The following functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on arrays:
| Function | Description | | Function | Description |
| ---------- | ------------------------------------------------------------------------------------- | | ---------- | ------------------------------------------------------------------------------------- |
@ -788,6 +803,8 @@ 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.
Comparison operators Comparison operators
-------------------- --------------------

View File

@ -104,8 +104,7 @@ impl<'e> Engine<'e> {
parse(&mut tokens.peekable(), self.optimize) parse(&mut tokens.peekable(), self.optimize)
} }
/// Compile a file into an AST. fn read_file(filename: &str) -> Result<String, EvalAltResult> {
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
let mut f = File::open(filename) let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?; .map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
@ -113,19 +112,18 @@ impl<'e> Engine<'e> {
f.read_to_string(&mut contents) f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err)) .map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.compile(&contents).map_err(EvalAltResult::ErrorParsing)) .map(|_| contents)
}
/// Compile a file into an AST.
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
Self::read_file(filename)
.and_then(|contents| self.compile(&contents).map_err(|err| err.into()))
} }
/// Evaluate a file. /// Evaluate a file.
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> { pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
let mut f = File::open(filename) Self::read_file(filename).and_then(|contents| self.eval::<T>(&contents))
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.eval::<T>(&contents))
} }
/// Evaluate a string. /// Evaluate a string.
@ -164,27 +162,36 @@ impl<'e> Engine<'e> {
retain_functions: bool, retain_functions: bool,
ast: &AST, ast: &AST,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
let AST(statements, functions) = ast; fn eval_ast_internal(
engine: &mut Engine,
scope: &mut Scope,
retain_functions: bool,
ast: &AST,
) -> Result<Dynamic, EvalAltResult> {
let AST(statements, functions) = ast;
functions.iter().for_each(|f| { functions.iter().for_each(|f| {
self.script_functions.insert( engine.script_functions.insert(
FnSpec { FnSpec {
name: f.name.clone().into(), name: f.name.clone().into(),
args: None, args: None,
}, },
Arc::new(FnIntExt::Int(f.clone())), Arc::new(FnIntExt::Int(f.clone())),
); );
}); });
let result = statements let result = statements
.iter() .iter()
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt)); .try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt));
if !retain_functions { if !retain_functions {
self.clear_functions(); engine.clear_functions();
}
result
} }
match result { match eval_ast_internal(self, scope, retain_functions, ast) {
Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| { Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(), self.map_type_name((*a).type_name()).to_string(),
@ -267,6 +274,8 @@ impl<'e> Engine<'e> {
/// ///
/// ```rust /// ```rust
/// # fn main() -> Result<(), rhai::EvalAltResult> { /// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # #[cfg(not(feature = "no_stdlib"))]
/// # {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
@ -276,6 +285,7 @@ impl<'e> Engine<'e> {
/// let result: i64 = engine.call_fn("add", &ast, (String::from("abc"), 123_i64))?; /// let result: i64 = engine.call_fn("add", &ast, (String::from("abc"), 123_i64))?;
/// ///
/// assert_eq!(result, 126); /// assert_eq!(result, 126);
/// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -285,45 +295,45 @@ impl<'e> Engine<'e> {
ast: &AST, ast: &AST,
args: A, args: A,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
fn call_fn_internal(
engine: &mut Engine,
name: &str,
ast: &AST,
args: FnCallArgs,
) -> Result<Dynamic, EvalAltResult> {
ast.1.iter().for_each(|f| {
engine.script_functions.insert(
FnSpec {
name: f.name.clone().into(),
args: None,
},
Arc::new(FnIntExt::Int(f.clone())),
);
});
let result = engine.call_fn_raw(name, args, None, Position::none());
engine.clear_functions();
result
}
let mut arg_values = args.into_vec(); let mut arg_values = args.into_vec();
self.call_fn_internal( call_fn_internal(
self,
name, name,
ast, ast,
arg_values.iter_mut().map(|v| v.as_mut()).collect(), arg_values.iter_mut().map(|v| v.as_mut()).collect(),
) )
} .and_then(|b| {
pub(crate) fn call_fn_internal<T: Any + Clone>(
&mut self,
name: &str,
ast: &AST,
args: FnCallArgs,
) -> Result<T, EvalAltResult> {
let pos = Default::default();
ast.1.iter().for_each(|f| {
self.script_functions.insert(
FnSpec {
name: f.name.clone().into(),
args: None,
},
Arc::new(FnIntExt::Int(f.clone())),
);
});
let result = self.call_fn_raw(name, args, None, pos).and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| { b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(), self.map_type_name((*a).type_name()).into(),
pos, Position::none(),
) )
}) })
}); })
self.clear_functions();
result
} }
/// Override default action of `print` (print to stdout using `println!`) /// Override default action of `print` (print to stdout using `println!`)

View File

@ -2,27 +2,45 @@
//! _standard library_ of utility functions. //! _standard library_ of utility functions.
use crate::any::Any; use crate::any::Any;
use crate::engine::{Array, Engine}; use crate::engine::Engine;
use crate::fn_register::RegisterFn; 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"))]
use crate::engine::Array;
#[cfg(not(feature = "no_float"))]
use crate::FLOAT;
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
i32, i64, ops::{BitAnd, BitOr, BitXor, Range},
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Sub},
u32,
}; };
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
use std::ops::{Shl, Shr}; use std::ops::{Shl, Shr};
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
use crate::{parser::Position, result::EvalAltResult, RegisterResultFn}; #[cfg(not(feature = "no_float"))]
use std::{i32, i64};
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
use std::convert::TryFrom; #[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"))] #[cfg(not(feature = "unchecked"))]
use num_traits::{ use {
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr,
CheckedSub,
},
std::convert::TryFrom,
}; };
macro_rules! reg_op { macro_rules! reg_op {
@ -102,6 +120,7 @@ macro_rules! reg_func2y {
} }
#[cfg(not(feature = "no_stdlib"))] #[cfg(not(feature = "no_stdlib"))]
#[cfg(not(feature = "no_index"))]
macro_rules! reg_func3 { macro_rules! reg_func3 {
($self:expr, $x:expr, $op:expr, $v:ty, $w:ty, $r:ty, $( $y:ty ),*) => ( ($self:expr, $x:expr, $op:expr, $v:ty, $w:ty, $r:ty, $( $y:ty ),*) => (
$( $(
@ -184,21 +203,27 @@ impl Engine<'_> {
}) })
} }
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn add_u<T: Add>(x: T, y: T) -> <T as Add>::Output { fn add_u<T: Add>(x: T, y: T) -> <T as Add>::Output {
x + y x + y
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn sub_u<T: Sub>(x: T, y: T) -> <T as Sub>::Output { fn sub_u<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
x - y x - y
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn mul_u<T: Mul>(x: T, y: T) -> <T as Mul>::Output { fn mul_u<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
x * y x * y
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn div_u<T: Div>(x: T, y: T) -> <T as Div>::Output { fn div_u<T: Div>(x: T, y: T) -> <T as Div>::Output {
x / y x / y
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn neg_u<T: Neg>(x: T) -> <T as Neg>::Output { fn neg_u<T: Neg>(x: T) -> <T as Neg>::Output {
-x -x
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn abs_u<T: Neg + PartialOrd + From<i8>>(x: T) -> T fn abs_u<T: Neg + PartialOrd + From<i8>>(x: T) -> T
where where
<T as Neg>::Output: Into<T>, <T as Neg>::Output: Into<T>,
@ -246,7 +271,7 @@ impl Engine<'_> {
x ^ y x ^ y
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn shl<T: Display + CheckedShl>(x: T, y: i64) -> Result<T, EvalAltResult> { fn shl<T: Display + CheckedShl>(x: T, y: INT) -> Result<T, EvalAltResult> {
if y < 0 { if y < 0 {
return Err(EvalAltResult::ErrorArithmetic( return Err(EvalAltResult::ErrorArithmetic(
format!("Left-shift by a negative number: {} << {}", x, y), format!("Left-shift by a negative number: {} << {}", x, y),
@ -262,7 +287,7 @@ impl Engine<'_> {
}) })
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn shr<T: Display + CheckedShr>(x: T, y: i64) -> Result<T, EvalAltResult> { fn shr<T: Display + CheckedShr>(x: T, y: INT) -> Result<T, EvalAltResult> {
if y < 0 { if y < 0 {
return Err(EvalAltResult::ErrorArithmetic( return Err(EvalAltResult::ErrorArithmetic(
format!("Right-shift by a negative number: {} >> {}", x, y), format!("Right-shift by a negative number: {} >> {}", x, y),
@ -294,35 +319,63 @@ impl Engine<'_> {
) )
}) })
} }
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output { fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output {
x % y x % y
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn pow_i64_i64_u(x: i64, y: i64) -> Result<i64, EvalAltResult> { fn pow_i_i_u(x: INT, y: INT) -> Result<INT, EvalAltResult> {
if y > (u32::MAX as i64) { #[cfg(not(feature = "only_i32"))]
return Err(EvalAltResult::ErrorArithmetic( {
format!("Power overflow: {} ~ {}", x, y), if y > (u32::MAX as INT) {
Position::none(), Err(EvalAltResult::ErrorArithmetic(
)); format!("Power overflow: {} ~ {}", x, y),
Position::none(),
))
} else if y < 0 {
Err(EvalAltResult::ErrorArithmetic(
format!("Power underflow: {} ~ {}", x, y),
Position::none(),
))
} else {
x.checked_pow(y as u32).ok_or_else(|| {
EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y),
Position::none(),
)
})
}
} }
x.checked_pow(y as u32).ok_or_else(|| { #[cfg(feature = "only_i32")]
EvalAltResult::ErrorArithmetic( {
format!("Power overflow: {} ~ {}", x, y), if y < 0 {
Position::none(), Err(EvalAltResult::ErrorArithmetic(
) format!("Power underflow: {} ~ {}", x, y),
}) Position::none(),
))
} else {
x.checked_pow(y as u32).ok_or_else(|| {
EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y),
Position::none(),
)
})
}
}
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
fn pow_i64_i64(x: i64, y: i64) -> i64 { fn pow_i_i(x: INT, y: INT) -> INT {
x.pow(y as u32) x.pow(y as u32)
} }
fn pow_f64_f64(x: f64, y: f64) -> f64 { #[cfg(not(feature = "no_float"))]
fn pow_f_f(x: FLOAT, y: FLOAT) -> FLOAT {
x.powf(y) x.powf(y)
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn pow_f64_i64_u(x: f64, y: i64) -> Result<f64, EvalAltResult> { #[cfg(not(feature = "no_float"))]
if y > (i32::MAX as i64) { fn pow_f_i_u(x: FLOAT, y: INT) -> Result<FLOAT, EvalAltResult> {
if y > (i32::MAX as INT) {
return Err(EvalAltResult::ErrorArithmetic( return Err(EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y), format!("Power overflow: {} ~ {}", x, y),
Position::none(), Position::none(),
@ -332,257 +385,394 @@ impl Engine<'_> {
Ok(x.powi(y as i32)) Ok(x.powi(y as i32))
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
fn pow_f64_i64(x: f64, y: i64) -> f64 { #[cfg(not(feature = "no_float"))]
fn pow_f_i(x: FLOAT, y: INT) -> FLOAT {
x.powi(y as i32) x.powi(y as i32)
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
reg_op_result!(self, "+", add, i8, u8, i16, u16, i32, i64, u32, u64); reg_op_result!(self, "+", add, INT);
reg_op_result!(self, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64); reg_op_result!(self, "-", sub, INT);
reg_op_result!(self, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64); reg_op_result!(self, "*", mul, INT);
reg_op_result!(self, "/", div, i8, u8, i16, u16, i32, i64, u32, u64); reg_op_result!(self, "/", div, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_op_result!(self, "+", add, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result!(self, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result!(self, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result!(self, "/", div, i8, u8, i16, u16, i32, i64, u32, u64);
}
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
{ {
reg_op!(self, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64); reg_op!(self, "+", add_u, INT);
reg_op!(self, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64); reg_op!(self, "-", sub_u, INT);
reg_op!(self, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64); reg_op!(self, "*", mul_u, INT);
reg_op!(self, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64); reg_op!(self, "/", div_u, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_op!(self, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64);
}
} }
reg_op!(self, "+", add_u, f32, f64); #[cfg(not(feature = "no_float"))]
reg_op!(self, "-", sub_u, f32, f64); {
reg_op!(self, "*", mul_u, f32, f64); reg_op!(self, "+", add_u, f32, f64);
reg_op!(self, "/", div_u, f32, f64); reg_op!(self, "-", sub_u, f32, f64);
reg_op!(self, "*", mul_u, f32, f64);
reg_op!(self, "/", div_u, f32, f64);
}
reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); reg_cmp!(self, "<", lt, INT, String, char);
reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); reg_cmp!(self, "<=", lte, INT, String, char);
reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); reg_cmp!(self, ">", gt, INT, String, char);
reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); reg_cmp!(self, ">=", gte, INT, String, char);
reg_cmp!( reg_cmp!(self, "==", eq, INT, String, char, bool);
self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, bool, f32, f64, String, char reg_cmp!(self, "!=", ne, INT, String, char, bool);
);
reg_cmp!( #[cfg(not(feature = "only_i32"))]
self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, bool, f32, f64, String, char #[cfg(not(feature = "only_i64"))]
); {
reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64);
reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64);
reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64);
reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64);
reg_cmp!(self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64);
reg_cmp!(self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64);
}
#[cfg(not(feature = "no_float"))]
{
reg_cmp!(self, "<", lt, f32, f64);
reg_cmp!(self, "<=", lte, f32, f64);
reg_cmp!(self, ">", gt, f32, f64);
reg_cmp!(self, ">=", gte, f32, f64);
reg_cmp!(self, "==", eq, f32, f64);
reg_cmp!(self, "!=", ne, f32, f64);
}
//reg_op!(self, "||", or, bool); //reg_op!(self, "||", or, bool);
//reg_op!(self, "&&", and, bool); //reg_op!(self, "&&", and, bool);
reg_op!(self, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, "|", or, bool); reg_op!(self, "|", or, bool);
reg_op!(self, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, "&", and, bool); reg_op!(self, "&", and, bool);
reg_op!(self, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64);
#[cfg(not(feature = "unchecked"))] reg_op!(self, "|", binary_or, INT);
reg_op!(self, "&", binary_and, INT);
reg_op!(self, "^", binary_xor, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{ {
reg_op_result1!(self, "<<", shl, i64, i8, u8, i16, u16, i32, i64, u32, u64); reg_op!(self, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result1!(self, ">>", shr, i64, i8, u8, i16, u16); reg_op!(self, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result1!(self, ">>", shr, i64, i32, i64, u32, u64); reg_op!(self, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result!(self, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64);
}
#[cfg(feature = "unchecked")]
{
reg_op!(self, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, ">>", shr_u, i64, i8, u8, i16, u16);
reg_op!(self, ">>", shr_u, i64, i32, i64, u32, u64);
reg_op!(self, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64);
}
reg_op!(self, "%", modulo_u, f32, f64);
self.register_fn("~", pow_f64_f64);
#[cfg(not(feature = "unchecked"))]
{
self.register_result_fn("~", pow_i64_i64_u);
self.register_result_fn("~", pow_f64_i64_u);
}
#[cfg(feature = "unchecked")]
{
self.register_fn("~", pow_i64_i64);
self.register_fn("~", pow_f64_i64);
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
reg_un_result!(self, "-", neg, i8, i16, i32, i64); reg_op_result1!(self, "<<", shl, INT, INT);
reg_un_result!(self, "abs", abs, i8, i16, i32, i64); reg_op_result1!(self, ">>", shr, INT, INT);
reg_op_result!(self, "%", modulo, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_op_result1!(self, "<<", shl, i64, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result1!(self, ">>", shr, i64, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op_result!(self, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64);
}
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
{ {
reg_un!(self, "-", neg_u, i8, i16, i32, i64); reg_op!(self, "<<", shl_u, INT, INT);
reg_un!(self, "abs", abs_u, i8, i16, i32, i64); reg_op!(self, ">>", shr_u, INT, INT);
reg_op!(self, "%", modulo_u, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_op!(self, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64);
reg_op!(self, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64);
}
}
#[cfg(not(feature = "no_float"))]
{
reg_op!(self, "%", modulo_u, f32, f64);
self.register_fn("~", pow_f_f);
}
#[cfg(not(feature = "unchecked"))]
{
self.register_result_fn("~", pow_i_i_u);
#[cfg(not(feature = "no_float"))]
self.register_result_fn("~", pow_f_i_u);
}
#[cfg(feature = "unchecked")]
{
self.register_fn("~", pow_i_i);
#[cfg(not(feature = "no_float"))]
self.register_fn("~", pow_f_i);
}
#[cfg(not(feature = "unchecked"))]
{
reg_un_result!(self, "-", neg, INT);
reg_un_result!(self, "abs", abs, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_un_result!(self, "-", neg, i8, i16, i32, i64);
reg_un_result!(self, "abs", abs, i8, i16, i32, i64);
}
}
#[cfg(feature = "unchecked")]
{
reg_un!(self, "-", neg_u, INT);
reg_un!(self, "abs", abs_u, INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_un!(self, "-", neg_u, i8, i16, i32, i64);
reg_un!(self, "abs", abs_u, i8, i16, i32, i64);
}
}
#[cfg(not(feature = "no_float"))]
{
reg_un!(self, "-", neg_u, f32, f64);
reg_un!(self, "abs", abs_u, f32, f64);
} }
reg_un!(self, "-", neg_u, f32, f64);
reg_un!(self, "abs", abs_u, f32, f64);
reg_un!(self, "!", not, bool); reg_un!(self, "!", not, bool);
self.register_fn("+", |x: String, y: String| x + &y); // String + String self.register_fn("+", |x: String, y: String| x + &y); // String + String
self.register_fn("==", |_: (), _: ()| true); // () == () self.register_fn("==", |_: (), _: ()| true); // () == ()
// Register print and debug // Register print and debug
fn print_debug<T: Debug>(x: T) -> String { fn debug<T: Debug>(x: T) -> String {
format!("{:?}", x) format!("{:?}", x)
} }
fn print<T: Display>(x: T) -> String { fn print<T: Display>(x: T) -> String {
format!("{}", x) format!("{}", x)
} }
reg_func1!(self, "print", print, String, i8, u8, i16, u16); reg_func1!(self, "print", print, String, INT, bool, char, String);
reg_func1!(self, "print", print, String, i32, i64, u32, u64);
reg_func1!(self, "print", print, String, f32, f64, bool, char, String);
reg_func1!(self, "print", print_debug, String, Array);
self.register_fn("print", || "".to_string()); self.register_fn("print", || "".to_string());
self.register_fn("print", |_: ()| "".to_string()); self.register_fn("print", |_: ()| "".to_string());
reg_func1!(self, "debug", debug, String, INT, bool, char, String, ());
reg_func1!(self, "debug", print_debug, String, i8, u8, i16, u16); #[cfg(not(feature = "only_i32"))]
reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64); #[cfg(not(feature = "only_i64"))]
reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char); {
reg_func1!(self, "debug", print_debug, String, String, Array, ()); reg_func1!(self, "print", print, String, i8, u8, i16, u16);
reg_func1!(self, "print", print, String, i32, i64, u32, u64);
reg_func1!(self, "debug", debug, String, i8, u8, i16, u16);
reg_func1!(self, "debug", debug, String, i32, i64, u32, u64);
}
// Register array iterator #[cfg(not(feature = "no_float"))]
self.register_iterator::<Array, _>(|a| { {
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter()) reg_func1!(self, "print", print, String, f32, f64);
}); reg_func1!(self, "debug", debug, String, f32, f64);
}
#[cfg(not(feature = "no_index"))]
{
reg_func1!(self, "print", debug, String, Array);
reg_func1!(self, "debug", debug, String, Array);
// Register array iterator
self.register_iterator::<Array, _>(|a| {
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
});
}
// Register range function // Register range function
self.register_iterator::<Range<i64>, _>(|a| { self.register_iterator::<Range<INT>, _>(|a| {
Box::new( Box::new(
a.downcast_ref::<Range<i64>>() a.downcast_ref::<Range<INT>>()
.unwrap() .unwrap()
.clone() .clone()
.map(|n| n.into_dynamic()), .map(|n| n.into_dynamic()),
) )
}); });
self.register_fn("range", |i1: i64, i2: i64| (i1..i2)); self.register_fn("range", |i1: INT, i2: INT| (i1..i2));
} }
/// Register the built-in library. /// Register the built-in library.
#[cfg(not(feature = "no_stdlib"))] #[cfg(not(feature = "no_stdlib"))]
pub(crate) fn register_stdlib(&mut self) { pub(crate) fn register_stdlib(&mut self) {
#[cfg(not(feature = "no_index"))]
use crate::fn_register::RegisterDynamicFn; use crate::fn_register::RegisterDynamicFn;
// Advanced math functions #[cfg(not(feature = "no_float"))]
self.register_fn("sin", |x: f64| x.to_radians().sin());
self.register_fn("cos", |x: f64| x.to_radians().cos());
self.register_fn("tan", |x: f64| x.to_radians().tan());
self.register_fn("sinh", |x: f64| x.to_radians().sinh());
self.register_fn("cosh", |x: f64| x.to_radians().cosh());
self.register_fn("tanh", |x: f64| x.to_radians().tanh());
self.register_fn("asin", |x: f64| x.asin().to_degrees());
self.register_fn("acos", |x: f64| x.acos().to_degrees());
self.register_fn("atan", |x: f64| x.atan().to_degrees());
self.register_fn("asinh", |x: f64| x.asinh().to_degrees());
self.register_fn("acosh", |x: f64| x.acosh().to_degrees());
self.register_fn("atanh", |x: f64| x.atanh().to_degrees());
self.register_fn("sqrt", |x: f64| x.sqrt());
self.register_fn("exp", |x: f64| x.exp());
self.register_fn("ln", |x: f64| x.ln());
self.register_fn("log", |x: f64, base: f64| x.log(base));
self.register_fn("log10", |x: f64| x.log10());
self.register_fn("floor", |x: f64| x.floor());
self.register_fn("ceiling", |x: f64| x.ceil());
self.register_fn("round", |x: f64| x.ceil());
self.register_fn("int", |x: f64| x.trunc());
self.register_fn("fraction", |x: f64| x.fract());
self.register_fn("is_nan", |x: f64| x.is_nan());
self.register_fn("is_finite", |x: f64| x.is_finite());
self.register_fn("is_infinite", |x: f64| x.is_infinite());
// Register conversion functions
self.register_fn("to_float", |x: i8| x as f64);
self.register_fn("to_float", |x: u8| x as f64);
self.register_fn("to_float", |x: i16| x as f64);
self.register_fn("to_float", |x: u16| x as f64);
self.register_fn("to_float", |x: i32| x as f64);
self.register_fn("to_float", |x: u32| x as f64);
self.register_fn("to_float", |x: i64| x as f64);
self.register_fn("to_float", |x: u64| x as f64);
self.register_fn("to_float", |x: f32| x as f64);
self.register_fn("to_int", |x: i8| x as i64);
self.register_fn("to_int", |x: u8| x as i64);
self.register_fn("to_int", |x: i16| x as i64);
self.register_fn("to_int", |x: u16| x as i64);
self.register_fn("to_int", |x: i32| x as i64);
self.register_fn("to_int", |x: u32| x as i64);
self.register_fn("to_int", |x: u64| x as i64);
self.register_fn("to_int", |ch: char| ch as i64);
#[cfg(not(feature = "unchecked"))]
{ {
self.register_result_fn("to_int", |x: f32| { // Advanced math functions
if x > (i64::MAX as f32) { self.register_fn("sin", |x: FLOAT| x.to_radians().sin());
return Err(EvalAltResult::ErrorArithmetic( self.register_fn("cos", |x: FLOAT| x.to_radians().cos());
format!("Integer overflow: to_int({})", x), self.register_fn("tan", |x: FLOAT| x.to_radians().tan());
Position::none(), self.register_fn("sinh", |x: FLOAT| x.to_radians().sinh());
)); self.register_fn("cosh", |x: FLOAT| x.to_radians().cosh());
} self.register_fn("tanh", |x: FLOAT| x.to_radians().tanh());
self.register_fn("asin", |x: FLOAT| x.asin().to_degrees());
self.register_fn("acos", |x: FLOAT| x.acos().to_degrees());
self.register_fn("atan", |x: FLOAT| x.atan().to_degrees());
self.register_fn("asinh", |x: FLOAT| x.asinh().to_degrees());
self.register_fn("acosh", |x: FLOAT| x.acosh().to_degrees());
self.register_fn("atanh", |x: FLOAT| x.atanh().to_degrees());
self.register_fn("sqrt", |x: FLOAT| x.sqrt());
self.register_fn("exp", |x: FLOAT| x.exp());
self.register_fn("ln", |x: FLOAT| x.ln());
self.register_fn("log", |x: FLOAT, base: FLOAT| x.log(base));
self.register_fn("log10", |x: FLOAT| x.log10());
self.register_fn("floor", |x: FLOAT| x.floor());
self.register_fn("ceiling", |x: FLOAT| x.ceil());
self.register_fn("round", |x: FLOAT| x.ceil());
self.register_fn("int", |x: FLOAT| x.trunc());
self.register_fn("fraction", |x: FLOAT| x.fract());
self.register_fn("is_nan", |x: FLOAT| x.is_nan());
self.register_fn("is_finite", |x: FLOAT| x.is_finite());
self.register_fn("is_infinite", |x: FLOAT| x.is_infinite());
Ok(x.trunc() as i64) // Register conversion functions
}); self.register_fn("to_float", |x: INT| x as FLOAT);
self.register_result_fn("to_int", |x: f64| { self.register_fn("to_float", |x: f32| x as FLOAT);
if x > (i64::MAX as f64) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x),
Position::none(),
));
}
Ok(x.trunc() as i64) #[cfg(not(feature = "only_i32"))]
}); #[cfg(not(feature = "only_i64"))]
} {
self.register_fn("to_float", |x: i8| x as FLOAT);
#[cfg(feature = "unchecked")] self.register_fn("to_float", |x: u8| x as FLOAT);
{ self.register_fn("to_float", |x: i16| x as FLOAT);
self.register_fn("to_int", |x: f32| x as i64); self.register_fn("to_float", |x: u16| x as FLOAT);
self.register_fn("to_int", |x: f64| x as i64); self.register_fn("to_float", |x: i32| x as FLOAT);
} self.register_fn("to_float", |x: u32| x as FLOAT);
self.register_fn("to_float", |x: i64| x as FLOAT);
// Register array utility functions self.register_fn("to_float", |x: u64| x as FLOAT);
fn push<T: Any>(list: &mut Array, item: T) {
list.push(Box::new(item));
}
fn pad<T: Any + Clone>(list: &mut Array, len: i64, item: T) {
if len >= 0 {
while list.len() < len as usize {
push(list, item.clone());
}
} }
} }
reg_func2x!(self, "push", push, &mut Array, (), i8, u8, i16, u16); self.register_fn("to_int", |ch: char| ch as INT);
reg_func2x!(self, "push", push, &mut Array, (), i32, i64, u32, u64);
reg_func2x!(self, "push", push, &mut Array, (), f32, f64, bool, char);
reg_func2x!(self, "push", push, &mut Array, (), String, Array, ());
reg_func3!(self, "pad", pad, &mut Array, i64, (), i8, u8, i16, u16);
reg_func3!(self, "pad", pad, &mut Array, i64, (), i32, u32, f32);
reg_func3!(self, "pad", pad, &mut Array, i64, (), i64, u64, f64);
reg_func3!(self, "pad", pad, &mut Array, i64, (), bool, char);
reg_func3!(self, "pad", pad, &mut Array, i64, (), String, Array, ());
self.register_dynamic_fn("pop", |list: &mut Array| { #[cfg(not(feature = "only_i32"))]
list.pop().unwrap_or_else(|| ().into_dynamic()) #[cfg(not(feature = "only_i64"))]
}); {
self.register_dynamic_fn("shift", |list: &mut Array| match list.len() { self.register_fn("to_int", |x: i8| x as INT);
0 => ().into_dynamic(), self.register_fn("to_int", |x: u8| x as INT);
_ => list.remove(0), self.register_fn("to_int", |x: i16| x as INT);
}); self.register_fn("to_int", |x: u16| x as INT);
self.register_fn("len", |list: &mut Array| list.len() as i64); }
self.register_fn("clear", |list: &mut Array| list.clear());
self.register_fn("truncate", |list: &mut Array, len: i64| { #[cfg(not(feature = "only_i32"))]
if len >= 0 { {
list.truncate(len as usize); self.register_fn("to_int", |x: i32| x as INT);
self.register_fn("to_int", |x: u64| x as INT);
#[cfg(feature = "only_i64")]
self.register_fn("to_int", |x: u32| x as INT);
}
#[cfg(not(feature = "no_float"))]
{
#[cfg(not(feature = "unchecked"))]
{
self.register_result_fn("to_int", |x: f32| {
if x > (i64::MAX as f32) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x),
Position::none(),
));
}
Ok(x.trunc() as INT)
});
self.register_result_fn("to_int", |x: FLOAT| {
if x > (i64::MAX as FLOAT) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x),
Position::none(),
));
}
Ok(x.trunc() as INT)
});
} }
});
#[cfg(feature = "unchecked")]
{
self.register_fn("to_int", |x: f32| x as INT);
self.register_fn("to_int", |x: f64| x as INT);
}
}
#[cfg(not(feature = "no_index"))]
{
// Register array utility functions
fn push<T: Any>(list: &mut Array, item: T) {
list.push(Box::new(item));
}
fn pad<T: Any + Clone>(list: &mut Array, len: INT, item: T) {
if len >= 0 {
while list.len() < len as usize {
push(list, item.clone());
}
}
}
reg_func2x!(self, "push", push, &mut Array, (), INT, bool, char);
reg_func2x!(self, "push", push, &mut Array, (), String, Array, ());
reg_func3!(self, "pad", pad, &mut Array, INT, (), INT, bool, char);
reg_func3!(self, "pad", pad, &mut Array, INT, (), String, Array, ());
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_func2x!(self, "push", push, &mut Array, (), i8, u8, i16, u16);
reg_func2x!(self, "push", push, &mut Array, (), i32, i64, u32, u64);
reg_func3!(self, "pad", pad, &mut Array, INT, (), i8, u8, i16, u16);
reg_func3!(self, "pad", pad, &mut Array, INT, (), i32, u32, i64, u64);
}
#[cfg(not(feature = "no_float"))]
{
reg_func2x!(self, "push", push, &mut Array, (), f32, f64);
reg_func3!(self, "pad", pad, &mut Array, INT, (), f32, f64);
}
self.register_dynamic_fn("pop", |list: &mut Array| {
list.pop().unwrap_or_else(|| ().into_dynamic())
});
self.register_dynamic_fn("shift", |list: &mut Array| match list.len() {
0 => ().into_dynamic(),
_ => list.remove(0),
});
self.register_fn("len", |list: &mut Array| list.len() as INT);
self.register_fn("clear", |list: &mut Array| list.clear());
self.register_fn("truncate", |list: &mut Array, len: INT| {
if len >= 0 {
list.truncate(len as usize);
}
});
}
// Register string concatenate functions // Register string concatenate functions
fn prepend<T: Display>(x: T, y: String) -> String { fn prepend<T: Display>(x: T, y: String) -> String {
@ -592,28 +782,39 @@ impl Engine<'_> {
format!("{}{}", x, y) format!("{}{}", x, y)
} }
reg_func2x!( reg_func2x!(self, "+", append, String, String, INT, bool, char);
self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
bool, char
);
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
self.register_fn("+", |x: String, _: ()| format!("{}", x)); self.register_fn("+", |x: String, _: ()| format!("{}", x));
reg_func2y!( reg_func2y!(self, "+", prepend, String, String, INT, bool, char);
self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
bool, char
);
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
self.register_fn("+", |_: (), y: String| format!("{}", y)); self.register_fn("+", |_: (), y: String| format!("{}", y));
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_func2x!(self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64);
reg_func2y!(self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64);
}
#[cfg(not(feature = "no_float"))]
{
reg_func2x!(self, "+", append, String, String, f32, f64);
reg_func2y!(self, "+", prepend, String, String, f32, f64);
}
#[cfg(not(feature = "no_index"))]
{
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
}
// Register string utility functions // Register string utility functions
self.register_fn("len", |s: &mut String| s.chars().count() as i64); self.register_fn("len", |s: &mut String| s.chars().count() as INT);
self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch)); self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch));
self.register_fn("contains", |s: &mut String, find: String| s.contains(&find)); self.register_fn("contains", |s: &mut String, find: String| s.contains(&find));
self.register_fn("clear", |s: &mut String| s.clear()); self.register_fn("clear", |s: &mut String| s.clear());
self.register_fn("append", |s: &mut String, ch: char| s.push(ch)); self.register_fn("append", |s: &mut String, ch: char| s.push(ch));
self.register_fn("append", |s: &mut String, add: String| s.push_str(&add)); self.register_fn("append", |s: &mut String, add: String| s.push_str(&add));
self.register_fn("truncate", |s: &mut String, len: i64| { self.register_fn("truncate", |s: &mut String, len: INT| {
if len >= 0 { if len >= 0 {
let chars: Vec<_> = s.chars().take(len as usize).collect(); let chars: Vec<_> = s.chars().take(len as usize).collect();
s.clear(); s.clear();
@ -622,7 +823,7 @@ impl Engine<'_> {
s.clear(); s.clear();
} }
}); });
self.register_fn("pad", |s: &mut String, len: i64, ch: char| { self.register_fn("pad", |s: &mut String, len: INT, ch: char| {
for _ in 0..s.chars().count() - len as usize { for _ in 0..s.chars().count() - len as usize {
s.push(ch); s.push(ch);
} }

View File

@ -4,6 +4,10 @@ use crate::any::{Any, AnyExt, Dynamic, Variant};
use crate::parser::{Expr, FnDef, Position, Stmt}; use crate::parser::{Expr, FnDef, Position, Stmt};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
#[cfg(not(feature = "no_index"))]
use crate::INT;
use std::{ use std::{
any::{type_name, TypeId}, any::{type_name, TypeId},
borrow::Cow, borrow::Cow,
@ -14,6 +18,7 @@ use std::{
}; };
/// An dynamic array of `Dynamic` values. /// An dynamic array of `Dynamic` values.
#[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>; pub type Array = Vec<Dynamic>;
pub type FnCallArgs<'a> = Vec<&'a mut Variant>; pub type FnCallArgs<'a> = Vec<&'a mut Variant>;
@ -29,6 +34,7 @@ pub(crate) const FUNC_GETTER: &'static str = "get$";
pub(crate) const FUNC_SETTER: &'static str = "set$"; pub(crate) const FUNC_SETTER: &'static str = "set$";
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg(not(feature = "no_index"))]
enum IndexSourceType { enum IndexSourceType {
Array, Array,
String, String,
@ -82,8 +88,9 @@ impl Engine<'_> {
// User-friendly names for built-in types // User-friendly names for built-in types
let type_names = [ let type_names = [
(type_name::<String>(), "string"), (type_name::<String>(), "string"),
(type_name::<Array>(), "array"),
(type_name::<Dynamic>(), "dynamic"), (type_name::<Dynamic>(), "dynamic"),
#[cfg(not(feature = "no_index"))]
(type_name::<Array>(), "array"),
] ]
.iter() .iter()
.map(|(k, v)| (k.to_string(), v.to_string())) .map(|(k, v)| (k.to_string(), v.to_string()))
@ -251,7 +258,7 @@ impl Engine<'_> {
match dot_rhs { match dot_rhs {
// xxx.fn_name(args) // xxx.fn_name(args)
Expr::FunctionCall(fn_name, args, def_val, pos) => { Expr::FunctionCall(fn_name, args, def_val, pos) => {
let mut args: Array = args let mut args = args
.iter() .iter()
.map(|arg| self.eval_expr(scope, arg)) .map(|arg| self.eval_expr(scope, arg))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
@ -271,6 +278,7 @@ impl Engine<'_> {
} }
// xxx.idx_lhs[idx_expr] // xxx.idx_lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (expr, _) = match idx_lhs.as_ref() { let (expr, _) = match idx_lhs.as_ref() {
// xxx.id[idx_expr] // xxx.id[idx_expr]
@ -309,6 +317,7 @@ impl Engine<'_> {
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), rhs)) .and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), rhs))
} }
// xxx.idx_lhs[idx_expr].rhs // xxx.idx_lhs[idx_expr].rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (expr, _) = match idx_lhs.as_ref() { let (expr, _) = match idx_lhs.as_ref() {
// xxx.id[idx_expr].rhs // xxx.id[idx_expr].rhs
@ -371,6 +380,7 @@ impl Engine<'_> {
} }
// idx_lhs[idx_expr].??? // idx_lhs[idx_expr].???
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, mut target) = let (src_type, src, idx, mut target) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?; self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?;
@ -413,23 +423,25 @@ impl Engine<'_> {
.and_then(move |(idx, _, val)| map(val).map(|v| (idx, v))) .and_then(move |(idx, _, val)| map(val).map(|v| (idx, v)))
} }
/// Evaluate the value of an index (must evaluate to i64) /// Evaluate the value of an index (must evaluate to INT)
#[cfg(not(feature = "no_index"))]
fn eval_index_value( fn eval_index_value(
&mut self, &mut self,
scope: &mut Scope, scope: &mut Scope,
idx_expr: &Expr, idx_expr: &Expr,
) -> Result<i64, EvalAltResult> { ) -> Result<INT, EvalAltResult> {
self.eval_expr(scope, idx_expr)? self.eval_expr(scope, idx_expr)?
.downcast::<i64>() .downcast::<INT>()
.map(|v| *v) .map(|v| *v)
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position())) .map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))
} }
/// Get the value at the indexed position of a base type /// Get the value at the indexed position of a base type
#[cfg(not(feature = "no_index"))]
fn get_indexed_value( fn get_indexed_value(
&self, &self,
val: Dynamic, val: Dynamic,
idx: i64, idx: INT,
val_pos: Position, val_pos: Position,
idx_pos: Position, idx_pos: Position,
) -> Result<(Dynamic, IndexSourceType), EvalAltResult> { ) -> Result<(Dynamic, IndexSourceType), EvalAltResult> {
@ -473,6 +485,7 @@ impl Engine<'_> {
} }
/// Evaluate an index expression /// Evaluate an index expression
#[cfg(not(feature = "no_index"))]
fn eval_index_expr<'a>( fn eval_index_expr<'a>(
&mut self, &mut self,
scope: &mut Scope, scope: &mut Scope,
@ -505,6 +518,7 @@ impl Engine<'_> {
} }
/// Replace a character at an index position in a mutable string /// Replace a character at an index position in a mutable string
#[cfg(not(feature = "no_index"))]
fn str_replace_char(s: &mut String, idx: usize, new_ch: char) { fn str_replace_char(s: &mut String, idx: usize, new_ch: char) {
let mut chars: Vec<char> = s.chars().collect(); let mut chars: Vec<char> = s.chars().collect();
let ch = *chars.get(idx).expect("string index out of bounds"); let ch = *chars.get(idx).expect("string index out of bounds");
@ -518,6 +532,7 @@ impl Engine<'_> {
} }
/// Update the value at an index position in a variable inside the scope /// Update the value at an index position in a variable inside the scope
#[cfg(not(feature = "no_index"))]
fn update_indexed_var_in_scope( fn update_indexed_var_in_scope(
src_type: IndexSourceType, src_type: IndexSourceType,
scope: &mut Scope, scope: &mut Scope,
@ -550,6 +565,7 @@ impl Engine<'_> {
} }
/// Update the value at an index position /// Update the value at an index position
#[cfg(not(feature = "no_index"))]
fn update_indexed_value( fn update_indexed_value(
mut target: Dynamic, mut target: Dynamic,
idx: usize, idx: usize,
@ -593,6 +609,7 @@ impl Engine<'_> {
// xxx.lhs[idx_expr] // xxx.lhs[idx_expr]
// TODO - Allow chaining of indexing! // TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() { Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() {
// xxx.id[idx_expr] // xxx.id[idx_expr]
Expr::Identifier(id, pos) => { Expr::Identifier(id, pos) => {
@ -636,6 +653,7 @@ impl Engine<'_> {
// xxx.lhs[idx_expr].rhs // xxx.lhs[idx_expr].rhs
// TODO - Allow chaining of indexing! // TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() { Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() {
// xxx.id[idx_expr].rhs // xxx.id[idx_expr].rhs
Expr::Identifier(id, pos) => { Expr::Identifier(id, pos) => {
@ -720,6 +738,7 @@ impl Engine<'_> {
// lhs[idx_expr].??? // lhs[idx_expr].???
// TODO - Allow chaining of indexing! // TODO - Allow chaining of indexing!
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => { Expr::Index(lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, mut target) = let (src_type, src, idx, mut target) =
self.eval_index_expr(scope, lhs, idx_expr, *idx_pos)?; self.eval_index_expr(scope, lhs, idx_expr, *idx_pos)?;
@ -753,8 +772,10 @@ impl Engine<'_> {
/// Evaluate an expression /// Evaluate an expression
fn eval_expr(&mut self, scope: &mut Scope, expr: &Expr) -> Result<Dynamic, EvalAltResult> { fn eval_expr(&mut self, scope: &mut Scope, expr: &Expr) -> Result<Dynamic, EvalAltResult> {
match expr { match expr {
Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()), #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()), Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()),
Expr::StringConstant(s, _) => Ok(s.into_dynamic()), Expr::StringConstant(s, _) => Ok(s.into_dynamic()),
Expr::CharConstant(c, _) => Ok(c.into_dynamic()), Expr::CharConstant(c, _) => Ok(c.into_dynamic()),
Expr::Identifier(id, pos) => { Expr::Identifier(id, pos) => {
@ -762,6 +783,7 @@ impl Engine<'_> {
} }
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => self Expr::Index(lhs, idx_expr, idx_pos) => self
.eval_index_expr(scope, lhs, idx_expr, *idx_pos) .eval_index_expr(scope, lhs, idx_expr, *idx_pos)
.map(|(_, _, _, x)| x), .map(|(_, _, _, x)| x),
@ -785,6 +807,7 @@ impl Engine<'_> {
} }
// idx_lhs[idx_expr] = rhs // idx_lhs[idx_expr] = rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, _) = let (src_type, src, idx, _) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?; self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?;
@ -818,6 +841,7 @@ impl Engine<'_> {
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs), Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs),
#[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => { Expr::Array(contents, _) => {
let mut arr = Vec::new(); let mut arr = Vec::new();
@ -836,7 +860,7 @@ impl Engine<'_> {
let mut args = args let mut args = args
.iter() .iter()
.map(|expr| self.eval_expr(scope, expr)) .map(|expr| self.eval_expr(scope, expr))
.collect::<Result<Array, _>>()?; .collect::<Result<Vec<Dynamic>, _>>()?;
self.call_fn_raw( self.call_fn_raw(
fn_name, fn_name,

View File

@ -75,9 +75,12 @@ mod scope;
pub use any::{Any, AnyExt, Dynamic, Variant}; pub use any::{Any, AnyExt, Dynamic, Variant};
pub use call::FuncArgs; pub use call::FuncArgs;
pub use engine::{Array, Engine}; pub use engine::Engine;
pub use error::{ParseError, ParseErrorType}; pub use error::{ParseError, ParseErrorType};
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
pub use parser::{Position, AST}; pub use parser::{Position, AST, FLOAT, INT};
pub use result::EvalAltResult; pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
#[cfg(not(feature = "no_index"))]
pub use engine::Array;

View File

@ -61,7 +61,7 @@ fn optimize_stmt(stmt: Stmt, changed: &mut bool) -> Stmt {
// Remove all raw expression statements that evaluate to constants // Remove all raw expression statements that evaluate to constants
// except for the very last statement // except for the very last statement
result.retain(|stmt| match stmt { result.retain(|stmt| match stmt {
Stmt::Expr(expr) if expr.is_constant() => false, Stmt::Expr(expr) if expr.is_constant() || expr.is_identifier() => false,
_ => true, _ => true,
}); });
@ -112,7 +112,6 @@ fn optimize_stmt(stmt: Stmt, changed: &mut bool) -> Stmt {
fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr { fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
match expr { match expr {
Expr::IntegerConstant(_, _) Expr::IntegerConstant(_, _)
| Expr::FloatConstant(_, _)
| Expr::Identifier(_, _) | Expr::Identifier(_, _)
| Expr::CharConstant(_, _) | Expr::CharConstant(_, _)
| Expr::StringConstant(_, _) | Expr::StringConstant(_, _)
@ -120,6 +119,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
| Expr::False(_) | Expr::False(_)
| Expr::Unit(_) => expr, | Expr::Unit(_) => expr,
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, _) => expr,
Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, changed) { Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, changed) {
Stmt::Noop(_) => { Stmt::Noop(_) => {
*changed = true; *changed = true;
@ -139,11 +141,24 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
Box::new(optimize_expr(*rhs, changed)), Box::new(optimize_expr(*rhs, changed)),
pos, pos,
), ),
Expr::Index(lhs, rhs, pos) => Expr::Index( #[cfg(not(feature = "no_index"))]
Box::new(optimize_expr(*lhs, changed)), Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
Box::new(optimize_expr(*rhs, changed)), (Expr::Array(mut items, _), Expr::IntegerConstant(i, _))
pos, if i >= 0
), && (i as usize) < items.len()
&& !items.iter().any(|x| x.is_constant() || x.is_identifier()) =>
{
// Array where everything is a constant or identifier - promote the item
*changed = true;
items.remove(i as usize)
}
(lhs, rhs) => Expr::Index(
Box::new(optimize_expr(lhs, changed)),
Box::new(optimize_expr(rhs, changed)),
pos,
),
},
#[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => { Expr::Array(items, pos) => {
let original_len = items.len(); let original_len = items.len();

View File

@ -5,6 +5,17 @@ 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
#[cfg(not(feature = "only_i32"))]
pub type INT = i64;
/// The system integer type
#[cfg(feature = "only_i32")]
pub type INT = i32;
/// The system floating-point type
pub type FLOAT = f64;
type LERR = LexError; type LERR = LexError;
type PERR = ParseErrorType; type PERR = ParseErrorType;
@ -158,8 +169,9 @@ impl Stmt {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Expr { pub enum Expr {
IntegerConstant(i64, Position), IntegerConstant(INT, Position),
FloatConstant(f64, Position), #[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT, Position),
Identifier(String, Position), Identifier(String, Position),
CharConstant(char, Position), CharConstant(char, Position),
StringConstant(String, Position), StringConstant(String, Position),
@ -167,7 +179,9 @@ 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>),
@ -180,36 +194,49 @@ impl Expr {
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
Expr::IntegerConstant(_, pos) Expr::IntegerConstant(_, pos)
| Expr::FloatConstant(_, pos)
| Expr::Identifier(_, pos) | Expr::Identifier(_, pos)
| Expr::CharConstant(_, pos) | Expr::CharConstant(_, pos)
| Expr::StringConstant(_, pos) | Expr::StringConstant(_, pos)
| Expr::FunctionCall(_, _, _, pos) | Expr::FunctionCall(_, _, _, pos)
| Expr::Stmt(_, pos) | Expr::Stmt(_, 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::Index(e, _, _) Expr::Assignment(e, _, _) | Expr::Dot(e, _, _) | Expr::And(e, _) | Expr::Or(e, _) => {
| Expr::Assignment(e, _, _) e.position()
| Expr::Dot(e, _, _) }
| Expr::And(e, _)
| Expr::Or(e, _) => e.position(), #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => *pos,
#[cfg(not(feature = "no_index"))]
Expr::Index(e, _, _) => e.position(),
#[cfg(not(feature = "no_index"))]
Expr::Array(_, pos) => *pos,
} }
} }
pub fn is_constant(&self) -> bool { pub fn is_constant(&self) -> bool {
match self { match self {
Expr::IntegerConstant(_, _) Expr::IntegerConstant(_, _)
| Expr::FloatConstant(_, _)
| Expr::Identifier(_, _)
| Expr::CharConstant(_, _) | Expr::CharConstant(_, _)
| Expr::StringConstant(_, _) | Expr::StringConstant(_, _)
| Expr::True(_) | Expr::True(_)
| Expr::False(_) | Expr::False(_)
| Expr::Unit(_) => true, | Expr::Unit(_) => true,
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, _) => true,
_ => false,
}
}
pub fn is_identifier(&self) -> bool {
match self {
Expr::Identifier(_, _) => true,
_ => false, _ => false,
} }
} }
@ -217,8 +244,9 @@ impl Expr {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Token { pub enum Token {
IntegerConstant(i64), IntegerConstant(INT),
FloatConstant(f64), #[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT),
Identifier(String), Identifier(String),
CharConstant(char), CharConstant(char),
StringConst(String), StringConst(String),
@ -288,6 +316,7 @@ impl Token {
match *self { match *self {
IntegerConstant(ref i) => i.to_string().into(), IntegerConstant(ref i) => i.to_string().into(),
#[cfg(not(feature = "no_float"))]
FloatConstant(ref f) => f.to_string().into(), FloatConstant(ref f) => f.to_string().into(),
Identifier(ref s) => s.into(), Identifier(ref s) => s.into(),
CharConstant(ref c) => c.to_string().into(), CharConstant(ref c) => c.to_string().into(),
@ -617,6 +646,7 @@ impl<'a> TokenIterator<'a> {
self.char_stream.next(); self.char_stream.next();
self.advance(); self.advance();
} }
#[cfg(not(feature = "no_float"))]
'.' => { '.' => {
result.push(next_char); result.push(next_char);
self.char_stream.next(); self.char_stream.next();
@ -692,7 +722,7 @@ impl<'a> TokenIterator<'a> {
let out: String = result.iter().skip(2).filter(|&&c| c != '_').collect(); let out: String = result.iter().skip(2).filter(|&&c| c != '_').collect();
return Some(( return Some((
i64::from_str_radix(&out, radix) INT::from_str_radix(&out, radix)
.map(Token::IntegerConstant) .map(Token::IntegerConstant)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
Token::LexError(LERR::MalformedNumber(result.iter().collect())) Token::LexError(LERR::MalformedNumber(result.iter().collect()))
@ -702,10 +732,21 @@ impl<'a> TokenIterator<'a> {
} else { } else {
let out: String = result.iter().filter(|&&c| c != '_').collect(); let out: String = result.iter().filter(|&&c| c != '_').collect();
#[cfg(feature = "no_float")]
return Some(( return Some((
i64::from_str(&out) INT::from_str(&out)
.map(Token::IntegerConstant) .map(Token::IntegerConstant)
.or_else(|_| f64::from_str(&out).map(Token::FloatConstant)) .unwrap_or_else(|_| {
Token::LexError(LERR::MalformedNumber(result.iter().collect()))
}),
pos,
));
#[cfg(not(feature = "no_float"))]
return Some((
INT::from_str(&out)
.map(Token::IntegerConstant)
.or_else(|_| FLOAT::from_str(&out).map(Token::FloatConstant))
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
Token::LexError(LERR::MalformedNumber(result.iter().collect())) Token::LexError(LERR::MalformedNumber(result.iter().collect()))
}), }),
@ -1166,6 +1207,7 @@ fn parse_call_expr<'a>(
} }
} }
#[cfg(not(feature = "no_index"))]
fn parse_index_expr<'a>( fn parse_index_expr<'a>(
lhs: Box<Expr>, lhs: Box<Expr>,
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
@ -1184,6 +1226,7 @@ fn parse_index_expr<'a>(
*pos, *pos,
)) ))
} }
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => { Expr::FloatConstant(_, pos) => {
return Err(ParseError::new( return Err(ParseError::new(
PERR::MalformedIndexExpr("Array access expects integer index, not a float".into()), PERR::MalformedIndexExpr("Array access expects integer index, not a float".into()),
@ -1260,6 +1303,7 @@ fn parse_ident_expr<'a>(
input.next(); input.next();
parse_call_expr(id, input, begin) parse_call_expr(id, input, begin)
} }
#[cfg(not(feature = "no_index"))]
Some(&(Token::LeftBracket, pos)) => { Some(&(Token::LeftBracket, pos)) => {
input.next(); input.next();
parse_index_expr(Box::new(Expr::Identifier(id, begin)), input, pos) parse_index_expr(Box::new(Expr::Identifier(id, begin)), input, pos)
@ -1269,6 +1313,7 @@ fn parse_ident_expr<'a>(
} }
} }
#[cfg(not(feature = "no_index"))]
fn parse_array_expr<'a>( fn parse_array_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
begin: Position, begin: Position,
@ -1320,26 +1365,30 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
let token = input.next(); let token = input.next();
let mut follow_on = false; let mut can_be_indexed = false;
#[allow(unused_mut)]
let mut root_expr = match token { let mut root_expr = match token {
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)), #[cfg(not(feature = "no_float"))]
Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)), Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)),
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)),
Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)), Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)),
Some((Token::StringConst(s), pos)) => { Some((Token::StringConst(s), pos)) => {
follow_on = true; can_be_indexed = true;
Ok(Expr::StringConstant(s, pos)) Ok(Expr::StringConstant(s, pos))
} }
Some((Token::Identifier(s), pos)) => { Some((Token::Identifier(s), pos)) => {
follow_on = true; can_be_indexed = true;
parse_ident_expr(s, input, pos) parse_ident_expr(s, input, pos)
} }
Some((Token::LeftParen, pos)) => { Some((Token::LeftParen, pos)) => {
follow_on = true; can_be_indexed = true;
parse_paren_expr(input, pos) parse_paren_expr(input, pos)
} }
#[cfg(not(feature = "no_index"))]
Some((Token::LeftBracket, pos)) => { Some((Token::LeftBracket, pos)) => {
follow_on = true; can_be_indexed = true;
parse_array_expr(input, pos) parse_array_expr(input, pos)
} }
Some((Token::True, pos)) => Ok(Expr::True(pos)), Some((Token::True, pos)) => Ok(Expr::True(pos)),
@ -1354,14 +1403,13 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())), None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
}?; }?;
if !follow_on { if can_be_indexed {
return Ok(root_expr); // Tail processing all possible indexing
} #[cfg(not(feature = "no_index"))]
while let Some(&(Token::LeftBracket, pos)) = input.peek() {
// Tail processing all possible indexing input.next();
while let Some(&(Token::LeftBracket, pos)) = input.peek() { root_expr = parse_index_expr(Box::new(root_expr), input, pos)?;
input.next(); }
root_expr = parse_index_expr(Box::new(root_expr), input, pos)?;
} }
Ok(root_expr) Ok(root_expr)
@ -1374,14 +1422,31 @@ fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pars
match parse_unary(input) { match parse_unary(input) {
// Negative integer // Negative integer
Ok(Expr::IntegerConstant(i, pos)) => Ok(i #[cfg(not(feature = "no_float"))]
Ok(Expr::IntegerConstant(i, _)) => Ok(i
.checked_neg() .checked_neg()
.map(|x| Expr::IntegerConstant(x, pos)) .map(|x| Expr::IntegerConstant(x, pos))
.unwrap_or_else(|| Expr::FloatConstant(-(i as f64), pos))), .unwrap_or_else(|| Expr::FloatConstant(-(i as FLOAT), pos))),
// Negative integer
#[cfg(feature = "no_float")]
Ok(Expr::IntegerConstant(i, _)) => i
.checked_neg()
.map(|x| Expr::IntegerConstant(x, pos))
.ok_or_else(|| {
ParseError::new(
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string()),
pos,
)
}),
// Negative float // Negative float
#[cfg(not(feature = "no_float"))]
Ok(Expr::FloatConstant(f, pos)) => Ok(Expr::FloatConstant(-f, pos)), Ok(Expr::FloatConstant(f, pos)) => Ok(Expr::FloatConstant(-f, pos)),
// Call negative function // Call negative function
Ok(expr) => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)), Ok(expr) => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)),
err @ Err(_) => err, err @ Err(_) => err,
} }
} }
@ -1408,17 +1473,20 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
match expr { match expr {
Expr::Identifier(_, pos) => (true, *pos), Expr::Identifier(_, pos) => (true, *pos),
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() { #[cfg(not(feature = "no_index"))]
Expr::Identifier(_, _) => (true, idx_lhs.position()), Expr::Index(idx_lhs, _, _) if idx_lhs.is_identifier() => (true, idx_lhs.position()),
_ => (false, idx_lhs.position()), #[cfg(not(feature = "no_index"))]
}, Expr::Index(idx_lhs, _, _) => (false, idx_lhs.position()),
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() { Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs), Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() { Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs), Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
_ => (false, idx_lhs.position()), _ => (false, idx_lhs.position()),
}, },
_ => (false, dot_lhs.position()), _ => (false, dot_lhs.position()),
}, },
@ -1447,29 +1515,6 @@ fn parse_op_assignment(
Expr::FunctionCall(function.into(), vec![lhs_copy, rhs], None, pos), Expr::FunctionCall(function.into(), vec![lhs_copy, rhs], None, pos),
pos, pos,
) )
/*
const LHS_VALUE: &'static str = "@LHS_VALUE@";
let lhs_pos = lhs.position();
Ok(Expr::Block(
Box::new(Stmt::Block(vec![
Stmt::Let(LHS_VALUE.to_string(), Some(Box::new(lhs)), lhs_pos),
Stmt::Expr(Box::new(parse_assignment(
lhs,
Expr::FunctionCall(
function.into(),
vec![Expr::Identifier(LHS_VALUE.to_string(), lhs_pos), rhs],
None,
pos,
),
pos,
)?)),
])),
pos,
))
*/
} }
fn parse_binary_op<'a>( fn parse_binary_op<'a>(
@ -1519,6 +1564,7 @@ fn parse_binary_op<'a>(
Token::Equals => parse_assignment(current_lhs, rhs, pos)?, Token::Equals => parse_assignment(current_lhs, rhs, pos)?,
Token::PlusAssign => parse_op_assignment("+", current_lhs, rhs, pos)?, Token::PlusAssign => parse_op_assignment("+", current_lhs, rhs, pos)?,
Token::MinusAssign => parse_op_assignment("-", current_lhs, rhs, pos)?, Token::MinusAssign => parse_op_assignment("-", current_lhs, rhs, pos)?,
Token::Period => Expr::Dot(Box::new(current_lhs), Box::new(rhs), pos), Token::Period => Expr::Dot(Box::new(current_lhs), Box::new(rhs), pos),
// Comparison operators default to false when passed invalid operands // Comparison operators default to false when passed invalid operands

View File

@ -2,7 +2,7 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::error::ParseError; use crate::error::ParseError;
use crate::parser::Position; use crate::parser::{Position, INT};
use std::{error::Error, fmt}; use std::{error::Error, fmt};
/// Evaluation result. /// Evaluation result.
@ -24,10 +24,10 @@ pub enum EvalAltResult {
ErrorCharMismatch(Position), ErrorCharMismatch(Position),
/// Array access out-of-bounds. /// Array access out-of-bounds.
/// Wrapped values are the current number of elements in the array and the index number. /// Wrapped values are the current number of elements in the array and the index number.
ErrorArrayBounds(usize, i64, Position), ErrorArrayBounds(usize, INT, Position),
/// String indexing out-of-bounds. /// String indexing out-of-bounds.
/// Wrapped values are the current number of characters in the string and the index number. /// Wrapped values are the current number of characters in the string and the index number.
ErrorStringBounds(usize, i64, Position), ErrorStringBounds(usize, INT, Position),
/// Trying to index into a type that is not an array and not a string. /// Trying to index into a type that is not an array and not a string.
ErrorIndexingType(String, Position), ErrorIndexingType(String, Position),
/// Trying to index into an array or string with an index that is not `i64`. /// Trying to index into an array or string with an index that is not `i64`.
@ -173,6 +173,12 @@ impl From<ParseError> for EvalAltResult {
} }
} }
impl<T: AsRef<str>> From<T> for EvalAltResult {
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
}
}
impl EvalAltResult { impl EvalAltResult {
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
@ -225,9 +231,3 @@ impl EvalAltResult {
} }
} }
} }
impl<T: AsRef<str>> From<T> for EvalAltResult {
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
}
}

View File

@ -93,6 +93,7 @@ impl<'a> Scope<'a> {
} }
/// Get a mutable reference to a variable in the Scope and downcast it to a specific type /// Get a mutable reference to a variable in the Scope and downcast it to a specific type
#[cfg(not(feature = "no_index"))]
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: &str, index: usize) -> &mut T { pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: &str, index: usize) -> &mut T {
self.get_mut(key, index) self.get_mut(key, index)
.downcast_mut::<T>() .downcast_mut::<T>()

View File

@ -1,11 +1,12 @@
use rhai::{Engine, EvalAltResult, RegisterFn}; #![cfg(not(feature = "no_index"))]
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
#[test] #[test]
fn test_arrays() -> Result<(), EvalAltResult> { fn test_arrays() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = [1, 2, 3]; x[1]")?, 2); assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2);
assert_eq!(engine.eval::<i64>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5); assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5);
Ok(()) Ok(())
} }
@ -14,7 +15,7 @@ fn test_arrays() -> Result<(), EvalAltResult> {
fn test_array_with_structs() -> Result<(), EvalAltResult> { fn test_array_with_structs() -> Result<(), EvalAltResult> {
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
x: i64, x: INT,
} }
impl TestStruct { impl TestStruct {
@ -22,11 +23,11 @@ fn test_array_with_structs() -> Result<(), EvalAltResult> {
self.x += 1000; self.x += 1000;
} }
fn get_x(&mut self) -> i64 { fn get_x(&mut self) -> INT {
self.x self.x
} }
fn set_x(&mut self, new_x: i64) { fn set_x(&mut self, new_x: INT) {
self.x = new_x; self.x = new_x;
} }
@ -43,10 +44,10 @@ fn test_array_with_structs() -> Result<(), EvalAltResult> {
engine.register_fn("update", TestStruct::update); engine.register_fn("update", TestStruct::update);
engine.register_fn("new_ts", TestStruct::new); engine.register_fn("new_ts", TestStruct::new);
assert_eq!(engine.eval::<i64>("let a = [new_ts()]; a[0].x")?, 1); assert_eq!(engine.eval::<INT>("let a = [new_ts()]; a[0].x")?, 1);
assert_eq!( assert_eq!(
engine.eval::<i64>( engine.eval::<INT>(
"let a = [new_ts()]; \ "let a = [new_ts()]; \
a[0].x = 100; \ a[0].x = 100; \
a[0].update(); \ a[0].update(); \

View File

@ -1,15 +1,15 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_binary_ops() -> Result<(), EvalAltResult> { fn test_binary_ops() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("10 % 4")?, 2); assert_eq!(engine.eval::<INT>("10 % 4")?, 2);
assert_eq!(engine.eval::<i64>("10 << 4")?, 160); assert_eq!(engine.eval::<INT>("10 << 4")?, 160);
assert_eq!(engine.eval::<i64>("10 >> 4")?, 0); assert_eq!(engine.eval::<INT>("10 >> 4")?, 0);
assert_eq!(engine.eval::<i64>("10 & 4")?, 0); assert_eq!(engine.eval::<INT>("10 & 4")?, 0);
assert_eq!(engine.eval::<i64>("10 | 4")?, 14); assert_eq!(engine.eval::<INT>("10 | 4")?, 14);
assert_eq!(engine.eval::<i64>("10 ^ 4")?, 14); assert_eq!(engine.eval::<INT>("10 ^ 4")?, 14);
assert_eq!(engine.eval::<bool>("42 == 42")?, true); assert_eq!(engine.eval::<bool>("42 == 42")?, true);
assert_eq!(engine.eval::<bool>("42 > 42")?, false); assert_eq!(engine.eval::<bool>("42 > 42")?, false);

View File

@ -1,15 +1,15 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_left_shift() -> Result<(), EvalAltResult> { fn test_left_shift() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("4 << 2")?, 16); assert_eq!(engine.eval::<INT>("4 << 2")?, 16);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_right_shift() -> Result<(), EvalAltResult> { fn test_right_shift() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("9 >> 1")?, 4); assert_eq!(engine.eval::<INT>("9 >> 1")?, 4);
Ok(()) Ok(())
} }

View File

@ -6,11 +6,15 @@ fn test_chars() -> Result<(), EvalAltResult> {
assert_eq!(engine.eval::<char>("'y'")?, 'y'); assert_eq!(engine.eval::<char>("'y'")?, 'y');
assert_eq!(engine.eval::<char>("'\\u2764'")?, '❤'); assert_eq!(engine.eval::<char>("'\\u2764'")?, '❤');
assert_eq!(engine.eval::<char>(r#"let x="hello"; x[2]"#)?, 'l');
assert_eq!( #[cfg(not(feature = "no_index"))]
engine.eval::<String>(r#"let x="hello"; x[2]='$'; x"#)?, {
"he$lo".to_string() assert_eq!(engine.eval::<char>(r#"let x="hello"; x[2]"#)?, 'l');
); assert_eq!(
engine.eval::<String>(r#"let x="hello"; x[2]='$'; x"#)?,
"he$lo".to_string()
);
}
assert!(engine.eval::<char>("'\\uhello'").is_err()); assert!(engine.eval::<char>("'\\uhello'").is_err());
assert!(engine.eval::<char>("''").is_err()); assert!(engine.eval::<char>("''").is_err());

View File

@ -1,14 +1,14 @@
use rhai::Engine; use rhai::{Engine, INT};
#[test] #[test]
fn test_comments() { fn test_comments() {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert!(engine assert!(engine
.eval::<i64>("let x = 5; x // I am a single line comment, yay!") .eval::<INT>("let x = 5; x // I am a single line comment, yay!")
.is_ok()); .is_ok());
assert!(engine assert!(engine
.eval::<i64>("let /* I am a multiline comment, yay! */ x = 5; x") .eval::<INT>("let /* I am a multiline comment, yay! */ x = 5; x")
.is_ok()); .is_ok());
} }

View File

@ -1,10 +1,10 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_or_equals() -> Result<(), EvalAltResult> { fn test_or_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 16; x |= 74; x")?, 90); assert_eq!(engine.eval::<INT>("let x = 16; x |= 74; x")?, 90);
assert_eq!(engine.eval::<bool>("let x = true; x |= false; x")?, true); assert_eq!(engine.eval::<bool>("let x = true; x |= false; x")?, true);
assert_eq!(engine.eval::<bool>("let x = false; x |= true; x")?, true); assert_eq!(engine.eval::<bool>("let x = false; x |= true; x")?, true);
@ -15,7 +15,7 @@ fn test_or_equals() -> Result<(), EvalAltResult> {
fn test_and_equals() -> Result<(), EvalAltResult> { fn test_and_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 16; x &= 31; x")?, 16); assert_eq!(engine.eval::<INT>("let x = 16; x &= 31; x")?, 16);
assert_eq!(engine.eval::<bool>("let x = true; x &= false; x")?, false); assert_eq!(engine.eval::<bool>("let x = true; x &= false; x")?, false);
assert_eq!(engine.eval::<bool>("let x = false; x &= true; x")?, false); assert_eq!(engine.eval::<bool>("let x = false; x &= true; x")?, false);
assert_eq!(engine.eval::<bool>("let x = true; x &= true; x")?, true); assert_eq!(engine.eval::<bool>("let x = true; x &= true; x")?, true);
@ -26,41 +26,41 @@ fn test_and_equals() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_xor_equals() -> Result<(), EvalAltResult> { fn test_xor_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 90; x ^= 12; x")?, 86); assert_eq!(engine.eval::<INT>("let x = 90; x ^= 12; x")?, 86);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_multiply_equals() -> Result<(), EvalAltResult> { fn test_multiply_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 2; x *= 3; x")?, 6); assert_eq!(engine.eval::<INT>("let x = 2; x *= 3; x")?, 6);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_divide_equals() -> Result<(), EvalAltResult> { fn test_divide_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 6; x /= 2; x")?, 3); assert_eq!(engine.eval::<INT>("let x = 6; x /= 2; x")?, 3);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_left_shift_equals() -> Result<(), EvalAltResult> { fn test_left_shift_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 9; x >>=1; x")?, 4); assert_eq!(engine.eval::<INT>("let x = 9; x >>=1; x")?, 4);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_right_shift_equals() -> Result<(), EvalAltResult> { fn test_right_shift_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 4; x<<= 2; x")?, 16); assert_eq!(engine.eval::<INT>("let x = 4; x<<= 2; x")?, 16);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_modulo_equals() -> Result<(), EvalAltResult> { fn test_modulo_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 10; x %= 4; x")?, 2); assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2);
Ok(()) Ok(())
} }

View File

@ -1,10 +1,10 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_decrement() -> Result<(), EvalAltResult> { fn test_decrement() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 10; x -= 7; x")?, 3); assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);
let r = engine.eval::<String>("let s = \"test\"; s -= \"ing\"; s"); let r = engine.eval::<String>("let s = \"test\"; s -= \"ing\"; s");

View File

@ -1,4 +1,5 @@
use rhai::{Engine, EvalAltResult}; #![cfg(not(feature = "no_stdlib"))]
use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_engine_call_fn() -> Result<(), EvalAltResult> { fn test_engine_call_fn() -> Result<(), EvalAltResult> {
@ -12,7 +13,7 @@ fn test_engine_call_fn() -> Result<(), EvalAltResult> {
", ",
)?; )?;
let result: i64 = engine.call_fn("hello", &ast, (String::from("abc"), 123_i64))?; let result: INT = engine.call_fn("hello", &ast, (String::from("abc"), 123 as INT))?;
assert_eq!(result, 126); assert_eq!(result, 126);

View File

@ -1,3 +1,4 @@
#![cfg(not(feature = "no_float"))]
use rhai::{Engine, EvalAltResult, RegisterFn}; use rhai::{Engine, EvalAltResult, RegisterFn};
#[test] #[test]

View File

@ -1,4 +1,5 @@
use rhai::{Engine, EvalAltResult}; #![cfg(not(feature = "no_index"))]
use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_for() -> Result<(), EvalAltResult> { fn test_for() -> Result<(), EvalAltResult> {
@ -20,7 +21,7 @@ fn test_for() -> Result<(), EvalAltResult> {
sum1 + sum2 sum1 + sum2
"; ";
assert_eq!(engine.eval::<i64>(script)?, 30); assert_eq!(engine.eval::<INT>(script)?, 30);
Ok(()) Ok(())
} }

View File

@ -1,18 +1,18 @@
use rhai::{Engine, EvalAltResult, RegisterFn}; use rhai::{Engine, EvalAltResult, RegisterFn, INT};
#[test] #[test]
fn test_get_set() -> Result<(), EvalAltResult> { fn test_get_set() -> Result<(), EvalAltResult> {
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
x: i64, x: INT,
} }
impl TestStruct { impl TestStruct {
fn get_x(&mut self) -> i64 { fn get_x(&mut self) -> INT {
self.x self.x
} }
fn set_x(&mut self, new_x: i64) { fn set_x(&mut self, new_x: INT) {
self.x = new_x; self.x = new_x;
} }
@ -28,7 +28,7 @@ fn test_get_set() -> Result<(), EvalAltResult> {
engine.register_get_set("x", TestStruct::get_x, TestStruct::set_x); engine.register_get_set("x", TestStruct::get_x, TestStruct::set_x);
engine.register_fn("new_ts", TestStruct::new); engine.register_fn("new_ts", TestStruct::new);
assert_eq!(engine.eval::<i64>("let a = new_ts(); a.x = 500; a.x")?, 500); assert_eq!(engine.eval::<INT>("let a = new_ts(); a.x = 500; a.x")?, 500);
Ok(()) Ok(())
} }
@ -37,15 +37,15 @@ fn test_get_set() -> Result<(), EvalAltResult> {
fn test_big_get_set() -> Result<(), EvalAltResult> { fn test_big_get_set() -> Result<(), EvalAltResult> {
#[derive(Clone)] #[derive(Clone)]
struct TestChild { struct TestChild {
x: i64, x: INT,
} }
impl TestChild { impl TestChild {
fn get_x(&mut self) -> i64 { fn get_x(&mut self) -> INT {
self.x self.x
} }
fn set_x(&mut self, new_x: i64) { fn set_x(&mut self, new_x: INT) {
self.x = new_x; self.x = new_x;
} }
@ -86,7 +86,7 @@ fn test_big_get_set() -> Result<(), EvalAltResult> {
engine.register_fn("new_tp", TestParent::new); engine.register_fn("new_tp", TestParent::new);
assert_eq!( assert_eq!(
engine.eval::<i64>("let a = new_tp(); a.child.x = 500; a.child.x")?, engine.eval::<INT>("let a = new_tp(); a.child.x = 500; a.child.x")?,
500 500
); );

View File

@ -1,18 +1,18 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_if() -> Result<(), EvalAltResult> { fn test_if() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("if true { 55 }")?, 55); assert_eq!(engine.eval::<INT>("if true { 55 }")?, 55);
assert_eq!(engine.eval::<i64>("if false { 55 } else { 44 }")?, 44); assert_eq!(engine.eval::<INT>("if false { 55 } else { 44 }")?, 44);
assert_eq!(engine.eval::<i64>("if true { 55 } else { 44 }")?, 55); assert_eq!(engine.eval::<INT>("if true { 55 } else { 44 }")?, 55);
assert_eq!( assert_eq!(
engine.eval::<i64>("if false { 55 } else if true { 33 } else { 44 }")?, engine.eval::<INT>("if false { 55 } else if true { 33 } else { 44 }")?,
33 33
); );
assert_eq!( assert_eq!(
engine.eval::<i64>( engine.eval::<INT>(
r" r"
if false { 55 } if false { 55 }
else if false { 33 } else if false { 33 }

View File

@ -1,10 +1,10 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_increment() -> Result<(), EvalAltResult> { fn test_increment() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 1; x += 2; x")?, 3); assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3);
assert_eq!( assert_eq!(
engine.eval::<String>("let s = \"test\"; s += \"ing\"; s")?, engine.eval::<String>("let s = \"test\"; s += \"ing\"; s")?,
"testing".to_string() "testing".to_string()

View File

@ -1,11 +1,11 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_internal_fn() -> Result<(), EvalAltResult> { fn test_internal_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("fn addme(a, b) { a+b } addme(3, 4)")?, 7); assert_eq!(engine.eval::<INT>("fn addme(a, b) { a+b } addme(3, 4)")?, 7);
assert_eq!(engine.eval::<i64>("fn bob() { return 4; 5 } bob()")?, 4); assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4);
Ok(()) Ok(())
} }
@ -15,7 +15,7 @@ fn test_big_internal_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<i64>( engine.eval::<INT>(
r" r"
fn mathme(a, b, c, d, e, f) { fn mathme(a, b, c, d, e, f) {
a - b * c + d * e - f a - b * c + d * e - f

View File

@ -1,41 +1,73 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_math() -> Result<(), EvalAltResult> { fn test_math() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("1 + 2")?, 3); assert_eq!(engine.eval::<INT>("1 + 2")?, 3);
assert_eq!(engine.eval::<i64>("1 - 2")?, -1); assert_eq!(engine.eval::<INT>("1 - 2")?, -1);
assert_eq!(engine.eval::<i64>("2 * 3")?, 6); assert_eq!(engine.eval::<INT>("2 * 3")?, 6);
assert_eq!(engine.eval::<i64>("1 / 2")?, 0); assert_eq!(engine.eval::<INT>("1 / 2")?, 0);
assert_eq!(engine.eval::<i64>("3 % 2")?, 1); assert_eq!(engine.eval::<INT>("3 % 2")?, 1);
#[cfg(not(feature = "only_i32"))]
assert_eq!( assert_eq!(
engine.eval::<i64>("(-9223372036854775807).abs()")?, engine.eval::<INT>("(-9223372036854775807).abs()")?,
9223372036854775807 9223372036854775807
); );
#[cfg(feature = "only_i32")]
assert_eq!(engine.eval::<INT>("(-2147483647).abs()")?, 2147483647);
// Overflow/underflow/division-by-zero errors // Overflow/underflow/division-by-zero errors
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
match engine.eval::<i64>("9223372036854775807 + 1") { #[cfg(not(feature = "only_i32"))]
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), {
r => panic!("should return overflow error: {:?}", r), match engine.eval::<INT>("9223372036854775807 + 1") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return overflow error: {:?}", r),
}
match engine.eval::<INT>("-9223372036854775808 - 1") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return underflow error: {:?}", r),
}
match engine.eval::<INT>("9223372036854775807 * 9223372036854775807") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return overflow error: {:?}", r),
}
match engine.eval::<INT>("9223372036854775807 / 0") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return division by zero error: {:?}", r),
}
match engine.eval::<INT>("9223372036854775807 % 0") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return division by zero error: {:?}", r),
}
} }
match engine.eval::<i64>("-9223372036854775808 - 1") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), #[cfg(feature = "only_i32")]
r => panic!("should return underflow error: {:?}", r), {
} match engine.eval::<INT>("2147483647 + 1") {
match engine.eval::<i64>("9223372036854775807 * 9223372036854775807") { Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), r => panic!("should return overflow error: {:?}", r),
r => panic!("should return overflow error: {:?}", r), }
} match engine.eval::<INT>("-2147483648 - 1") {
match engine.eval::<i64>("9223372036854775807 / 0") { Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), r => panic!("should return underflow error: {:?}", r),
r => panic!("should return division by zero error: {:?}", r), }
} match engine.eval::<INT>("2147483647 * 2147483647") {
match engine.eval::<i64>("9223372036854775807 % 0") { Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
Err(EvalAltResult::ErrorArithmetic(_, _)) => (), r => panic!("should return overflow error: {:?}", r),
r => panic!("should return division by zero error: {:?}", r), }
match engine.eval::<INT>("2147483647 / 0") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return division by zero error: {:?}", r),
}
match engine.eval::<INT>("2147483647 % 0") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return division by zero error: {:?}", r),
}
} }
} }

View File

@ -1,10 +1,10 @@
use rhai::{Engine, EvalAltResult, RegisterFn}; use rhai::{Engine, EvalAltResult, RegisterFn, INT};
#[test] #[test]
fn test_method_call() -> Result<(), EvalAltResult> { fn test_method_call() -> Result<(), EvalAltResult> {
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
x: i64, x: INT,
} }
impl TestStruct { impl TestStruct {

View File

@ -1,10 +1,11 @@
use rhai::{Engine, EvalAltResult, RegisterFn}; use rhai::{Engine, EvalAltResult, RegisterFn, INT};
#[test] #[test]
#[cfg(not(feature = "no_stdlib"))]
fn test_mismatched_op() { fn test_mismatched_op() {
let mut engine = Engine::new(); let mut engine = Engine::new();
let r = engine.eval::<i64>("60 + \"hello\""); let r = engine.eval::<INT>("60 + \"hello\"");
match r { match r {
Err(EvalAltResult::ErrorMismatchOutputType(err, _)) if err == "string" => (), Err(EvalAltResult::ErrorMismatchOutputType(err, _)) if err == "string" => (),
@ -16,7 +17,7 @@ fn test_mismatched_op() {
fn test_mismatched_op_custom_type() { fn test_mismatched_op_custom_type() {
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
x: i64, x: INT,
} }
impl TestStruct { impl TestStruct {
@ -26,17 +27,18 @@ fn test_mismatched_op_custom_type() {
} }
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.register_type::<TestStruct>(); engine.register_type_with_name::<TestStruct>("TestStruct");
engine.register_fn("new_ts", TestStruct::new); engine.register_fn("new_ts", TestStruct::new);
let r = engine.eval::<i64>("60 + new_ts()"); let r = engine.eval::<INT>("60 + new_ts()");
match r { match r {
Err(EvalAltResult::ErrorFunctionNotFound(err, _)) #[cfg(feature = "only_i32")]
if err == "+ (i64, mismatched_op::test_mismatched_op_custom_type::TestStruct)" => Err(EvalAltResult::ErrorFunctionNotFound(err, _)) if err == "+ (i32, TestStruct)" => (),
{
() #[cfg(not(feature = "only_i32"))]
} Err(EvalAltResult::ErrorFunctionNotFound(err, _)) if err == "+ (i64, TestStruct)" => (),
_ => panic!(), _ => panic!(),
} }
} }

View File

@ -1,10 +1,10 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_number_literal() -> Result<(), EvalAltResult> { fn test_number_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("65")?, 65); assert_eq!(engine.eval::<INT>("65")?, 65);
Ok(()) Ok(())
} }
@ -13,8 +13,8 @@ fn test_number_literal() -> Result<(), EvalAltResult> {
fn test_hex_literal() -> Result<(), EvalAltResult> { fn test_hex_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 0xf; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0xf; x")?, 15);
assert_eq!(engine.eval::<i64>("let x = 0xff; x")?, 255); assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255);
Ok(()) Ok(())
} }
@ -23,8 +23,8 @@ fn test_hex_literal() -> Result<(), EvalAltResult> {
fn test_octal_literal() -> Result<(), EvalAltResult> { fn test_octal_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 0o77; x")?, 63); assert_eq!(engine.eval::<INT>("let x = 0o77; x")?, 63);
assert_eq!(engine.eval::<i64>("let x = 0o1234; x")?, 668); assert_eq!(engine.eval::<INT>("let x = 0o1234; x")?, 668);
Ok(()) Ok(())
} }
@ -33,9 +33,9 @@ fn test_octal_literal() -> Result<(), EvalAltResult> {
fn test_binary_literal() -> Result<(), EvalAltResult> { fn test_binary_literal() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 0b1111; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0b1111; x")?, 15);
assert_eq!( assert_eq!(
engine.eval::<i64>("let x = 0b0011_1100_1010_0101; x")?, engine.eval::<INT>("let x = 0b0011_1100_1010_0101; x")?,
15525 15525
); );

View File

@ -1,11 +1,11 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_ops() -> Result<(), EvalAltResult> { fn test_ops() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("60 + 5")?, 65); assert_eq!(engine.eval::<INT>("60 + 5")?, 65);
assert_eq!(engine.eval::<i64>("(1 + 2) * (6 - 4) / 2")?, 3); assert_eq!(engine.eval::<INT>("(1 + 2) * (6 - 4) / 2")?, 3);
Ok(()) Ok(())
} }
@ -15,7 +15,7 @@ fn test_op_prec() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<i64>("let x = 0; if x == 10 || true { x = 1} x")?, engine.eval::<INT>("let x = 0; if x == 10 || true { x = 1} x")?,
1 1
); );

View File

@ -1,16 +1,23 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, FLOAT, INT};
#[test] #[test]
fn test_power_of() -> Result<(), EvalAltResult> { fn test_power_of() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("2 ~ 3")?, 8); assert_eq!(engine.eval::<INT>("2 ~ 3")?, 8);
assert_eq!(engine.eval::<i64>("(-2 ~ 3)")?, -8); assert_eq!(engine.eval::<INT>("(-2 ~ 3)")?, -8);
assert_eq!(engine.eval::<f64>("2.2 ~ 3.3")?, 13.489468760533386_f64);
assert_eq!(engine.eval::<f64>("2.0~-2.0")?, 0.25_f64); #[cfg(not(feature = "no_float"))]
assert_eq!(engine.eval::<f64>("(-2.0~-2.0)")?, 0.25_f64); {
assert_eq!(engine.eval::<f64>("(-2.0~-2)")?, 0.25_f64); assert_eq!(
assert_eq!(engine.eval::<i64>("4~3")?, 64); engine.eval::<FLOAT>("2.2 ~ 3.3")?,
13.489468760533386 as FLOAT
);
assert_eq!(engine.eval::<FLOAT>("2.0~-2.0")?, 0.25 as FLOAT);
assert_eq!(engine.eval::<FLOAT>("(-2.0~-2.0)")?, 0.25 as FLOAT);
assert_eq!(engine.eval::<FLOAT>("(-2.0~-2)")?, 0.25 as FLOAT);
assert_eq!(engine.eval::<INT>("4~3")?, 64);
}
Ok(()) Ok(())
} }
@ -19,16 +26,29 @@ fn test_power_of() -> Result<(), EvalAltResult> {
fn test_power_of_equals() -> Result<(), EvalAltResult> { fn test_power_of_equals() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = 2; x ~= 3; x")?, 8); assert_eq!(engine.eval::<INT>("let x = 2; x ~= 3; x")?, 8);
assert_eq!(engine.eval::<i64>("let x = -2; x ~= 3; x")?, -8); assert_eq!(engine.eval::<INT>("let x = -2; x ~= 3; x")?, -8);
assert_eq!(
engine.eval::<f64>("let x = 2.2; x ~= 3.3; x")?, #[cfg(not(feature = "no_float"))]
13.489468760533386_f64 {
); assert_eq!(
assert_eq!(engine.eval::<f64>("let x = 2.0; x ~= -2.0; x")?, 0.25_f64); engine.eval::<FLOAT>("let x = 2.2; x ~= 3.3; x")?,
assert_eq!(engine.eval::<f64>("let x = -2.0; x ~= -2.0; x")?, 0.25_f64); 13.489468760533386 as FLOAT
assert_eq!(engine.eval::<f64>("let x = -2.0; x ~= -2; x")?, 0.25_f64); );
assert_eq!(engine.eval::<i64>("let x =4; x ~= 3; x")?, 64); assert_eq!(
engine.eval::<FLOAT>("let x = 2.0; x ~= -2.0; x")?,
0.25 as FLOAT
);
assert_eq!(
engine.eval::<FLOAT>("let x = -2.0; x ~= -2.0; x")?,
0.25 as FLOAT
);
assert_eq!(
engine.eval::<FLOAT>("let x = -2.0; x ~= -2; x")?,
0.25 as FLOAT
);
assert_eq!(engine.eval::<INT>("let x =4; x ~= 3; x")?, 64);
}
Ok(()) Ok(())
} }

View File

@ -12,10 +12,20 @@ fn test_string() -> Result<(), EvalAltResult> {
engine.eval::<String>(r#""Test string: \x58""#)?, engine.eval::<String>(r#""Test string: \x58""#)?,
"Test string: X".to_string() "Test string: X".to_string()
); );
assert_eq!( assert_eq!(
engine.eval::<String>(r#""foo" + "bar""#)?, engine.eval::<String>(r#""foo" + "bar""#)?,
"foobar".to_string() "foobar".to_string()
); );
#[cfg(not(feature = "no_stdlib"))]
assert_eq!(
engine.eval::<String>(r#""foo" + 123"#)?,
"foo123".to_string()
);
#[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "no_stdlib"))]
assert_eq!( assert_eq!(
engine.eval::<String>(r#""foo" + 123.4556"#)?, engine.eval::<String>(r#""foo" + 123.4556"#)?,
"foo123.4556".to_string() "foo123.4556".to_string()

View File

@ -1,16 +1,16 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_throw() { fn test_throw() {
let mut engine = Engine::new(); let mut engine = Engine::new();
match engine.eval::<i64>(r#"if true { throw "hello" }"#) { match engine.eval::<INT>(r#"if true { throw "hello" }"#) {
Ok(_) => panic!("not an error"), Ok(_) => panic!("not an error"),
Err(EvalAltResult::ErrorRuntime(s, _)) if s == "hello" => (), Err(EvalAltResult::ErrorRuntime(s, _)) if s == "hello" => (),
Err(err) => panic!("wrong error: {}", err), Err(err) => panic!("wrong error: {}", err),
} }
match engine.eval::<i64>(r#"throw;"#) { match engine.eval::<INT>(r#"throw;"#) {
Ok(_) => panic!("not an error"), Ok(_) => panic!("not an error"),
Err(EvalAltResult::ErrorRuntime(s, _)) if s == "" => (), Err(EvalAltResult::ErrorRuntime(s, _)) if s == "" => (),
Err(err) => panic!("wrong error: {}", err), Err(err) => panic!("wrong error: {}", err),

View File

@ -4,14 +4,29 @@ use rhai::{Engine, EvalAltResult};
fn test_type_of() -> Result<(), EvalAltResult> { fn test_type_of() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
#[cfg(not(feature = "only_i32"))]
assert_eq!(engine.eval::<String>("type_of(60 + 5)")?, "i64"); assert_eq!(engine.eval::<String>("type_of(60 + 5)")?, "i64");
#[cfg(feature = "only_i32")]
assert_eq!(engine.eval::<String>("type_of(60 + 5)")?, "i32");
#[cfg(not(feature = "no_float"))]
assert_eq!(engine.eval::<String>("type_of(1.0 + 2.0)")?, "f64"); assert_eq!(engine.eval::<String>("type_of(1.0 + 2.0)")?, "f64");
#[cfg(not(feature = "no_index"))]
#[cfg(not(feature = "no_float"))]
assert_eq!( assert_eq!(
engine.eval::<String>(r#"type_of([1.0, 2, "hello"])"#)?, engine.eval::<String>(r#"type_of([1.0, 2, "hello"])"#)?,
"array" "array"
); );
assert_eq!(engine.eval::<String>(r#"type_of("hello")"#)?, "string"); assert_eq!(engine.eval::<String>(r#"type_of("hello")"#)?, "string");
#[cfg(not(feature = "only_i32"))]
assert_eq!(engine.eval::<String>("let x = 123; x.type_of()")?, "i64"); assert_eq!(engine.eval::<String>("let x = 123; x.type_of()")?, "i64");
#[cfg(feature = "only_i32")]
assert_eq!(engine.eval::<String>("let x = 123; x.type_of()")?, "i32");
Ok(()) Ok(())
} }

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
// TODO also add test case for unary after compound // TODO also add test case for unary after compound
@ -6,12 +6,12 @@ use rhai::{Engine, EvalAltResult};
fn test_unary_after_binary() -> Result<(), EvalAltResult> { fn test_unary_after_binary() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("10 % +4")?, 2); assert_eq!(engine.eval::<INT>("10 % +4")?, 2);
assert_eq!(engine.eval::<i64>("10 << +4")?, 160); assert_eq!(engine.eval::<INT>("10 << +4")?, 160);
assert_eq!(engine.eval::<i64>("10 >> +4")?, 0); assert_eq!(engine.eval::<INT>("10 >> +4")?, 0);
assert_eq!(engine.eval::<i64>("10 & +4")?, 0); assert_eq!(engine.eval::<INT>("10 & +4")?, 0);
assert_eq!(engine.eval::<i64>("10 | +4")?, 14); assert_eq!(engine.eval::<INT>("10 | +4")?, 14);
assert_eq!(engine.eval::<i64>("10 ^ +4")?, 14); assert_eq!(engine.eval::<INT>("10 ^ +4")?, 14);
Ok(()) Ok(())
} }

View File

@ -1,12 +1,12 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_unary_minus() -> Result<(), EvalAltResult> { fn test_unary_minus() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!(engine.eval::<i64>("let x = -5; x")?, -5); assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5);
assert_eq!(engine.eval::<i64>("fn neg(x) { -x } neg(5)")?, -5); assert_eq!(engine.eval::<INT>("fn neg(x) { -x } neg(5)")?, -5);
assert_eq!(engine.eval::<i64>("5 - -+++--+-5")?, 0); assert_eq!(engine.eval::<INT>("5 - -+++--+-5")?, 0);
Ok(()) Ok(())
} }

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, Scope}; use rhai::{Engine, EvalAltResult, Scope, INT};
#[test] #[test]
fn test_var_scope() -> Result<(), EvalAltResult> { fn test_var_scope() -> Result<(), EvalAltResult> {
@ -6,14 +6,14 @@ fn test_var_scope() -> Result<(), EvalAltResult> {
let mut scope = Scope::new(); let mut scope = Scope::new();
engine.eval_with_scope::<()>(&mut scope, false, "let x = 4 + 5")?; engine.eval_with_scope::<()>(&mut scope, false, "let x = 4 + 5")?;
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, false, "x")?, 9); assert_eq!(engine.eval_with_scope::<INT>(&mut scope, false, "x")?, 9);
engine.eval_with_scope::<()>(&mut scope, false, "x = x + 1; x = x + 2;")?; engine.eval_with_scope::<()>(&mut scope, false, "x = x + 1; x = x + 2;")?;
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, false, "x")?, 12); assert_eq!(engine.eval_with_scope::<INT>(&mut scope, false, "x")?, 12);
assert_eq!( assert_eq!(
engine.eval_with_scope::<()>(&mut scope, false, "{let x = 3}")?, engine.eval_with_scope::<()>(&mut scope, false, "{let x = 3}")?,
() ()
); );
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, false, "x")?, 12); assert_eq!(engine.eval_with_scope::<INT>(&mut scope, false, "x")?, 12);
Ok(()) Ok(())
} }
@ -26,10 +26,10 @@ fn test_scope_eval() -> Result<(), EvalAltResult> {
let mut scope = Scope::new(); let mut scope = Scope::new();
// Then push some initialized variables into the state // Then push some initialized variables into the state
// NOTE: Remember the default numbers used by Rhai are i64 and f64. // NOTE: Remember the default numbers used by Rhai are INT and f64.
// Better stick to them or it gets hard to work with other variables in the script. // Better stick to them or it gets hard to work with other variables in the script.
scope.push("y", 42_i64); scope.push("y", 42 as INT);
scope.push("z", 999_i64); scope.push("z", 999 as INT);
// First invocation // First invocation
engine engine
@ -37,12 +37,12 @@ fn test_scope_eval() -> Result<(), EvalAltResult> {
.expect("y and z not found?"); .expect("y and z not found?");
// Second invocation using the same state // Second invocation using the same state
let result = engine.eval_with_scope::<i64>(&mut scope, false, "x")?; let result = engine.eval_with_scope::<INT>(&mut scope, false, "x")?;
println!("result: {}", result); // should print 966 println!("result: {}", result); // should print 966
// Variable y is changed in the script // Variable y is changed in the script
assert_eq!(scope.get_value::<i64>("y").unwrap(), 1); assert_eq!(scope.get_value::<INT>("y").unwrap(), 1);
Ok(()) Ok(())
} }

View File

@ -1,11 +1,11 @@
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_while() -> Result<(), EvalAltResult> { fn test_while() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); let mut engine = Engine::new();
assert_eq!( assert_eq!(
engine.eval::<i64>( engine.eval::<INT>(
"let x = 0; while x < 10 { x = x + 1; if x > 5 { \ "let x = 0; while x < 10 { x = x + 1; if x > 5 { \
break } } x", break } } x",
)?, )?,