Merge pull request #363 from schungx/master
Allow evaluating AST for Engine::call_fn_dynamic
This commit is contained in:
commit
1fb63c32a4
@ -14,14 +14,15 @@ Breaking changes
|
|||||||
|
|
||||||
* For plugin functions, constants passed to methods (i.e. `&mut` parameter) now raise an error unless the functions are marked with `#[rhai_fn(pure)]`.
|
* For plugin functions, constants passed to methods (i.e. `&mut` parameter) now raise an error unless the functions are marked with `#[rhai_fn(pure)]`.
|
||||||
* Visibility (i.e. `pub` or not) for generated _plugin_ modules now follow the visibility of the underlying module.
|
* Visibility (i.e. `pub` or not) for generated _plugin_ modules now follow the visibility of the underlying module.
|
||||||
|
* Comparison operators between the sames types or different _numeric_ types now throw errors when they're not defined instead of returning the default. Only comparing between _different_ types will return the default.
|
||||||
* Default stack-overflow and top-level expression nesting limits for release builds are lowered to 64 from 128.
|
* Default stack-overflow and top-level expression nesting limits for release builds are lowered to 64 from 128.
|
||||||
|
* `Engine::call_fn_dynamic` takes an additional parameter to optionally evaluate the given `AST` before calling the function.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Functions are now allowed to have `Dynamic` arguments.
|
* Functions are now allowed to have `Dynamic` arguments.
|
||||||
* `#[rhai_fn(pure)]` attribute to mark a plugin function with `&mut` parameter as _pure_ so constants can be passed to it. Without it, passing a constant value into the `&mut` parameter will now raise an error.
|
* `#[rhai_fn(pure)]` attribute to mark a plugin function with `&mut` parameter as _pure_ so constants can be passed to it. Without it, passing a constant value into the `&mut` parameter will now raise an error.
|
||||||
* Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in.
|
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
@ -30,8 +31,12 @@ Enhancements
|
|||||||
* Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`.
|
* Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`.
|
||||||
* `Position` now implements `Add` and `AddAssign`.
|
* `Position` now implements `Add` and `AddAssign`.
|
||||||
* `Scope` now implements `IntoIterator`.
|
* `Scope` now implements `IntoIterator`.
|
||||||
|
* Strings now have the `-`/`-=` operators and the `remove` method to delete a sub-string/character.
|
||||||
* Strings now have the `split_rev` method and variations of `split` with maximum number of segments.
|
* Strings now have the `split_rev` method and variations of `split` with maximum number of segments.
|
||||||
* Arrays now have the `split` method.
|
* Arrays now have the `split` method.
|
||||||
|
* Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in.
|
||||||
|
* Comparisons between string and `char` are now built in.
|
||||||
|
* `Engine::call_fn_dynamic` can now optionally evaluate the given `AST` before calling the function.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.12
|
Version 0.19.12
|
@ -126,7 +126,7 @@ fn bench_eval_loop_number(bench: &mut Bencher) {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let s = 0;
|
let s = "hello";
|
||||||
for x in range(0, 10000) {
|
for x in range(0, 10000) {
|
||||||
s += "x";
|
s += "x";
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
Procedural Macros for Plugins
|
Procedural Macros for Plugins
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
|
![Rhai logo](https://rhai.rs/book/images/logo/rhai-banner-transparent-colour.svg)
|
||||||
|
|
||||||
This crate holds procedural macros for code generation, supporting the plugins system
|
This crate holds procedural macros for code generation, supporting the plugins system
|
||||||
for [Rhai](https://github.com/rhaiscript/rhai).
|
for [Rhai](https://github.com/rhaiscript/rhai).
|
||||||
|
|
||||||
|
This crate is automatically referenced by the [Rhai crate](https://crates.io/crates/rhai).
|
||||||
|
Normally it should not be used directly.
|
||||||
|
@ -1112,8 +1112,6 @@ pub struct FnCallExpr {
|
|||||||
pub hash_script: Option<NonZeroU64>,
|
pub hash_script: Option<NonZeroU64>,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture: bool,
|
pub capture: bool,
|
||||||
/// Default value when the function is not found, mostly used to provide a default for comparison functions.
|
|
||||||
pub def_value: Option<Dynamic>,
|
|
||||||
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
||||||
pub namespace: Option<NamespaceRef>,
|
pub namespace: Option<NamespaceRef>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
|
@ -475,7 +475,45 @@ impl fmt::Display for Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_, _) => f.write_str("<timestamp>"),
|
Union::TimeStamp(_, _) => f.write_str("<timestamp>"),
|
||||||
|
|
||||||
Union::Variant(value, _) => f.write_str((*value).type_name()),
|
Union::Variant(value, _) => {
|
||||||
|
let _type_id = (***value).type_id();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
if _type_id == TypeId::of::<u8>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<u8>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<u16>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<u16>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<u32>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<u32>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<u64>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<u64>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i8>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<i8>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i16>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<i16>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i32>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<i32>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i64>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<i64>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if _type_id == TypeId::of::<f32>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<f64>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
|
if _type_id == TypeId::of::<u128>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<u128>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i128>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<i128>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
f.write_str((***value).type_name())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -516,7 +554,53 @@ impl fmt::Debug for Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_, _) => write!(f, "<timestamp>"),
|
Union::TimeStamp(_, _) => write!(f, "<timestamp>"),
|
||||||
|
|
||||||
Union::Variant(value, _) => write!(f, "{}", (*value).type_name()),
|
Union::Variant(value, _) => {
|
||||||
|
let _type_id = (***value).type_id();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
if _type_id == TypeId::of::<u8>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u8>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<u16>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u16>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<u32>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u32>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<u64>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u64>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i8>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i8>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i16>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i16>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i32>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i32>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<i64>() {
|
||||||
|
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i64>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if _type_id == TypeId::of::<f32>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
|
||||||
|
} else if _type_id == TypeId::of::<f64>() {
|
||||||
|
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
|
if _type_id == TypeId::of::<u128>() {
|
||||||
|
return write!(
|
||||||
|
f,
|
||||||
|
"{:?}",
|
||||||
|
(**value).as_any().downcast_ref::<u128>().unwrap()
|
||||||
|
);
|
||||||
|
} else if _type_id == TypeId::of::<i128>() {
|
||||||
|
return write!(
|
||||||
|
f,
|
||||||
|
"{:?}",
|
||||||
|
(**value).as_any().downcast_ref::<i128>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", (*value).type_name())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
|
222
src/engine.rs
222
src/engine.rs
@ -193,6 +193,8 @@ pub const MAX_EXPR_DEPTH: usize = 64;
|
|||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
||||||
|
|
||||||
|
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
|
||||||
|
|
||||||
pub const KEYWORD_PRINT: &str = "print";
|
pub const KEYWORD_PRINT: &str = "print";
|
||||||
pub const KEYWORD_DEBUG: &str = "debug";
|
pub const KEYWORD_DEBUG: &str = "debug";
|
||||||
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
||||||
@ -1088,7 +1090,7 @@ impl Engine {
|
|||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, Position)>,
|
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
if chain_type == ChainType::NonChaining {
|
if chain_type == ChainType::NonChaining {
|
||||||
unreachable!("should not be ChainType::NonChaining");
|
unreachable!("should not be ChainType::NonChaining");
|
||||||
@ -1128,7 +1130,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
}
|
}
|
||||||
// xxx[rhs] = new_val
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
let idx_val = idx_val.as_index_value();
|
let idx_val = idx_val.as_index_value();
|
||||||
let mut idx_val2 = idx_val.clone();
|
let mut idx_val2 = idx_val.clone();
|
||||||
@ -1138,9 +1140,11 @@ impl Engine {
|
|||||||
mods, state, lib, target_val, idx_val, pos, true, is_ref, false, level,
|
mods, state, lib, target_val, idx_val, pos, true, is_ref, false, level,
|
||||||
) {
|
) {
|
||||||
// Indexed value is a reference - update directly
|
// Indexed value is a reference - update directly
|
||||||
Ok(ref mut obj_ptr) => {
|
Ok(obj_ptr) => {
|
||||||
let (new_val, new_val_pos) = new_val.unwrap();
|
let ((new_val, new_pos), (op, op_pos)) = new_val.unwrap();
|
||||||
obj_ptr.set_value(new_val, new_val_pos)?;
|
self.eval_op_assignment(
|
||||||
|
mods, state, lib, op, op_pos, obj_ptr, new_val, new_pos,
|
||||||
|
)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
@ -1155,11 +1159,13 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if let Some(mut new_val) = _call_setter {
|
if let Some(mut new_val) = _call_setter {
|
||||||
let val_type_name = target_val.type_name();
|
let val_type_name = target_val.type_name();
|
||||||
let args = &mut [target_val, &mut idx_val2, &mut new_val.0];
|
let ((_, val_pos), _) = new_val;
|
||||||
|
|
||||||
|
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false,
|
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false,
|
||||||
new_val.1, None, None, level,
|
val_pos, None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
@ -1195,14 +1201,11 @@ impl Engine {
|
|||||||
let FnCallExpr {
|
let FnCallExpr {
|
||||||
name,
|
name,
|
||||||
hash_script: hash,
|
hash_script: hash,
|
||||||
def_value,
|
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.as_ref();
|
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, def_value, false, *pos,
|
mods, state, lib, name, *hash, target, args, false, *pos, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.fn_name(...) = ???
|
// xxx.fn_name(...) = ???
|
||||||
@ -1213,18 +1216,18 @@ impl Engine {
|
|||||||
Expr::FnCall(_, _) => {
|
Expr::FnCall(_, _) => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
// {xxx:map}.id = ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
||||||
let Ident { name, pos } = &x.2;
|
let Ident { name, pos } = &x.2;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let mut val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
||||||
)?;
|
)?;
|
||||||
|
let ((new_val, new_pos), (op, op_pos)) = new_val.unwrap();
|
||||||
let (new_val, new_val_pos) = new_val.unwrap();
|
self.eval_op_assignment(
|
||||||
val.set_value(new_val, new_val_pos)?;
|
mods, state, lib, op, op_pos, val, new_val, new_pos,
|
||||||
|
)?;
|
||||||
Ok((Default::default(), true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target_val.is::<Map>() => {
|
Expr::Property(x) if target_val.is::<Map>() => {
|
||||||
@ -1240,10 +1243,10 @@ impl Engine {
|
|||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let (_, setter, Ident { pos, .. }) = x.as_ref();
|
let (_, setter, Ident { pos, .. }) = x.as_ref();
|
||||||
let mut new_val = new_val;
|
let mut new_val = new_val;
|
||||||
let mut args = [target_val, &mut new_val.as_mut().unwrap().0];
|
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos,
|
mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos,
|
||||||
None, None, level,
|
None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.map(|(v, _)| (v, true))
|
||||||
}
|
}
|
||||||
@ -1253,7 +1256,7 @@ impl Engine {
|
|||||||
let mut args = [target_val];
|
let mut args = [target_val];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, None, &mut args, is_ref, true, false, *pos,
|
mods, state, lib, getter, None, &mut args, is_ref, true, false, *pos,
|
||||||
None, None, level,
|
None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map(|(v, _)| (v, false))
|
||||||
}
|
}
|
||||||
@ -1273,14 +1276,11 @@ impl Engine {
|
|||||||
let FnCallExpr {
|
let FnCallExpr {
|
||||||
name,
|
name,
|
||||||
hash_script: hash,
|
hash_script: hash,
|
||||||
def_value,
|
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.as_ref();
|
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (val, _) = self.make_method_call(
|
let (val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, def_value, false,
|
mods, state, lib, name, *hash, target, args, false, *pos, level,
|
||||||
*pos, level,
|
|
||||||
)?;
|
)?;
|
||||||
val.into()
|
val.into()
|
||||||
}
|
}
|
||||||
@ -1308,7 +1308,7 @@ impl Engine {
|
|||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self.exec_fn_call(
|
let (mut val, updated) = self.exec_fn_call(
|
||||||
mods, state, lib, getter, None, args, is_ref, true, false,
|
mods, state, lib, getter, None, args, is_ref, true, false,
|
||||||
*pos, None, None, level,
|
*pos, None, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
@ -1334,7 +1334,7 @@ impl Engine {
|
|||||||
arg_values[1] = val;
|
arg_values[1] = val;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, arg_values, is_ref, true,
|
mods, state, lib, setter, None, arg_values, is_ref, true,
|
||||||
false, *pos, None, None, level,
|
false, *pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1355,14 +1355,11 @@ impl Engine {
|
|||||||
let FnCallExpr {
|
let FnCallExpr {
|
||||||
name,
|
name,
|
||||||
hash_script: hash,
|
hash_script: hash,
|
||||||
def_value,
|
|
||||||
..
|
..
|
||||||
} = f.as_ref();
|
} = f.as_ref();
|
||||||
let def_value = def_value.as_ref();
|
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self.make_method_call(
|
let (mut val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, def_value, false,
|
mods, state, lib, name, *hash, target, args, false, *pos, level,
|
||||||
*pos, level,
|
|
||||||
)?;
|
)?;
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
let target = &mut val.into();
|
let target = &mut val.into();
|
||||||
@ -1401,7 +1398,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, Position)>,
|
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||||
@ -1633,7 +1630,7 @@ impl Engine {
|
|||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
_mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, false, idx_pos,
|
_mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, false, idx_pos,
|
||||||
None, None, _level,
|
None, _level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
@ -1676,23 +1673,16 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(mut rhs_value, _)) => {
|
Dynamic(Union::Array(mut rhs_value, _)) => {
|
||||||
// Call the `==` operator to compare each value
|
// Call the `==` operator to compare each value
|
||||||
let def_value = Some(false.into());
|
|
||||||
let def_value = def_value.as_ref();
|
|
||||||
|
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
let args = &mut [&mut lhs_value.clone(), value];
|
let args = &mut [&mut lhs_value.clone(), value];
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
|
||||||
let hash_fn =
|
let hash_fn =
|
||||||
calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id()))
|
calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
mods, state, lib, OP_EQUALS, hash_fn, args, false, false, pos,
|
mods, state, lib, OP_EQUALS, hash_fn, args, false, false, pos,
|
||||||
def_value,
|
|
||||||
)?
|
)?
|
||||||
.0
|
.0
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -1798,13 +1788,11 @@ impl Engine {
|
|||||||
capture: cap_scope,
|
capture: cap_scope,
|
||||||
hash_script: hash,
|
hash_script: hash,
|
||||||
args,
|
args,
|
||||||
def_value,
|
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.as_ref();
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, def_value, *hash, false, *pos,
|
scope, mods, state, lib, this_ptr, name, args, *hash, false, *pos, *cap_scope,
|
||||||
*cap_scope, level,
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1815,15 +1803,12 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hash_script,
|
hash_script,
|
||||||
args,
|
args,
|
||||||
def_value,
|
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace.as_ref();
|
||||||
let hash = hash_script.unwrap();
|
let hash = hash_script.unwrap();
|
||||||
let def_value = def_value.as_ref();
|
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, def_value, hash,
|
scope, mods, state, lib, this_ptr, namespace, name, args, hash, *pos, level,
|
||||||
*pos, level,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1944,6 +1929,62 @@ impl Engine {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn eval_op_assignment(
|
||||||
|
&self,
|
||||||
|
mods: &mut Imports,
|
||||||
|
state: &mut State,
|
||||||
|
lib: &[&Module],
|
||||||
|
op: &str,
|
||||||
|
op_pos: Position,
|
||||||
|
mut target: Target,
|
||||||
|
mut new_value: Dynamic,
|
||||||
|
new_value_pos: Position,
|
||||||
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
if target.as_ref().is_read_only() {
|
||||||
|
unreachable!("LHS should not be read-only");
|
||||||
|
}
|
||||||
|
|
||||||
|
if op.is_empty() {
|
||||||
|
// Normal assignment
|
||||||
|
target.set_value(new_value, new_value_pos)?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let mut lock_guard;
|
||||||
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
|
if cfg!(not(feature = "no_closure")) && target.is_shared() {
|
||||||
|
lock_guard = target.as_mut().write_lock::<Dynamic>().unwrap();
|
||||||
|
lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
|
} else {
|
||||||
|
lhs_ptr_inner = target.as_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = &mut [lhs_ptr_inner, &mut new_value];
|
||||||
|
let hash_fn =
|
||||||
|
calc_native_fn_hash(empty(), op, args.iter().map(|a| a.type_id())).unwrap();
|
||||||
|
|
||||||
|
match self.call_native_fn(mods, state, lib, op, hash_fn, args, true, true, op_pos) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
|
||||||
|
{
|
||||||
|
// Expand to `var = var op rhs`
|
||||||
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
let hash_fn =
|
||||||
|
calc_native_fn_hash(empty(), op, args.iter().map(|a| a.type_id())).unwrap();
|
||||||
|
|
||||||
|
// Run function
|
||||||
|
let (value, _) = self
|
||||||
|
.call_native_fn(mods, state, lib, op, hash_fn, args, true, false, op_pos)?;
|
||||||
|
|
||||||
|
*args[0] = value.flatten();
|
||||||
|
}
|
||||||
|
err => return err.map(|_| ()),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Evaluate a statement.
|
/// Evaluate a statement.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -1972,10 +2013,10 @@ impl Engine {
|
|||||||
// var op= rhs
|
// var op= rhs
|
||||||
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => {
|
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => {
|
||||||
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
||||||
let mut rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
let (mut lhs_ptr, pos) =
|
let (lhs_ptr, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
||||||
|
|
||||||
if !lhs_ptr.is_ref() {
|
if !lhs_ptr.is_ref() {
|
||||||
@ -1994,47 +2035,17 @@ impl Engine {
|
|||||||
lhs_expr.get_variable_access(false).unwrap().to_string(),
|
lhs_expr.get_variable_access(false).unwrap().to_string(),
|
||||||
pos,
|
pos,
|
||||||
)))
|
)))
|
||||||
} else if op.is_empty() {
|
|
||||||
// Normal assignment
|
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
|
||||||
*lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap() = rhs_val;
|
|
||||||
} else {
|
|
||||||
*lhs_ptr.as_mut() = rhs_val;
|
|
||||||
}
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
} else {
|
} else {
|
||||||
let mut lock_guard;
|
self.eval_op_assignment(
|
||||||
let lhs_ptr_inner;
|
mods,
|
||||||
|
state,
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
lib,
|
||||||
lock_guard = lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap();
|
op,
|
||||||
lhs_ptr_inner = lock_guard.deref_mut();
|
*op_pos,
|
||||||
} else {
|
lhs_ptr,
|
||||||
lhs_ptr_inner = lhs_ptr.as_mut();
|
rhs_val,
|
||||||
}
|
rhs_expr.position(),
|
||||||
|
)?;
|
||||||
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
|
||||||
|
|
||||||
match self.exec_fn_call(
|
|
||||||
mods, state, lib, op, None, args, true, false, false, *op_pos, None, None,
|
|
||||||
level,
|
|
||||||
) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op.as_ref())) =>
|
|
||||||
{
|
|
||||||
// Expand to `var = var op rhs`
|
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
|
||||||
|
|
||||||
// Run function
|
|
||||||
let (value, _) = self.exec_fn_call(
|
|
||||||
mods, state, lib, op, None, args, false, false, false, *op_pos,
|
|
||||||
None, None, level,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
*args[0] = value.flatten();
|
|
||||||
}
|
|
||||||
err => return err.map(|(v, _)| v),
|
|
||||||
}
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2042,28 +2053,8 @@ impl Engine {
|
|||||||
// lhs op= rhs
|
// lhs op= rhs
|
||||||
Stmt::Assignment(x, op_pos) => {
|
Stmt::Assignment(x, op_pos) => {
|
||||||
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
||||||
let mut rhs_val =
|
let rhs_val = self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?;
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?;
|
let _new_val = Some(((rhs_val, rhs_expr.position()), (op.as_ref(), *op_pos)));
|
||||||
|
|
||||||
let _new_val = if op.is_empty() {
|
|
||||||
// Normal assignment
|
|
||||||
Some((rhs_val, rhs_expr.position()))
|
|
||||||
} else {
|
|
||||||
// Op-assignment - always map to `lhs = lhs op rhs`
|
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
|
||||||
let args = &mut [
|
|
||||||
&mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?,
|
|
||||||
&mut rhs_val,
|
|
||||||
];
|
|
||||||
|
|
||||||
Some(
|
|
||||||
self.exec_fn_call(
|
|
||||||
mods, state, lib, op, None, args, false, false, false, *op_pos, None,
|
|
||||||
None, level,
|
|
||||||
)
|
|
||||||
.map(|(v, _)| (v, rhs_expr.position()))?,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
match lhs_expr {
|
match lhs_expr {
|
||||||
@ -2582,9 +2573,12 @@ impl Engine {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a type_name into a pretty-print name
|
/// Pretty-print a type name.
|
||||||
|
///
|
||||||
|
/// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name],
|
||||||
|
/// the type name provided for the registration will be used.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
||||||
self.type_names
|
self.type_names
|
||||||
.get(name)
|
.get(name)
|
||||||
.map(String::as_str)
|
.map(String::as_str)
|
||||||
|
@ -1613,6 +1613,13 @@ impl Engine {
|
|||||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||||
/// Arguments are passed as a tuple.
|
/// Arguments are passed as a tuple.
|
||||||
///
|
///
|
||||||
|
/// ## Warning
|
||||||
|
///
|
||||||
|
/// The [`AST`] is _not_ evaluated before calling the function. The function is called as-is.
|
||||||
|
///
|
||||||
|
/// If the [`AST`] needs to be evaluated before calling the function (usually to load external modules),
|
||||||
|
/// use [`call_fn_dynamic`][Engine::call_fn_dynamic].
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -1633,11 +1640,11 @@ impl Engine {
|
|||||||
/// scope.push("foo", 42_i64);
|
/// scope.push("foo", 42_i64);
|
||||||
///
|
///
|
||||||
/// // Call the script-defined function
|
/// // Call the script-defined function
|
||||||
/// let result: i64 = engine.call_fn(&mut scope, &ast, "add", ( String::from("abc"), 123_i64 ) )?;
|
/// let result: i64 = engine.call_fn(&mut scope, &ast, "add", ( "abc", 123_i64 ) )?;
|
||||||
/// assert_eq!(result, 168);
|
/// assert_eq!(result, 168);
|
||||||
///
|
///
|
||||||
/// let result: i64 = engine.call_fn(&mut scope, &ast, "add1", ( String::from("abc"), ) )?;
|
/// let result: i64 = engine.call_fn(&mut scope, &ast, "add1", ( "abc", ) )?;
|
||||||
/// // ^^^^^^^^^^^^^^^^^^^^^^^^ tuple of one
|
/// // ^^^^^^^^^^ tuple of one
|
||||||
/// assert_eq!(result, 46);
|
/// assert_eq!(result, 46);
|
||||||
///
|
///
|
||||||
/// let result: i64 = engine.call_fn(&mut scope, &ast, "bar", () )?;
|
/// let result: i64 = engine.call_fn(&mut scope, &ast, "bar", () )?;
|
||||||
@ -1659,8 +1666,7 @@ impl Engine {
|
|||||||
args.parse(&mut arg_values);
|
args.parse(&mut arg_values);
|
||||||
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||||
|
|
||||||
let result =
|
let result = self.call_fn_dynamic_raw(scope, ast, false, name, &mut None, args.as_mut())?;
|
||||||
self.call_fn_dynamic_raw(scope, &[ast.lib()], name, &mut None, args.as_mut())?;
|
|
||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -1676,6 +1682,9 @@ impl Engine {
|
|||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments
|
||||||
/// and optionally a value for binding to the `this` pointer.
|
/// and optionally a value for binding to the `this` pointer.
|
||||||
///
|
///
|
||||||
|
/// There is also an option to evaluate the [`AST`] (e.g. to configuration the environment)
|
||||||
|
/// before calling the function.
|
||||||
|
///
|
||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||||
@ -1704,19 +1713,19 @@ impl Engine {
|
|||||||
/// scope.push("foo", 42_i64);
|
/// scope.push("foo", 42_i64);
|
||||||
///
|
///
|
||||||
/// // Call the script-defined function
|
/// // Call the script-defined function
|
||||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add", None, [ String::from("abc").into(), 123_i64.into() ])?;
|
/// let result = engine.call_fn_dynamic(&mut scope, &ast, false, "add", None, [ "abc".into(), 123_i64.into() ])?;
|
||||||
/// // ^^^^ no 'this' pointer
|
/// // ^^^^ no 'this' pointer
|
||||||
/// assert_eq!(result.cast::<i64>(), 168);
|
/// assert_eq!(result.cast::<i64>(), 168);
|
||||||
///
|
///
|
||||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add1", None, [ String::from("abc").into() ])?;
|
/// let result = engine.call_fn_dynamic(&mut scope, &ast, false, "add1", None, [ "abc".into() ])?;
|
||||||
/// assert_eq!(result.cast::<i64>(), 46);
|
/// assert_eq!(result.cast::<i64>(), 46);
|
||||||
///
|
///
|
||||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "bar", None, [])?;
|
/// let result = engine.call_fn_dynamic(&mut scope, &ast, false, "bar", None, [])?;
|
||||||
/// assert_eq!(result.cast::<i64>(), 21);
|
/// assert_eq!(result.cast::<i64>(), 21);
|
||||||
///
|
///
|
||||||
/// let mut value: Dynamic = 1_i64.into();
|
/// let mut value: Dynamic = 1_i64.into();
|
||||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "action", Some(&mut value), [ 41_i64.into() ])?;
|
/// let result = engine.call_fn_dynamic(&mut scope, &ast, false, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||||
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
/// assert_eq!(value.as_int().unwrap(), 42);
|
||||||
/// # }
|
/// # }
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
@ -1727,14 +1736,15 @@ impl Engine {
|
|||||||
pub fn call_fn_dynamic(
|
pub fn call_fn_dynamic(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
lib: impl AsRef<crate::Module>,
|
ast: &AST,
|
||||||
|
eval_ast: bool,
|
||||||
name: &str,
|
name: &str,
|
||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||||
|
|
||||||
self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut())
|
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, args.as_mut())
|
||||||
}
|
}
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
///
|
///
|
||||||
@ -1749,18 +1759,24 @@ impl Engine {
|
|||||||
pub(crate) fn call_fn_dynamic_raw(
|
pub(crate) fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
lib: &[&crate::Module],
|
ast: &AST,
|
||||||
|
eval_ast: bool,
|
||||||
name: &str,
|
name: &str,
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let fn_def = lib
|
let state = &mut Default::default();
|
||||||
.iter()
|
let mods = &mut (&self.global_sub_modules).into();
|
||||||
.find_map(|&m| m.get_script_fn(name, args.len(), true))
|
let lib = &[ast.lib()];
|
||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
|
||||||
|
|
||||||
let mut state = Default::default();
|
if eval_ast {
|
||||||
let mut mods = (&self.global_sub_modules).into();
|
self.eval_global_statements(scope, mods, state, ast.statements(), lib, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fn_def = ast
|
||||||
|
.lib()
|
||||||
|
.get_script_fn(name, args.len(), true)
|
||||||
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
if cfg!(not(feature = "no_closure")) {
|
if cfg!(not(feature = "no_closure")) {
|
||||||
@ -1769,8 +1785,8 @@ impl Engine {
|
|||||||
|
|
||||||
self.call_script_fn(
|
self.call_script_fn(
|
||||||
scope,
|
scope,
|
||||||
&mut mods,
|
mods,
|
||||||
&mut state,
|
state,
|
||||||
lib,
|
lib,
|
||||||
this_ptr,
|
this_ptr,
|
||||||
fn_def,
|
fn_def,
|
||||||
@ -1933,7 +1949,7 @@ impl Engine {
|
|||||||
/// # use std::sync::Arc;
|
/// # use std::sync::Arc;
|
||||||
/// use rhai::Engine;
|
/// use rhai::Engine;
|
||||||
///
|
///
|
||||||
/// let result = Arc::new(RwLock::new(String::from("")));
|
/// let result = Arc::new(RwLock::new(String::new()));
|
||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
@ -1962,7 +1978,7 @@ impl Engine {
|
|||||||
/// # use std::sync::Arc;
|
/// # use std::sync::Arc;
|
||||||
/// use rhai::Engine;
|
/// use rhai::Engine;
|
||||||
///
|
///
|
||||||
/// let result = Arc::new(RwLock::new(String::from("")));
|
/// let result = Arc::new(RwLock::new(String::new()));
|
||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
|
268
src/fn_call.rs
268
src/fn_call.rs
@ -4,6 +4,7 @@ use crate::ast::{Expr, Stmt};
|
|||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
search_imports, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
|
search_imports, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
|
||||||
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
|
MAX_DYNAMIC_PARAMETERS,
|
||||||
};
|
};
|
||||||
use crate::fn_native::FnCallArgs;
|
use crate::fn_native::FnCallArgs;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
@ -16,7 +17,6 @@ use crate::stdlib::{
|
|||||||
iter::{empty, once},
|
iter::{empty, once},
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU64,
|
num::NonZeroU64,
|
||||||
ops::Deref,
|
|
||||||
string::ToString,
|
string::ToString,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -172,9 +172,8 @@ impl Engine {
|
|||||||
hash_fn: NonZeroU64,
|
hash_fn: NonZeroU64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
pub_only: bool,
|
is_op_assignment: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
def_val: Option<&Dynamic>,
|
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
@ -186,15 +185,15 @@ impl Engine {
|
|||||||
.entry(hash_fn)
|
.entry(hash_fn)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let num_args = args.len();
|
let num_args = args.len();
|
||||||
let max_bitmask = 1usize << num_args;
|
let max_bitmask = 1usize << args.len().min(MAX_DYNAMIC_PARAMETERS);
|
||||||
let mut hash = hash_fn;
|
let mut hash = hash_fn;
|
||||||
let mut bitmask = 1usize;
|
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
//lib.get_fn(hash, pub_only).or_else(||
|
//lib.get_fn(hash, false).or_else(||
|
||||||
match self
|
match self
|
||||||
.global_namespace
|
.global_namespace
|
||||||
.get_fn(hash, pub_only)
|
.get_fn(hash, false)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|f| (f, None))
|
.map(|f| (f, None))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
@ -210,21 +209,26 @@ impl Engine {
|
|||||||
// Specific version found
|
// Specific version found
|
||||||
Some(f) => return Some(f),
|
Some(f) => return Some(f),
|
||||||
|
|
||||||
// No more permutations with `Dynamic` wildcards
|
// Stop when all permutations are exhausted
|
||||||
_ if bitmask >= max_bitmask => return None,
|
_ if bitmask >= max_bitmask => return None,
|
||||||
|
|
||||||
// Try all permutations with `Dynamic` wildcards
|
// Try all permutations with `Dynamic` wildcards
|
||||||
_ => {
|
_ => {
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
hash = calc_native_fn_hash(
|
||||||
let arg_types = args.iter().enumerate().map(|(i, a)| {
|
empty(),
|
||||||
let mask = 1usize << (num_args - i - 1);
|
fn_name,
|
||||||
if bitmask & mask != 0 {
|
args.iter().enumerate().map(|(i, a)| {
|
||||||
TypeId::of::<Dynamic>()
|
let mask = 1usize << (num_args - i - 1);
|
||||||
} else {
|
if bitmask & mask != 0 {
|
||||||
a.type_id()
|
// Replace with `Dynamic`
|
||||||
}
|
TypeId::of::<Dynamic>()
|
||||||
});
|
} else {
|
||||||
hash = calc_native_fn_hash(empty(), fn_name, arg_types).unwrap();
|
a.type_id()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
bitmask += 1;
|
bitmask += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,25 +284,24 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See if it is built in.
|
// See if it is built in.
|
||||||
if args.len() == 2 {
|
if args.len() == 2 && !args[0].is_variant() && !args[1].is_variant() {
|
||||||
if is_ref {
|
// Op-assignment?
|
||||||
|
if is_op_assignment {
|
||||||
|
if !is_ref {
|
||||||
|
unreachable!("op-assignments must have ref argument");
|
||||||
|
}
|
||||||
let (first, second) = args.split_first_mut().unwrap();
|
let (first, second) = args.split_first_mut().unwrap();
|
||||||
|
|
||||||
match run_builtin_op_assignment(fn_name, first, second[0])? {
|
match run_builtin_op_assignment(fn_name, first, second[0])? {
|
||||||
Some(_) => return Ok((Dynamic::UNIT, false)),
|
Some(_) => return Ok((Dynamic::UNIT, false)),
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
||||||
|
Some(v) => return Ok((v, false)),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
|
||||||
Some(v) => return Ok((v, false)),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return default value (if any)
|
|
||||||
if let Some(val) = def_val {
|
|
||||||
return Ok((val.clone(), false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter function not found?
|
// Getter function not found?
|
||||||
@ -599,7 +602,6 @@ impl Engine {
|
|||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
_capture_scope: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
def_val: Option<&Dynamic>,
|
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
@ -607,9 +609,8 @@ impl Engine {
|
|||||||
ensure_no_data_race(fn_name, args, is_ref)?;
|
ensure_no_data_race(fn_name, args, is_ref)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
let hash_fn =
|
||||||
let arg_types = args.iter().map(|a| a.type_id());
|
calc_native_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id())).unwrap();
|
||||||
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types).unwrap();
|
|
||||||
|
|
||||||
match fn_name {
|
match fn_name {
|
||||||
// type_of
|
// type_of
|
||||||
@ -756,15 +757,13 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// Native function call
|
// Native function call
|
||||||
self.call_native_fn(
|
self.call_native_fn(
|
||||||
mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, pos, def_val,
|
mods, state, lib, fn_name, hash_fn, args, is_ref, false, pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native function call
|
// Native function call
|
||||||
_ => self.call_native_fn(
|
_ => self.call_native_fn(mods, state, lib, fn_name, hash_fn, args, is_ref, false, pos),
|
||||||
mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, pos, def_val,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -845,7 +844,6 @@ impl Engine {
|
|||||||
hash_script: Option<NonZeroU64>,
|
hash_script: Option<NonZeroU64>,
|
||||||
target: &mut crate::engine::Target,
|
target: &mut crate::engine::Target,
|
||||||
mut call_args: StaticVec<Dynamic>,
|
mut call_args: StaticVec<Dynamic>,
|
||||||
def_val: Option<&Dynamic>,
|
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -874,8 +872,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, false, false, pub_only, pos, None, def_val,
|
mods, state, lib, fn_name, hash, args, false, false, pub_only, pos, None, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
} else if fn_name == KEYWORD_FN_PTR_CALL
|
} else if fn_name == KEYWORD_FN_PTR_CALL
|
||||||
&& call_args.len() > 0
|
&& call_args.len() > 0
|
||||||
@ -898,8 +895,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, def_val,
|
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
||||||
// Curry call
|
// Curry call
|
||||||
@ -964,8 +960,7 @@ impl Engine {
|
|||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, def_val,
|
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
@ -987,7 +982,6 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<&Dynamic>,
|
|
||||||
mut hash_script: Option<NonZeroU64>,
|
mut hash_script: Option<NonZeroU64>,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -1212,7 +1206,6 @@ impl Engine {
|
|||||||
pub_only,
|
pub_only,
|
||||||
pos,
|
pos,
|
||||||
capture,
|
capture,
|
||||||
def_val,
|
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
@ -1229,7 +1222,6 @@ impl Engine {
|
|||||||
namespace: Option<&NamespaceRef>,
|
namespace: Option<&NamespaceRef>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<&Dynamic>,
|
|
||||||
hash_script: NonZeroU64,
|
hash_script: NonZeroU64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1351,7 +1343,6 @@ impl Engine {
|
|||||||
args.as_mut(),
|
args.as_mut(),
|
||||||
),
|
),
|
||||||
Some(f) => unreachable!("unknown function type: {:?}", f),
|
Some(f) => unreachable!("unknown function type: {:?}", f),
|
||||||
None if def_val.is_some() => Ok(def_val.unwrap().clone()),
|
|
||||||
None => EvalAltResult::ErrorFunctionNotFound(
|
None => EvalAltResult::ErrorFunctionNotFound(
|
||||||
format!(
|
format!(
|
||||||
"{}{} ({})",
|
"{}{} ({})",
|
||||||
@ -1379,17 +1370,29 @@ pub fn run_builtin_binary_op(
|
|||||||
x: &Dynamic,
|
x: &Dynamic,
|
||||||
y: &Dynamic,
|
y: &Dynamic,
|
||||||
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
||||||
let first_type = x.type_id();
|
let type1 = x.type_id();
|
||||||
let second_type = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
let type_id = (first_type, second_type);
|
if x.is_variant() || y.is_variant() {
|
||||||
|
// One of the operands is a custom type, so it is never built-in
|
||||||
|
return Ok(match op {
|
||||||
|
"!=" if type1 != type2 => Some(Dynamic::TRUE),
|
||||||
|
"==" | ">" | ">=" | "<" | "<=" if type1 != type2 => Some(Dynamic::FALSE),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let types_pair = (type1, type2);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if let Some((x, y)) = if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
if let Some((x, y)) = if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
||||||
|
// FLOAT op FLOAT
|
||||||
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<FLOAT>()))
|
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<FLOAT>()))
|
||||||
} else if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
} else if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
||||||
|
// FLOAT op INT
|
||||||
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<INT>() as FLOAT))
|
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<INT>() as FLOAT))
|
||||||
} else if type_id == (TypeId::of::<INT>(), TypeId::of::<FLOAT>()) {
|
} else if types_pair == (TypeId::of::<INT>(), TypeId::of::<FLOAT>()) {
|
||||||
|
// INT op FLOAT
|
||||||
Some((x.clone().cast::<INT>() as FLOAT, y.clone().cast::<FLOAT>()))
|
Some((x.clone().cast::<INT>() as FLOAT, y.clone().cast::<FLOAT>()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -1412,17 +1415,20 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if let Some((x, y)) = if type_id == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
if let Some((x, y)) = if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
||||||
|
// Decimal op Decimal
|
||||||
Some((
|
Some((
|
||||||
*x.read_lock::<Decimal>().unwrap(),
|
*x.read_lock::<Decimal>().unwrap(),
|
||||||
*y.read_lock::<Decimal>().unwrap(),
|
*y.read_lock::<Decimal>().unwrap(),
|
||||||
))
|
))
|
||||||
} else if type_id == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
} else if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
||||||
|
// Decimal op INT
|
||||||
Some((
|
Some((
|
||||||
*x.read_lock::<Decimal>().unwrap(),
|
*x.read_lock::<Decimal>().unwrap(),
|
||||||
y.clone().cast::<INT>().into(),
|
y.clone().cast::<INT>().into(),
|
||||||
))
|
))
|
||||||
} else if type_id == (TypeId::of::<INT>(), TypeId::of::<Decimal>()) {
|
} else if types_pair == (TypeId::of::<INT>(), TypeId::of::<Decimal>()) {
|
||||||
|
// INT op Decimal
|
||||||
Some((
|
Some((
|
||||||
x.clone().cast::<INT>().into(),
|
x.clone().cast::<INT>().into(),
|
||||||
*y.read_lock::<Decimal>().unwrap(),
|
*y.read_lock::<Decimal>().unwrap(),
|
||||||
@ -1463,31 +1469,70 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if second_type != first_type {
|
// char op string
|
||||||
if type_id == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
let x = x.clone().cast::<char>();
|
let x = x.clone().cast::<char>();
|
||||||
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
||||||
_ => return Ok(None),
|
"==" | "!=" | ">" | ">=" | "<" | "<=" => {
|
||||||
|
let s1 = [x, '\0'];
|
||||||
|
let mut y = y.chars();
|
||||||
|
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"==" => return Ok(Some((s1 == s2).into())),
|
||||||
|
"!=" => return Ok(Some((s1 != s2).into())),
|
||||||
|
">" => return Ok(Some((s1 > s2).into())),
|
||||||
|
">=" => return Ok(Some((s1 >= s2).into())),
|
||||||
|
"<" => return Ok(Some((s1 < s2).into())),
|
||||||
|
"<=" => return Ok(Some((s1 <= s2).into())),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// string op char
|
||||||
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
|
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = y.clone().cast::<char>();
|
||||||
|
|
||||||
if type_id == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
match op {
|
||||||
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
"+" => return Ok(Some((x + y).into())),
|
||||||
let y = y.clone().cast::<char>();
|
"-" => return Ok(Some((x - y).into())),
|
||||||
|
"==" | "!=" | ">" | ">=" | "<" | "<=" => {
|
||||||
|
let mut x = x.chars();
|
||||||
|
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
||||||
|
let s2 = [y, '\0'];
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+" => return Ok(Some((x + y).into())),
|
"==" => return Ok(Some((s1 == s2).into())),
|
||||||
_ => return Ok(None),
|
"!=" => return Ok(Some((s1 != s2).into())),
|
||||||
|
">" => return Ok(Some((s1 > s2).into())),
|
||||||
|
">=" => return Ok(Some((s1 >= s2).into())),
|
||||||
|
"<" => return Ok(Some((s1 < s2).into())),
|
||||||
|
"<=" => return Ok(Some((s1 <= s2).into())),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<INT>() {
|
// Default comparison operators for different types
|
||||||
|
if type2 != type1 {
|
||||||
|
return Ok(match op {
|
||||||
|
"!=" => Some(Dynamic::TRUE),
|
||||||
|
"==" | ">" | ">=" | "<" | "<=" => Some(Dynamic::FALSE),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beyond here, type1 == type2
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<INT>() {
|
||||||
let x = x.clone().cast::<INT>();
|
let x = x.clone().cast::<INT>();
|
||||||
let y = y.clone().cast::<INT>();
|
let y = y.clone().cast::<INT>();
|
||||||
|
|
||||||
@ -1533,7 +1578,7 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<bool>() {
|
if type1 == TypeId::of::<bool>() {
|
||||||
let x = x.clone().cast::<bool>();
|
let x = x.clone().cast::<bool>();
|
||||||
let y = y.clone().cast::<bool>();
|
let y = y.clone().cast::<bool>();
|
||||||
|
|
||||||
@ -1547,12 +1592,13 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<ImmutableString>() {
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
||||||
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+" => return Ok(Some((x + y).into())),
|
"+" => return Ok(Some((x + y).into())),
|
||||||
|
"-" => return Ok(Some((x - y).into())),
|
||||||
"==" => return Ok(Some((x == y).into())),
|
"==" => return Ok(Some((x == y).into())),
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
"!=" => return Ok(Some((x != y).into())),
|
||||||
">" => return Ok(Some((x > y).into())),
|
">" => return Ok(Some((x > y).into())),
|
||||||
@ -1563,7 +1609,7 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<char>() {
|
if type1 == TypeId::of::<char>() {
|
||||||
let x = x.clone().cast::<char>();
|
let x = x.clone().cast::<char>();
|
||||||
let y = y.clone().cast::<char>();
|
let y = y.clone().cast::<char>();
|
||||||
|
|
||||||
@ -1579,7 +1625,7 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<()>() {
|
if type1 == TypeId::of::<()>() {
|
||||||
match op {
|
match op {
|
||||||
"==" => return Ok(Some(true.into())),
|
"==" => return Ok(Some(true.into())),
|
||||||
"!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())),
|
"!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())),
|
||||||
@ -1596,16 +1642,18 @@ pub fn run_builtin_op_assignment(
|
|||||||
x: &mut Dynamic,
|
x: &mut Dynamic,
|
||||||
y: &Dynamic,
|
y: &Dynamic,
|
||||||
) -> Result<Option<()>, Box<EvalAltResult>> {
|
) -> Result<Option<()>, Box<EvalAltResult>> {
|
||||||
let first_type = x.type_id();
|
let type1 = x.type_id();
|
||||||
let second_type = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
let type_id = (first_type, second_type);
|
let types_pair = (type1, type2);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if let Some((mut x, y)) = if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
if let Some((mut x, y)) = if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
||||||
|
// FLOAT op= FLOAT
|
||||||
let y = y.clone().cast::<FLOAT>();
|
let y = y.clone().cast::<FLOAT>();
|
||||||
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
||||||
} else if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
} else if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
||||||
|
// FLOAT op= INT
|
||||||
let y = y.clone().cast::<INT>() as FLOAT;
|
let y = y.clone().cast::<INT>() as FLOAT;
|
||||||
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
||||||
} else {
|
} else {
|
||||||
@ -1623,10 +1671,12 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if let Some((mut x, y)) = if type_id == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
if let Some((mut x, y)) = if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
||||||
|
// Decimal op= Decimal
|
||||||
let y = *y.read_lock::<Decimal>().unwrap();
|
let y = *y.read_lock::<Decimal>().unwrap();
|
||||||
Some((x.write_lock::<Decimal>().unwrap(), y))
|
Some((x.write_lock::<Decimal>().unwrap(), y))
|
||||||
} else if type_id == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
} else if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
||||||
|
// Decimal op= INT
|
||||||
let y = y.clone().cast::<INT>().into();
|
let y = y.clone().cast::<INT>().into();
|
||||||
Some((x.write_lock::<Decimal>().unwrap(), y))
|
Some((x.write_lock::<Decimal>().unwrap(), y))
|
||||||
} else {
|
} else {
|
||||||
@ -1655,21 +1705,40 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if second_type != first_type {
|
// string op= char
|
||||||
if type_id == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
let y = y.read_lock::<char>().unwrap().deref().clone();
|
let y = y.clone().cast::<char>();
|
||||||
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+=" => return Ok(Some(*x += y)),
|
"+=" => return Ok(Some(*x += y)),
|
||||||
_ => return Ok(None),
|
"-=" => return Ok(Some(*x -= y)),
|
||||||
}
|
_ => return Ok(None),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// char op= string
|
||||||
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
let y = y.read_lock::<ImmutableString>().unwrap();
|
||||||
|
let mut ch = x.read_lock::<char>().unwrap().to_string();
|
||||||
|
let mut x = x.write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+=" => {
|
||||||
|
ch.push_str(y.as_str());
|
||||||
|
return Ok(Some(*x = ch.into()));
|
||||||
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No built-in op-assignments for different types.
|
||||||
|
if type2 != type1 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<INT>() {
|
// Beyond here, type1 == type2
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<INT>() {
|
||||||
let y = y.clone().cast::<INT>();
|
let y = y.clone().cast::<INT>();
|
||||||
let mut x = x.write_lock::<INT>().unwrap();
|
let mut x = x.write_lock::<INT>().unwrap();
|
||||||
|
|
||||||
@ -1709,7 +1778,7 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<bool>() {
|
if type1 == TypeId::of::<bool>() {
|
||||||
let y = y.clone().cast::<bool>();
|
let y = y.clone().cast::<bool>();
|
||||||
let mut x = x.write_lock::<bool>().unwrap();
|
let mut x = x.write_lock::<bool>().unwrap();
|
||||||
|
|
||||||
@ -1720,8 +1789,8 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<char>() {
|
if type1 == TypeId::of::<char>() {
|
||||||
let y = y.read_lock::<char>().unwrap().deref().clone();
|
let y = y.clone().cast::<char>();
|
||||||
let mut x = x.write_lock::<Dynamic>().unwrap();
|
let mut x = x.write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
@ -1730,12 +1799,13 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_type == TypeId::of::<ImmutableString>() {
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
let y = y.read_lock::<ImmutableString>().unwrap().deref().clone();
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
||||||
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+=" => return Ok(Some(*x += y)),
|
"+=" => return Ok(Some(*x += y)),
|
||||||
|
"-=" => return Ok(Some(*x -= y)),
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,6 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
|||||||
is_method: bool,
|
is_method: bool,
|
||||||
public_only: bool,
|
public_only: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
def_value: Option<&Dynamic>,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
self.engine()
|
self.engine()
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
@ -201,7 +200,6 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
|||||||
public_only,
|
public_only,
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
None,
|
None,
|
||||||
def_value,
|
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.map(|(r, _)| r)
|
.map(|(r, _)| r)
|
||||||
@ -339,7 +337,7 @@ impl FnPtr {
|
|||||||
args.insert(0, obj);
|
args.insert(0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, true, args.as_mut(), None)
|
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, true, args.as_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1855,7 +1855,6 @@ impl Module {
|
|||||||
|
|
||||||
// Index all variables
|
// Index all variables
|
||||||
module.variables.iter().for_each(|(var_name, value)| {
|
module.variables.iter().for_each(|(var_name, value)| {
|
||||||
// Qualifiers + variable name
|
|
||||||
let hash_var =
|
let hash_var =
|
||||||
crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0).unwrap();
|
crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0).unwrap();
|
||||||
variables.insert(hash_var, value.clone());
|
variables.insert(hash_var, value.clone());
|
||||||
@ -1888,7 +1887,6 @@ impl Module {
|
|||||||
functions.insert(hash, func.clone());
|
functions.insert(hash, func.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Qualifiers + function name + number of arguments.
|
|
||||||
let hash_qualified_script =
|
let hash_qualified_script =
|
||||||
crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params)
|
crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -149,9 +149,8 @@ fn call_fn_with_constant_arguments(
|
|||||||
hash_fn.unwrap(),
|
hash_fn.unwrap(),
|
||||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||||
false,
|
false,
|
||||||
true,
|
false,
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
@ -717,8 +716,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
// Handle `type_of()`
|
// Handle `type_of()`
|
||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
// Otherwise use the default value, if any
|
None
|
||||||
x.def_value.clone()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
|
@ -647,11 +647,9 @@ mod array_functions {
|
|||||||
return Ok(true.into());
|
return Ok(true.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let def_value = Some(false.into());
|
|
||||||
|
|
||||||
for (a1, a2) in array.iter_mut().zip(array2.iter_mut()) {
|
for (a1, a2) in array.iter_mut().zip(array2.iter_mut()) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2], def_value.as_ref())
|
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2])
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.map(|v| v.as_bool().unwrap_or(false))?;
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
|
use crate::fn_call::run_builtin_binary_op;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
@ -17,30 +18,12 @@ macro_rules! gen_cmp_functions {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
#[rhai_fn(name = "<")]
|
#[rhai_fn(name = "<")] pub fn lt(x: $arg_type, y: $arg_type) -> bool { x < y }
|
||||||
pub fn lt(x: $arg_type, y: $arg_type) -> bool {
|
#[rhai_fn(name = "<=")] pub fn lte(x: $arg_type, y: $arg_type) -> bool { x <= y }
|
||||||
x < y
|
#[rhai_fn(name = ">")] pub fn gt(x: $arg_type, y: $arg_type) -> bool { x > y }
|
||||||
}
|
#[rhai_fn(name = ">=")] pub fn gte(x: $arg_type, y: $arg_type) -> bool { x >= y }
|
||||||
#[rhai_fn(name = "<=")]
|
#[rhai_fn(name = "==")] pub fn eq(x: $arg_type, y: $arg_type) -> bool { x == y }
|
||||||
pub fn lte(x: $arg_type, y: $arg_type) -> bool {
|
#[rhai_fn(name = "!=")] pub fn ne(x: $arg_type, y: $arg_type) -> bool { x != y }
|
||||||
x <= y
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = ">")]
|
|
||||||
pub fn gt(x: $arg_type, y: $arg_type) -> bool {
|
|
||||||
x > y
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = ">=")]
|
|
||||||
pub fn gte(x: $arg_type, y: $arg_type) -> bool {
|
|
||||||
x >= y
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "==")]
|
|
||||||
pub fn eq(x: $arg_type, y: $arg_type) -> bool {
|
|
||||||
x == y
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "!=")]
|
|
||||||
pub fn ne(x: $arg_type, y: $arg_type) -> bool {
|
|
||||||
x != y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})* }
|
})* }
|
||||||
};
|
};
|
||||||
@ -57,6 +40,8 @@ macro_rules! reg_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||||
|
combine_with_exported_module!(lib, "logic", logic_functions);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
@ -112,6 +97,65 @@ gen_cmp_functions!(float => f64);
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
gen_cmp_functions!(decimal => Decimal);
|
gen_cmp_functions!(decimal => Decimal);
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod logic_functions {
|
||||||
|
fn is_numeric(type_id: TypeId) -> bool {
|
||||||
|
let result = type_id == TypeId::of::<u8>()
|
||||||
|
|| type_id == TypeId::of::<u16>()
|
||||||
|
|| type_id == TypeId::of::<u32>()
|
||||||
|
|| type_id == TypeId::of::<u64>()
|
||||||
|
|| type_id == TypeId::of::<i8>()
|
||||||
|
|| type_id == TypeId::of::<i16>()
|
||||||
|
|| type_id == TypeId::of::<i32>()
|
||||||
|
|| type_id == TypeId::of::<i64>();
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
|
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
let result = result || type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>();
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
let result = result || type_id == TypeId::of::<rust_decimal::Decimal>();
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(
|
||||||
|
name = "==",
|
||||||
|
name = "!=",
|
||||||
|
name = ">",
|
||||||
|
name = ">=",
|
||||||
|
name = "<",
|
||||||
|
name = "<=",
|
||||||
|
return_raw
|
||||||
|
)]
|
||||||
|
pub fn cmp(
|
||||||
|
ctx: NativeCallContext,
|
||||||
|
x: Dynamic,
|
||||||
|
y: Dynamic,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let type_x = x.type_id();
|
||||||
|
let type_y = y.type_id();
|
||||||
|
|
||||||
|
if type_x != type_y && is_numeric(type_x) && is_numeric(type_y) {
|
||||||
|
// Disallow comparisons between different number types
|
||||||
|
} else if let Some(x) = run_builtin_binary_op(ctx.fn_name(), &x, &y)? {
|
||||||
|
return Ok(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||||
|
format!(
|
||||||
|
"{} ({}, {})",
|
||||||
|
ctx.fn_name(),
|
||||||
|
ctx.engine().map_type_name(x.type_name()),
|
||||||
|
ctx.engine().map_type_name(y.type_name())
|
||||||
|
),
|
||||||
|
Position::NONE,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod f32_functions {
|
mod f32_functions {
|
||||||
|
@ -58,12 +58,10 @@ mod map_functions {
|
|||||||
return Ok(true.into());
|
return Ok(true.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let def_value = Some(false.into());
|
|
||||||
|
|
||||||
for (m1, v1) in map.iter_mut() {
|
for (m1, v1) in map.iter_mut() {
|
||||||
if let Some(v2) = map2.get_mut(m1) {
|
if let Some(v2) = map2.get_mut(m1) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2], def_value.as_ref())
|
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2])
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.map(|v| v.as_bool().unwrap_or(false))?;
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_PRINT};
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{format, string::ToString};
|
||||||
fmt::{Debug, Display},
|
use crate::{def_package, FnPtr, ImmutableString};
|
||||||
format,
|
|
||||||
string::ToString,
|
|
||||||
};
|
|
||||||
use crate::{def_package, FnPtr, ImmutableString, INT};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
@ -15,145 +10,18 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
use rust_decimal::Decimal;
|
|
||||||
|
|
||||||
const FUNC_TO_STRING: &'static str = "to_string";
|
|
||||||
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
||||||
|
|
||||||
type Unit = ();
|
|
||||||
|
|
||||||
macro_rules! gen_functions {
|
|
||||||
($root:ident => $fn_name:ident ( $($arg_type:ident),+ )) => {
|
|
||||||
pub mod $root { $(pub mod $arg_type {
|
|
||||||
use super::super::*;
|
|
||||||
|
|
||||||
#[export_fn(pure)]
|
|
||||||
pub fn to_string_func(x: &mut $arg_type) -> ImmutableString {
|
|
||||||
super::super::$fn_name(x)
|
|
||||||
}
|
|
||||||
})* }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! reg_print_functions {
|
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
|
||||||
set_exported_fn!($mod_name, FUNC_TO_STRING, $root::$arg_type::to_string_func);
|
|
||||||
set_exported_fn!($mod_name, KEYWORD_PRINT, $root::$arg_type::to_string_func);
|
|
||||||
)* }
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! reg_debug_functions {
|
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
|
||||||
set_exported_fn!($mod_name, FUNC_TO_DEBUG, $root::$arg_type::to_string_func);
|
|
||||||
set_exported_fn!($mod_name, KEYWORD_DEBUG, $root::$arg_type::to_string_func);
|
|
||||||
)* }
|
|
||||||
}
|
|
||||||
|
|
||||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||||
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
||||||
|
|
||||||
reg_print_functions!(lib += print_basic; INT, bool, char, FnPtr);
|
|
||||||
reg_debug_functions!(lib += debug_basic; INT, bool, Unit, char, ImmutableString);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
reg_print_functions!(lib += print_numbers; i8, u8, i16, u16, i32, u32, i64, u64);
|
|
||||||
reg_debug_functions!(lib += debug_numbers; i8, u8, i16, u16, i32, u32, i64, u64);
|
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
|
||||||
{
|
|
||||||
reg_print_functions!(lib += print_num_128; i128, u128);
|
|
||||||
reg_debug_functions!(lib += debug_num_128; i128, u128);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
{
|
|
||||||
reg_print_functions!(lib += print_float_64; f64);
|
|
||||||
reg_debug_functions!(lib += print_float_64; f64);
|
|
||||||
reg_print_functions!(lib += print_float_32; f32);
|
|
||||||
reg_debug_functions!(lib += print_float_32; f32);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
{
|
|
||||||
reg_print_functions!(lib += print_decimal; Decimal);
|
|
||||||
reg_debug_functions!(lib += debug_decimal; Decimal);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
|
||||||
x.to_string().into()
|
|
||||||
}
|
|
||||||
fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
|
||||||
format!("{:?}", x).into()
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
fn print_f64(x: &mut f64) -> ImmutableString {
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
use num_traits::Float;
|
|
||||||
|
|
||||||
let abs = x.abs();
|
|
||||||
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
|
||||||
format!("{:e}", x).into()
|
|
||||||
} else {
|
|
||||||
x.to_string().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
fn print_f32(x: &mut f32) -> ImmutableString {
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
use num_traits::Float;
|
|
||||||
|
|
||||||
let abs = x.abs();
|
|
||||||
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
|
||||||
format!("{:e}", x).into()
|
|
||||||
} else {
|
|
||||||
x.to_string().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gen_functions!(print_basic => to_string(INT, bool, char, FnPtr));
|
|
||||||
gen_functions!(debug_basic => to_debug(INT, bool, Unit, char, ImmutableString));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
gen_functions!(print_numbers => to_string(i8, u8, i16, u16, i32, u32, i64, u64));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
gen_functions!(debug_numbers => to_debug(i8, u8, i16, u16, i32, u32, i64, u64));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
|
||||||
gen_functions!(print_num_128 => to_string(i128, u128));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
|
||||||
gen_functions!(debug_num_128 => to_debug(i128, u128));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
gen_functions!(print_float_64 => print_f64(f64));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
gen_functions!(print_float_32 => print_f32(f32));
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
gen_functions!(print_decimal => to_string(Decimal));
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
gen_functions!(debug_decimal => to_debug(Decimal));
|
|
||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString {
|
fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString {
|
||||||
match ctx.call_fn_dynamic_raw(fn_name, true, false, &mut [value], None) {
|
match ctx.call_fn_dynamic_raw(fn_name, true, false, &mut [value]) {
|
||||||
Ok(result) if result.is::<ImmutableString>() => result.take_immutable_string().unwrap(),
|
Ok(result) if result.is::<ImmutableString>() => result.take_immutable_string().unwrap(),
|
||||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||||
@ -162,21 +30,63 @@ fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic)
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod print_debug_functions {
|
mod print_debug_functions {
|
||||||
|
use crate::ImmutableString;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "print", name = "to_string", pure)]
|
||||||
|
pub fn print_generic(item: &mut Dynamic) -> ImmutableString {
|
||||||
|
item.to_string().into()
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
||||||
|
pub fn debug_generic(item: &mut Dynamic) -> ImmutableString {
|
||||||
|
format!("{:?}", item).into()
|
||||||
|
}
|
||||||
#[rhai_fn(name = "print", name = "debug")]
|
#[rhai_fn(name = "print", name = "debug")]
|
||||||
pub fn print_empty_string() -> ImmutableString {
|
pub fn print_empty_string() -> ImmutableString {
|
||||||
"".to_string().into()
|
"".to_string().into()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_unit(_x: ()) -> ImmutableString {
|
|
||||||
"".to_string().into()
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
|
||||||
pub fn print_string(s: ImmutableString) -> ImmutableString {
|
pub fn print_string(s: ImmutableString) -> ImmutableString {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "debug", pure)]
|
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
||||||
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
||||||
to_string(f)
|
f.to_string().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub mod float_functions {
|
||||||
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
|
pub fn print_f64(number: f64) -> ImmutableString {
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use num_traits::Float;
|
||||||
|
|
||||||
|
let abs = number.abs();
|
||||||
|
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
||||||
|
format!("{:e}", number).into()
|
||||||
|
} else {
|
||||||
|
number.to_string().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
|
pub fn print_f32(number: f32) -> ImmutableString {
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use num_traits::Float;
|
||||||
|
|
||||||
|
let abs = number.abs();
|
||||||
|
if abs > 10000000000000.0 || abs < 0.0000000000001 {
|
||||||
|
format!("{:e}", number).into()
|
||||||
|
} else {
|
||||||
|
number.to_string().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
|
pub fn debug_f64(number: f64) -> ImmutableString {
|
||||||
|
number.to_string().into()
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
|
pub fn debug_f32(number: f32) -> ImmutableString {
|
||||||
|
number.to_string().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -186,15 +96,14 @@ mod print_debug_functions {
|
|||||||
#[rhai_fn(
|
#[rhai_fn(
|
||||||
name = "print",
|
name = "print",
|
||||||
name = "to_string",
|
name = "to_string",
|
||||||
name = "to_debug",
|
|
||||||
name = "debug",
|
name = "debug",
|
||||||
|
name = "to_debug",
|
||||||
pure
|
pure
|
||||||
)]
|
)]
|
||||||
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
|
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
|
||||||
let mut result = crate::stdlib::string::String::with_capacity(16);
|
|
||||||
result.push_str("[");
|
|
||||||
|
|
||||||
let len = array.len();
|
let len = array.len();
|
||||||
|
let mut result = crate::stdlib::string::String::with_capacity(len * 5 + 2);
|
||||||
|
result.push_str("[");
|
||||||
|
|
||||||
array.iter_mut().enumerate().for_each(|(i, x)| {
|
array.iter_mut().enumerate().for_each(|(i, x)| {
|
||||||
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x));
|
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x));
|
||||||
@ -214,15 +123,14 @@ mod print_debug_functions {
|
|||||||
#[rhai_fn(
|
#[rhai_fn(
|
||||||
name = "print",
|
name = "print",
|
||||||
name = "to_string",
|
name = "to_string",
|
||||||
name = "to_debug",
|
|
||||||
name = "debug",
|
name = "debug",
|
||||||
|
name = "to_debug",
|
||||||
pure
|
pure
|
||||||
)]
|
)]
|
||||||
pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString {
|
pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString {
|
||||||
let mut result = crate::stdlib::string::String::with_capacity(16);
|
|
||||||
result.push_str("#{");
|
|
||||||
|
|
||||||
let len = map.len();
|
let len = map.len();
|
||||||
|
let mut result = crate::stdlib::string::String::with_capacity(len * 5 + 3);
|
||||||
|
result.push_str("#{");
|
||||||
|
|
||||||
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
||||||
result.push_str(&format!(
|
result.push_str(&format!(
|
||||||
|
@ -4,50 +4,9 @@ use crate::plugin::*;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec,
|
any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::{def_package, Dynamic, FnPtr, ImmutableString, StaticVec, INT};
|
use crate::{def_package, Dynamic, ImmutableString, StaticVec, INT};
|
||||||
|
|
||||||
macro_rules! gen_concat_functions {
|
|
||||||
($root:ident => $($arg_type:ident),+ ) => {
|
|
||||||
pub mod $root { $( pub mod $arg_type {
|
|
||||||
use super::super::*;
|
|
||||||
|
|
||||||
#[export_module]
|
|
||||||
pub mod functions {
|
|
||||||
#[rhai_fn(name = "+")]
|
|
||||||
pub fn append_func(string: &str, arg: $arg_type) -> String {
|
|
||||||
format!("{}{}", string, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rhai_fn(name = "+", pure)]
|
|
||||||
pub fn prepend_func(arg: &mut $arg_type, string: &str) -> String {
|
|
||||||
format!("{}{}", arg, string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} )* }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! reg_functions {
|
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
|
||||||
combine_with_exported_module!($mod_name, "strings_concat", $root::$arg_type::functions);
|
|
||||||
)* }
|
|
||||||
}
|
|
||||||
|
|
||||||
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
||||||
reg_functions!(lib += basic; INT, bool, FnPtr);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
reg_functions!(lib += numbers; i8, u8, i16, u16, i32, i64, u32, u64);
|
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
|
||||||
reg_functions!(lib += num_128; i128, u128);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
reg_functions!(lib += float; f32, f64);
|
|
||||||
|
|
||||||
combine_with_exported_module!(lib, "string", string_functions);
|
combine_with_exported_module!(lib, "string", string_functions);
|
||||||
|
|
||||||
// Register string iterator
|
// Register string iterator
|
||||||
@ -57,24 +16,19 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
gen_concat_functions!(basic => INT, bool, char, FnPtr);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
gen_concat_functions!(numbers => i8, u8, i16, u16, i32, i64, u32, u64);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
|
||||||
gen_concat_functions!(num_128 => i128, u128);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
gen_concat_functions!(float => f32, f64);
|
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod string_functions {
|
mod string_functions {
|
||||||
use crate::ImmutableString;
|
use crate::ImmutableString;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "+", name = "append")]
|
||||||
|
pub fn add_append(string: &str, item: Dynamic) -> ImmutableString {
|
||||||
|
format!("{}{}", string, item).into()
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "+", pure)]
|
||||||
|
pub fn add_prepend(item: &mut Dynamic, string: &str) -> ImmutableString {
|
||||||
|
format!("{}{}", item, string).into()
|
||||||
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_append_unit(string: ImmutableString, _x: ()) -> ImmutableString {
|
pub fn add_append_unit(string: ImmutableString, _x: ()) -> ImmutableString {
|
||||||
string
|
string
|
||||||
@ -88,7 +42,13 @@ mod string_functions {
|
|||||||
pub fn len(string: &str) -> INT {
|
pub fn len(string: &str) -> INT {
|
||||||
string.chars().count() as INT
|
string.chars().count() as INT
|
||||||
}
|
}
|
||||||
|
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
||||||
|
*string -= sub_string;
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "remove")]
|
||||||
|
pub fn remove_char(string: &mut ImmutableString, character: char) {
|
||||||
|
*string -= character;
|
||||||
|
}
|
||||||
pub fn clear(string: &mut ImmutableString) {
|
pub fn clear(string: &mut ImmutableString) {
|
||||||
string.make_mut().clear();
|
string.make_mut().clear();
|
||||||
}
|
}
|
||||||
@ -111,15 +71,15 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "contains")]
|
#[rhai_fn(name = "contains")]
|
||||||
pub fn contains_char(string: &str, ch: char) -> bool {
|
pub fn contains_char(string: &str, character: char) -> bool {
|
||||||
string.contains(ch)
|
string.contains(character)
|
||||||
}
|
}
|
||||||
pub fn contains(string: &str, find_string: &str) -> bool {
|
pub fn contains(string: &str, find_string: &str) -> bool {
|
||||||
string.contains(find_string)
|
string.contains(find_string)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char_starting_from(string: &str, ch: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if start as usize >= string.chars().count() {
|
} else if start as usize >= string.chars().count() {
|
||||||
@ -133,14 +93,14 @@ mod string_functions {
|
|||||||
};
|
};
|
||||||
|
|
||||||
string[start..]
|
string[start..]
|
||||||
.find(ch)
|
.find(character)
|
||||||
.map(|index| string[0..start + index].chars().count() as INT)
|
.map(|index| string[0..start + index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char(string: &str, ch: char) -> INT {
|
pub fn index_of_char(string: &str, character: char) -> INT {
|
||||||
string
|
string
|
||||||
.find(ch)
|
.find(character)
|
||||||
.map(|index| string[0..index].chars().count() as INT)
|
.map(|index| string[0..index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
@ -243,26 +203,33 @@ mod string_functions {
|
|||||||
pub fn replace_string_with_char(
|
pub fn replace_string_with_char(
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
find_string: &str,
|
find_string: &str,
|
||||||
substitute_char: char,
|
substitute_character: char,
|
||||||
) {
|
) {
|
||||||
*string = string
|
*string = string
|
||||||
.replace(find_string, &substitute_char.to_string())
|
.replace(find_string, &substitute_character.to_string())
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
pub fn replace_char_with_string(
|
pub fn replace_char_with_string(
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
find_char: char,
|
find_character: char,
|
||||||
substitute_string: &str,
|
substitute_string: &str,
|
||||||
) {
|
) {
|
||||||
*string = string
|
*string = string
|
||||||
.replace(&find_char.to_string(), substitute_string)
|
.replace(&find_character.to_string(), substitute_string)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
pub fn replace_char(string: &mut ImmutableString, find_char: char, substitute_char: char) {
|
pub fn replace_char(
|
||||||
|
string: &mut ImmutableString,
|
||||||
|
find_character: char,
|
||||||
|
substitute_character: char,
|
||||||
|
) {
|
||||||
*string = string
|
*string = string
|
||||||
.replace(&find_char.to_string(), &substitute_char.to_string())
|
.replace(
|
||||||
|
&find_character.to_string(),
|
||||||
|
&substitute_character.to_string(),
|
||||||
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +238,7 @@ mod string_functions {
|
|||||||
_ctx: NativeCallContext,
|
_ctx: NativeCallContext,
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
len: INT,
|
len: INT,
|
||||||
ch: char,
|
character: char,
|
||||||
) -> Result<Dynamic, Box<crate::EvalAltResult>> {
|
) -> Result<Dynamic, Box<crate::EvalAltResult>> {
|
||||||
// Check if string will be over max size limit
|
// Check if string will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -290,7 +257,7 @@ mod string_functions {
|
|||||||
let p = string.make_mut();
|
let p = string.make_mut();
|
||||||
|
|
||||||
for _ in 0..(len as usize - orig_len) {
|
for _ in 0..(len as usize - orig_len) {
|
||||||
p.push(ch);
|
p.push(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -363,14 +330,6 @@ mod string_functions {
|
|||||||
use crate::stdlib::vec;
|
use crate::stdlib::vec;
|
||||||
use crate::{Array, ImmutableString};
|
use crate::{Array, ImmutableString};
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
|
||||||
pub fn append(string: &str, array: Array) -> String {
|
|
||||||
format!("{}{:?}", string, array)
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "+", pure)]
|
|
||||||
pub fn prepend(array: &mut Array, string: &str) -> String {
|
|
||||||
format!("{:?}{}", array, string)
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn chars(string: &str) -> Array {
|
pub fn chars(string: &str) -> Array {
|
||||||
string.chars().map(Into::<Dynamic>::into).collect()
|
string.chars().map(Into::<Dynamic>::into).collect()
|
||||||
@ -439,18 +398,4 @@ mod string_functions {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub mod maps {
|
|
||||||
use crate::Map;
|
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
|
||||||
pub fn append(string: &str, map: Map) -> String {
|
|
||||||
format!("{}#{:?}", string, map)
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "+", pure)]
|
|
||||||
pub fn prepend(map: &mut Map, string: &str) -> String {
|
|
||||||
format!("#{:?}{}", map, string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,6 @@ fn parse_fn_call(
|
|||||||
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
||||||
calc_script_fn_hash(qualifiers, &id, 0)
|
calc_script_fn_hash(qualifiers, &id, 0)
|
||||||
} else {
|
} else {
|
||||||
// Qualifiers (none) + function name + no parameters.
|
|
||||||
calc_script_fn_hash(empty(), &id, 0)
|
calc_script_fn_hash(empty(), &id, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -399,7 +398,6 @@ fn parse_fn_call(
|
|||||||
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
||||||
calc_script_fn_hash(qualifiers, &id, args.len())
|
calc_script_fn_hash(qualifiers, &id, args.len())
|
||||||
} else {
|
} else {
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
|
||||||
calc_script_fn_hash(empty(), &id, args.len())
|
calc_script_fn_hash(empty(), &id, args.len())
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1016,7 +1014,6 @@ fn parse_primary(
|
|||||||
});
|
});
|
||||||
|
|
||||||
lib.insert(
|
lib.insert(
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
|
||||||
calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(),
|
calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(),
|
||||||
func,
|
func,
|
||||||
);
|
);
|
||||||
@ -1246,7 +1243,6 @@ fn parse_primary(
|
|||||||
}
|
}
|
||||||
.map(|x| match x.as_mut() {
|
.map(|x| match x.as_mut() {
|
||||||
(_, Some((ref mut hash, ref mut namespace)), Ident { name, .. }) => {
|
(_, Some((ref mut hash, ref mut namespace)), Ident { name, .. }) => {
|
||||||
// Qualifiers + variable name
|
|
||||||
*hash =
|
*hash =
|
||||||
calc_script_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0).unwrap();
|
calc_script_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0).unwrap();
|
||||||
|
|
||||||
@ -1351,7 +1347,6 @@ fn parse_unary(
|
|||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: op.into(),
|
name: op.into(),
|
||||||
args,
|
args,
|
||||||
def_value: Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
pos,
|
pos,
|
||||||
@ -1796,7 +1791,6 @@ fn parse_binary_op(
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let cmp_def = Some(false.into());
|
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
|
|
||||||
let op_base = FnCallExpr {
|
let op_base = FnCallExpr {
|
||||||
@ -1823,28 +1817,16 @@ fn parse_binary_op(
|
|||||||
| Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos),
|
| Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos),
|
||||||
|
|
||||||
// '!=' defaults to true when passed invalid operands
|
// '!=' defaults to true when passed invalid operands
|
||||||
Token::NotEqualsTo => Expr::FnCall(
|
Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos),
|
||||||
Box::new(FnCallExpr {
|
|
||||||
args,
|
|
||||||
def_value: Some(true.into()),
|
|
||||||
..op_base
|
|
||||||
}),
|
|
||||||
pos,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Comparison operators default to false when passed invalid operands
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::EqualsTo
|
Token::EqualsTo
|
||||||
| Token::LessThan
|
| Token::LessThan
|
||||||
| Token::LessThanEqualsTo
|
| Token::LessThanEqualsTo
|
||||||
| Token::GreaterThan
|
| Token::GreaterThan
|
||||||
| Token::GreaterThanEqualsTo => Expr::FnCall(
|
| Token::GreaterThanEqualsTo => {
|
||||||
Box::new(FnCallExpr {
|
Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos)
|
||||||
args,
|
}
|
||||||
def_value: cmp_def,
|
|
||||||
..op_base
|
|
||||||
}),
|
|
||||||
pos,
|
|
||||||
),
|
|
||||||
|
|
||||||
Token::Or => {
|
Token::Or => {
|
||||||
let rhs = args.pop().unwrap();
|
let rhs = args.pop().unwrap();
|
||||||
@ -2621,8 +2603,6 @@ fn parse_stmt(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let func = parse_fn(input, &mut new_state, lib, access, settings, _comments)?;
|
let func = parse_fn(input, &mut new_state, lib, access, settings, _comments)?;
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
|
||||||
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap();
|
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap();
|
||||||
|
|
||||||
if lib.contains_key(&hash) {
|
if lib.contains_key(&hash) {
|
||||||
|
12
src/token.rs
12
src/token.rs
@ -1657,27 +1657,31 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
|
|||||||
first_alphabetic
|
first_alphabetic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a character valid to start an identifier?
|
||||||
#[cfg(feature = "unicode-xid-ident")]
|
#[cfg(feature = "unicode-xid-ident")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_id_first_alphabetic(x: char) -> bool {
|
pub fn is_id_first_alphabetic(x: char) -> bool {
|
||||||
unicode_xid::UnicodeXID::is_xid_start(x)
|
unicode_xid::UnicodeXID::is_xid_start(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a character valid for an identifier?
|
||||||
#[cfg(feature = "unicode-xid-ident")]
|
#[cfg(feature = "unicode-xid-ident")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_id_continue(x: char) -> bool {
|
pub fn is_id_continue(x: char) -> bool {
|
||||||
unicode_xid::UnicodeXID::is_xid_continue(x)
|
unicode_xid::UnicodeXID::is_xid_continue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a character valid to start an identifier?
|
||||||
#[cfg(not(feature = "unicode-xid-ident"))]
|
#[cfg(not(feature = "unicode-xid-ident"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_id_first_alphabetic(x: char) -> bool {
|
pub fn is_id_first_alphabetic(x: char) -> bool {
|
||||||
x.is_ascii_alphabetic()
|
x.is_ascii_alphabetic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a character valid for an identifier?
|
||||||
#[cfg(not(feature = "unicode-xid-ident"))]
|
#[cfg(not(feature = "unicode-xid-ident"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_id_continue(x: char) -> bool {
|
pub fn is_id_continue(x: char) -> bool {
|
||||||
x.is_ascii_alphanumeric() || x == '_'
|
x.is_ascii_alphanumeric() || x == '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
127
src/utils.rs
127
src/utils.rs
@ -12,7 +12,7 @@ use crate::stdlib::{
|
|||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
iter::{empty, FromIterator},
|
iter::{empty, FromIterator},
|
||||||
num::NonZeroU64,
|
num::NonZeroU64,
|
||||||
ops::{Add, AddAssign, Deref, DerefMut},
|
ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -468,6 +468,13 @@ impl Add<String> for &ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AddAssign<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn add_assign(&mut self, rhs: String) {
|
||||||
|
self.make_mut().push_str(&rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Add<char> for ImmutableString {
|
impl Add<char> for ImmutableString {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -496,6 +503,124 @@ impl AddAssign<char> for ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sub for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs
|
||||||
|
} else {
|
||||||
|
self.replace(rhs.as_str(), "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.clone()
|
||||||
|
} else {
|
||||||
|
self.replace(rhs.as_str(), "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<&ImmutableString> for ImmutableString {
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign(&mut self, rhs: &ImmutableString) {
|
||||||
|
if !rhs.is_empty() {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.0 = rhs.0.clone();
|
||||||
|
} else {
|
||||||
|
self.0 = self.replace(rhs.as_str(), "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<ImmutableString> for ImmutableString {
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign(&mut self, rhs: ImmutableString) {
|
||||||
|
if !rhs.is_empty() {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.0 = rhs.0;
|
||||||
|
} else {
|
||||||
|
self.0 = self.replace(rhs.as_str(), "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<String> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: String) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.into()
|
||||||
|
} else {
|
||||||
|
self.replace(&rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<String> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: String) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.into()
|
||||||
|
} else {
|
||||||
|
self.replace(&rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub_assign(&mut self, rhs: String) {
|
||||||
|
self.0 = self.replace(&rhs, "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<char> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub(self, rhs: char) -> Self::Output {
|
||||||
|
self.replace(rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<char> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub(self, rhs: char) -> Self::Output {
|
||||||
|
self.replace(rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<char> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub_assign(&mut self, rhs: char) {
|
||||||
|
self.0 = self.replace(rhs, "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &S) -> bool {
|
fn eq(&self, other: &S) -> bool {
|
||||||
|
@ -6,10 +6,10 @@ fn test_decrement() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);
|
assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
*engine.eval::<String>(r#"let s = "test"; s -= "ing"; s"#).expect_err("expects error"),
|
engine.eval::<String>(r#"let s = "test"; s -= 's'; s"#)?,
|
||||||
EvalAltResult::ErrorFunctionNotFound(err, _) if err == "- (&str | ImmutableString | String, &str | ImmutableString | String)"
|
"tet"
|
||||||
));
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ fn test_increment() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("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>(r#"let s = "test"; s += "ing"; s"#)?,
|
||||||
"testing"
|
"testing"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ fn test_mismatched_op() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn test_mismatched_op_custom_type() {
|
fn test_mismatched_op_custom_type() -> Result<(), Box<EvalAltResult>> {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: INT,
|
x: INT,
|
||||||
@ -30,9 +30,18 @@ fn test_mismatched_op_custom_type() {
|
|||||||
.register_type_with_name::<TestStruct>("TestStruct")
|
.register_type_with_name::<TestStruct>("TestStruct")
|
||||||
.register_fn("new_ts", TestStruct::new);
|
.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<bool>(r"
|
||||||
|
let x = new_ts();
|
||||||
|
let y = new_ts();
|
||||||
|
x == y
|
||||||
|
").expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f == "== (TestStruct, TestStruct)"));
|
||||||
|
|
||||||
|
assert!(!engine.eval::<bool>("new_ts() == 42")?);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("60 + new_ts()").expect_err("should error"),
|
*engine.eval::<INT>("60 + new_ts()").expect_err("should error"),
|
||||||
EvalAltResult::ErrorFunctionNotFound(err, _) if err == format!("+ ({}, TestStruct)", std::any::type_name::<INT>())
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f == format!("+ ({}, TestStruct)", std::any::type_name::<INT>())
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -40,4 +49,6 @@ fn test_mismatched_op_custom_type() {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(need, actual, _)
|
EvalAltResult::ErrorMismatchOutputType(need, actual, _)
|
||||||
if need == "TestStruct" && actual == std::any::type_name::<INT>()
|
if need == "TestStruct" && actual == std::any::type_name::<INT>()
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
39
tests/ops.rs
39
tests/ops.rs
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ops() -> Result<(), Box<EvalAltResult>> {
|
fn test_ops() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -11,7 +11,42 @@ fn test_ops() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_op_precedence() -> Result<(), Box<EvalAltResult>> {
|
fn test_ops_numbers() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
scope.push("x", 42_u16);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.eval_with_scope::<bool>(&mut scope, "x == 42").expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("== (u16,")
|
||||||
|
));
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.eval_with_scope::<bool>(&mut scope, "x == 42.0").expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("== (u16,")
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(!engine.eval_with_scope::<bool>(&mut scope, r#"x == "hello""#)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ops_strings() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(r#""hello" > 'c'"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""" < 'c'"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#"'x' > "hello""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""hello" > "foo""#)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ops_precedence() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1,6 +1,33 @@
|
|||||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
#[test]
|
||||||
|
fn test_to_string() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
scope.push("x", 42_u8);
|
||||||
|
scope.push("y", 42_i32);
|
||||||
|
scope.push("z", 42_i16);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<String>(&mut scope, "to_string(x)")?,
|
||||||
|
"42"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<String>(&mut scope, "to_string(x)")?,
|
||||||
|
"42"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<String>(&mut scope, "to_string(x)")?,
|
||||||
|
"42"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
|
fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
|
||||||
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
||||||
|
@ -66,7 +66,7 @@ fn test_side_effects_command() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_side_effects_print() -> Result<(), Box<EvalAltResult>> {
|
fn test_side_effects_print() -> Result<(), Box<EvalAltResult>> {
|
||||||
let result = Arc::new(RwLock::new(String::from("")));
|
let result = Arc::new(RwLock::new(String::new()));
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ fn test_string() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_string_dynamic() -> Result<(), Box<EvalAltResult>> {
|
fn test_string_dynamic() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
scope.push("x", Dynamic::from("foo"));
|
scope.push("x", "foo");
|
||||||
scope.push("y", String::from("foo"));
|
scope.push("y", "foo");
|
||||||
scope.push("z", "foo");
|
scope.push("z", "foo");
|
||||||
|
|
||||||
assert!(engine.eval_with_scope::<bool>(&mut scope, r#"x == "foo""#)?);
|
assert!(engine.eval_with_scope::<bool>(&mut scope, r#"x == "foo""#)?);
|
||||||
@ -132,6 +132,19 @@ fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"❤❤ hello! ❤❤❤"
|
"❤❤ hello! ❤❤❤"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(
|
||||||
|
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x -= 'l'; x"#
|
||||||
|
)?,
|
||||||
|
"❤❤❤ heo! ❤❤❤"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(
|
||||||
|
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x -= "\u2764\u2764"; x"#
|
||||||
|
)?,
|
||||||
|
"❤ hello! ❤"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764')"#
|
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764')"#
|
||||||
|
Loading…
Reference in New Issue
Block a user