commit
56602927d5
19
CHANGELOG.md
19
CHANGELOG.md
@ -21,6 +21,8 @@ Breaking changes
|
|||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
* Custom syntax now works properly inside binary expressions and with method calls.
|
||||||
|
* Hex numbers with the high-bit set now parse correctly into negative integer numbers.
|
||||||
* Constructing a literal array or object map now checks for size limits for each item instead of at the very end when it is already too late.
|
* Constructing a literal array or object map now checks for size limits for each item instead of at the very end when it is already too late.
|
||||||
* Non-`INT` integer types are now treated exactly as custom types under `only_i64` and `only_i32`.
|
* Non-`INT` integer types are now treated exactly as custom types under `only_i64` and `only_i32`.
|
||||||
* Calling `pad` on an array now checks for total size over limit after each item added.
|
* Calling `pad` on an array now checks for total size over limit after each item added.
|
||||||
@ -29,10 +31,12 @@ New features
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Added support for integer _ranges_ via the `..` and `..=` operators.
|
* Added support for integer _ranges_ via the `..` and `..=` operators.
|
||||||
|
* Added `EvalAltResult::ErrorCustomSyntax` to catch errors in custom syntax, which should not happen unless an `AST` is compiled on one `Engine` but evaluated on another unrelated `Engine`.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
* `BLOB`'s are refined to display in a more compact hex format.
|
||||||
* A new syntax is introduced for `def_package!` that will replace the old syntax in future versions.
|
* A new syntax is introduced for `def_package!` that will replace the old syntax in future versions.
|
||||||
* Added `NativeCallContext::call_fn` to easily call a function.
|
* Added `NativeCallContext::call_fn` to easily call a function.
|
||||||
* Doc-comments on plugin module functions are extracted into the functions' metadata.
|
* Doc-comments on plugin module functions are extracted into the functions' metadata.
|
||||||
@ -44,21 +48,6 @@ Deprecated API's
|
|||||||
* The old syntax of `def_package!` is deprecated in favor of the new syntax.
|
* The old syntax of `def_package!` is deprecated in favor of the new syntax.
|
||||||
|
|
||||||
|
|
||||||
Version 1.3.1
|
|
||||||
=============
|
|
||||||
|
|
||||||
Bug fixes
|
|
||||||
---------
|
|
||||||
|
|
||||||
* Custom syntax now works properly inside binary expressions and with method calls.
|
|
||||||
* Hex numbers with the high-bit set now parse correctly into negative integer numbers.
|
|
||||||
|
|
||||||
Enhancements
|
|
||||||
------------
|
|
||||||
|
|
||||||
* `BLOB`'s are refined to display in a more compact hex format.
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.3.0
|
Version 1.3.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
@ -106,6 +106,21 @@ fn bench_eval_call(bench: &mut Bencher) {
|
|||||||
bench.iter(|| engine.eval::<bool>(script).unwrap());
|
bench.iter(|| engine.eval::<bool>(script).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_eval_deeply_nested(bench: &mut Bencher) {
|
||||||
|
let script = r#"
|
||||||
|
(1 + 2 * 3 - 9) * 4 < 5 * 6 - 70 / 8 &&
|
||||||
|
(42 + 99 > 1 + 2 - 3 + 4 * 5 || 123 - 88 < 123 + 88 - 99 + 100)
|
||||||
|
&& true
|
||||||
|
&& !!!!!!!!false
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
engine.set_optimization_level(OptimizationLevel::None);
|
||||||
|
|
||||||
|
bench.iter(|| engine.eval::<bool>(script).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_eval_loop_number(bench: &mut Bencher) {
|
fn bench_eval_loop_number(bench: &mut Bencher) {
|
||||||
let script = "
|
let script = "
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
cargo-features = ["named-profiles"]
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
|
@ -18,7 +18,7 @@ cargo +nightly build --release
|
|||||||
A specific profile can also be used:
|
A specific profile can also be used:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo +nightly build --profile unix -Z unstable-options
|
cargo +nightly build --profile unix
|
||||||
```
|
```
|
||||||
|
|
||||||
Three profiles are defined: `unix`, `windows` and `macos`.
|
Three profiles are defined: `unix`, `windows` and `macos`.
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
//! Module defining script expressions.
|
//! Module defining script expressions.
|
||||||
|
|
||||||
use super::{ASTNode, Ident, Stmt, StmtBlock};
|
use super::{ASTNode, Ident, Stmt, StmtBlock};
|
||||||
use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||||
use crate::func::hashing::ALT_ZERO_HASH;
|
use crate::func::hashing::ALT_ZERO_HASH;
|
||||||
use crate::module::Namespace;
|
use crate::module::Namespace;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{Dynamic, Identifier, ImmutableString, Position, StaticVec, INT};
|
use crate::{calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, StaticVec, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
fmt,
|
fmt,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
iter::once,
|
||||||
num::{NonZeroU8, NonZeroUsize},
|
num::{NonZeroU8, NonZeroUsize},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -522,8 +523,23 @@ impl Expr {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fn
|
||||||
|
Self::FnCall(ref x, _)
|
||||||
|
if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR =>
|
||||||
|
{
|
||||||
|
if let Expr::StringConstant(ref s, _) = x.args[0] {
|
||||||
|
if let Ok(fn_ptr) = FnPtr::new(s) {
|
||||||
|
fn_ptr.into()
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Binary operators
|
// Binary operators
|
||||||
Self::FnCall(x, _) if x.args.len() == 2 => match x.name.as_str() {
|
Self::FnCall(x, _) if !x.is_qualified() && x.args.len() == 2 => match x.name.as_str() {
|
||||||
// x..y
|
// x..y
|
||||||
OP_EXCLUSIVE_RANGE => {
|
OP_EXCLUSIVE_RANGE => {
|
||||||
if let Expr::IntegerConstant(ref start, _) = x.args[0] {
|
if let Expr::IntegerConstant(ref start, _) = x.args[0] {
|
||||||
@ -577,6 +593,19 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos),
|
Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos),
|
||||||
|
|
||||||
|
Union::FnPtr(f, _, _) if !f.is_curried() => Self::FnCall(
|
||||||
|
FnCallExpr {
|
||||||
|
namespace: None,
|
||||||
|
name: KEYWORD_FN_PTR.into(),
|
||||||
|
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
||||||
|
args: once(Self::Stack(0, pos)).collect(),
|
||||||
|
constants: once(f.fn_name().into()).collect(),
|
||||||
|
capture_parent_scope: false,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
pos,
|
||||||
|
),
|
||||||
|
|
||||||
_ => Self::DynamicConstant(value.into(), pos),
|
_ => Self::DynamicConstant(value.into(), pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,7 +483,7 @@ impl Stmt {
|
|||||||
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent),
|
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent),
|
||||||
|
|
||||||
Self::FnCall(x, _) | Self::Expr(Expr::FnCall(x, _)) => {
|
Self::FnCall(x, _) | Self::Expr(Expr::FnCall(x, _)) => {
|
||||||
x.namespace.is_none() && x.name == KEYWORD_EVAL
|
!x.is_qualified() && x.name == KEYWORD_EVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -140,9 +140,37 @@ pub struct Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Engine {
|
impl fmt::Debug for Engine {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("Engine")
|
let mut f = f.debug_struct("Engine");
|
||||||
|
|
||||||
|
f.field("global_modules", &self.global_modules)
|
||||||
|
.field("global_sub_modules", &self.global_sub_modules);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
f.field("module_resolver", &self.module_resolver.is_some());
|
||||||
|
|
||||||
|
f.field("type_names", &self.type_names)
|
||||||
|
.field("disabled_symbols", &self.disabled_symbols)
|
||||||
|
.field("custom_keywords", &self.custom_keywords)
|
||||||
|
.field("custom_syntax", &(!self.custom_syntax.is_empty()))
|
||||||
|
.field("resolve_var", &self.resolve_var.is_some())
|
||||||
|
.field("token_mapper", &self.token_mapper.is_some())
|
||||||
|
.field("print", &self.print.is_some())
|
||||||
|
.field("debug", &self.debug.is_some());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
f.field("progress", &self.progress.is_some());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
f.field("optimization_level", &self.optimization_level);
|
||||||
|
|
||||||
|
f.field("options", &self.options);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
f.field("limits", &self.limits);
|
||||||
|
|
||||||
|
f.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,9 +229,14 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Normal function call
|
// Normal function call
|
||||||
|
let (first_arg, args) = args.split_first().map_or_else(
|
||||||
|
|| (None, args.as_ref()),
|
||||||
|
|(first, rest)| (Some(first), rest),
|
||||||
|
);
|
||||||
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture,
|
scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes,
|
||||||
level,
|
pos, *capture, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,10 +416,18 @@ impl Engine {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Custom(custom, _) => {
|
Expr::Custom(custom, pos) => {
|
||||||
let expressions: StaticVec<_> = custom.inputs.iter().map(Into::into).collect();
|
let expressions: StaticVec<_> = custom.inputs.iter().map(Into::into).collect();
|
||||||
|
// The first token acts as the custom syntax's key
|
||||||
let key_token = custom.tokens.first().unwrap();
|
let key_token = custom.tokens.first().unwrap();
|
||||||
let custom_def = self.custom_syntax.get(key_token).unwrap();
|
// The key should exist, unless the AST is compiled in a different Engine
|
||||||
|
let custom_def = self.custom_syntax.get(key_token).ok_or_else(|| {
|
||||||
|
Box::new(ERR::ErrorCustomSyntax(
|
||||||
|
format!("Invalid custom syntax prefix: {}", key_token),
|
||||||
|
custom.tokens.iter().map(|s| s.to_string()).collect(),
|
||||||
|
*pos,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
let mut context = EvalContext {
|
let mut context = EvalContext {
|
||||||
engine: self,
|
engine: self,
|
||||||
scope,
|
scope,
|
||||||
|
@ -245,6 +245,7 @@ impl IntoIterator for GlobalRuntimeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Into<Identifier>, M: Into<Shared<Module>>> FromIterator<(K, M)> for GlobalRuntimeState {
|
impl<K: Into<Identifier>, M: Into<Shared<Module>>> FromIterator<(K, M)> for GlobalRuntimeState {
|
||||||
|
#[inline]
|
||||||
fn from_iter<T: IntoIterator<Item = (K, M)>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = (K, M)>>(iter: T) -> Self {
|
||||||
let mut lib = Self::new();
|
let mut lib = Self::new();
|
||||||
lib.extend(iter);
|
lib.extend(iter);
|
||||||
@ -253,6 +254,7 @@ impl<K: Into<Identifier>, M: Into<Shared<Module>>> FromIterator<(K, M)> for Glob
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Into<Identifier>, M: Into<Shared<Module>>> Extend<(K, M)> for GlobalRuntimeState {
|
impl<K: Into<Identifier>, M: Into<Shared<Module>>> Extend<(K, M)> for GlobalRuntimeState {
|
||||||
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
||||||
iter.into_iter().for_each(|(k, m)| {
|
iter.into_iter().for_each(|(k, m)| {
|
||||||
self.keys.push(k.into());
|
self.keys.push(k.into());
|
||||||
@ -262,10 +264,25 @@ impl<K: Into<Identifier>, M: Into<Shared<Module>>> Extend<(K, M)> for GlobalRunt
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for GlobalRuntimeState {
|
impl fmt::Debug for GlobalRuntimeState {
|
||||||
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("Imports")?;
|
let mut f = f.debug_struct("GlobalRuntimeState");
|
||||||
f.debug_map()
|
|
||||||
.entries(self.keys.iter().zip(self.modules.iter()))
|
f.field("imports", &self.keys.iter().zip(self.modules.iter()))
|
||||||
.finish()
|
.field("source", &self.source)
|
||||||
|
.field("num_operations", &self.num_operations)
|
||||||
|
.field("num_modules_loaded", &self.num_modules_loaded);
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
f.field("fn_hash_indexing", &self.fn_hash_indexing);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
f.field("embedded_module_resolver", &self.embedded_module_resolver);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
f.field("constants", &self.constants);
|
||||||
|
|
||||||
|
f.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,10 @@ impl Engine {
|
|||||||
let args = &mut [lhs_ptr_inner, &mut new_val];
|
let args = &mut [lhs_ptr_inner, &mut new_val];
|
||||||
|
|
||||||
match self.call_native_fn(global, state, lib, op, hash, args, true, true, op_pos) {
|
match self.call_native_fn(global, state, lib, op, hash, args, true, true, op_pos) {
|
||||||
|
Ok(_) => {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.check_data_size(&mut args[0], root.1)?;
|
||||||
|
}
|
||||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) =>
|
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
@ -153,7 +157,7 @@ impl Engine {
|
|||||||
|
|
||||||
*args[0] = value.flatten();
|
*args[0] = value.flatten();
|
||||||
}
|
}
|
||||||
err => return err.map(|_| ()),
|
Err(err) => return Err(err),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
|
152
src/func/call.rs
152
src/func/call.rs
@ -887,12 +887,16 @@ impl Engine {
|
|||||||
arg_expr: &Expr,
|
arg_expr: &Expr,
|
||||||
constants: &[Dynamic],
|
constants: &[Dynamic],
|
||||||
) -> RhaiResultOf<(Dynamic, Position)> {
|
) -> RhaiResultOf<(Dynamic, Position)> {
|
||||||
match arg_expr {
|
Ok((
|
||||||
Expr::Stack(slot, pos) => Ok((constants[*slot].clone(), *pos)),
|
if let Expr::Stack(slot, _) = arg_expr {
|
||||||
ref arg => self
|
constants[*slot].clone()
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, arg, level)
|
} else if let Some(value) = arg_expr.get_literal_value() {
|
||||||
.map(|v| (v, arg.position())),
|
value
|
||||||
}
|
} else {
|
||||||
|
self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level)?
|
||||||
|
},
|
||||||
|
arg_expr.position(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
@ -904,6 +908,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
constants: &[Dynamic],
|
constants: &[Dynamic],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -911,8 +916,9 @@ impl Engine {
|
|||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let mut first_arg = first_arg;
|
||||||
let mut a_expr = args_expr;
|
let mut a_expr = args_expr;
|
||||||
let mut total_args = a_expr.len();
|
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
|
||||||
let mut curry = FnArgsVec::new_const();
|
let mut curry = FnArgsVec::new_const();
|
||||||
let mut name = fn_name;
|
let mut name = fn_name;
|
||||||
let mut hashes = hashes;
|
let mut hashes = hashes;
|
||||||
@ -921,26 +927,29 @@ impl Engine {
|
|||||||
match name {
|
match name {
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
|
|
||||||
if !arg.is::<FnPtr>() {
|
if !arg_value.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(arg.type_name()),
|
self.map_type_name(arg_value.type_name()),
|
||||||
arg_pos,
|
arg_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = arg.cast::<FnPtr>();
|
let fn_ptr = arg_value.cast::<FnPtr>();
|
||||||
curry.extend(fn_ptr.curry().iter().cloned());
|
curry.extend(fn_ptr.curry().iter().cloned());
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
redirected = fn_ptr.take_data().0;
|
redirected = fn_ptr.take_data().0;
|
||||||
name = &redirected;
|
name = &redirected;
|
||||||
|
|
||||||
// Skip the first argument
|
// Shift the arguments
|
||||||
a_expr = &a_expr[1..];
|
first_arg = a_expr.get(0);
|
||||||
|
if !a_expr.is_empty() {
|
||||||
|
a_expr = &a_expr[1..];
|
||||||
|
}
|
||||||
total_args -= 1;
|
total_args -= 1;
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
@ -953,12 +962,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
|
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
return arg
|
return arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
|
||||||
.and_then(FnPtr::try_from)
|
.and_then(FnPtr::try_from)
|
||||||
@ -968,30 +977,30 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle curry()
|
// Handle curry()
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let first = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) = self
|
||||||
)?;
|
.get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?;
|
||||||
|
|
||||||
if !arg.is::<FnPtr>() {
|
if !arg_value.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(arg.type_name()),
|
self.map_type_name(arg_value.type_name()),
|
||||||
arg_pos,
|
arg_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, fn_curry) = arg.cast::<FnPtr>().take_data();
|
let (name, fn_curry) = arg_value.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
let fn_curry = a_expr.iter().skip(1).try_fold(
|
let fn_curry =
|
||||||
fn_curry,
|
a_expr
|
||||||
|mut curried, expr| -> RhaiResultOf<_> {
|
.iter()
|
||||||
let (value, _) = self.get_arg_value(
|
.try_fold(fn_curry, |mut curried, expr| -> RhaiResultOf<_> {
|
||||||
scope, global, state, lib, this_ptr, level, expr, constants,
|
let (value, _) = self.get_arg_value(
|
||||||
)?;
|
scope, global, state, lib, this_ptr, level, expr, constants,
|
||||||
curried.push(value);
|
)?;
|
||||||
Ok(curried)
|
curried.push(value);
|
||||||
},
|
Ok(curried)
|
||||||
)?;
|
})?;
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
||||||
}
|
}
|
||||||
@ -999,28 +1008,28 @@ impl Engine {
|
|||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let (arg, _) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, _) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
return Ok(arg.is_shared().into());
|
return Ok(arg_value.is_shared().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_fn()
|
// Handle is_def_fn()
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let first = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) = self
|
||||||
)?;
|
.get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?;
|
||||||
|
|
||||||
let fn_name = arg
|
let fn_name = arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let (arg_value, arg_pos) = self.get_arg_value(
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[1], constants,
|
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let num_params = arg
|
let num_params = arg_value
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
||||||
|
|
||||||
@ -1035,10 +1044,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, arg_pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
let var_name = arg
|
let var_name = arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
return Ok(scope.contains(&var_name).into());
|
return Ok(scope.contains(&var_name).into());
|
||||||
@ -1048,10 +1057,10 @@ impl Engine {
|
|||||||
KEYWORD_EVAL if total_args == 1 => {
|
KEYWORD_EVAL if total_args == 1 => {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
let (value, pos) = self.get_arg_value(
|
let arg = first_arg.unwrap();
|
||||||
scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
|
let (arg_value, pos) =
|
||||||
)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
|
||||||
let script = &value
|
let script = &arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
||||||
let result = self.eval_script_expr_in_place(
|
let result = self.eval_script_expr_in_place(
|
||||||
@ -1085,8 +1094,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call - except for Fn, curry, call and eval (handled above)
|
// Normal function call - except for Fn, curry, call and eval (handled above)
|
||||||
let mut arg_values = FnArgsVec::with_capacity(a_expr.len());
|
let mut arg_values = FnArgsVec::with_capacity(total_args);
|
||||||
let mut args = FnArgsVec::with_capacity(a_expr.len() + curry.len());
|
let mut args = FnArgsVec::with_capacity(total_args + curry.len());
|
||||||
let mut is_ref_mut = false;
|
let mut is_ref_mut = false;
|
||||||
|
|
||||||
// Capture parent scope?
|
// Capture parent scope?
|
||||||
@ -1094,10 +1103,14 @@ impl Engine {
|
|||||||
// If so, do it separately because we cannot convert the first argument (if it is a simple
|
// If so, do it separately because we cannot convert the first argument (if it is a simple
|
||||||
// variable access) to &mut because `scope` is needed.
|
// variable access) to &mut because `scope` is needed.
|
||||||
if capture_scope && !scope.is_empty() {
|
if capture_scope && !scope.is_empty() {
|
||||||
a_expr.iter().try_for_each(|expr| {
|
first_arg
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
.iter()
|
||||||
.map(|(value, _)| arg_values.push(value.flatten()))
|
.map(|&v| v)
|
||||||
})?;
|
.chain(a_expr.iter())
|
||||||
|
.try_for_each(|expr| {
|
||||||
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
||||||
|
.map(|(value, _)| arg_values.push(value.flatten()))
|
||||||
|
})?;
|
||||||
args.extend(curry.iter_mut());
|
args.extend(curry.iter_mut());
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
|
|
||||||
@ -1113,17 +1126,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call with blank scope
|
// Call with blank scope
|
||||||
if a_expr.is_empty() && curry.is_empty() {
|
if total_args == 0 && curry.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
} else {
|
} else {
|
||||||
// If the first argument is a variable, and there is no curried arguments,
|
// If the first argument is a variable, and there is no curried arguments,
|
||||||
// convert to method-call style in order to leverage potential &mut first argument and
|
// convert to method-call style in order to leverage potential &mut first argument and
|
||||||
// avoid cloning the value
|
// avoid cloning the value
|
||||||
if curry.is_empty() && !a_expr.is_empty() && a_expr[0].is_variable_access(false) {
|
if curry.is_empty() && first_arg.map_or(false, |expr| expr.is_variable_access(false)) {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
let (first_expr, rest_expr) = a_expr.split_first().unwrap();
|
let first_expr = first_arg.unwrap();
|
||||||
|
|
||||||
rest_expr.iter().try_for_each(|expr| {
|
a_expr.iter().try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
||||||
.map(|(value, _)| arg_values.push(value.flatten()))
|
.map(|(value, _)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
@ -1155,10 +1168,15 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
a_expr.iter().try_for_each(|expr| {
|
first_arg
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
|
.into_iter()
|
||||||
|
.chain(a_expr.iter())
|
||||||
|
.try_for_each(|expr| {
|
||||||
|
self.get_arg_value(
|
||||||
|
scope, global, state, lib, this_ptr, level, expr, constants,
|
||||||
|
)
|
||||||
.map(|(value, _)| arg_values.push(value.flatten()))
|
.map(|(value, _)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
args.extend(curry.iter_mut());
|
args.extend(curry.iter_mut());
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ pub enum EvalAltResult {
|
|||||||
ErrorDotExpr(String, Position),
|
ErrorDotExpr(String, Position),
|
||||||
/// Arithmetic error encountered. Wrapped value is the error message.
|
/// Arithmetic error encountered. Wrapped value is the error message.
|
||||||
ErrorArithmetic(String, Position),
|
ErrorArithmetic(String, Position),
|
||||||
|
|
||||||
/// Number of operations over maximum limit.
|
/// Number of operations over maximum limit.
|
||||||
ErrorTooManyOperations(Position),
|
ErrorTooManyOperations(Position),
|
||||||
/// [Modules][crate::Module] over maximum limit.
|
/// [Modules][crate::Module] over maximum limit.
|
||||||
@ -81,6 +82,14 @@ pub enum EvalAltResult {
|
|||||||
ErrorDataTooLarge(String, Position),
|
ErrorDataTooLarge(String, Position),
|
||||||
/// The script is prematurely terminated. Wrapped value is the termination token.
|
/// The script is prematurely terminated. Wrapped value is the termination token.
|
||||||
ErrorTerminated(Dynamic, Position),
|
ErrorTerminated(Dynamic, Position),
|
||||||
|
|
||||||
|
/// Error encountered for a custom syntax. Wrapped values are the error message and
|
||||||
|
/// custom syntax symbols stream.
|
||||||
|
///
|
||||||
|
/// Normally this should never happen, unless an [`AST`][crate::AST] is compiled on one
|
||||||
|
/// [`Engine`][crate::Engine] but evaluated on another unrelated [`Engine`][crate::Engine].
|
||||||
|
ErrorCustomSyntax(String, Vec<String>, Position),
|
||||||
|
|
||||||
/// Run-time error encountered. Wrapped value is the error token.
|
/// Run-time error encountered. Wrapped value is the error token.
|
||||||
ErrorRuntime(Dynamic, Position),
|
ErrorRuntime(Dynamic, Position),
|
||||||
|
|
||||||
@ -204,6 +213,8 @@ impl fmt::Display for EvalAltResult {
|
|||||||
index, max
|
index, max
|
||||||
)?,
|
)?,
|
||||||
Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?,
|
Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?,
|
||||||
|
|
||||||
|
Self::ErrorCustomSyntax(s, tokens, _) => write!(f, "{}: {}", s, tokens.join(" "))?,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not write any position if None
|
// Do not write any position if None
|
||||||
@ -266,7 +277,8 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorArithmetic(_, _)
|
| Self::ErrorArithmetic(_, _)
|
||||||
| Self::ErrorRuntime(_, _) => true,
|
| Self::ErrorRuntime(_, _) => true,
|
||||||
|
|
||||||
Self::ErrorTooManyOperations(_)
|
Self::ErrorCustomSyntax(_, _, _)
|
||||||
|
| Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(_)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(_)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorDataTooLarge(_, _)
|
| Self::ErrorDataTooLarge(_, _)
|
||||||
@ -282,7 +294,8 @@ impl EvalAltResult {
|
|||||||
Self::ErrorSystem(_, _) => true,
|
Self::ErrorSystem(_, _) => true,
|
||||||
Self::ErrorParsing(_, _) => true,
|
Self::ErrorParsing(_, _) => true,
|
||||||
|
|
||||||
Self::ErrorTooManyOperations(_)
|
Self::ErrorCustomSyntax(_, _, _)
|
||||||
|
| Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(_)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(_)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorDataTooLarge(_, _) => true,
|
| Self::ErrorDataTooLarge(_, _) => true,
|
||||||
@ -358,6 +371,20 @@ impl EvalAltResult {
|
|||||||
Self::ErrorTerminated(t, _) => {
|
Self::ErrorTerminated(t, _) => {
|
||||||
map.insert("token".into(), t.clone());
|
map.insert("token".into(), t.clone());
|
||||||
}
|
}
|
||||||
|
Self::ErrorCustomSyntax(_, tokens, _) => {
|
||||||
|
map.insert(
|
||||||
|
"tokens".into(),
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Dynamic::from_array(tokens.iter().map(Into::into).collect()),
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
tokens
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/// Get the [position][Position] of this error.
|
/// Get the [position][Position] of this error.
|
||||||
@ -389,6 +416,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorDataTooLarge(_, pos)
|
| Self::ErrorDataTooLarge(_, pos)
|
||||||
| Self::ErrorTerminated(_, pos)
|
| Self::ErrorTerminated(_, pos)
|
||||||
|
| Self::ErrorCustomSyntax(_, _, pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::LoopBreak(_, pos)
|
| Self::LoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
@ -436,6 +464,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorDataTooLarge(_, pos)
|
| Self::ErrorDataTooLarge(_, pos)
|
||||||
| Self::ErrorTerminated(_, pos)
|
| Self::ErrorTerminated(_, pos)
|
||||||
|
| Self::ErrorCustomSyntax(_, _, pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::LoopBreak(_, pos)
|
| Self::LoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos = new_position,
|
| Self::Return(_, pos) => *pos = new_position,
|
||||||
|
@ -120,6 +120,18 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.into_typed_array::<INT>()?,
|
.into_typed_array::<INT>()?,
|
||||||
[1, 2, 3, 4, 5]
|
[1, 2, 3, 4, 5]
|
||||||
);
|
);
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
assert!(!engine.eval::<bool>(
|
||||||
|
"
|
||||||
|
let x = 42;
|
||||||
|
let y = [];
|
||||||
|
let f = || x;
|
||||||
|
for n in 0..10 {
|
||||||
|
y += x;
|
||||||
|
}
|
||||||
|
some(y, |x| is_shared(x))
|
||||||
|
"
|
||||||
|
)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ fn test_comments() -> Result<(), Box<EvalAltResult>> {
|
|||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<()>("/* Hello world */")?, ());
|
assert_eq!(engine.run("/* Hello world */")?, ());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg(not(feature = "unchecked"))]
|
#![cfg(not(feature = "unchecked"))]
|
||||||
use rhai::{Engine, EvalAltResult, ParseErrorType};
|
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use rhai::Array;
|
use rhai::Array;
|
||||||
@ -101,6 +101,39 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
EvalAltResult::ErrorDataTooLarge(_, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = 42;
|
||||||
|
let y = [];
|
||||||
|
let f = || x;
|
||||||
|
for n in 0..10 {
|
||||||
|
y += x;
|
||||||
|
}
|
||||||
|
len(y)
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.run(
|
||||||
|
"
|
||||||
|
let x = 42;
|
||||||
|
let y = [];
|
||||||
|
let f = || x;
|
||||||
|
for n in 0..11 {
|
||||||
|
y += x;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
)
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run(
|
.run(
|
||||||
@ -113,13 +146,25 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
EvalAltResult::ErrorDataTooLarge(_, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = [1,2,3,4,5,6];
|
||||||
|
x.pad(10, 42);
|
||||||
|
len(x)
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run(
|
.run(
|
||||||
"
|
"
|
||||||
let x = [1,2,3,4,5,6];
|
let x = [1,2,3,4,5,6];
|
||||||
x.pad(100, 42);
|
x.pad(11, 42);
|
||||||
x
|
x
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
@ -127,6 +172,16 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
EvalAltResult::ErrorDataTooLarge(_, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = [1,2,3];
|
||||||
|
len([x, x, x])
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run(
|
.run(
|
||||||
|
@ -36,7 +36,7 @@ fn test_map_indexing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
5
|
5
|
||||||
);
|
);
|
||||||
|
|
||||||
engine.eval::<()>("let y = #{a: 1, b: 2, c: 3}; y.z")?;
|
engine.run("let y = #{a: 1, b: 2, c: 3}; y.z")?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -243,7 +243,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.set_max_modules(1000);
|
engine.set_max_modules(1000);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
engine.eval::<()>(
|
engine.run(
|
||||||
r#"
|
r#"
|
||||||
fn foo() {
|
fn foo() {
|
||||||
import "hello" as h;
|
import "hello" as h;
|
||||||
|
@ -15,18 +15,48 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
|||||||
None
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.eval::<()>("let x = 0; while x < 20 { x += 1; }")?;
|
engine.run("let x = 0; while x < 20 { x += 1; }")?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine.run("for x in 0..500 {}").expect_err("should error"),
|
||||||
.eval::<()>("for x in 0..500 {}")
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorTooManyOperations(_)
|
EvalAltResult::ErrorTooManyOperations(_)
|
||||||
));
|
));
|
||||||
|
|
||||||
engine.set_max_operations(0);
|
engine.set_max_operations(0);
|
||||||
|
|
||||||
engine.eval::<()>("for x in 0..10000 {}")?;
|
engine.run("for x in 0..10000 {}")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max_operations_literal() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
||||||
|
engine.set_max_operations(10);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
engine.run("[1, 2, 3, 4, 5, 6, 7]")?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.run("[1, 2, 3, 4, 5, 6, 7, 8, 9]")
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorTooManyOperations(_)
|
||||||
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
engine.run("#{a:1, b:2, c:3, d:4, e:5, f:6, g:7}")?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.run("#{a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9}")
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorTooManyOperations(_)
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -43,7 +73,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
None
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.eval::<()>(
|
engine.run(
|
||||||
r#"
|
r#"
|
||||||
print("Test1");
|
print("Test1");
|
||||||
let x = 0;
|
let x = 0;
|
||||||
@ -56,7 +86,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
engine.eval::<()>(
|
engine.run(
|
||||||
r#"
|
r#"
|
||||||
print("Test2");
|
print("Test2");
|
||||||
fn inc(x) { x + 1 }
|
fn inc(x) { x + 1 }
|
||||||
@ -68,7 +98,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>(
|
.run(
|
||||||
r#"
|
r#"
|
||||||
print("Test3");
|
print("Test3");
|
||||||
fn inc(x) { x + 1 }
|
fn inc(x) { x + 1 }
|
||||||
@ -101,7 +131,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>(
|
.run(
|
||||||
r#"
|
r#"
|
||||||
let script = "for x in 0..500 {}";
|
let script = "for x in 0..500 {}";
|
||||||
eval(script);
|
eval(script);
|
||||||
@ -131,7 +161,7 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>("for x in 0..500 {}")
|
.run("for x in 0..500 {}")
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
|
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
|
||||||
));
|
));
|
||||||
|
@ -21,7 +21,7 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>(&format!(
|
.run(&format!(
|
||||||
"
|
"
|
||||||
fn foo(n) {{ if n == 0 {{ 0 }} else {{ n + foo(n-1) }} }}
|
fn foo(n) {{ if n == 0 {{ 0 }} else {{ n + foo(n-1) }} }}
|
||||||
foo({})
|
foo({})
|
||||||
|
@ -11,7 +11,7 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
|||||||
'a'
|
'a'
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<()>("switch 3 { 1 => (), 2 => 'a', 42 => true }")?,
|
engine.run("switch 3 { 1 => (), 2 => 'a', 42 => true }")?,
|
||||||
()
|
()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -5,12 +5,12 @@ fn test_throw() {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<()>("if true { throw 42 }").expect_err("expects error"),
|
*engine.run("if true { throw 42 }").expect_err("expects error"),
|
||||||
EvalAltResult::ErrorRuntime(s, _) if s.as_int().unwrap() == 42
|
EvalAltResult::ErrorRuntime(s, _) if s.as_int().unwrap() == 42
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<()>(r#"throw"#).expect_err("expects error"),
|
*engine.run(r#"throw"#).expect_err("expects error"),
|
||||||
EvalAltResult::ErrorRuntime(s, _) if s.is::<()>()
|
EvalAltResult::ErrorRuntime(s, _) if s.is::<()>()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ fn test_try_catch() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>("try { 42/0; } catch { throw; }")
|
.run("try { 42/0; } catch { throw; }")
|
||||||
.expect_err("expects error"),
|
.expect_err("expects error"),
|
||||||
EvalAltResult::ErrorArithmetic(_, _)
|
EvalAltResult::ErrorArithmetic(_, _)
|
||||||
));
|
));
|
||||||
|
@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult};
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unit() -> Result<(), Box<EvalAltResult>> {
|
fn test_unit() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
engine.eval::<()>("let x = (); x")?;
|
engine.run("let x = (); x")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,6 +17,6 @@ fn test_unit_eq() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> {
|
fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
engine.eval::<()>("let x = ( ); x")?;
|
engine.run("let x = ( ); x")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user