Enable termination token.

This commit is contained in:
Stephen Chung 2020-11-02 11:04:45 +08:00
parent b07a2aa79c
commit 6f3ce96d9d
9 changed files with 82 additions and 122 deletions

View File

@ -20,6 +20,8 @@ Breaking changes
* Custom syntax can no longer start with a keyword (even a _reserved_ one), even if it has been disabled. That is to avoid breaking scripts later when the keyword is no longer disabled.
* `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed.
* `EvalAltResult::ErrorDataTooLarge` is simplified.
* `Engine::on_progress` closure signature now returns `Option<Dynamic>` with the termination value passed on to `EvalAltResult::ErrorTerminated`.
New features
------------

View File

@ -17,13 +17,14 @@ engine.on_progress(|&count| { // parameter is '&u64' - number of operations al
if count % 1000 == 0 {
println!("{}", count); // print out a progress log every 1,000 operations
}
true // return 'true' to continue running the script
// return 'false' to immediately terminate the script
None // return 'None' to continue running the script
// return 'Some(token)' to immediately terminate the script
});
```
The closure passed to `Engine::on_progress` will be called once for every operation.
Return `false` to terminate the script immediately.
Return `Some(token)` to terminate the script immediately, with the provided value
(any [`Dynamic`] value) passed to `EvalAltResult::ErrorTerminated` as a termination token.
Operations Count vs. Progress Percentage

View File

