Propagate constants to functions for Engine::XXX_with_scope calls.
This commit is contained in:
parent
31ef7e6c69
commit
5083df3096
17
CHANGELOG.md
17
CHANGELOG.md
@ -4,6 +4,11 @@ Rhai Release Notes
|
|||||||
Version 1.2.0
|
Version 1.2.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* `Engine::XXX_with_scope` API's now properly propagate constants within the provided scope also to _functions_ in the script.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -28,16 +33,6 @@ Deprecated API's
|
|||||||
* `From<EvalAltResult>` for `Result<T, Box<EvalAltResult>>` is deprecated so it will no longer be possible to do `EvalAltResult::ErrorXXXXX.into()` to convert to a `Result`; instead, `Err(EvalAltResult:ErrorXXXXX.into())` must be used. Code is clearer if errors are explicitly wrapped in `Err`.
|
* `From<EvalAltResult>` for `Result<T, Box<EvalAltResult>>` is deprecated so it will no longer be possible to do `EvalAltResult::ErrorXXXXX.into()` to convert to a `Result`; instead, `Err(EvalAltResult:ErrorXXXXX.into())` must be used. Code is clearer if errors are explicitly wrapped in `Err`.
|
||||||
|
|
||||||
|
|
||||||
Version 1.1.3
|
|
||||||
=============
|
|
||||||
|
|
||||||
Bug fixes
|
|
||||||
---------
|
|
||||||
|
|
||||||
* Reverses a regression on string `+` operations.
|
|
||||||
* The global namespace is now searched before packages, which is the correct behavior.
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.1.2
|
Version 1.1.2
|
||||||
=============
|
=============
|
||||||
|
|
||||||
@ -46,6 +41,8 @@ Bug fixes
|
|||||||
|
|
||||||
* `0.0` now prints correctly (used to print `0e0`).
|
* `0.0` now prints correctly (used to print `0e0`).
|
||||||
* Unary operators are now properly recognized as an expression statement.
|
* Unary operators are now properly recognized as an expression statement.
|
||||||
|
* Reverses a regression on string `+` operations.
|
||||||
|
* The global namespace is now searched before packages, which is the correct behavior.
|
||||||
|
|
||||||
|
|
||||||
Version 1.1.1
|
Version 1.1.1
|
||||||
|
@ -1006,8 +1006,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Compile a string into an [`AST`] using own scope, which can be used later for evaluation.
|
/// Compile a string into an [`AST`] using own scope, which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
/// ## Constants Propagation
|
||||||
/// when using [`OptimizationLevel::Full`].
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1019,10 +1022,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// // Set optimization level to 'Full' so the Engine can fold constants
|
|
||||||
/// // into function calls and operators.
|
|
||||||
/// engine.set_optimization_level(OptimizationLevel::Full);
|
|
||||||
///
|
|
||||||
/// // Create initialized scope
|
/// // Create initialized scope
|
||||||
/// let mut scope = Scope::new();
|
/// let mut scope = Scope::new();
|
||||||
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
||||||
@ -1130,6 +1129,12 @@ impl Engine {
|
|||||||
/// All strings are simply parsed one after another with nothing inserted in between, not even
|
/// All strings are simply parsed one after another with nothing inserted in between, not even
|
||||||
/// a newline or space.
|
/// a newline or space.
|
||||||
///
|
///
|
||||||
|
/// ## Constants Propagation
|
||||||
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -1140,10 +1145,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// // Set optimization level to 'Full' so the Engine can fold constants
|
|
||||||
/// // into function calls and operators.
|
|
||||||
/// engine.set_optimization_level(OptimizationLevel::Full);
|
|
||||||
///
|
|
||||||
/// // Create initialized scope
|
/// // Create initialized scope
|
||||||
/// let mut scope = Scope::new();
|
/// let mut scope = Scope::new();
|
||||||
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
||||||
@ -1179,6 +1180,12 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
|
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
|
||||||
|
///
|
||||||
|
/// ## Constants Propagation
|
||||||
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn compile_with_scope_and_optimization_level(
|
pub(crate) fn compile_with_scope_and_optimization_level(
|
||||||
&self,
|
&self,
|
||||||
@ -1262,8 +1269,11 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_std` or `WASM`.
|
/// Not available under `no_std` or `WASM`.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
/// ## Constants Propagation
|
||||||
/// when using [`OptimizationLevel::Full`].
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1275,9 +1285,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// // Set optimization level to 'Full' so the Engine can fold constants.
|
|
||||||
/// engine.set_optimization_level(OptimizationLevel::Full);
|
|
||||||
///
|
|
||||||
/// // Create initialized scope
|
/// // Create initialized scope
|
||||||
/// let mut scope = Scope::new();
|
/// let mut scope = Scope::new();
|
||||||
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
||||||
@ -1429,9 +1436,6 @@ impl Engine {
|
|||||||
/// Compile a string containing an expression into an [`AST`] using own scope,
|
/// Compile a string containing an expression into an [`AST`] using own scope,
|
||||||
/// which can be used later for evaluation.
|
/// which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
|
||||||
/// when using [`OptimizationLevel::Full`].
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -1442,10 +1446,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// // Set optimization level to 'Full' so the Engine can fold constants
|
|
||||||
/// // into function calls and operators.
|
|
||||||
/// engine.set_optimization_level(OptimizationLevel::Full);
|
|
||||||
///
|
|
||||||
/// // Create initialized scope
|
/// // Create initialized scope
|
||||||
/// let mut scope = Scope::new();
|
/// let mut scope = Scope::new();
|
||||||
/// scope.push_constant("x", 10_i64); // 'x' is a constant
|
/// scope.push_constant("x", 10_i64); // 'x' is a constant
|
||||||
@ -1515,6 +1515,12 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_std` or `WASM`.
|
/// Not available under `no_std` or `WASM`.
|
||||||
///
|
///
|
||||||
|
/// ## Constants Propagation
|
||||||
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
@ -1562,6 +1568,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Evaluate a string with own scope.
|
/// Evaluate a string with own scope.
|
||||||
///
|
///
|
||||||
|
/// ## Constants Propagation
|
||||||
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -1768,6 +1780,12 @@ impl Engine {
|
|||||||
/// Evaluate a file with own scope, returning any error (if any).
|
/// Evaluate a file with own scope, returning any error (if any).
|
||||||
///
|
///
|
||||||
/// Not available under `no_std` or `WASM`.
|
/// Not available under `no_std` or `WASM`.
|
||||||
|
///
|
||||||
|
/// ## Constants Propagation
|
||||||
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1784,6 +1802,12 @@ impl Engine {
|
|||||||
self.run_with_scope(&mut Scope::new(), script)
|
self.run_with_scope(&mut Scope::new(), script)
|
||||||
}
|
}
|
||||||
/// Evaluate a script with own scope, returning any error (if any).
|
/// Evaluate a script with own scope, returning any error (if any).
|
||||||
|
///
|
||||||
|
/// ## Constants Propagation
|
||||||
|
///
|
||||||
|
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
|
||||||
|
/// throughout the script _including_ functions. This allows functions to be optimized based on
|
||||||
|
/// dynamic global constants.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run_with_scope(
|
pub fn run_with_scope(
|
||||||
&self,
|
&self,
|
||||||
@ -2065,8 +2089,9 @@ impl Engine {
|
|||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let lib = crate::StaticVec::new();
|
let lib = crate::StaticVec::new();
|
||||||
|
|
||||||
let stmt = std::mem::take(ast.statements_mut());
|
let statements = std::mem::take(ast.statements_mut());
|
||||||
crate::optimize::optimize_into_ast(self, scope, stmt, lib, optimization_level)
|
|
||||||
|
crate::optimize::optimize_into_ast(self, scope, statements, lib, optimization_level)
|
||||||
}
|
}
|
||||||
/// _(metadata)_ Generate a list of all registered functions.
|
/// _(metadata)_ Generate a list of all registered functions.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
|
@ -113,17 +113,17 @@ impl<'a> OptimizerState<'a> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.variables.iter().rev().find_map(|(n, access, value)| {
|
for (n, access, value) in self.variables.iter().rev() {
|
||||||
if n == name {
|
if n == name {
|
||||||
match access {
|
return match access {
|
||||||
AccessMode::ReadWrite => None,
|
AccessMode::ReadWrite => None,
|
||||||
AccessMode::ReadOnly => value.as_ref(),
|
AccessMode::ReadOnly => value.as_ref(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn call_fn_with_constant_arguments(
|
pub fn call_fn_with_constant_arguments(
|
||||||
@ -1095,6 +1095,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize a block of [statements][Stmt] at top level.
|
/// Optimize a block of [statements][Stmt] at top level.
|
||||||
|
///
|
||||||
|
/// Constants and variables from the scope are added.
|
||||||
fn optimize_top_level(
|
fn optimize_top_level(
|
||||||
statements: StaticVec<Stmt>,
|
statements: StaticVec<Stmt>,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
@ -1179,11 +1181,9 @@ pub fn optimize_into_ast(
|
|||||||
let mut fn_def = crate::fn_native::shared_take_or_clone(fn_def);
|
let mut fn_def = crate::fn_native::shared_take_or_clone(fn_def);
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let state = &mut OptimizerState::new(engine, lib2, level);
|
|
||||||
|
|
||||||
let body = mem::take(fn_def.body.deref_mut());
|
let body = mem::take(fn_def.body.deref_mut());
|
||||||
|
|
||||||
*fn_def.body = optimize_stmt_block(body, state, true, true, true);
|
*fn_def.body = optimize_top_level(body, engine, scope, lib2, level);
|
||||||
|
|
||||||
fn_def
|
fn_def
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![cfg(not(feature = "no_optimize"))]
|
#![cfg(not(feature = "no_optimize"))]
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult, OptimizationLevel, INT};
|
use rhai::{Engine, EvalAltResult, OptimizationLevel, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_optimizer() -> Result<(), Box<EvalAltResult>> {
|
fn test_optimizer() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -107,3 +107,24 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_optimizer_scope() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
scope.push_constant("FOO", 42 as INT);
|
||||||
|
|
||||||
|
let ast = engine.compile_with_scope(&scope, "fn foo() { FOO } foo()")?;
|
||||||
|
|
||||||
|
scope.push("FOO", 123 as INT);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval_ast::<INT>(&ast)?, 42);
|
||||||
|
assert_eq!(engine.eval_ast_with_scope::<INT>(&mut scope, &ast)?, 42);
|
||||||
|
|
||||||
|
let ast = engine.compile_with_scope(&scope, "fn foo() { FOO } foo()")?;
|
||||||
|
|
||||||
|
assert!(engine.eval_ast_with_scope::<INT>(&mut scope, &ast).is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user