Use eval_statements_block when at global.

This commit is contained in:
Stephen Chung 2021-02-07 17:06:33 +08:00
parent f388d22c0f
commit a76bed2f46
4 changed files with 50 additions and 53 deletions

View File

@ -1712,7 +1712,7 @@ impl Engine {
// Statement block // Statement block
Expr::Stmt(x, _) => { Expr::Stmt(x, _) => {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), level) self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), true, level)
} }
// lhs[idx_expr] // lhs[idx_expr]
@ -1856,45 +1856,49 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
statements: impl IntoIterator<Item = &'a Stmt>, statements: impl IntoIterator<Item = &'a Stmt>,
restore: bool,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut has_imports = false; let mut _has_imports = false;
let prev_always_search = state.always_search; let prev_always_search = state.always_search;
let prev_scope_len = scope.len(); let prev_scope_len = scope.len();
let prev_mods_len = mods.len(); let prev_mods_len = mods.len();
state.scope_level += 1;
let result = statements if restore {
.into_iter() state.scope_level += 1;
.try_fold(Default::default(), |_, stmt| {
match stmt {
#[cfg(not(feature = "no_module"))]
Stmt::Import(_, _, _) => {
// When imports list is modified, clear the functions lookup cache
if has_imports {
state.functions_caches.last_mut().map(|c| c.clear());
} else {
state.functions_caches.push(Default::default());
}
has_imports = true;
}
_ => (),
}
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
});
scope.rewind(prev_scope_len);
if has_imports {
// If imports list is modified, pop the functions lookup cache
state.functions_caches.pop();
} }
mods.truncate(prev_mods_len);
state.scope_level -= 1;
// The impact of new local variables goes away at the end of a block let result = statements.into_iter().try_fold(Dynamic::UNIT, |_, stmt| {
// because any new variables introduced will go out of scope #[cfg(not(feature = "no_module"))]
state.always_search = prev_always_search; match stmt {
Stmt::Import(_, _, _) => {
// When imports list is modified, clear the functions lookup cache
if _has_imports {
state.functions_caches.last_mut().map(|c| c.clear());
} else if restore {
state.functions_caches.push(Default::default());
_has_imports = true;
}
}
_ => (),
}
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
});
if restore {
scope.rewind(prev_scope_len);
if _has_imports {
// If imports list is modified, pop the functions lookup cache
state.functions_caches.pop();
}
mods.truncate(prev_mods_len);
state.scope_level -= 1;
// The impact of new local variables goes away at the end of a block
// because any new variables introduced will go out of scope
state.always_search = prev_always_search;
}
result result
} }
@ -2088,7 +2092,7 @@ impl Engine {
// Block scope // Block scope
Stmt::Block(statements, _) => { Stmt::Block(statements, _) => {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, level) self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
} }
// If statement // If statement

View File

@ -1533,7 +1533,9 @@ impl Engine {
resolver: ast.resolver(), resolver: ast.resolver(),
..Default::default() ..Default::default()
}; };
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()], level) let statements = ast.statements();
let lib = &[ast.lib()];
self.eval_global_statements(scope, mods, state, statements, lib, level)
} }
/// Evaluate a file, but throw away the result and only return error (if any). /// Evaluate a file, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
@ -1602,7 +1604,9 @@ impl Engine {
resolver: ast.resolver(), resolver: ast.resolver(),
..Default::default() ..Default::default()
}; };
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()], 0)?; let statements = ast.statements();
let lib = &[ast.lib()];
self.eval_global_statements(scope, mods, state, statements, lib, 0)?;
Ok(()) Ok(())
} }
/// Call a script function defined in an [`AST`] with multiple arguments. /// Call a script function defined in an [`AST`] with multiple arguments.

View File

@ -768,10 +768,10 @@ impl Engine {
} }
} }
/// Evaluate a list of statements with an empty state and no `this` pointer. /// Evaluate a list of statements with no `this` pointer.
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body. /// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
#[inline] #[inline]
pub(crate) fn eval_statements_raw<'a>( pub(crate) fn eval_global_statements<'a>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, mods: &mut Imports,
@ -780,11 +780,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
statements self.eval_stmt_block(scope, mods, state, lib, &mut None, statements, false, level)
.into_iter()
.try_fold(Dynamic::UNIT, |_, stmt| {
self.eval_stmt(scope, mods, state, lib, &mut None, stmt, level)
})
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out), EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::LoopBreak(_, _) => { EvalAltResult::LoopBreak(_, _) => {
@ -826,14 +822,14 @@ impl Engine {
} }
// Evaluate the AST // Evaluate the AST
let mut new_state = State { let new_state = &mut State {
source: state.source.clone(), source: state.source.clone(),
operations: state.operations, operations: state.operations,
..Default::default() ..Default::default()
}; };
let result = let result =
self.eval_statements_raw(scope, mods, &mut new_state, ast.statements(), lib, level); self.eval_global_statements(scope, mods, new_state, ast.statements(), lib, level);
state.operations = new_state.operations; state.operations = new_state.operations;
result result

View File

@ -4,14 +4,7 @@ use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, Scope, I
fn test_eval() -> Result<(), Box<EvalAltResult>> { fn test_eval() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert_eq!( assert_eq!(engine.eval::<INT>(r#"eval("40 + 2")"#)?, 42);
engine.eval::<INT>(
r#"
eval("40 + 2")
"#
)?,
42
);
Ok(()) Ok(())
} }
@ -62,7 +55,7 @@ fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
script += "x + y"; script += "x + y";
eval(script) + x + y eval(script) + x + y
"# "#
)?, )?,
84 84
); );