@ -576,7 +576,7 @@ pub struct Engine {
/// Callback closure for implementing the `debug` command.
pub(crate) debug: Callback<str, ()>,
/// Callback closure for progress reporting.
pub(crate) progress: Option<Callback<u64, bool>>,
pub(crate) progress: Option<Callback<u64, Option<Dynamic>>>,
/// Optimize the AST after compilation.
pub(crate) optimization_level: OptimizationLevel,
@ -2300,8 +2300,6 @@ impl Engine {
if s > self.max_string_size() {
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
self.max_string_size(),
s,
Position::none(),
)
.into();
@ -2309,21 +2307,14 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
if _arr > self.max_array_size() {
return EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(),
self.max_array_size(),
_arr,
Position::none(),
)
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::none())
.into();
}
#[cfg(not(feature = "no_object"))]
if _map > self.max_map_size() {
return EvalAltResult::ErrorDataTooLarge(
"Number of properties in object map".to_string(),
self.max_map_size(),
_map,
"Size of object map".to_string(),
Position::none(),
)
.into();
@ -2345,9 +2336,9 @@ impl Engine {
// Report progress - only in steps
if let Some(progress) = &self.progress {
if !progress(&state.operations) {
// Terminate script if progress returns false
return EvalAltResult::ErrorTerminated(Position::none()).into();
if let Some(token) = progress(&state.operations) {
// Terminate script if progress returns a termination token
return EvalAltResult::ErrorTerminated(token, Position::none()).into();
}
}

View File

@ -1732,12 +1732,12 @@ impl Engine {
///
/// engine.on_progress(move |&ops| {
/// if ops > 10000 {
/// false
/// Some("Over 10,000 operations!".into())
/// } else if ops % 800 == 0 {
/// *logger.write().unwrap() = ops;
/// true
/// None
/// } else {
/// true
/// None
/// }
/// });
///
@ -1752,7 +1752,7 @@ impl Engine {
#[inline(always)]
pub fn on_progress(
&mut self,
callback: impl Fn(&u64) -> bool + SendSync + 'static,
callback: impl Fn(&u64) -> Option<Dynamic> + SendSync + 'static,
) -> &mut Self {
self.progress = Some(Box::new(callback));
self

View File

@ -160,7 +160,7 @@ impl FnPtr {
/// clone them _before_ calling this function.
pub fn call_dynamic(
&self,
context: NativeCallContext,
ctx: NativeCallContext,
this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -182,11 +182,10 @@ impl FnPtr {
args.insert(0, obj);
}
context
.engine()
ctx.engine()
.exec_fn_call(
&mut Default::default(),
context.lib,
ctx.lib,
fn_name,
hash_script,
args.as_mut(),

View File

@ -529,9 +529,8 @@ impl Module {
+ SendSync
+ 'static,
) -> u64 {
let f = move |context: NativeCallContext, args: &mut FnCallArgs| {
func(context, args).map(Dynamic::from)
};
let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
self.set_fn(
name,
FnAccess::Public,

View File

@ -41,12 +41,12 @@ macro_rules! gen_array_functions {
}
#[rhai_fn(return_raw)]
pub fn pad(_context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
pub fn pad(_ctx: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_array_size() > 0 && len > 0 && (len as usize) > _context.engine().max_array_size() {
if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() {
return EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(), _context.engine().max_array_size(), len as usize, Position::none(),
"Size of array".to_string(), Position::none()
).into();
}
@ -197,7 +197,7 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn map(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
mapper: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -206,12 +206,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() {
array.push(
mapper
.call_dynamic(context, None, [item.clone()])
.call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(mapper.fn_name()) =>
{
mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()])
mapper.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -229,7 +229,7 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn filter(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -237,12 +237,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() {
if filter
.call_dynamic(context, None, [item.clone()])
.call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) =>
{
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -264,18 +264,18 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn some(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() {
if filter
.call_dynamic(context, None, [item.clone()])
.call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) =>
{
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -297,18 +297,18 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn all(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() {
if !filter
.call_dynamic(context, None, [item.clone()])
.call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) =>
{
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -330,7 +330,7 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn reduce(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -338,16 +338,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() {
result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()])
.call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) =>
{
reducer.call_dynamic(
context,
None,
[result, item.clone(), (i as INT).into()],
)
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -364,12 +360,12 @@ mod array_functions {
}
#[rhai_fn(name = "reduce", return_raw)]
pub fn reduce_with_initial(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
reducer: FnPtr,
initial: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(),
err,
@ -379,16 +375,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() {
result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()])
.call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) =>
{
reducer.call_dynamic(
context,
None,
[result, item.clone(), (i as INT).into()],
)
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -405,7 +397,7 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn reduce_rev(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -413,16 +405,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate().rev() {
result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()])
.call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) =>
{
reducer.call_dynamic(
context,
None,
[result, item.clone(), (i as INT).into()],
)
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -439,12 +427,12 @@ mod array_functions {
}
#[rhai_fn(name = "reduce_rev", return_raw)]
pub fn reduce_rev_with_initial(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
reducer: FnPtr,
initial: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(),
err,
@ -454,16 +442,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate().rev() {
result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()])
.call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) =>
{
reducer.call_dynamic(
context,
None,
[result, item.clone(), (i as INT).into()],
)
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -480,13 +464,13 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn sort(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
comparer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
list.sort_by(|x, y| {
comparer
.call_dynamic(context, None, [x.clone(), y.clone()])
.call_dynamic(ctx, None, [x.clone(), y.clone()])
.ok()
.and_then(|v| v.as_int().ok())
.map(|v| {
@ -516,7 +500,7 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn drain(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -528,12 +512,12 @@ mod array_functions {
i -= 1;
if filter
.call_dynamic(context, None, [list[i].clone()])
.call_dynamic(ctx, None, [list[i].clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) =>
{
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()])
}
_ => Err(err),
})
@ -575,7 +559,7 @@ mod array_functions {
}
#[rhai_fn(return_raw)]
pub fn retain(
context: NativeCallContext,
ctx: NativeCallContext,
list: &mut Array,
filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -587,12 +571,12 @@ mod array_functions {
i -= 1;
if !filter
.call_dynamic(context, None, [list[i].clone()])
.call_dynamic(ctx, None, [list[i].clone()])
.or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) =>
{
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()])
}
_ => Err(err),
})

View File

@ -251,20 +251,16 @@ mod string_functions {
#[rhai_fn(return_raw)]
pub fn pad(
_context: NativeCallContext,
_ctx: NativeCallContext,
s: &mut ImmutableString,
len: INT,
ch: char,
) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& len as usize > _context.engine().max_string_size()
{
if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
)
.into();
@ -281,13 +277,10 @@ mod string_functions {
}
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& s.len() > _context.engine().max_string_size()
if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
s.len(),
Position::none(),
)
.into();
@ -299,20 +292,16 @@ mod string_functions {
}
#[rhai_fn(name = "pad", return_raw)]
pub fn pad_with_string(
_context: NativeCallContext,
_ctx: NativeCallContext,
s: &mut ImmutableString,
len: INT,
padding: &str,
) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& len as usize > _context.engine().max_string_size()
{
if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
)
.into();
@ -336,13 +325,10 @@ mod string_functions {
}
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& s.len() > _context.engine().max_string_size()
if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
s.len(),
Position::none(),
)
.into();

View File

@ -83,11 +83,11 @@ pub enum EvalAltResult {
ErrorTooManyModules(Position),
/// Call stack over maximum limit.
ErrorStackOverflow(Position),
/// Data value over maximum size limit. Wrapped values are the type name, maximum size and current size.
ErrorDataTooLarge(String, usize, usize, Position),
/// The script is prematurely terminated.
ErrorTerminated(Position),
/// Run-time error encountered. Wrapped value is the error.
/// Data value over maximum size limit. Wrapped value is the type name.
ErrorDataTooLarge(String, Position),
/// The script is prematurely terminated. Wrapped value is the termination token.
ErrorTerminated(Dynamic, Position),
/// Run-time error encountered. Wrapped value is the error token.
ErrorRuntime(Dynamic, Position),
/// Breaking out of loops - not an error if within a loop.
@ -135,8 +135,8 @@ impl EvalAltResult {
Self::ErrorTooManyOperations(_) => "Too many operations",
Self::ErrorTooManyModules(_) => "Too many modules imported",
Self::ErrorStackOverflow(_) => "Stack overflow",
Self::ErrorDataTooLarge(_, _, _, _) => "Data size exceeds maximum limit",
Self::ErrorTerminated(_) => "Script terminated.",
Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit",
Self::ErrorTerminated(_,_) => "Script terminated.",
Self::ErrorRuntime(_, _) => "Runtime error",
Self::LoopBreak(true, _) => "Break statement not inside a loop",
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
@ -185,7 +185,7 @@ impl fmt::Display for EvalAltResult {
| Self::ErrorTooManyOperations(_)
| Self::ErrorTooManyModules(_)
| Self::ErrorStackOverflow(_)
| Self::ErrorTerminated(_) => f.write_str(desc)?,
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
let s = d.as_str().unwrap();
@ -237,9 +237,7 @@ impl fmt::Display for EvalAltResult {
"String index {} is out of bounds: only {} characters in the string",
index, max
)?,
Self::ErrorDataTooLarge(typ, max, size, _) => {
write!(f, "{} ({}) exceeds the maximum limit ({})", typ, size, max)?
}
Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?,
}
// Do not write any position if None
@ -297,8 +295,8 @@ impl EvalAltResult {
Self::ErrorTooManyOperations(_)
| Self::ErrorTooManyModules(_)
| Self::ErrorStackOverflow(_)
| Self::ErrorDataTooLarge(_, _, _, _)
| Self::ErrorTerminated(_) => false,
| Self::ErrorDataTooLarge(_, _)
| Self::ErrorTerminated(_, _) => false,
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
}
@ -313,9 +311,9 @@ impl EvalAltResult {
Self::ErrorTooManyOperations(_)
| Self::ErrorTooManyModules(_)
| Self::ErrorStackOverflow(_)
| Self::ErrorDataTooLarge(_, _, _, _) => true,
| Self::ErrorDataTooLarge(_, _) => true,
Self::ErrorTerminated(_) => true,
Self::ErrorTerminated(_, _) => true,
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
@ -349,8 +347,8 @@ impl EvalAltResult {
| Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(_, _, _, pos)
| Self::ErrorTerminated(pos)
| Self::ErrorDataTooLarge(_, pos)
| Self::ErrorTerminated(_, pos)
| Self::ErrorRuntime(_, pos)
| Self::LoopBreak(_, pos)
| Self::Return(_, pos) => *pos,
@ -383,8 +381,8 @@ impl EvalAltResult {
| Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(_, _, _, pos)
| Self::ErrorTerminated(pos)
| Self::ErrorDataTooLarge(_, pos)
| Self::ErrorTerminated(_, pos)
| Self::ErrorRuntime(_, pos)
| Self::LoopBreak(_, pos)
| Self::Return(_, pos) => *pos = new_position,