Merge pull request #369 from schungx/master
Fix switch statement panic.
This commit is contained in:
commit
ec0d2ddcb0
@ -10,6 +10,8 @@ Bug fixes
|
||||
* Errors in native Rust functions now contain the correct function call positions.
|
||||
* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped.
|
||||
* Some expressions involving shared variables now work properly, for example `x in shared_value`, `return shared_value`, `obj.field = shared_value` etc. Previously, the resultant value is still shared which is counter-intuitive.
|
||||
* Potential overflow panics in `range(from, to, step)` is fixed.
|
||||
* `switch` statements no longer panic on custom types.
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
@ -23,6 +25,7 @@ Breaking changes
|
||||
* Function keywords (e.g. `type_of`, `eval`, `Fn`) can no longer be overloaded. It is more trouble than worth. To disable these keywords, use `Engine::disable_symbol`.
|
||||
* `is_def_var` and `is_def_fn` are now reserved keywords.
|
||||
* `Engine::id` field is removed.
|
||||
* `num-traits` is now a required dependency.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
@ -26,6 +26,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
||||
[dependencies]
|
||||
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
||||
ahash = { version = "0.6", default-features = false }
|
||||
num-traits = { version = "0.2", default_features = false }
|
||||
rhai_codegen = { version = "0.3.3", path = "codegen" }
|
||||
|
||||
[features]
|
||||
@ -65,11 +66,6 @@ version = "0.2"
|
||||
default_features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.core-error]
|
||||
version = "0.0"
|
||||
default_features = false
|
||||
|
@ -35,7 +35,7 @@ Standard features
|
||||
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, integers, floating-point numbers (including [`Decimal`](https://crates.io/crates/rust_decimal)), strings, Unicode characters, arrays and maps.
|
||||
* Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust.
|
||||
* Relatively little `unsafe` code (yes there are some for performance reasons).
|
||||
* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec) and [`ahash`](https://crates.io/crates/ahash)).
|
||||
* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits) and [`ahash`](https://crates.io/crates/ahash)).
|
||||
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
||||
* Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations.
|
||||
* Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).
|
||||
|
@ -1109,7 +1109,7 @@ pub struct FloatWrapper(FLOAT);
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl Hash for FloatWrapper {
|
||||
fn hash<H: crate::stdlib::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.to_le_bytes().hash(state);
|
||||
self.0.to_ne_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,13 +414,13 @@ impl Hash for Dynamic {
|
||||
#[cfg(feature = "sync")]
|
||||
Union::Shared(cell, _) => (*cell.read().unwrap()).hash(state),
|
||||
|
||||
_ => unimplemented!(),
|
||||
_ => unimplemented!("{} cannot be hashed", self.type_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Map the name of a standard type into a friendly form.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||
if name == type_name::<String>() {
|
||||
"string"
|
||||
@ -761,6 +761,32 @@ impl Dynamic {
|
||||
_ => self.access_mode().is_read_only(),
|
||||
}
|
||||
}
|
||||
/// Can this [`Dynamic`] be hashed?
|
||||
pub(crate) fn is_hashable(&self) -> bool {
|
||||
match &self.0 {
|
||||
Union::Unit(_, _)
|
||||
| Union::Bool(_, _)
|
||||
| Union::Str(_, _)
|
||||
| Union::Char(_, _)
|
||||
| Union::Int(_, _) => true,
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_, _) => true,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_, _) => true,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, _) => true,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(not(feature = "sync"))]
|
||||
Union::Shared(cell, _) => cell.borrow().is_hashable(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(feature = "sync")]
|
||||
Union::Shared(cell, _) => cell.read().unwrap().is_hashable(),
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
||||
///
|
||||
/// # Safety
|
||||
@ -1496,7 +1522,7 @@ impl Dynamic {
|
||||
}
|
||||
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
match self.0 {
|
||||
Union::Str(s, _) => Ok(s),
|
||||
|
@ -862,7 +862,7 @@ impl Engine {
|
||||
/// Create a new [`Engine`] with minimal built-in functions.
|
||||
///
|
||||
/// Use [`register_global_module`][Engine::register_global_module] to add packages of functions.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn new_raw() -> Self {
|
||||
Self {
|
||||
global_namespace: Default::default(),
|
||||
@ -2111,18 +2111,29 @@ impl Engine {
|
||||
Stmt::Switch(match_expr, x, _) => {
|
||||
let (table, def_stmt) = x.as_ref();
|
||||
|
||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?;
|
||||
|
||||
if value.is_hashable() {
|
||||
let hasher = &mut get_hasher();
|
||||
self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?
|
||||
.hash(hasher);
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
if let Some(stmt) = table.get(&hash) {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||
} else if let Some(def_stmt) = def_stmt {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||
table
|
||||
.get(&hash)
|
||||
.map(|stmt| self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level))
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
// Non-hashable values never match any specific clause
|
||||
None
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
// Default match clause
|
||||
def_stmt.as_ref().map_or_else(
|
||||
|| Ok(Dynamic::UNIT),
|
||||
|def_stmt| {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// While loop
|
||||
|
@ -1034,7 +1034,6 @@ impl Engine {
|
||||
/// Read the contents of a file into a string.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
#[inline]
|
||||
fn read_file(path: crate::stdlib::path::PathBuf) -> Result<String, Box<EvalAltResult>> {
|
||||
use crate::stdlib::io::Read;
|
||||
|
||||
@ -1276,7 +1275,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn compile_expression_with_scope(
|
||||
&self,
|
||||
scope: &Scope,
|
||||
@ -1385,7 +1384,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn eval_with_scope<T: Variant + Clone>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1437,7 +1436,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn eval_expression_with_scope<T: Variant + Clone>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1502,7 +1501,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn eval_ast_with_scope<T: Variant + Clone>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1578,7 +1577,7 @@ impl Engine {
|
||||
}
|
||||
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn consume_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1659,7 +1658,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn call_fn<T: Variant + Clone>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1760,7 +1759,7 @@ impl Engine {
|
||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||
/// clone them _before_ calling this function.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn call_fn_dynamic_raw(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1814,7 +1813,7 @@ impl Engine {
|
||||
/// (i.e. with [`Scope::push_constant`]).
|
||||
/// Then, the [`AST`] is cloned and the copy re-optimized before running.
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn optimize_ast(
|
||||
&self,
|
||||
scope: &Scope,
|
||||
|
@ -15,6 +15,7 @@ use rust_decimal::Decimal;
|
||||
use num_traits::float::Float;
|
||||
|
||||
/// Is the type a numeric type?
|
||||
#[inline(always)]
|
||||
fn is_numeric(type_id: TypeId) -> bool {
|
||||
let result = type_id == TypeId::of::<u8>()
|
||||
|| type_id == TypeId::of::<u16>()
|
||||
@ -75,55 +76,48 @@ pub fn get_builtin_binary_op_fn(
|
||||
Ok((x $op y).into())
|
||||
})
|
||||
};
|
||||
($xx:ident $op:tt $yy:ident -> $base:ty) => {
|
||||
($base:ty => $xx:ident $op:tt $yy:ident) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok((x $op y).into())
|
||||
})
|
||||
};
|
||||
($xx:ident . $func:ident ( $yy:ident as $yyy:ty) -> $base:ty) => {
|
||||
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok(x.$func(y as $yyy).into())
|
||||
})
|
||||
};
|
||||
($func:ident ( $xx:ident, $yy:ident ) -> $base:ty) => {
|
||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
$func(x, y)
|
||||
})
|
||||
};
|
||||
($xx:ident $op:tt $yy:ident -> from $base:ty) => {
|
||||
(from $base:ty => $xx:ident $op:tt $yy:ident) => {
|
||||
return Some(|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok((x $op y).into())
|
||||
})
|
||||
};
|
||||
($xx:ident . $func:ident ( $yy:ident ) -> from $base:ty) => {
|
||||
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => {
|
||||
return Some(|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok(x.$func(y).into())
|
||||
})
|
||||
};
|
||||
($func:ident ( $xx:ident, $yy:ident ) -> from $base:ty) => {
|
||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||
return Some(|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
$func(x, y)
|
||||
})
|
||||
};
|
||||
(& $x:ident $op:tt & $y:ident) => {
|
||||
return Some(|_, args| {
|
||||
let x = &*args[0].read_lock::<$x>().unwrap();
|
||||
let y = &*args[1].read_lock::<$y>().unwrap();
|
||||
Ok((x $op y).into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_float {
|
||||
@ -131,18 +125,18 @@ pub fn get_builtin_binary_op_fn(
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
match op {
|
||||
"+" => impl_op!($xx + $yy -> FLOAT),
|
||||
"-" => impl_op!($xx - $yy -> FLOAT),
|
||||
"*" => impl_op!($xx * $yy -> FLOAT),
|
||||
"/" => impl_op!($xx / $yy -> FLOAT),
|
||||
"%" => impl_op!($xx % $yy -> FLOAT),
|
||||
"**" => impl_op!($xx.powf($yy as FLOAT) -> FLOAT),
|
||||
"==" => impl_op!($xx == $yy -> FLOAT),
|
||||
"!=" => impl_op!($xx != $yy -> FLOAT),
|
||||
">" => impl_op!($xx > $yy -> FLOAT),
|
||||
">=" => impl_op!($xx >= $yy -> FLOAT),
|
||||
"<" => impl_op!($xx < $yy -> FLOAT),
|
||||
"<=" => impl_op!($xx <= $yy -> FLOAT),
|
||||
"+" => impl_op!(FLOAT => $xx + $yy),
|
||||
"-" => impl_op!(FLOAT => $xx - $yy),
|
||||
"*" => impl_op!(FLOAT => $xx * $yy),
|
||||
"/" => impl_op!(FLOAT => $xx / $yy),
|
||||
"%" => impl_op!(FLOAT => $xx % $yy),
|
||||
"**" => impl_op!(FLOAT => $xx.powf($yy as FLOAT)),
|
||||
"==" => impl_op!(FLOAT => $xx == $yy),
|
||||
"!=" => impl_op!(FLOAT => $xx != $yy),
|
||||
">" => impl_op!(FLOAT => $xx > $yy),
|
||||
">=" => impl_op!(FLOAT => $xx >= $yy),
|
||||
"<" => impl_op!(FLOAT => $xx < $yy),
|
||||
"<=" => impl_op!(FLOAT => $xx <= $yy),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
@ -161,31 +155,31 @@ pub fn get_builtin_binary_op_fn(
|
||||
use crate::packages::arithmetic::decimal_functions::*;
|
||||
|
||||
match op {
|
||||
"+" => impl_op!(add($xx, $yy) -> from Decimal),
|
||||
"-" => impl_op!(subtract($xx, $yy) -> from Decimal),
|
||||
"*" => impl_op!(multiply($xx, $yy) -> from Decimal),
|
||||
"/" => impl_op!(divide($xx, $yy) -> from Decimal),
|
||||
"%" => impl_op!(modulo($xx, $yy) -> from Decimal),
|
||||
"+" => impl_op!(from Decimal => add($xx, $yy)),
|
||||
"-" => impl_op!(from Decimal => subtract($xx, $yy)),
|
||||
"*" => impl_op!(from Decimal => multiply($xx, $yy)),
|
||||
"/" => impl_op!(from Decimal => divide($xx, $yy)),
|
||||
"%" => impl_op!(from Decimal => modulo($xx, $yy)),
|
||||
_ => ()
|
||||
}
|
||||
} else {
|
||||
match op {
|
||||
"+" => impl_op!($xx + $yy -> from Decimal),
|
||||
"-" => impl_op!($xx - $yy -> from Decimal),
|
||||
"*" => impl_op!($xx * $yy -> from Decimal),
|
||||
"/" => impl_op!($xx / $yy -> from Decimal),
|
||||
"%" => impl_op!($xx % $yy -> from Decimal),
|
||||
"+" => impl_op!(from Decimal => $xx + $yy),
|
||||
"-" => impl_op!(from Decimal => $xx - $yy),
|
||||
"*" => impl_op!(from Decimal => $xx * $yy),
|
||||
"/" => impl_op!(from Decimal => $xx / $yy),
|
||||
"%" => impl_op!(from Decimal => $xx % $yy),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
match op {
|
||||
"==" => impl_op!($xx == $yy -> from Decimal),
|
||||
"!=" => impl_op!($xx != $yy -> from Decimal),
|
||||
">" => impl_op!($xx > $yy -> from Decimal),
|
||||
">=" => impl_op!($xx >= $yy -> from Decimal),
|
||||
"<" => impl_op!($xx < $yy -> from Decimal),
|
||||
"<=" => impl_op!($xx <= $yy -> from Decimal),
|
||||
"==" => impl_op!(from Decimal => $xx == $yy),
|
||||
"!=" => impl_op!(from Decimal => $xx != $yy),
|
||||
">" => impl_op!(from Decimal => $xx > $yy),
|
||||
">=" => impl_op!(from Decimal => $xx >= $yy),
|
||||
"<" => impl_op!(from Decimal => $xx < $yy),
|
||||
"<=" => impl_op!(from Decimal => $xx <= $yy),
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
@ -278,81 +272,100 @@ pub fn get_builtin_binary_op_fn(
|
||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||
|
||||
match op {
|
||||
"+" => impl_op!(add(as_int, as_int) -> INT),
|
||||
"-" => impl_op!(subtract(as_int, as_int) -> INT),
|
||||
"*" => impl_op!(multiply(as_int, as_int) -> INT),
|
||||
"/" => impl_op!(divide(as_int, as_int) -> INT),
|
||||
"%" => impl_op!(modulo(as_int, as_int) -> INT),
|
||||
"**" => impl_op!(power(as_int, as_int) -> INT),
|
||||
">>" => impl_op!(shift_right(as_int, as_int) -> INT),
|
||||
"<<" => impl_op!(shift_left(as_int, as_int) -> INT),
|
||||
"+" => impl_op!(INT => add(as_int, as_int)),
|
||||
"-" => impl_op!(INT => subtract(as_int, as_int)),
|
||||
"*" => impl_op!(INT => multiply(as_int, as_int)),
|
||||
"/" => impl_op!(INT => divide(as_int, as_int)),
|
||||
"%" => impl_op!(INT => modulo(as_int, as_int)),
|
||||
"**" => impl_op!(INT => power(as_int, as_int)),
|
||||
">>" => impl_op!(INT => shift_right(as_int, as_int)),
|
||||
"<<" => impl_op!(INT => shift_left(as_int, as_int)),
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
match op {
|
||||
"+" => impl_op!(as_int + as_int -> INT),
|
||||
"-" => impl_op!(as_int - as_int -> INT),
|
||||
"*" => impl_op!(as_int * as_int -> INT),
|
||||
"/" => impl_op!(as_int / as_int -> INT),
|
||||
"%" => impl_op!(as_int % as_int -> INT),
|
||||
"**" => impl_op!(as_int.pow(as_int as u32) -> INT),
|
||||
">>" => impl_op!(as_int >> as_int -> INT),
|
||||
"<<" => impl_op!(as_int << as_int -> INT),
|
||||
"+" => impl_op!(INT => as_int + as_int),
|
||||
"-" => impl_op!(INT => as_int - as_int),
|
||||
"*" => impl_op!(INT => as_int * as_int),
|
||||
"/" => impl_op!(INT => as_int / as_int),
|
||||
"%" => impl_op!(INT => as_int % as_int),
|
||||
"**" => impl_op!(INT => as_int.pow(as_int as u32)),
|
||||
">>" => impl_op!(INT => as_int >> as_int),
|
||||
"<<" => impl_op!(INT => as_int << as_int),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
match op {
|
||||
"==" => impl_op!(as_int == as_int -> INT),
|
||||
"!=" => impl_op!(as_int != as_int -> INT),
|
||||
">" => impl_op!(as_int > as_int -> INT),
|
||||
">=" => impl_op!(as_int >= as_int -> INT),
|
||||
"<" => impl_op!(as_int < as_int -> INT),
|
||||
"<=" => impl_op!(as_int <= as_int -> INT),
|
||||
"&" => impl_op!(as_int & as_int -> INT),
|
||||
"|" => impl_op!(as_int | as_int -> INT),
|
||||
"^" => impl_op!(as_int ^ as_int -> INT),
|
||||
"==" => impl_op!(INT => as_int == as_int),
|
||||
"!=" => impl_op!(INT => as_int != as_int),
|
||||
">" => impl_op!(INT => as_int > as_int),
|
||||
">=" => impl_op!(INT => as_int >= as_int),
|
||||
"<" => impl_op!(INT => as_int < as_int),
|
||||
"<=" => impl_op!(INT => as_int <= as_int),
|
||||
"&" => impl_op!(INT => as_int & as_int),
|
||||
"|" => impl_op!(INT => as_int | as_int),
|
||||
"^" => impl_op!(INT => as_int ^ as_int),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<bool>() {
|
||||
match op {
|
||||
"==" => impl_op!(as_bool == as_bool -> bool),
|
||||
"!=" => impl_op!(as_bool != as_bool -> bool),
|
||||
">" => impl_op!(as_bool > as_bool -> bool),
|
||||
">=" => impl_op!(as_bool >= as_bool -> bool),
|
||||
"<" => impl_op!(as_bool < as_bool -> bool),
|
||||
"<=" => impl_op!(as_bool <= as_bool -> bool),
|
||||
"&" => impl_op!(as_bool & as_bool -> bool),
|
||||
"|" => impl_op!(as_bool | as_bool -> bool),
|
||||
"^" => impl_op!(as_bool ^ as_bool -> bool),
|
||||
"==" => impl_op!(bool => as_bool == as_bool),
|
||||
"!=" => impl_op!(bool => as_bool != as_bool),
|
||||
">" => impl_op!(bool => as_bool > as_bool),
|
||||
">=" => impl_op!(bool => as_bool >= as_bool),
|
||||
"<" => impl_op!(bool => as_bool < as_bool),
|
||||
"<=" => impl_op!(bool => as_bool <= as_bool),
|
||||
"&" => impl_op!(bool => as_bool & as_bool),
|
||||
"|" => impl_op!(bool => as_bool | as_bool),
|
||||
"^" => impl_op!(bool => as_bool ^ as_bool),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<ImmutableString>() {
|
||||
match op {
|
||||
"+" => impl_op!(&ImmutableString + &ImmutableString),
|
||||
"-" => impl_op!(&ImmutableString - &ImmutableString),
|
||||
"==" => impl_op!(&ImmutableString == &ImmutableString),
|
||||
"!=" => impl_op!(&ImmutableString != &ImmutableString),
|
||||
">" => impl_op!(&ImmutableString > &ImmutableString),
|
||||
">=" => impl_op!(&ImmutableString >= &ImmutableString),
|
||||
"<" => impl_op!(&ImmutableString < &ImmutableString),
|
||||
"<=" => impl_op!(&ImmutableString <= &ImmutableString),
|
||||
"+" => {
|
||||
return Some(|_, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||
Ok((x + y).into())
|
||||
})
|
||||
}
|
||||
"-" => {
|
||||
return Some(|_, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||
Ok((x - y).into())
|
||||
})
|
||||
}
|
||||
"==" => impl_op!(&str => as_str == as_str),
|
||||
"!=" => impl_op!(&str => as_str != as_str),
|
||||
">" => impl_op!(&str => as_str > as_str),
|
||||
">=" => impl_op!(&str => as_str >= as_str),
|
||||
"<" => impl_op!(&str => as_str < as_str),
|
||||
"<=" => impl_op!(&str => as_str <= as_str),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<char>() {
|
||||
match op {
|
||||
"==" => impl_op!(as_char == as_char -> char),
|
||||
"!=" => impl_op!(as_char != as_char -> char),
|
||||
">" => impl_op!(as_char > as_char -> char),
|
||||
">=" => impl_op!(as_char >= as_char -> char),
|
||||
"<" => impl_op!(as_char < as_char -> char),
|
||||
"<=" => impl_op!(as_char <= as_char -> char),
|
||||
"+" => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].as_char().unwrap();
|
||||
let y = args[1].as_char().unwrap();
|
||||
Ok(format!("{}{}", x, y).into())
|
||||
})
|
||||
}
|
||||
"==" => impl_op!(char => as_char == as_char),
|
||||
"!=" => impl_op!(char => as_char != as_char),
|
||||
">" => impl_op!(char => as_char > as_char),
|
||||
">=" => impl_op!(char => as_char >= as_char),
|
||||
"<" => impl_op!(char => as_char < as_char),
|
||||
"<=" => impl_op!(char => as_char <= as_char),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
@ -380,7 +393,7 @@ pub fn get_builtin_op_assignment_fn(
|
||||
let types_pair = (type1, type2);
|
||||
|
||||
macro_rules! impl_op {
|
||||
($x:ident = x $op:tt $yy:ident) => {
|
||||
($x:ty = x $op:tt $yy:ident) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$yy().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
@ -399,14 +412,14 @@ pub fn get_builtin_op_assignment_fn(
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||
})
|
||||
};
|
||||
($xx:ident . $func:ident ( $yy:ident as $yyy:ty ) -> $x:ty) => {
|
||||
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||
})
|
||||
};
|
||||
($func:ident ( $xx:ident, $yy:ident ) -> $x:ty) => {
|
||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
@ -419,14 +432,14 @@ pub fn get_builtin_op_assignment_fn(
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||
})
|
||||
};
|
||||
($xx:ident . $func:ident ( $yy:ident ) -> from $x:ty) => {
|
||||
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||
})
|
||||
};
|
||||
($func:ident ( $xx:ident, $yy:ident ) -> from $x:ty) => {
|
||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
|
||||
return Some(|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
@ -445,7 +458,7 @@ pub fn get_builtin_op_assignment_fn(
|
||||
"*=" => impl_op!($x *= $yy),
|
||||
"/=" => impl_op!($x /= $yy),
|
||||
"%=" => impl_op!($x %= $yy),
|
||||
"**=" => impl_op!($xx.powf($yy as $x) -> $x),
|
||||
"**=" => impl_op!($x => $xx.powf($yy as $x)),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
@ -463,11 +476,11 @@ pub fn get_builtin_op_assignment_fn(
|
||||
use crate::packages::arithmetic::decimal_functions::*;
|
||||
|
||||
match op {
|
||||
"+=" => impl_op!(add($xx, $yy) -> from $x),
|
||||
"-=" => impl_op!(subtract($xx, $yy) -> from $x),
|
||||
"*=" => impl_op!(multiply($xx, $yy) -> from $x),
|
||||
"/=" => impl_op!(divide($xx, $yy) -> from $x),
|
||||
"%=" => impl_op!(modulo($xx, $yy) -> from $x),
|
||||
"+=" => impl_op!(from $x => add($xx, $yy)),
|
||||
"-=" => impl_op!(from $x => subtract($xx, $yy)),
|
||||
"*=" => impl_op!(from $x => multiply($xx, $yy)),
|
||||
"/=" => impl_op!(from $x => divide($xx, $yy)),
|
||||
"%=" => impl_op!(from $x => modulo($xx, $yy)),
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
@ -522,14 +535,14 @@ pub fn get_builtin_op_assignment_fn(
|
||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||
|
||||
match op {
|
||||
"+=" => impl_op!(add(as_int, as_int) -> INT),
|
||||
"-=" => impl_op!(subtract(as_int, as_int) -> INT),
|
||||
"*=" => impl_op!(multiply(as_int, as_int) -> INT),
|
||||
"/=" => impl_op!(divide(as_int, as_int) -> INT),
|
||||
"%=" => impl_op!(modulo(as_int, as_int) -> INT),
|
||||
"**=" => impl_op!(power(as_int, as_int) -> INT),
|
||||
">>=" => impl_op!(shift_right(as_int, as_int) -> INT),
|
||||
"<<=" => impl_op!(shift_left(as_int, as_int) -> INT),
|
||||
"+=" => impl_op!(INT => add(as_int, as_int)),
|
||||
"-=" => impl_op!(INT => subtract(as_int, as_int)),
|
||||
"*=" => impl_op!(INT => multiply(as_int, as_int)),
|
||||
"/=" => impl_op!(INT => divide(as_int, as_int)),
|
||||
"%=" => impl_op!(INT => modulo(as_int, as_int)),
|
||||
"**=" => impl_op!(INT => power(as_int, as_int)),
|
||||
">>=" => impl_op!(INT => shift_right(as_int, as_int)),
|
||||
"<<=" => impl_op!(INT => shift_left(as_int, as_int)),
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
@ -539,7 +552,7 @@ pub fn get_builtin_op_assignment_fn(
|
||||
"*=" => impl_op!(INT *= as_int),
|
||||
"/=" => impl_op!(INT /= as_int),
|
||||
"%=" => impl_op!(INT %= as_int),
|
||||
"**=" => impl_op!(as_int.pow(as_int as u32) -> INT),
|
||||
"**=" => impl_op!(INT => as_int.pow(as_int as u32)),
|
||||
">>=" => impl_op!(INT >>= as_int),
|
||||
"<<=" => impl_op!(INT <<= as_int),
|
||||
_ => (),
|
||||
|
@ -176,7 +176,7 @@ impl Engine {
|
||||
/// 3) Global modules - packages
|
||||
/// 4) Imported modules - functions marked with global namespace
|
||||
/// 5) Global sub-modules - functions marked with global namespace
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn resolve_function<'s>(
|
||||
&self,
|
||||
mods: &Imports,
|
||||
@ -831,7 +831,7 @@ impl Engine {
|
||||
|
||||
/// Evaluate a list of statements with no `this` pointer.
|
||||
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn eval_global_statements(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
|
@ -18,11 +18,6 @@ use crate::{
|
||||
Position, RhaiResult,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
use crate::stdlib::rc::Rc;
|
||||
#[cfg(feature = "sync")]
|
||||
use crate::stdlib::sync::Arc;
|
||||
|
||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||
#[cfg(feature = "sync")]
|
||||
pub trait SendSync: Send + Sync {}
|
||||
@ -39,19 +34,19 @@ impl<T> SendSync for T {}
|
||||
|
||||
/// Immutable reference-counted container.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type Shared<T> = Rc<T>;
|
||||
pub use crate::stdlib::rc::Rc as Shared;
|
||||
/// Immutable reference-counted container.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type Shared<T> = Arc<T>;
|
||||
pub use crate::stdlib::sync::Arc as Shared;
|
||||
|
||||
/// Synchronized shared object.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type Locked<T> = crate::stdlib::cell::RefCell<T>;
|
||||
pub use crate::stdlib::cell::RefCell as Locked;
|
||||
/// Synchronized shared object.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(feature = "sync")]
|
||||
pub type Locked<T> = crate::stdlib::sync::RwLock<T>;
|
||||
pub use crate::stdlib::sync::RwLock as Locked;
|
||||
|
||||
/// Context of a native Rust function call.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -215,10 +210,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
||||
#[inline(always)]
|
||||
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return Rc::make_mut(value);
|
||||
#[cfg(feature = "sync")]
|
||||
return Arc::make_mut(value);
|
||||
Shared::make_mut(value)
|
||||
}
|
||||
|
||||
/// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise.
|
||||
@ -230,10 +222,7 @@ pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
|
||||
/// Consume a [`Shared`] resource if is unique (i.e. not shared).
|
||||
#[inline(always)]
|
||||
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return Rc::try_unwrap(value);
|
||||
#[cfg(feature = "sync")]
|
||||
return Arc::try_unwrap(value);
|
||||
Shared::try_unwrap(value)
|
||||
}
|
||||
|
||||
/// Consume a [`Shared`] resource, assuming that it is unique (i.e. not shared).
|
||||
|
@ -183,7 +183,7 @@ macro_rules! def_register {
|
||||
RET: Variant + Clone
|
||||
> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
||||
{
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
||||
&[$(map_type_id::<$par>()),*],
|
||||
@ -198,7 +198,7 @@ macro_rules! def_register {
|
||||
FN: Fn($($param),*) -> RhaiResult + SendSync + 'static,
|
||||
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
||||
{
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
||||
&[$(map_type_id::<$par>()),*],
|
||||
|
@ -125,7 +125,7 @@ pub type FLOAT = f32;
|
||||
pub use ast::{FnAccess, ScriptFnMetadata, AST};
|
||||
pub use dynamic::Dynamic;
|
||||
pub use engine::{Engine, EvalContext};
|
||||
pub use fn_native::{FnPtr, NativeCallContext, Shared};
|
||||
pub use fn_native::{FnPtr, NativeCallContext};
|
||||
pub use fn_register::{RegisterFn, RegisterResultFn};
|
||||
pub use module::{FnNamespace, Module};
|
||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||
@ -135,6 +135,9 @@ pub use syntax::Expression;
|
||||
pub use token::Position;
|
||||
pub use utils::ImmutableString;
|
||||
|
||||
/// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag.
|
||||
pub use fn_native::Shared;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
use fn_native::Locked;
|
||||
|
||||
|
@ -598,7 +598,7 @@ impl Module {
|
||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn contains_fn(&self, hash_fn: NonZeroU64, public_only: bool) -> bool {
|
||||
if public_only {
|
||||
self.functions
|
||||
|
@ -1,10 +1,15 @@
|
||||
use crate::dynamic::Variant;
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
ops::{Add, Range},
|
||||
string::ToString,
|
||||
};
|
||||
use crate::{def_package, EvalAltResult, Position, INT};
|
||||
use crate::stdlib::{boxed::Box, ops::Range};
|
||||
use crate::{def_package, EvalAltResult, INT};
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
use crate::stdlib::string::ToString;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
use num_traits::{CheckedAdd as Add, CheckedSub as Sub};
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
use crate::stdlib::ops::{Add, Sub};
|
||||
|
||||
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
||||
Ok(from..to)
|
||||
@ -14,30 +19,35 @@ fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAlt
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
struct StepRange<T>(T, T, T)
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd;
|
||||
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>;
|
||||
|
||||
impl<T> StepRange<T>
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd,
|
||||
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>,
|
||||
{
|
||||
pub fn new(from: T, to: T, step: T) -> Result<Self, Box<EvalAltResult>> {
|
||||
if &from + &step == from {
|
||||
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
"invalid step value".to_string(),
|
||||
Position::NONE,
|
||||
)))
|
||||
} else {
|
||||
Ok(Self(from, to, step))
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if let Some(r) = from.checked_add(&step) {
|
||||
if r == from {
|
||||
return Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"range".to_string(),
|
||||
"".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
"step value cannot be zero".to_string(),
|
||||
crate::Position::NONE,
|
||||
)),
|
||||
crate::Position::NONE,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self(from, to, step))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for StepRange<T>
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd,
|
||||
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
@ -45,29 +55,90 @@ where
|
||||
if self.0 == self.1 {
|
||||
None
|
||||
} else if self.0 < self.1 {
|
||||
let v = self.0.clone();
|
||||
let n = self.0.add(&self.2);
|
||||
self.0 = if n >= self.1 { self.1.clone() } else { n };
|
||||
Some(v)
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let diff1 = if let Some(diff) = self.1.checked_sub(&self.0) {
|
||||
diff
|
||||
} else {
|
||||
let v = self.0.clone();
|
||||
let n = self.0.add(&self.2);
|
||||
self.0 = if n <= self.1 { self.1.clone() } else { n };
|
||||
return None;
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let diff1 = self.1 - self.0;
|
||||
|
||||
let v = self.0;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let n = if let Some(num) = self.0.checked_add(&self.2) {
|
||||
num
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let n = self.0 + self.2;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let diff2 = if let Some(diff) = self.1.checked_sub(&n) {
|
||||
diff
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let diff2 = self.1 - n;
|
||||
|
||||
if diff2 >= diff1 {
|
||||
None
|
||||
} else {
|
||||
self.0 = if n >= self.1 { self.1 } else { n };
|
||||
Some(v)
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let diff1 = if let Some(diff) = self.0.checked_sub(&self.1) {
|
||||
diff
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let diff1 = self.0 - self.1;
|
||||
|
||||
let v = self.0;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let n = if let Some(num) = self.0.checked_add(&self.2) {
|
||||
num
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let n = self.0 + self.2;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let diff2 = if let Some(diff) = n.checked_sub(&self.1) {
|
||||
diff
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let diff2 = n - self.1;
|
||||
|
||||
if diff2 >= diff1 {
|
||||
None
|
||||
} else {
|
||||
self.0 = if n <= self.1 { self.1 } else { n };
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_step_range<T>(from: T, to: T, step: T) -> Result<StepRange<T>, Box<EvalAltResult>>
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Variant + Clone + PartialOrd,
|
||||
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>,
|
||||
{
|
||||
StepRange::<T>::new(from, to, step)
|
||||
}
|
||||
|
||||
macro_rules! reg_range {
|
||||
($lib:expr, $x:expr, $( $y:ty ),*) => (
|
||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||
$(
|
||||
$lib.set_iterator::<Range<$y>>();
|
||||
let hash = $lib.set_fn_2($x, get_range::<$y>);
|
||||
@ -77,11 +148,8 @@ macro_rules! reg_range {
|
||||
concat!("Iterator<Item=", stringify!($y), ">")
|
||||
]);
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! reg_stepped_range {
|
||||
($lib:expr, $x:expr, $( $y:ty ),*) => (
|
||||
};
|
||||
($lib:ident | step $x:expr => $( $y:ty ),*) => {
|
||||
$(
|
||||
$lib.set_iterator::<StepRange<$y>>();
|
||||
let hash = $lib.set_fn_3($x, get_step_range::<$y>);
|
||||
@ -92,31 +160,98 @@ macro_rules! reg_stepped_range {
|
||||
concat!("Iterator<Item=", stringify!($y), ">")
|
||||
]);
|
||||
)*
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
reg_range!(lib, "range", INT);
|
||||
reg_range!(lib | "range" => INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_range!(lib, "range", i8, u8, i16, u16, i32, u32, i64, u64);
|
||||
reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64);
|
||||
|
||||
if cfg!(not(target_arch = "wasm32")) {
|
||||
reg_range!(lib, "range", i128, u128);
|
||||
reg_range!(lib | "range" => i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
reg_stepped_range!(lib, "range", INT);
|
||||
reg_range!(lib | step "range" => INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_stepped_range!(lib, "range", i8, u8, i16, u16, i32, u32, i64, u64);
|
||||
reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
|
||||
|
||||
if cfg!(not(target_arch = "wasm32")) {
|
||||
reg_stepped_range!(lib, "range", i128, u128);
|
||||
reg_range!(lib | step "range" => i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use rust_decimal::{
|
||||
prelude::{One, Zero},
|
||||
Decimal,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
struct StepDecimalRange(Decimal, Decimal, Decimal);
|
||||
|
||||
impl StepDecimalRange {
|
||||
pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result<Self, Box<EvalAltResult>> {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if step.is_zero() {
|
||||
use crate::stdlib::string::ToString;
|
||||
|
||||
return Err(Box::new(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||
crate::Position::NONE,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(Self(from, to, step))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for StepDecimalRange {
|
||||
type Item = Decimal;
|
||||
|
||||
fn next(&mut self) -> Option<Decimal> {
|
||||
if self.0 == self.1 {
|
||||
None
|
||||
} else if self.0 < self.1 {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.2.is_sign_negative() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let v = self.0;
|
||||
let n = self.0 + self.2;
|
||||
|
||||
self.0 = if n >= self.1 { self.1 } else { n };
|
||||
Some(v)
|
||||
} else {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.2.is_sign_positive() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let v = self.0;
|
||||
let n = self.0 + self.2;
|
||||
|
||||
self.0 = if n <= self.1 { self.1 } else { n };
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lib.set_iterator::<StepDecimalRange>();
|
||||
|
||||
let hash = lib.set_fn_2("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
|
||||
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]);
|
||||
|
||||
let hash = lib.set_fn_3("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
||||
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||
}
|
||||
});
|
||||
|
@ -3,9 +3,6 @@
|
||||
use crate::def_package;
|
||||
use crate::plugin::*;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[cfg(any(
|
||||
not(feature = "no_float"),
|
||||
all(not(feature = "only_i32"), not(feature = "only_i64"))
|
||||
@ -59,12 +56,6 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||
combine_with_exported_module!(lib, "f64", f64_functions);
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
reg_functions!(lib += decimal; Decimal);
|
||||
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||
}
|
||||
|
||||
set_exported_fn!(lib, "!", not);
|
||||
});
|
||||
|
||||
@ -91,9 +82,6 @@ gen_cmp_functions!(float => f32);
|
||||
#[cfg(feature = "f32_float")]
|
||||
gen_cmp_functions!(float => f64);
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_cmp_functions!(decimal => Decimal);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod f32_functions {
|
||||
@ -203,59 +191,3 @@ mod f64_functions {
|
||||
(x as f64) <= (y as f64)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[export_module]
|
||||
mod decimal_functions {
|
||||
use crate::INT;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[rhai_fn(name = "==")]
|
||||
pub fn eq_if(x: INT, y: Decimal) -> bool {
|
||||
Decimal::from(x) == y
|
||||
}
|
||||
#[rhai_fn(name = "==")]
|
||||
pub fn eq_fi(x: Decimal, y: INT) -> bool {
|
||||
x == Decimal::from(y)
|
||||
}
|
||||
#[rhai_fn(name = "!=")]
|
||||
pub fn neq_if(x: INT, y: Decimal) -> bool {
|
||||
Decimal::from(x) != y
|
||||
}
|
||||
#[rhai_fn(name = "!=")]
|
||||
pub fn neq_fi(x: Decimal, y: INT) -> bool {
|
||||
x != Decimal::from(y)
|
||||
}
|
||||
#[rhai_fn(name = ">")]
|
||||
pub fn gt_if(x: INT, y: Decimal) -> bool {
|
||||
Decimal::from(x) > y
|
||||
}
|
||||
#[rhai_fn(name = ">")]
|
||||
pub fn gt_fi(x: Decimal, y: INT) -> bool {
|
||||
x > Decimal::from(y)
|
||||
}
|
||||
#[rhai_fn(name = ">=")]
|
||||
pub fn gte_if(x: INT, y: Decimal) -> bool {
|
||||
Decimal::from(x) >= y
|
||||
}
|
||||
#[rhai_fn(name = ">=")]
|
||||
pub fn gte_fi(x: Decimal, y: INT) -> bool {
|
||||
x >= Decimal::from(y)
|
||||
}
|
||||
#[rhai_fn(name = "<")]
|
||||
pub fn lt_if(x: INT, y: Decimal) -> bool {
|
||||
Decimal::from(x) < y
|
||||
}
|
||||
#[rhai_fn(name = "<")]
|
||||
pub fn lt_fi(x: Decimal, y: INT) -> bool {
|
||||
x < Decimal::from(y)
|
||||
}
|
||||
#[rhai_fn(name = "<=")]
|
||||
pub fn lte_if(x: INT, y: Decimal) -> bool {
|
||||
Decimal::from(x) <= y
|
||||
}
|
||||
#[rhai_fn(name = "<=")]
|
||||
pub fn lte_fi(x: Decimal, y: INT) -> bool {
|
||||
x <= Decimal::from(y)
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl<'e> ParseState<'e> {
|
||||
/// i.e. the top element of the [`ParseState`] is offset 1.
|
||||
///
|
||||
/// Return `None` when the variable name is not found in the `stack`.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
||||
let mut barrier = false;
|
||||
|
||||
@ -230,7 +230,7 @@ impl Expr {
|
||||
/// Convert a [`Variable`][Expr::Variable] into a [`Property`][Expr::Property].
|
||||
/// All other variants are untouched.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn into_property(self, state: &mut ParseState) -> Self {
|
||||
match self {
|
||||
Self::Variable(x) if x.1.is_none() => {
|
||||
|
@ -153,13 +153,13 @@ impl fmt::Display for EvalAltResult {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::ErrorInFunctionCall(s, src, err, _) if crate::engine::is_anonymous_fn(s) => {
|
||||
write!(f, "{}, in call to closure", err)?;
|
||||
write!(f, "{} in call to closure", err)?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
}
|
||||
}
|
||||
Self::ErrorInFunctionCall(s, src, err, _) => {
|
||||
write!(f, "{}, in call to function {}", err, s)?;
|
||||
write!(f, "{} in call to function {}", err, s)?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ impl<'a> Scope<'a> {
|
||||
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
|
||||
}
|
||||
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_dynamic_value(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
@ -420,7 +420,7 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
/// Clone the [`Scope`], keeping only the last instances of each variable name.
|
||||
/// Shadowed variables are omitted in the copy.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn clone_visible(&self) -> Self {
|
||||
let mut entries: Self = Default::default();
|
||||
|
||||
|
@ -1029,7 +1029,7 @@ fn scan_block_comment(
|
||||
/// # Volatile API
|
||||
///
|
||||
/// This function is volatile and may change.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn get_next_token(
|
||||
stream: &mut impl InputStream,
|
||||
state: &mut TokenizeState,
|
||||
@ -1856,7 +1856,7 @@ impl Engine {
|
||||
self.lex_raw(input, Some(map))
|
||||
}
|
||||
/// Tokenize an input text stream with an optional mapping function.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn lex_raw<'a>(
|
||||
&'a self,
|
||||
input: impl IntoIterator<Item = &'a &'a str>,
|
||||
|
@ -49,7 +49,7 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
||||
///
|
||||
/// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves
|
||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
|
||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
||||
// this is safe because all local variables are cleared at the end of the block
|
||||
|
11
src/utils.rs
11
src/utils.rs
@ -34,11 +34,13 @@ impl Hasher for StraightHasher {
|
||||
}
|
||||
#[inline(always)]
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
|
||||
|
||||
let mut key = [0_u8; 8];
|
||||
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes
|
||||
key.copy_from_slice(bytes);
|
||||
|
||||
// HACK - If it so happens to hash directly to zero (OMG!) then change it to 42...
|
||||
self.0 = NonZeroU64::new(u64::from_le_bytes(key))
|
||||
self.0 = NonZeroU64::new(u64::from_ne_bytes(key))
|
||||
.unwrap_or_else(|| NonZeroU64::new(42).unwrap());
|
||||
}
|
||||
}
|
||||
@ -58,9 +60,8 @@ impl BuildHasher for StraightHasherBuilder {
|
||||
|
||||
/// Create an instance of the default hasher.
|
||||
#[inline(always)]
|
||||
pub fn get_hasher() -> impl Hasher {
|
||||
let s: ahash::AHasher = Default::default();
|
||||
s
|
||||
pub fn get_hasher() -> ahash::AHasher {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and
|
||||
|
77
tests/for.rs
77
tests/for.rs
@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, Module, INT};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[test]
|
||||
fn test_for_array() -> Result<(), Box<EvalAltResult>> {
|
||||
fn test_for() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
let script = r"
|
||||
@ -27,6 +27,81 @@ fn test_for_array() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert_eq!(engine.eval::<INT>(script)?, 35);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r"
|
||||
let sum = 0;
|
||||
for x in range(1, 10, 2) { sum += x; }
|
||||
sum
|
||||
"
|
||||
)?,
|
||||
25
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r"
|
||||
let sum = 0;
|
||||
for x in range(10, 1, 2) { sum += x; }
|
||||
sum
|
||||
"
|
||||
)?,
|
||||
0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r"
|
||||
let sum = 0;
|
||||
for x in range(1, 10, -2) { sum += x; }
|
||||
sum
|
||||
"
|
||||
)?,
|
||||
0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r"
|
||||
let sum = 0;
|
||||
for x in range(10, 1, -2) { sum += x; }
|
||||
sum
|
||||
"
|
||||
)?,
|
||||
30
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[test]
|
||||
fn test_for_overflow() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
let script = r"
|
||||
let sum = 0;
|
||||
|
||||
for x in range(9223372036854775807, 0, 9223372036854775807) {
|
||||
sum += 1;
|
||||
}
|
||||
|
||||
sum
|
||||
";
|
||||
#[cfg(feature = "only_i32")]
|
||||
let script = r"
|
||||
let sum = 0;
|
||||
|
||||
for x in range(2147483647 , 0, 2147483647 ) {
|
||||
sum += 1;
|
||||
}
|
||||
|
||||
sum
|
||||
";
|
||||
|
||||
assert_eq!(engine.eval::<INT>(script)?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,10 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
||||
engine.eval_with_scope::<()>(&mut scope, "switch x { 1 => 123, 2 => 'a' }")?,
|
||||
()
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let x = timestamp(); switch x { 1 => 123, _ => 42 }")?,
|
||||
42
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval_with_scope::<INT>(
|
||||
&mut scope,
|
||||
|
Loading…
Reference in New Issue
Block a user