Check for constant values passed to methods.

This commit is contained in:
Stephen Chung 2021-02-19 23:13:53 +08:00
parent ac1b7debe9
commit 182fc2c3d1
13 changed files with 360 additions and 300 deletions

View File

@ -26,7 +26,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
[dependencies] [dependencies]
smallvec = { version = "1.6", default-features = false, features = ["union"] } smallvec = { version = "1.6", default-features = false, features = ["union"] }
ahash = { version = "0.6", default-features = false } ahash = { version = "0.6", default-features = false }
rhai_codegen = { version = "0.3", path = "codegen" } rhai_codegen = { version = "0.3.3", path = "codegen" }
[features] [features]
default = [] default = []

View File

@ -32,11 +32,12 @@ Standard features
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single-core, 2.3 GHz Linux VM). * Fairly efficient evaluation (1 million iterations in 0.3 sec on a single-core, 2.3 GHz Linux VM).
* Tight integration with native Rust [functions](https://rhai.rs/book/rust/functions.html) and [types]([#custom-types-and-methods](https://rhai.rs/book/rust/custom.html)), including [getters/setters](https://rhai.rs/book/rust/getters-setters.html), [methods](https://rhai.rs/book/rust/custom.html) and [indexers](https://rhai.rs/book/rust/indexers.html). * Tight integration with native Rust [functions](https://rhai.rs/book/rust/functions.html) and [types]([#custom-types-and-methods](https://rhai.rs/book/rust/custom.html)), including [getters/setters](https://rhai.rs/book/rust/getters-setters.html), [methods](https://rhai.rs/book/rust/custom.html) and [indexers](https://rhai.rs/book/rust/indexers.html).
* Freely pass Rust variables/constants into a script via an external [`Scope`](https://rhai.rs/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait. * Freely pass Rust variables/constants into a script via an external [`Scope`](https://rhai.rs/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait.
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, integers, floating-point numbers (including [`Decimal`](https://crates.io/crates/rust_decimal)), strings, Unicode characters, arrays and maps.
* Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust. * Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust.
* Relatively little `unsafe` code (yes there are some for performance reasons). * Relatively little `unsafe` code (yes there are some for performance reasons).
* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec) and [`ahash`](https://crates.io/crates/ahash)). * Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec) and [`ahash`](https://crates.io/crates/ahash)).
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature). * Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
* Compile once to AST form for repeated evaluations. * Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations.
* Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts). * Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).
* Easy custom API development via [plugins](https://rhai.rs/book/plugins/index.html) system powered by procedural macros. * Easy custom API development via [plugins](https://rhai.rs/book/plugins/index.html) system powered by procedural macros.
* [Function overloading](https://rhai.rs/book/language/overload.html) and [operator overloading](https://rhai.rs/book/rust/operators.html). * [Function overloading](https://rhai.rs/book/language/overload.html) and [operator overloading](https://rhai.rs/book/rust/operators.html).

View File

@ -12,6 +12,7 @@ Bug fixes
New features New features
------------ ------------
* `#[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. * Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in.
Enhancements Enhancements

View File

@ -22,22 +22,22 @@ macro_rules! gen_array_functions {
#[export_module] #[export_module]
pub mod functions { pub mod functions {
#[rhai_fn(name = "push", name = "+=")] #[rhai_fn(name = "push", name = "+=")]
pub fn push(list: &mut Array, item: $arg_type) { pub fn push(array: &mut Array, item: $arg_type) {
list.push(Dynamic::from(item)); array.push(Dynamic::from(item));
} }
pub fn insert(list: &mut Array, position: INT, item: $arg_type) { pub fn insert(array: &mut Array, position: INT, item: $arg_type) {
if position <= 0 { if position <= 0 {
list.insert(0, Dynamic::from(item)); array.insert(0, Dynamic::from(item));
} else if (position as usize) >= list.len() { } else if (position as usize) >= array.len() {
push(list, item); push(array, item);
} else { } else {
list.insert(position as usize, Dynamic::from(item)); array.insert(position as usize, Dynamic::from(item));
} }
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn pad(_ctx: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> { pub fn pad(_ctx: NativeCallContext, array: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if array will be over max size limit // Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() {
@ -46,8 +46,8 @@ macro_rules! gen_array_functions {
).into(); ).into();
} }
if len > 0 && len as usize > list.len() { if len > 0 && len as usize > array.len() {
list.resize(len as usize, Dynamic::from(item)); array.resize(len as usize, Dynamic::from(item));
} }
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
@ -90,117 +90,117 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
#[export_module] #[export_module]
mod array_functions { mod array_functions {
#[rhai_fn(name = "len", get = "len")] #[rhai_fn(name = "len", get = "len", pure)]
pub fn len(list: &mut Array) -> INT { pub fn len(array: &mut Array) -> INT {
list.len() as INT array.len() as INT
} }
#[rhai_fn(name = "append", name = "+=")] #[rhai_fn(name = "append", name = "+=")]
pub fn append(x: &mut Array, y: Array) { pub fn append(array: &mut Array, y: Array) {
x.extend(y); array.extend(y);
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn concat(mut x: Array, y: Array) -> Array { pub fn concat(mut array: Array, y: Array) -> Array {
x.extend(y); array.extend(y);
x array
} }
pub fn pop(list: &mut Array) -> Dynamic { pub fn pop(array: &mut Array) -> Dynamic {
list.pop().unwrap_or_else(|| ().into()) array.pop().unwrap_or_else(|| ().into())
} }
pub fn shift(list: &mut Array) -> Dynamic { pub fn shift(array: &mut Array) -> Dynamic {
if list.is_empty() { if array.is_empty() {
().into() ().into()
} else { } else {
list.remove(0) array.remove(0)
} }
} }
pub fn remove(list: &mut Array, len: INT) -> Dynamic { pub fn remove(array: &mut Array, len: INT) -> Dynamic {
if len < 0 || (len as usize) >= list.len() { if len < 0 || (len as usize) >= array.len() {
().into() ().into()
} else { } else {
list.remove(len as usize) array.remove(len as usize)
} }
} }
pub fn clear(list: &mut Array) { pub fn clear(array: &mut Array) {
list.clear(); array.clear();
} }
pub fn truncate(list: &mut Array, len: INT) { pub fn truncate(array: &mut Array, len: INT) {
if len >= 0 { if len >= 0 {
list.truncate(len as usize); array.truncate(len as usize);
} else { } else {
list.clear(); array.clear();
} }
} }
pub fn chop(list: &mut Array, len: INT) { pub fn chop(array: &mut Array, len: INT) {
if len as usize >= list.len() { if len as usize >= array.len() {
} else if len >= 0 { } else if len >= 0 {
list.drain(0..list.len() - len as usize); array.drain(0..array.len() - len as usize);
} else { } else {
list.clear(); array.clear();
} }
} }
pub fn reverse(list: &mut Array) { pub fn reverse(array: &mut Array) {
list.reverse(); array.reverse();
} }
pub fn splice(list: &mut Array, start: INT, len: INT, replace: Array) { pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= list.len() { } else if start as usize >= array.len() {
list.len() - 1 array.len() - 1
} else { } else {
start as usize start as usize
}; };
let len = if len < 0 { let len = if len < 0 {
0 0
} else if len as usize > list.len() - start { } else if len as usize > array.len() - start {
list.len() - start array.len() - start
} else { } else {
len as usize len as usize
}; };
list.splice(start..start + len, replace.into_iter()); array.splice(start..start + len, replace.into_iter());
} }
pub fn extract(list: &mut Array, start: INT, len: INT) -> Array { pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= list.len() { } else if start as usize >= array.len() {
list.len() - 1 array.len() - 1
} else { } else {
start as usize start as usize
}; };
let len = if len < 0 { let len = if len < 0 {
0 0
} else if len as usize > list.len() - start { } else if len as usize > array.len() - start {
list.len() - start array.len() - start
} else { } else {
len as usize len as usize
}; };
list[start..start + len].iter().cloned().collect() array[start..start + len].iter().cloned().collect()
} }
#[rhai_fn(name = "extract")] #[rhai_fn(name = "extract")]
pub fn extract_tail(list: &mut Array, start: INT) -> Array { pub fn extract_tail(array: &mut Array, start: INT) -> Array {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= list.len() { } else if start as usize >= array.len() {
list.len() - 1 array.len() - 1
} else { } else {
start as usize start as usize
}; };
list[start..].iter().cloned().collect() array[start..].iter().cloned().collect()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn map( pub fn map(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
mapper: FnPtr, mapper: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut array = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); let mut ar = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, array.len()));
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
array.push( ar.push(
mapper mapper
.call_dynamic(ctx, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -222,17 +222,17 @@ mod array_functions {
); );
} }
Ok(array.into()) Ok(ar.into())
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn filter( pub fn filter(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut array = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); let mut ar = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, array.len()));
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
if filter if filter
.call_dynamic(ctx, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -254,19 +254,19 @@ mod array_functions {
.as_bool() .as_bool()
.unwrap_or(false) .unwrap_or(false)
{ {
array.push(item.clone()); ar.push(item.clone());
} }
} }
Ok(array.into()) Ok(ar.into())
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn index_of( pub fn index_of(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
if filter if filter
.call_dynamic(ctx, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -297,10 +297,10 @@ mod array_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn some( pub fn some(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
if filter if filter
.call_dynamic(ctx, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -331,10 +331,10 @@ mod array_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn all( pub fn all(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
if !filter if !filter
.call_dynamic(ctx, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -365,12 +365,12 @@ mod array_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn reduce( pub fn reduce(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut result: Dynamic = Dynamic::UNIT; let mut result: Dynamic = Dynamic::UNIT;
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
result = reducer result = reducer
.call_dynamic(ctx, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -396,7 +396,7 @@ mod array_functions {
#[rhai_fn(name = "reduce", return_raw)] #[rhai_fn(name = "reduce", return_raw)]
pub fn reduce_with_initial( pub fn reduce_with_initial(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
initial: FnPtr, initial: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -409,7 +409,7 @@ mod array_functions {
)) ))
})?; })?;
for (i, item) in list.iter().enumerate() { for (i, item) in array.iter().enumerate() {
result = reducer result = reducer
.call_dynamic(ctx, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -435,12 +435,12 @@ mod array_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn reduce_rev( pub fn reduce_rev(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut result: Dynamic = Dynamic::UNIT; let mut result: Dynamic = Dynamic::UNIT;
for (i, item) in list.iter().enumerate().rev() { for (i, item) in array.iter().enumerate().rev() {
result = reducer result = reducer
.call_dynamic(ctx, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -466,7 +466,7 @@ mod array_functions {
#[rhai_fn(name = "reduce_rev", return_raw)] #[rhai_fn(name = "reduce_rev", return_raw)]
pub fn reduce_rev_with_initial( pub fn reduce_rev_with_initial(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
initial: FnPtr, initial: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -479,7 +479,7 @@ mod array_functions {
)) ))
})?; })?;
for (i, item) in list.iter().enumerate().rev() { for (i, item) in array.iter().enumerate().rev() {
result = reducer result = reducer
.call_dynamic(ctx, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
@ -505,10 +505,10 @@ mod array_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sort( pub fn sort(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
comparer: FnPtr, comparer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
list.sort_by(|x, y| { array.sort_by(|x, y| {
comparer comparer
.call_dynamic(ctx, None, [x.clone(), y.clone()]) .call_dynamic(ctx, None, [x.clone(), y.clone()])
.ok() .ok()
@ -541,23 +541,23 @@ mod array_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn drain( pub fn drain(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut drained = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); let mut drained = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, array.len()));
let mut i = list.len(); let mut i = array.len();
while i > 0 { while i > 0 {
i -= 1; i -= 1;
if filter if filter
.call_dynamic(ctx, None, [list[i].clone()]) .call_dynamic(ctx, None, [array[i].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [array[i].clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -572,52 +572,52 @@ mod array_functions {
.as_bool() .as_bool()
.unwrap_or(false) .unwrap_or(false)
{ {
drained.push(list.remove(i)); drained.push(array.remove(i));
} }
} }
Ok(drained.into()) Ok(drained.into())
} }
#[rhai_fn(name = "drain")] #[rhai_fn(name = "drain")]
pub fn drain_range(list: &mut Array, start: INT, len: INT) -> Array { pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= list.len() { } else if start as usize >= array.len() {
list.len() - 1 array.len() - 1
} else { } else {
start as usize start as usize
}; };
let len = if len < 0 { let len = if len < 0 {
0 0
} else if len as usize > list.len() - start { } else if len as usize > array.len() - start {
list.len() - start array.len() - start
} else { } else {
len as usize len as usize
}; };
list.drain(start..start + len - 1).collect() array.drain(start..start + len - 1).collect()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn retain( pub fn retain(
ctx: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut drained = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); let mut drained = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, array.len()));
let mut i = list.len(); let mut i = array.len();
while i > 0 { while i > 0 {
i -= 1; i -= 1;
if !filter if !filter
.call_dynamic(ctx, None, [list[i].clone()]) .call_dynamic(ctx, None, [array[i].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [array[i].clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -632,51 +632,51 @@ mod array_functions {
.as_bool() .as_bool()
.unwrap_or(false) .unwrap_or(false)
{ {
drained.push(list.remove(i)); drained.push(array.remove(i));
} }
} }
Ok(drained.into()) Ok(drained.into())
} }
#[rhai_fn(name = "retain")] #[rhai_fn(name = "retain")]
pub fn retain_range(list: &mut Array, start: INT, len: INT) -> Array { pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= list.len() { } else if start as usize >= array.len() {
list.len() - 1 array.len() - 1
} else { } else {
start as usize start as usize
}; };
let len = if len < 0 { let len = if len < 0 {
0 0
} else if len as usize > list.len() - start { } else if len as usize > array.len() - start {
list.len() - start array.len() - start
} else { } else {
len as usize len as usize
}; };
let mut drained = list.drain(start + len..).collect::<Array>(); let mut drained = array.drain(start + len..).collect::<Array>();
drained.extend(list.drain(..start)); drained.extend(array.drain(..start));
drained drained
} }
#[rhai_fn(name = "==", return_raw)] #[rhai_fn(name = "==", return_raw)]
pub fn equals( pub fn equals(
ctx: NativeCallContext, ctx: NativeCallContext,
arr1: &mut Array, array: &mut Array,
mut arr2: Array, mut array2: Array,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
if arr1.len() != arr2.len() { if array.len() != array2.len() {
return Ok(false.into()); return Ok(false.into());
} }
if arr1.is_empty() { if array.is_empty() {
return Ok(true.into()); return Ok(true.into());
} }
let def_value = Some(false.into()); let def_value = Some(false.into());
for (a1, a2) in arr1.iter_mut().zip(arr2.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], def_value.as_ref())
.map(|v| v.as_bool().unwrap_or(false))?; .map(|v| v.as_bool().unwrap_or(false))?;
@ -691,10 +691,10 @@ mod array_functions {
#[rhai_fn(name = "!=", return_raw)] #[rhai_fn(name = "!=", return_raw)]
pub fn not_equals( pub fn not_equals(
ctx: NativeCallContext, ctx: NativeCallContext,
arr1: &mut Array, array: &mut Array,
arr2: Array, array2: Array,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
equals(ctx, arr1, arr2).map(|r| (!r.as_bool().unwrap()).into()) equals(ctx, array, array2).map(|r| (!r.as_bool().unwrap()).into())
} }
} }

View File

@ -12,7 +12,7 @@ def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
#[export_module] #[export_module]
mod fn_ptr_functions { mod fn_ptr_functions {
#[rhai_fn(name = "name", get = "name")] #[rhai_fn(name = "name", get = "name", pure)]
pub fn name(f: &mut FnPtr) -> ImmutableString { pub fn name(f: &mut FnPtr) -> ImmutableString {
f.get_fn_name().clone() f.get_fn_name().clone()
} }
@ -21,7 +21,7 @@ mod fn_ptr_functions {
pub mod functions { pub mod functions {
use crate::{calc_script_fn_hash, stdlib::iter::empty, INT}; use crate::{calc_script_fn_hash, stdlib::iter::empty, INT};
#[rhai_fn(name = "is_anonymous", get = "is_anonymous")] #[rhai_fn(name = "is_anonymous", get = "is_anonymous", pure)]
pub fn is_anonymous(f: &mut FnPtr) -> bool { pub fn is_anonymous(f: &mut FnPtr) -> bool {
f.is_anonymous() f.is_anonymous()
} }

View File

@ -60,7 +60,7 @@ macro_rules! reg_range {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
macro_rules! reg_step { macro_rules! reg_stepped_range {
($lib:expr, $x:expr, $( $y:ty ),*) => ( ($lib:expr, $x:expr, $( $y:ty ),*) => (
$( $(
$lib.set_iterator::<StepRange<$y>>(); $lib.set_iterator::<StepRange<$y>>();
@ -68,38 +68,35 @@ macro_rules! reg_step {
$lib.update_fn_metadata(hash, [ $lib.update_fn_metadata(hash, [
concat!("from: ", stringify!($y)), concat!("from: ", stringify!($y)),
concat!("to: ", stringify!($y)), concat!("to: ", stringify!($y)),
concat!("step: ", stringify!($y)), concat!("Iterator<Item=", stringify!($y), ">") concat!("step: ", stringify!($y)),
concat!("Iterator<Item=", stringify!($y), ">")
]); ]);
)* )*
) )
} }
def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
lib.set_iterator::<Range<INT>>(); reg_range!(lib, "range", INT);
let hash = lib.set_fn_2("range", get_range::<INT>);
lib.update_fn_metadata(hash, ["from: INT", "to: INT", "Iterator<Item=INT>"]);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{ {
reg_range!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64); reg_range!(lib, "range", i8, u8, i16, u16, i32, u32, i64, u64);
if cfg!(not(target_arch = "wasm32")) { if cfg!(not(target_arch = "wasm32")) {
reg_range!(lib, "range", i128, u128); reg_range!(lib, "range", i128, u128);
} }
} }
lib.set_iterator::<StepRange<INT>>(); reg_stepped_range!(lib, "range", INT);
let hash = lib.set_fn_3("range", get_step_range::<INT>);
lib.update_fn_metadata(hash, ["from: INT", "to: INT", "step: INT", "Iterator<Item=INT>"]);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{ {
reg_step!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64); reg_stepped_range!(lib, "range", i8, u8, i16, u16, i32, u32, i64, u64);
if cfg!(not(target_arch = "wasm32")) { if cfg!(not(target_arch = "wasm32")) {
reg_step!(lib, "range", i128, u128); reg_stepped_range!(lib, "range", i128, u128);
} }
} }
}); });

View File

@ -13,52 +13,54 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
#[export_module] #[export_module]
mod map_functions { mod map_functions {
#[rhai_fn(pure)]
pub fn has(map: &mut Map, prop: ImmutableString) -> bool { pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
map.contains_key(&prop) map.contains_key(&prop)
} }
#[rhai_fn(pure)]
pub fn len(map: &mut Map) -> INT { pub fn len(map: &mut Map) -> INT {
map.len() as INT map.len() as INT
} }
pub fn clear(map: &mut Map) { pub fn clear(map: &mut Map) {
map.clear(); map.clear();
} }
pub fn remove(x: &mut Map, name: ImmutableString) -> Dynamic { pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic {
x.remove(&name).unwrap_or_else(|| ().into()) map.remove(&name).unwrap_or_else(|| ().into())
} }
#[rhai_fn(name = "mixin", name = "+=")] #[rhai_fn(name = "mixin", name = "+=")]
pub fn mixin(map1: &mut Map, map2: Map) { pub fn mixin(map: &mut Map, map2: Map) {
map2.into_iter().for_each(|(key, value)| { map2.into_iter().for_each(|(key, value)| {
map1.insert(key, value); map.insert(key, value);
}); });
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn merge(mut map1: Map, map2: Map) -> Map { pub fn merge(mut map: Map, map2: Map) -> Map {
map2.into_iter().for_each(|(key, value)| { map2.into_iter().for_each(|(key, value)| {
map1.insert(key, value); map.insert(key, value);
}); });
map1 map
} }
pub fn fill_with(map1: &mut Map, map2: Map) { pub fn fill_with(map: &mut Map, map2: Map) {
map2.into_iter().for_each(|(key, value)| { map2.into_iter().for_each(|(key, value)| {
map1.entry(key).or_insert(value); map.entry(key).or_insert(value);
}); });
} }
#[rhai_fn(name = "==", return_raw)] #[rhai_fn(name = "==", return_raw, pure)]
pub fn equals( pub fn equals(
ctx: NativeCallContext, ctx: NativeCallContext,
map1: &mut Map, map: &mut Map,
mut map2: Map, mut map2: Map,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
if map1.len() != map2.len() { if map.len() != map2.len() {
return Ok(false.into()); return Ok(false.into());
} }
if map1.is_empty() { if map.is_empty() {
return Ok(true.into()); return Ok(true.into());
} }
let def_value = Some(false.into()); let def_value = Some(false.into());
for (m1, v1) in map1.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], def_value.as_ref())
@ -74,20 +76,22 @@ mod map_functions {
Ok(true.into()) Ok(true.into())
} }
#[rhai_fn(name = "!=", return_raw)] #[rhai_fn(name = "!=", return_raw, pure)]
pub fn not_equals( pub fn not_equals(
ctx: NativeCallContext, ctx: NativeCallContext,
map1: &mut Map, map: &mut Map,
map2: Map, map2: Map,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
equals(ctx, map1, map2).map(|r| (!r.as_bool().unwrap()).into()) equals(ctx, map, map2).map(|r| (!r.as_bool().unwrap()).into())
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub mod indexing { pub mod indexing {
#[rhai_fn(pure)]
pub fn keys(map: &mut Map) -> Array { pub fn keys(map: &mut Map) -> Array {
map.iter().map(|(k, _)| k.clone().into()).collect() map.iter().map(|(k, _)| k.clone().into()).collect()
} }
#[rhai_fn(pure)]
pub fn values(map: &mut Map) -> Array { pub fn values(map: &mut Map) -> Array {
map.iter().map(|(_, v)| v.clone()).collect() map.iter().map(|(_, v)| v.clone()).collect()
} }

View File

@ -28,7 +28,7 @@ macro_rules! gen_functions {
pub mod $root { $(pub mod $arg_type { pub mod $root { $(pub mod $arg_type {
use super::super::*; use super::super::*;
#[export_fn] #[export_fn(pure)]
pub fn to_string_func(x: &mut $arg_type) -> ImmutableString { pub fn to_string_func(x: &mut $arg_type) -> ImmutableString {
super::super::$fn_name(x) super::super::$fn_name(x)
} }
@ -174,7 +174,7 @@ mod print_debug_functions {
pub fn print_string(s: ImmutableString) -> ImmutableString { pub fn print_string(s: ImmutableString) -> ImmutableString {
s s
} }
#[rhai_fn(name = "debug")] #[rhai_fn(name = "debug", pure)]
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString { pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
to_string(f) to_string(f)
} }
@ -183,14 +183,20 @@ mod print_debug_functions {
pub mod array_functions { pub mod array_functions {
use super::*; use super::*;
#[rhai_fn(name = "print", name = "to_string", name = "to_debug", name = "debug")] #[rhai_fn(
pub fn format_array(ctx: NativeCallContext, arr: &mut Array) -> ImmutableString { name = "print",
name = "to_string",
name = "to_debug",
name = "debug",
pure
)]
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
let mut result = crate::stdlib::string::String::with_capacity(16); let mut result = crate::stdlib::string::String::with_capacity(16);
result.push_str("["); result.push_str("[");
let len = arr.len(); let len = array.len();
arr.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));
if i < len - 1 { if i < len - 1 {
result.push_str(", "); result.push_str(", ");
@ -205,7 +211,13 @@ mod print_debug_functions {
pub mod map_functions { pub mod map_functions {
use super::*; use super::*;
#[rhai_fn(name = "print", name = "to_string", name = "to_debug", name = "debug")] #[rhai_fn(
name = "print",
name = "to_string",
name = "to_debug",
name = "debug",
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); let mut result = crate::stdlib::string::String::with_capacity(16);
result.push_str("#{"); result.push_str("#{");

View File

@ -14,13 +14,13 @@ macro_rules! gen_concat_functions {
#[export_module] #[export_module]
pub mod functions { pub mod functions {
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn append_func(x: &str, y: $arg_type) -> String { pub fn append_func(string: &str, arg: $arg_type) -> String {
format!("{}{}", x, y) format!("{}{}", string, arg)
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+", pure)]
pub fn prepend_func(x: &mut $arg_type, y: &str) -> String { pub fn prepend_func(arg: &mut $arg_type, string: &str) -> String {
format!("{}{}", x, y) format!("{}{}", arg, string)
} }
} }
} )* } } )* }
@ -53,7 +53,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
// Register string iterator // Register string iterator
lib.set_iter( lib.set_iter(
TypeId::of::<ImmutableString>(), TypeId::of::<ImmutableString>(),
|s: Dynamic| Box::new(s.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into)) |string: Dynamic| Box::new(string.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into))
); );
}); });
@ -73,104 +73,116 @@ gen_concat_functions!(float => f32, f64);
#[export_module] #[export_module]
mod string_functions { mod string_functions {
use crate::ImmutableString;
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_append_unit(s: ImmutableString, _x: ()) -> ImmutableString { pub fn add_append_unit(string: ImmutableString, _x: ()) -> ImmutableString {
s string
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_prepend_unit(_x: (), s: ImmutableString) -> ImmutableString { pub fn add_prepend_unit(_x: (), string: ImmutableString) -> ImmutableString {
s string
} }
#[rhai_fn(name = "len", get = "len")] #[rhai_fn(name = "len", get = "len")]
pub fn len(s: &str) -> INT { pub fn len(string: &str) -> INT {
s.chars().count() as INT string.chars().count() as INT
} }
pub fn clear(s: &mut ImmutableString) { pub fn clear(string: &mut ImmutableString) {
s.make_mut().clear(); string.make_mut().clear();
} }
pub fn truncate(s: &mut ImmutableString, len: INT) { pub fn truncate(string: &mut ImmutableString, len: INT) {
if len > 0 { if len > 0 {
let chars: StaticVec<_> = s.chars().collect(); let chars: StaticVec<_> = string.chars().collect();
let copy = s.make_mut(); let copy = string.make_mut();
copy.clear(); copy.clear();
copy.extend(chars.into_iter().take(len as usize)); copy.extend(chars.into_iter().take(len as usize));
} else { } else {
s.make_mut().clear(); string.make_mut().clear();
} }
} }
pub fn trim(s: &mut ImmutableString) { pub fn trim(string: &mut ImmutableString) {
let trimmed = s.trim(); let trimmed = string.trim();
if trimmed.len() < s.len() { if trimmed.len() < string.len() {
*s = trimmed.to_string().into(); *string = trimmed.to_string().into();
} }
} }
#[rhai_fn(name = "contains")] #[rhai_fn(name = "contains")]
pub fn contains_char(s: &str, ch: char) -> bool { pub fn contains_char(string: &str, ch: char) -> bool {
s.contains(ch) string.contains(ch)
} }
pub fn contains(s: &str, find: ImmutableString) -> bool { pub fn contains(string: &str, find_string: &str) -> bool {
s.contains(find.as_str()) string.contains(find_string)
} }
#[rhai_fn(name = "index_of")] #[rhai_fn(name = "index_of")]
pub fn index_of_char_starting_from(s: &str, ch: char, start: INT) -> INT { pub fn index_of_char_starting_from(string: &str, ch: char, start: INT) -> INT {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= s.chars().count() { } else if start as usize >= string.chars().count() {
return -1 as INT; return -1 as INT;
} else { } else {
s.chars().take(start as usize).collect::<String>().len() string
.chars()
.take(start as usize)
.collect::<String>()
.len()
}; };
s[start..] string[start..]
.find(ch) .find(ch)
.map(|index| s[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(s: &str, ch: char) -> INT { pub fn index_of_char(string: &str, ch: char) -> INT {
s.find(ch) string
.map(|index| s[0..index].chars().count() as INT) .find(ch)
.map(|index| string[0..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_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT { pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if start as usize >= s.chars().count() { } else if start as usize >= string.chars().count() {
return -1 as INT; return -1 as INT;
} else { } else {
s.chars().take(start as usize).collect::<String>().len() string
.chars()
.take(start as usize)
.collect::<String>()
.len()
}; };
s[start..] string[start..]
.find(find.as_str()) .find(find_string)
.map(|index| s[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(s: &str, find: ImmutableString) -> INT { pub fn index_of(string: &str, find_string: &str) -> INT {
s.find(find.as_str()) string
.map(|index| s[0..index].chars().count() as INT) .find(find_string)
.map(|index| string[0..index].chars().count() as INT)
.unwrap_or(-1 as INT) .unwrap_or(-1 as INT)
} }
pub fn sub_string(s: &str, start: INT, len: INT) -> ImmutableString { pub fn sub_string(string: &str, start: INT, len: INT) -> ImmutableString {
let offset = if s.is_empty() || len <= 0 { let offset = if string.is_empty() || len <= 0 {
return "".to_string().into(); return "".to_string().into();
} else if start < 0 { } else if start < 0 {
0 0
} else if start as usize >= s.chars().count() { } else if start as usize >= string.chars().count() {
return "".to_string().into(); return "".to_string().into();
} else { } else {
start as usize start as usize
}; };
let chars: StaticVec<_> = s.chars().collect(); let chars: StaticVec<_> = string.chars().collect();
let len = if offset + len as usize > chars.len() { let len = if offset + len as usize > chars.len() {
chars.len() - offset chars.len() - offset
@ -187,26 +199,26 @@ mod string_functions {
.into() .into()
} }
#[rhai_fn(name = "sub_string")] #[rhai_fn(name = "sub_string")]
pub fn sub_string_starting_from(s: &str, start: INT) -> ImmutableString { pub fn sub_string_starting_from(string: &str, start: INT) -> ImmutableString {
let len = s.len() as INT; let len = string.len() as INT;
sub_string(s, start, len) sub_string(string, start, len)
} }
#[rhai_fn(name = "crop")] #[rhai_fn(name = "crop")]
pub fn crop(s: &mut ImmutableString, start: INT, len: INT) { pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
let offset = if s.is_empty() || len <= 0 { let offset = if string.is_empty() || len <= 0 {
s.make_mut().clear(); string.make_mut().clear();
return; return;
} else if start < 0 { } else if start < 0 {
0 0
} else if start as usize >= s.chars().count() { } else if start as usize >= string.chars().count() {
s.make_mut().clear(); string.make_mut().clear();
return; return;
} else { } else {
start as usize start as usize
}; };
let chars: StaticVec<_> = s.chars().collect(); let chars: StaticVec<_> = string.chars().collect();
let len = if offset + len as usize > chars.len() { let len = if offset + len as usize > chars.len() {
chars.len() - offset chars.len() - offset
@ -214,36 +226,50 @@ mod string_functions {
len as usize len as usize
}; };
let copy = s.make_mut(); let copy = string.make_mut();
copy.clear(); copy.clear();
copy.extend(chars.iter().skip(offset).take(len)); copy.extend(chars.iter().skip(offset).take(len));
} }
#[rhai_fn(name = "crop")] #[rhai_fn(name = "crop")]
pub fn crop_string_starting_from(s: &mut ImmutableString, start: INT) { pub fn crop_string_starting_from(string: &mut ImmutableString, start: INT) {
crop(s, start, s.len() as INT); crop(string, start, string.len() as INT);
} }
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace(s: &mut ImmutableString, find: ImmutableString, sub: ImmutableString) { pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) {
*s = s.replace(find.as_str(), sub.as_str()).into(); *string = string.replace(find_string, substitute_string).into();
} }
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace_string_with_char(s: &mut ImmutableString, find: ImmutableString, sub: char) { pub fn replace_string_with_char(
*s = s.replace(find.as_str(), &sub.to_string()).into(); string: &mut ImmutableString,
find_string: &str,
substitute_char: char,
) {
*string = string
.replace(find_string, &substitute_char.to_string())
.into();
} }
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace_char_with_string(s: &mut ImmutableString, find: char, sub: ImmutableString) { pub fn replace_char_with_string(
*s = s.replace(&find.to_string(), sub.as_str()).into(); string: &mut ImmutableString,
find_char: char,
substitute_string: &str,
) {
*string = string
.replace(&find_char.to_string(), substitute_string)
.into();
} }
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) { pub fn replace_char(string: &mut ImmutableString, find_char: char, substitute_char: char) {
*s = s.replace(&find.to_string(), &sub.to_string()).into(); *string = string
.replace(&find_char.to_string(), &substitute_char.to_string())
.into();
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn pad( pub fn pad(
_ctx: NativeCallContext, _ctx: NativeCallContext,
s: &mut ImmutableString, string: &mut ImmutableString,
len: INT, len: INT,
ch: char, ch: char,
) -> Result<Dynamic, Box<crate::EvalAltResult>> { ) -> Result<Dynamic, Box<crate::EvalAltResult>> {
@ -258,17 +284,18 @@ mod string_functions {
} }
if len > 0 { if len > 0 {
let orig_len = s.chars().count(); let orig_len = string.chars().count();
if len as usize > orig_len { if len as usize > orig_len {
let p = s.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(ch);
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size() if _ctx.engine().max_string_size() > 0
&& string.len() > _ctx.engine().max_string_size()
{ {
return crate::EvalAltResult::ErrorDataTooLarge( return crate::EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
@ -284,7 +311,7 @@ mod string_functions {
#[rhai_fn(name = "pad", return_raw)] #[rhai_fn(name = "pad", return_raw)]
pub fn pad_with_string( pub fn pad_with_string(
_ctx: NativeCallContext, _ctx: NativeCallContext,
s: &mut ImmutableString, string: &mut ImmutableString,
len: INT, len: INT,
padding: &str, padding: &str,
) -> Result<Dynamic, Box<crate::EvalAltResult>> { ) -> Result<Dynamic, Box<crate::EvalAltResult>> {
@ -299,11 +326,11 @@ mod string_functions {
} }
if len > 0 { if len > 0 {
let mut str_len = s.chars().count(); let mut str_len = string.chars().count();
let padding_len = padding.chars().count(); let padding_len = padding.chars().count();
if len as usize > str_len { if len as usize > str_len {
let p = s.make_mut(); let p = string.make_mut();
while str_len < len as usize { while str_len < len as usize {
if str_len + padding_len <= len as usize { if str_len + padding_len <= len as usize {
@ -316,7 +343,8 @@ mod string_functions {
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size() if _ctx.engine().max_string_size() > 0
&& string.len() > _ctx.engine().max_string_size()
{ {
return crate::EvalAltResult::ErrorDataTooLarge( return crate::EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
@ -335,21 +363,19 @@ mod string_functions {
use crate::Array; use crate::Array;
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn append(x: &str, y: Array) -> String { pub fn append(string: &str, array: Array) -> String {
format!("{}{:?}", x, y) format!("{}{:?}", string, array)
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+", pure)]
pub fn prepend(x: &mut Array, y: &str) -> String { pub fn prepend(array: &mut Array, string: &str) -> String {
format!("{:?}{}", x, y) format!("{:?}{}", array, string)
} }
pub fn split(s: &str, delimiter: ImmutableString) -> Array { pub fn split(string: &str, delimiter: &str) -> Array {
s.split(delimiter.as_str()) string.split(delimiter).map(Into::<Dynamic>::into).collect()
.map(Into::<Dynamic>::into)
.collect()
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn split_char(s: &str, delimiter: char) -> Array { pub fn split_char(string: &str, delimiter: char) -> Array {
s.split(delimiter).map(Into::<Dynamic>::into).collect() string.split(delimiter).map(Into::<Dynamic>::into).collect()
} }
} }
@ -358,12 +384,12 @@ mod string_functions {
use crate::Map; use crate::Map;
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn append(x: &str, y: Map) -> String { pub fn append(string: &str, map: Map) -> String {
format!("{}#{:?}", x, y) format!("{}#{:?}", string, map)
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+", pure)]
pub fn prepend(x: &mut Map, y: &str) -> String { pub fn prepend(map: &mut Map, string: &str) -> String {
format!("#{:?}{}", x, y) format!("#{:?}{}", map, string)
} }
} }
} }

View File

@ -26,9 +26,9 @@ mod time_functions {
} }
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)] #[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
pub fn elapsed(timestamp: &mut Instant) -> Result<Dynamic, Box<EvalAltResult>> { pub fn elapsed(timestamp: Instant) -> Result<Dynamic, Box<EvalAltResult>> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
if *timestamp > Instant::now() { if timestamp > Instant::now() {
Err(make_arithmetic_err("Time-stamp is later than now")) Err(make_arithmetic_err("Time-stamp is later than now"))
} else { } else {
Ok((timestamp.elapsed().as_secs_f64() as FLOAT).into()) Ok((timestamp.elapsed().as_secs_f64() as FLOAT).into())
@ -43,7 +43,7 @@ mod time_functions {
"Integer overflow for timestamp.elapsed: {}", "Integer overflow for timestamp.elapsed: {}",
seconds seconds
))) )))
} else if *timestamp > Instant::now() { } else if timestamp > Instant::now() {
Err(make_arithmetic_err("Time-stamp is later than now")) Err(make_arithmetic_err("Time-stamp is later than now"))
} else { } else {
Ok((seconds as INT).into()) Ok((seconds as INT).into())
@ -52,18 +52,21 @@ mod time_functions {
} }
#[rhai_fn(return_raw, name = "-")] #[rhai_fn(return_raw, name = "-")]
pub fn time_diff(ts1: Instant, ts2: Instant) -> Result<Dynamic, Box<EvalAltResult>> { pub fn time_diff(
timestamp: Instant,
timestamp2: Instant,
) -> Result<Dynamic, Box<EvalAltResult>> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return Ok(if ts2 > ts1 { return Ok(if timestamp2 > timestamp {
-(ts2 - ts1).as_secs_f64() as FLOAT -(timestamp2 - timestamp).as_secs_f64() as FLOAT
} else { } else {
(ts1 - ts2).as_secs_f64() as FLOAT (timestamp - timestamp2).as_secs_f64() as FLOAT
} }
.into()); .into());
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
if ts2 > ts1 { if timestamp2 > timestamp {
let seconds = (ts2 - ts1).as_secs(); let seconds = (timestamp2 - timestamp).as_secs();
if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
@ -74,7 +77,7 @@ mod time_functions {
Ok((-(seconds as INT)).into()) Ok((-(seconds as INT)).into())
} }
} else { } else {
let seconds = (ts1 - ts2).as_secs(); let seconds = (timestamp - timestamp2).as_secs();
if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
@ -89,9 +92,9 @@ mod time_functions {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub mod float_functions { pub mod float_functions {
fn add_impl(x: Instant, seconds: FLOAT) -> Result<Instant, Box<EvalAltResult>> { fn add_impl(timestamp: Instant, seconds: FLOAT) -> Result<Instant, Box<EvalAltResult>> {
if seconds < 0.0 { if seconds < 0.0 {
subtract_impl(x, -seconds) subtract_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
if seconds > (MAX_INT as FLOAT) { if seconds > (MAX_INT as FLOAT) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
@ -99,7 +102,8 @@ mod time_functions {
seconds seconds
))) )))
} else { } else {
x.checked_add(Duration::from_millis((seconds * 1000.0) as u64)) timestamp
.checked_add(Duration::from_millis((seconds * 1000.0) as u64))
.ok_or_else(|| { .ok_or_else(|| {
make_arithmetic_err(format!( make_arithmetic_err(format!(
"Timestamp overflow when adding {} second(s)", "Timestamp overflow when adding {} second(s)",
@ -108,12 +112,15 @@ mod time_functions {
}) })
} }
} else { } else {
Ok(x + Duration::from_millis((seconds * 1000.0) as u64)) Ok(timestamp + Duration::from_millis((seconds * 1000.0) as u64))
} }
} }
fn subtract_impl(x: Instant, seconds: FLOAT) -> Result<Instant, Box<EvalAltResult>> { fn subtract_impl(
timestamp: Instant,
seconds: FLOAT,
) -> Result<Instant, Box<EvalAltResult>> {
if seconds < 0.0 { if seconds < 0.0 {
add_impl(x, -seconds) add_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
if seconds > (MAX_INT as FLOAT) { if seconds > (MAX_INT as FLOAT) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
@ -121,7 +128,8 @@ mod time_functions {
seconds seconds
))) )))
} else { } else {
x.checked_sub(Duration::from_millis((seconds * 1000.0) as u64)) timestamp
.checked_sub(Duration::from_millis((seconds * 1000.0) as u64))
.ok_or_else(|| { .ok_or_else(|| {
make_arithmetic_err(format!( make_arithmetic_err(format!(
"Timestamp overflow when adding {} second(s)", "Timestamp overflow when adding {} second(s)",
@ -130,38 +138,42 @@ mod time_functions {
}) })
} }
} else { } else {
Ok(x - Duration::from_millis((seconds * 1000.0) as u64)) Ok(timestamp - Duration::from_millis((seconds * 1000.0) as u64))
} }
} }
#[rhai_fn(return_raw, name = "+")] #[rhai_fn(return_raw, name = "+")]
pub fn add(x: Instant, seconds: FLOAT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn add(timestamp: Instant, seconds: FLOAT) -> Result<Dynamic, Box<EvalAltResult>> {
add_impl(x, seconds).map(Into::<Dynamic>::into) add_impl(timestamp, seconds).map(Into::<Dynamic>::into)
} }
#[rhai_fn(return_raw, name = "+=")] #[rhai_fn(return_raw, name = "+=")]
pub fn add_assign(x: &mut Instant, seconds: FLOAT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn add_assign(
*x = add_impl(*x, seconds)?; timestamp: &mut Instant,
seconds: FLOAT,
) -> Result<Dynamic, Box<EvalAltResult>> {
*timestamp = add_impl(*timestamp, seconds)?;
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
#[rhai_fn(return_raw, name = "-")] #[rhai_fn(return_raw, name = "-")]
pub fn subtract(x: Instant, seconds: FLOAT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn subtract(timestamp: Instant, seconds: FLOAT) -> Result<Dynamic, Box<EvalAltResult>> {
subtract_impl(x, seconds).map(Into::<Dynamic>::into) subtract_impl(timestamp, seconds).map(Into::<Dynamic>::into)
} }
#[rhai_fn(return_raw, name = "-=")] #[rhai_fn(return_raw, name = "-=")]
pub fn subtract_assign( pub fn subtract_assign(
x: &mut Instant, timestamp: &mut Instant,
seconds: FLOAT, seconds: FLOAT,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
*x = subtract_impl(*x, seconds)?; *timestamp = subtract_impl(*timestamp, seconds)?;
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
} }
fn add_impl(x: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> { fn add_impl(timestamp: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> {
if seconds < 0 { if seconds < 0 {
subtract_impl(x, -seconds) subtract_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
x.checked_add(Duration::from_secs(seconds as u64)) timestamp
.checked_add(Duration::from_secs(seconds as u64))
.ok_or_else(|| { .ok_or_else(|| {
make_arithmetic_err(format!( make_arithmetic_err(format!(
"Timestamp overflow when adding {} second(s)", "Timestamp overflow when adding {} second(s)",
@ -169,14 +181,15 @@ mod time_functions {
)) ))
}) })
} else { } else {
Ok(x + Duration::from_secs(seconds as u64)) Ok(timestamp + Duration::from_secs(seconds as u64))
} }
} }
fn subtract_impl(x: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> { fn subtract_impl(timestamp: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> {
if seconds < 0 { if seconds < 0 {
add_impl(x, -seconds) add_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
x.checked_sub(Duration::from_secs(seconds as u64)) timestamp
.checked_sub(Duration::from_secs(seconds as u64))
.ok_or_else(|| { .ok_or_else(|| {
make_arithmetic_err(format!( make_arithmetic_err(format!(
"Timestamp overflow when adding {} second(s)", "Timestamp overflow when adding {} second(s)",
@ -184,51 +197,57 @@ mod time_functions {
)) ))
}) })
} else { } else {
Ok(x - Duration::from_secs(seconds as u64)) Ok(timestamp - Duration::from_secs(seconds as u64))
} }
} }
#[rhai_fn(return_raw, name = "+")] #[rhai_fn(return_raw, name = "+")]
pub fn add(x: Instant, seconds: INT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn add(timestamp: Instant, seconds: INT) -> Result<Dynamic, Box<EvalAltResult>> {
add_impl(x, seconds).map(Into::<Dynamic>::into) add_impl(timestamp, seconds).map(Into::<Dynamic>::into)
} }
#[rhai_fn(return_raw, name = "+=")] #[rhai_fn(return_raw, name = "+=")]
pub fn add_assign(x: &mut Instant, seconds: INT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn add_assign(
*x = add_impl(*x, seconds)?; timestamp: &mut Instant,
seconds: INT,
) -> Result<Dynamic, Box<EvalAltResult>> {
*timestamp = add_impl(*timestamp, seconds)?;
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
#[rhai_fn(return_raw, name = "-")] #[rhai_fn(return_raw, name = "-")]
pub fn subtract(x: Instant, seconds: INT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn subtract(timestamp: Instant, seconds: INT) -> Result<Dynamic, Box<EvalAltResult>> {
subtract_impl(x, seconds).map(Into::<Dynamic>::into) subtract_impl(timestamp, seconds).map(Into::<Dynamic>::into)
} }
#[rhai_fn(return_raw, name = "-=")] #[rhai_fn(return_raw, name = "-=")]
pub fn subtract_assign(x: &mut Instant, seconds: INT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn subtract_assign(
*x = subtract_impl(*x, seconds)?; timestamp: &mut Instant,
seconds: INT,
) -> Result<Dynamic, Box<EvalAltResult>> {
*timestamp = subtract_impl(*timestamp, seconds)?;
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
#[rhai_fn(name = "==")] #[rhai_fn(name = "==")]
pub fn eq(x: Instant, y: Instant) -> bool { pub fn eq(timestamp: Instant, timestamp2: Instant) -> bool {
x == y timestamp == timestamp2
} }
#[rhai_fn(name = "!=")] #[rhai_fn(name = "!=")]
pub fn ne(x: Instant, y: Instant) -> bool { pub fn ne(timestamp: Instant, timestamp2: Instant) -> bool {
x != y timestamp != timestamp2
} }
#[rhai_fn(name = "<")] #[rhai_fn(name = "<")]
pub fn lt(x: Instant, y: Instant) -> bool { pub fn lt(timestamp: Instant, timestamp2: Instant) -> bool {
x < y timestamp < timestamp2
} }
#[rhai_fn(name = "<=")] #[rhai_fn(name = "<=")]
pub fn lte(x: Instant, y: Instant) -> bool { pub fn lte(timestamp: Instant, timestamp2: Instant) -> bool {
x <= y timestamp <= timestamp2
} }
#[rhai_fn(name = ">")] #[rhai_fn(name = ">")]
pub fn gt(x: Instant, y: Instant) -> bool { pub fn gt(timestamp: Instant, timestamp2: Instant) -> bool {
x > y timestamp > timestamp2
} }
#[rhai_fn(name = ">=")] #[rhai_fn(name = ">=")]
pub fn gte(x: Instant, y: Instant) -> bool { pub fn gte(timestamp: Instant, timestamp2: Instant) -> bool {
x >= y timestamp >= timestamp2
} }
} }

View File

@ -4,7 +4,7 @@ pub use crate::fn_native::{CallableFunction, FnCallArgs};
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec}; pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec};
pub use crate::{ pub use crate::{
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
NativeCallContext, RegisterFn, RegisterResultFn, NativeCallContext, Position, RegisterFn, RegisterResultFn,
}; };
#[cfg(not(features = "no_module"))] #[cfg(not(features = "no_module"))]

View File

@ -120,7 +120,7 @@ impl EvalAltResult {
Self::ErrorVariableNotFound(_, _) => "Variable not found", Self::ErrorVariableNotFound(_, _) => "Variable not found",
Self::ErrorModuleNotFound(_, _) => "Module not found", Self::ErrorModuleNotFound(_, _) => "Module not found",
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable", Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
Self::ErrorAssignmentToConstant(_, _) => "Cannot assign to a constant", Self::ErrorAssignmentToConstant(_, _) => "Cannot modify a constant",
Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect", Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect",
Self::ErrorInExpr(_) => "Malformed 'in' expression", Self::ErrorInExpr(_) => "Malformed 'in' expression",
Self::ErrorDotExpr(_, _) => "Malformed dot expression", Self::ErrorDotExpr(_, _) => "Malformed dot expression",
@ -196,7 +196,7 @@ impl fmt::Display for EvalAltResult {
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?, Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot assign to constant {}", s)?, Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant {}", s)?,
Self::ErrorMismatchOutputType(s, r, _) => { Self::ErrorMismatchOutputType(s, r, _) => {
write!(f, "Output type is incorrect: {} (expecting {})", r, s)? write!(f, "Output type is incorrect: {} (expecting {})", r, s)?
} }