commit
7d80c438e4
24
CHANGELOG.md
24
CHANGELOG.md
@ -15,17 +15,33 @@ Script-breaking changes
|
|||||||
|
|
||||||
* _Strict Variables Mode_ no longer returns an error when an undeclared variable matches a variable/constant in the provided external `Scope`.
|
* _Strict Variables Mode_ no longer returns an error when an undeclared variable matches a variable/constant in the provided external `Scope`.
|
||||||
|
|
||||||
|
Changes to unstable API's
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
* The `Engine::on_var` and `Engine::on_parse_token` API's are now marked unstable/volatile.
|
||||||
|
* The closures passed to `Engine::on_var`, `Engine::on_def_var` and `Engine::register_debugger` take `EvalContext` instead of `&EvalContext` or `&mut EvalContext`.
|
||||||
|
|
||||||
|
New API
|
||||||
|
-------
|
||||||
|
|
||||||
|
* `Module::eval_ast_as_new_raw` is made public as a low-level API.
|
||||||
|
* `format_map_as_json` is provided globally, which is the same as `to_json` for object maps.
|
||||||
|
* `Engine::call_fn_raw_raw` is added to add speed to repeated function calls.
|
||||||
|
* `Engine::eval_statements_raw` is added to evaluate a sequence of statements.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* A custom state is provided that is persistent during the entire evaluation run. This custom state is a `Dynamic`, which can hold any data, and can be accessed by the host via `EvalContext::tag`, `EvalContext::tag_mut`, `NativeCallContext::tag` and `GlobalRuntimeState.tag`.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* `Module::eval_ast_as_new_raw` is made public as a low-level API.
|
|
||||||
* Improper `switch` case condition syntax is now caught at parse time.
|
* Improper `switch` case condition syntax is now caught at parse time.
|
||||||
* `Engine::parse_json` now natively handles nested JSON inputs (using a token remap filter) without needing to replace `{` with `#{`.
|
* `Engine::parse_json` now natively handles nested JSON inputs (using a token remap filter) without needing to replace `{` with `#{`.
|
||||||
* `to_json` is added to object maps to cheaply convert it to JSON format (`()` is mapped to `null`, all other data types must be supported by JSON)
|
* `to_json` is added to object maps to cheaply convert it to JSON format (`()` is mapped to `null`, all other data types must be supported by JSON)
|
||||||
* A global function `format_map_as_json` is provided which is the same as `to_json` for object maps.
|
|
||||||
* `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization.
|
* `FileModuleResolver` now accepts a custom `Scope` to provide constants for optimization.
|
||||||
* A new low-level method `Engine::call_fn_raw_raw` is added to add speed to repeated function calls.
|
* New variants, `Start` and `End`, are added to `DebuggerEvent` triggered at the start/end of script evaluation.
|
||||||
* A new low-level method `Engine::eval_statements_raw` is added to evaluate a sequence of statements.
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.6.1
|
Version 1.6.1
|
||||||
|
@ -160,6 +160,8 @@ impl Engine {
|
|||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
arg_values: impl AsMut<[Dynamic]>,
|
arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let mut arg_values = arg_values;
|
||||||
|
|
||||||
self.call_fn_internal(
|
self.call_fn_internal(
|
||||||
scope,
|
scope,
|
||||||
&mut GlobalRuntimeState::new(self),
|
&mut GlobalRuntimeState::new(self),
|
||||||
@ -167,9 +169,9 @@ impl Engine {
|
|||||||
ast,
|
ast,
|
||||||
eval_ast,
|
eval_ast,
|
||||||
rewind_scope,
|
rewind_scope,
|
||||||
name,
|
name.as_ref(),
|
||||||
this_ptr,
|
this_ptr,
|
||||||
arg_values,
|
arg_values.as_mut(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// _(internals)_ Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// _(internals)_ Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
@ -209,9 +211,9 @@ impl Engine {
|
|||||||
ast: &AST,
|
ast: &AST,
|
||||||
eval_ast: bool,
|
eval_ast: bool,
|
||||||
rewind_scope: bool,
|
rewind_scope: bool,
|
||||||
name: impl AsRef<str>,
|
name: &str,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
arg_values: impl AsMut<[Dynamic]>,
|
arg_values: &mut [Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
self.call_fn_internal(
|
self.call_fn_internal(
|
||||||
scope,
|
scope,
|
||||||
@ -225,7 +227,6 @@ impl Engine {
|
|||||||
arg_values,
|
arg_values,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
fn call_fn_internal(
|
fn call_fn_internal(
|
||||||
&self,
|
&self,
|
||||||
@ -235,9 +236,9 @@ impl Engine {
|
|||||||
ast: &AST,
|
ast: &AST,
|
||||||
eval_ast: bool,
|
eval_ast: bool,
|
||||||
rewind_scope: bool,
|
rewind_scope: bool,
|
||||||
name: impl AsRef<str>,
|
name: &str,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
arg_values: impl AsMut<[Dynamic]>,
|
arg_values: &mut [Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
|
|
||||||
@ -251,31 +252,39 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = name.as_ref();
|
|
||||||
let mut this_ptr = this_ptr;
|
let mut this_ptr = this_ptr;
|
||||||
let mut arg_values = arg_values;
|
|
||||||
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||||
|
|
||||||
let fn_def = ast
|
|
||||||
.shared_lib()
|
|
||||||
.get_script_fn(name, args.len())
|
|
||||||
.ok_or_else(|| ERR::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::func::call::ensure_no_data_race(name, &mut args, false)?;
|
crate::func::call::ensure_no_data_race(name, &mut args, false)?;
|
||||||
|
|
||||||
self.call_script_fn(
|
let lib = &[ast.as_ref()];
|
||||||
|
let fn_def = ast
|
||||||
|
.shared_lib()
|
||||||
|
.get_script_fn(name, args.len())
|
||||||
|
.ok_or_else(|| ERR::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||||
|
|
||||||
|
let result = self.call_script_fn(
|
||||||
scope,
|
scope,
|
||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
&[ast.as_ref()],
|
lib,
|
||||||
&mut this_ptr,
|
&mut this_ptr,
|
||||||
fn_def,
|
fn_def,
|
||||||
&mut args,
|
&mut args,
|
||||||
rewind_scope,
|
rewind_scope,
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
0,
|
0,
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "debugging")]
|
||||||
|
if self.debugger.is_some() {
|
||||||
|
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||||
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
|
self.run_debugger(scope, global, lib, &mut this_ptr, node, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use crate::eval::{Caches, GlobalRuntimeState};
|
|||||||
use crate::parser::ParseState;
|
use crate::parser::ParseState;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, Module, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -191,6 +191,17 @@ impl Engine {
|
|||||||
|
|
||||||
let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
|
let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "debugging")]
|
||||||
|
if self.debugger.is_some() {
|
||||||
|
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||||
|
let lib = &[
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
ast.as_ref(),
|
||||||
|
];
|
||||||
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
|
self.run_debugger(scope, global, lib, &mut None, node, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
result.try_cast::<T>().ok_or_else(|| {
|
result.try_cast::<T>().ok_or_else(|| {
|
||||||
@ -222,18 +233,17 @@ impl Engine {
|
|||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lib = [
|
let mut _lib = &[
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
ast.as_ref(),
|
ast.as_ref(),
|
||||||
];
|
][..];
|
||||||
let lib = if lib.first().map(|m: &&Module| m.is_empty()).unwrap_or(true) {
|
#[cfg(not(feature = "no_function"))]
|
||||||
&[]
|
if !ast.has_functions() {
|
||||||
} else {
|
_lib = &[];
|
||||||
&lib[..]
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
self.eval_global_statements(scope, global, &mut caches, statements, lib, level);
|
self.eval_global_statements(scope, global, &mut caches, statements, _lib, level);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
@ -258,7 +268,7 @@ impl Engine {
|
|||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
statements: &[crate::ast::Stmt],
|
statements: &[crate::ast::Stmt],
|
||||||
lib: &[&Module],
|
lib: &[&crate::Module],
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
self.eval_global_statements(scope, global, caches, statements, lib, level)
|
self.eval_global_statements(scope, global, caches, statements, lib, level)
|
||||||
|
@ -21,11 +21,13 @@ pub struct VarDefInfo<'a> {
|
|||||||
impl Engine {
|
impl Engine {
|
||||||
/// Provide a callback that will be invoked before each variable access.
|
/// Provide a callback that will be invoked before each variable access.
|
||||||
///
|
///
|
||||||
|
/// # WARNING - Unstable API
|
||||||
|
///
|
||||||
|
/// This API is volatile and may change in the future.
|
||||||
|
///
|
||||||
/// # Callback Function Signature
|
/// # Callback Function Signature
|
||||||
///
|
///
|
||||||
/// The callback function signature takes the following form:
|
/// > `Fn(name: &str, index: usize, context: EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>`
|
||||||
///
|
|
||||||
/// > `Fn(name: &str, index: usize, context: &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>`
|
|
||||||
///
|
///
|
||||||
/// where:
|
/// where:
|
||||||
/// * `name`: name of the variable.
|
/// * `name`: name of the variable.
|
||||||
@ -66,10 +68,11 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[deprecated = "This API is volatile and may change in the future."]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_var(
|
pub fn on_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>>
|
callback: impl Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
@ -84,9 +87,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// # Callback Function Signature
|
/// # Callback Function Signature
|
||||||
///
|
///
|
||||||
/// The callback function signature takes the following form:
|
/// > `Fn(is_runtime: bool, info: VarInfo, context: EvalContext) -> Result<bool, Box<EvalAltResult>>`
|
||||||
///
|
|
||||||
/// > `Fn(is_runtime: bool, info: VarInfo, context: &EvalContext) -> Result<bool, Box<EvalAltResult>>`
|
|
||||||
///
|
///
|
||||||
/// where:
|
/// where:
|
||||||
/// * `is_runtime`: `true` if the variable definition event happens during runtime, `false` if during compilation.
|
/// * `is_runtime`: `true` if the variable definition event happens during runtime, `false` if during compilation.
|
||||||
@ -133,7 +134,7 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_def_var(
|
pub fn on_def_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(bool, VarDefInfo, &EvalContext) -> RhaiResultOf<bool> + SendSync + 'static,
|
callback: impl Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.def_var_filter = Some(Box::new(callback));
|
self.def_var_filter = Some(Box::new(callback));
|
||||||
self
|
self
|
||||||
@ -141,9 +142,11 @@ impl Engine {
|
|||||||
/// _(internals)_ Register a callback that will be invoked during parsing to remap certain tokens.
|
/// _(internals)_ Register a callback that will be invoked during parsing to remap certain tokens.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # Callback Function Signature
|
/// # WARNING - Unstable API
|
||||||
///
|
///
|
||||||
/// The callback function signature takes the following form:
|
/// This API is volatile and may change in the future.
|
||||||
|
///
|
||||||
|
/// # Callback Function Signature
|
||||||
///
|
///
|
||||||
/// > `Fn(token: Token, pos: Position, state: &TokenizeState) -> Token`
|
/// > `Fn(token: Token, pos: Position, state: &TokenizeState) -> Token`
|
||||||
///
|
///
|
||||||
@ -185,6 +188,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[deprecated = "This API is volatile and may change in the future."]
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_parse_token(
|
pub fn on_parse_token(
|
||||||
@ -348,7 +352,7 @@ impl Engine {
|
|||||||
&mut self,
|
&mut self,
|
||||||
init: impl Fn() -> Dynamic + SendSync + 'static,
|
init: impl Fn() -> Dynamic + SendSync + 'static,
|
||||||
callback: impl Fn(
|
callback: impl Fn(
|
||||||
&mut EvalContext,
|
EvalContext,
|
||||||
crate::eval::DebuggerEvent,
|
crate::eval::DebuggerEvent,
|
||||||
crate::ast::ASTNode,
|
crate::ast::ASTNode,
|
||||||
Option<&str>,
|
Option<&str>,
|
||||||
|
@ -64,6 +64,18 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
self.eval_global_statements(scope, global, caches, statements, lib, 0)?;
|
self.eval_global_statements(scope, global, caches, statements, lib, 0)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debugging")]
|
||||||
|
if self.debugger.is_some() {
|
||||||
|
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||||
|
let lib = &[
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
ast.as_ref(),
|
||||||
|
];
|
||||||
|
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||||
|
self.run_debugger(scope, global, lib, &mut None, node, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -848,6 +848,7 @@ impl AsRef<crate::Shared<crate::Module>> for AST {
|
|||||||
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, Clone, Copy, Hash)]
|
#[derive(Debug, Clone, Copy, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum ASTNode<'a> {
|
pub enum ASTNode<'a> {
|
||||||
/// A statement ([`Stmt`]).
|
/// A statement ([`Stmt`]).
|
||||||
Stmt(&'a Stmt),
|
Stmt(&'a Stmt),
|
||||||
|
@ -348,6 +348,7 @@ impl FloatWrapper<crate::FLOAT> {
|
|||||||
/// _(internals)_ An expression sub-tree.
|
/// _(internals)_ An expression sub-tree.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// Dynamic constant.
|
/// Dynamic constant.
|
||||||
///
|
///
|
||||||
|
@ -366,6 +366,7 @@ impl Extend<Stmt> for StmtBlock {
|
|||||||
/// _(internals)_ A statement.
|
/// _(internals)_ A statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
|
@ -68,7 +68,10 @@ fn print_current_source(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let src = source.unwrap_or("");
|
let src = source.unwrap_or("");
|
||||||
if src != current_source {
|
if src != current_source {
|
||||||
println!(">>> Source => {}", source.unwrap_or("main script"));
|
println!(
|
||||||
|
"\x1b[34m>>> Source => {}\x1b[39m",
|
||||||
|
source.unwrap_or("main script")
|
||||||
|
);
|
||||||
*current_source = src.into();
|
*current_source = src.into();
|
||||||
}
|
}
|
||||||
if !src.is_empty() {
|
if !src.is_empty() {
|
||||||
@ -118,8 +121,10 @@ fn print_debug_help() {
|
|||||||
println!("help, h => print this help");
|
println!("help, h => print this help");
|
||||||
println!("quit, q, exit, kill => quit");
|
println!("quit, q, exit, kill => quit");
|
||||||
println!("scope => print the scope");
|
println!("scope => print the scope");
|
||||||
|
println!("operations => print the total operations performed");
|
||||||
|
println!("source => print the current source");
|
||||||
println!("print, p => print all variables de-duplicated");
|
println!("print, p => print all variables de-duplicated");
|
||||||
println!("print/p this => print the 'this' pointer");
|
println!("print/p this => print the `this` pointer");
|
||||||
println!("print/p <variable> => print the current value of a variable");
|
println!("print/p <variable> => print the current value of a variable");
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
println!("imports => print all imported modules");
|
println!("imports => print all imported modules");
|
||||||
@ -225,7 +230,7 @@ fn load_script(engine: &Engine) -> (rhai::AST, String) {
|
|||||||
|
|
||||||
// Main callback for debugging.
|
// Main callback for debugging.
|
||||||
fn debug_callback(
|
fn debug_callback(
|
||||||
context: &mut rhai::EvalContext,
|
mut context: rhai::EvalContext,
|
||||||
event: DebuggerEvent,
|
event: DebuggerEvent,
|
||||||
node: rhai::ASTNode,
|
node: rhai::ASTNode,
|
||||||
source: Option<&str>,
|
source: Option<&str>,
|
||||||
@ -234,6 +239,8 @@ fn debug_callback(
|
|||||||
) -> Result<DebuggerCommand, Box<EvalAltResult>> {
|
) -> Result<DebuggerCommand, Box<EvalAltResult>> {
|
||||||
// Check event
|
// Check event
|
||||||
match event {
|
match event {
|
||||||
|
DebuggerEvent::Start => println!("\x1b[32m! Script start\x1b[39m"),
|
||||||
|
DebuggerEvent::End => println!("\x1b[31m! Script end\x1b[39m"),
|
||||||
DebuggerEvent::Step => (),
|
DebuggerEvent::Step => (),
|
||||||
DebuggerEvent::BreakPoint(n) => {
|
DebuggerEvent::BreakPoint(n) => {
|
||||||
match context.global_runtime_state().debugger.break_points()[n] {
|
match context.global_runtime_state().debugger.break_points()[n] {
|
||||||
@ -247,6 +254,7 @@ fn debug_callback(
|
|||||||
BreakPoint::AtProperty { ref name, .. } => {
|
BreakPoint::AtProperty { ref name, .. } => {
|
||||||
println!("! Property {} accessed.", name)
|
println!("! Property {} accessed.", name)
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DebuggerEvent::FunctionExitWithValue(r) => {
|
DebuggerEvent::FunctionExitWithValue(r) => {
|
||||||
@ -275,10 +283,11 @@ fn debug_callback(
|
|||||||
err
|
err
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print current source line
|
// Print current source line
|
||||||
print_current_source(context, source, pos, lines, (0, 0));
|
print_current_source(&mut context, source, pos, lines, (0, 0));
|
||||||
|
|
||||||
// Read stdin for commands
|
// Read stdin for commands
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
@ -313,14 +322,20 @@ fn debug_callback(
|
|||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
["list" | "l"] => print_current_source(context, source, pos, &lines, (3, 6)),
|
["operations"] => {
|
||||||
|
println!("{}", context.global_runtime_state().num_operations)
|
||||||
|
}
|
||||||
|
["source"] => {
|
||||||
|
println!("{}", context.global_runtime_state().source().unwrap_or(""))
|
||||||
|
}
|
||||||
|
["list" | "l"] => print_current_source(&mut context, source, pos, &lines, (3, 6)),
|
||||||
["list" | "l", n] if n.parse::<usize>().is_ok() => {
|
["list" | "l", n] if n.parse::<usize>().is_ok() => {
|
||||||
let num = n.parse::<usize>().unwrap();
|
let num = n.parse::<usize>().unwrap();
|
||||||
if num <= 0 || num > lines.len() {
|
if num <= 0 || num > lines.len() {
|
||||||
eprintln!("\x1b[31mInvalid line: {}\x1b[39m", num);
|
eprintln!("\x1b[31mInvalid line: {}\x1b[39m", num);
|
||||||
} else {
|
} else {
|
||||||
let pos = Position::new(num as u16, 0);
|
let pos = Position::new(num as u16, 0);
|
||||||
print_current_source(context, source, pos, &lines, (3, 6));
|
print_current_source(&mut context, source, pos, &lines, (3, 6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["continue" | "c"] => break Ok(DebuggerCommand::Continue),
|
["continue" | "c"] => break Ok(DebuggerCommand::Continue),
|
||||||
@ -333,7 +348,7 @@ fn debug_callback(
|
|||||||
if let Some(value) = context.this_ptr() {
|
if let Some(value) = context.this_ptr() {
|
||||||
println!("=> {:?}", value);
|
println!("=> {:?}", value);
|
||||||
} else {
|
} else {
|
||||||
println!("'this' pointer is unbound.");
|
println!("`this` pointer is unbound.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["print" | "p", var_name] => {
|
["print" | "p", var_name] => {
|
||||||
@ -632,4 +647,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Script terminated. Bye!");
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ pub type OnDebuggingInit = dyn Fn() -> Dynamic + Send + Sync;
|
|||||||
/// Callback function for debugging.
|
/// Callback function for debugging.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnDebuggerCallback = dyn Fn(
|
pub type OnDebuggerCallback = dyn Fn(
|
||||||
&mut EvalContext,
|
EvalContext,
|
||||||
DebuggerEvent,
|
DebuggerEvent,
|
||||||
ASTNode,
|
ASTNode,
|
||||||
Option<&str>,
|
Option<&str>,
|
||||||
@ -26,18 +26,13 @@ pub type OnDebuggerCallback = dyn Fn(
|
|||||||
) -> RhaiResultOf<DebuggerCommand>;
|
) -> RhaiResultOf<DebuggerCommand>;
|
||||||
/// Callback function for debugging.
|
/// Callback function for debugging.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnDebuggerCallback = dyn Fn(
|
pub type OnDebuggerCallback = dyn Fn(EvalContext, DebuggerEvent, ASTNode, Option<&str>, Position) -> RhaiResultOf<DebuggerCommand>
|
||||||
&mut EvalContext,
|
|
||||||
DebuggerEvent,
|
|
||||||
ASTNode,
|
|
||||||
Option<&str>,
|
|
||||||
Position,
|
|
||||||
) -> RhaiResultOf<DebuggerCommand>
|
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync;
|
+ Sync;
|
||||||
|
|
||||||
/// A command for the debugger on the next iteration.
|
/// A command for the debugger on the next iteration.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum DebuggerCommand {
|
pub enum DebuggerCommand {
|
||||||
// Continue normal execution.
|
// Continue normal execution.
|
||||||
Continue,
|
Continue,
|
||||||
@ -60,29 +55,31 @@ impl Default for DebuggerCommand {
|
|||||||
|
|
||||||
/// The debugger status.
|
/// The debugger status.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum DebuggerStatus {
|
pub enum DebuggerStatus {
|
||||||
|
// Script evaluation starts.
|
||||||
|
Init,
|
||||||
// Stop at the next statement or expression.
|
// Stop at the next statement or expression.
|
||||||
Next(bool, bool),
|
Next(bool, bool),
|
||||||
// Run to the end of the current level of function call.
|
// Run to the end of the current level of function call.
|
||||||
FunctionExit(usize),
|
FunctionExit(usize),
|
||||||
}
|
// Script evaluation ends.
|
||||||
|
Terminate,
|
||||||
impl Default for DebuggerStatus {
|
|
||||||
#[inline(always)]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::CONTINUE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebuggerStatus {
|
impl DebuggerStatus {
|
||||||
pub const CONTINUE: Self = Self::Next(false, false);
|
pub const CONTINUE: Self = Self::Next(false, false);
|
||||||
pub const STEP: Self = Self::Next(true, true);
|
pub const STEP: Self = Self::Next(true, true);
|
||||||
pub const NEXT: Self = Self::Next(true, false);
|
pub const NEXT: Self = Self::Next(true, false);
|
||||||
|
pub const INTO: Self = Self::Next(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A event that triggers the debugger.
|
/// A event that triggers the debugger.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum DebuggerEvent<'a> {
|
pub enum DebuggerEvent<'a> {
|
||||||
|
// Script evaluation starts.
|
||||||
|
Start,
|
||||||
// Break on next step.
|
// Break on next step.
|
||||||
Step,
|
Step,
|
||||||
// Break on break-point.
|
// Break on break-point.
|
||||||
@ -91,10 +88,13 @@ pub enum DebuggerEvent<'a> {
|
|||||||
FunctionExitWithValue(&'a Dynamic),
|
FunctionExitWithValue(&'a Dynamic),
|
||||||
// Return from a function with a value.
|
// Return from a function with a value.
|
||||||
FunctionExitWithError(&'a EvalAltResult),
|
FunctionExitWithError(&'a EvalAltResult),
|
||||||
|
// Script evaluation ends.
|
||||||
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A break-point for debugging.
|
/// A break-point for debugging.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum BreakPoint {
|
pub enum BreakPoint {
|
||||||
/// Break at a particular position under a particular source.
|
/// Break at a particular position under a particular source.
|
||||||
///
|
///
|
||||||
@ -264,7 +264,7 @@ impl Debugger {
|
|||||||
pub fn new(engine: &Engine) -> Self {
|
pub fn new(engine: &Engine) -> Self {
|
||||||
Self {
|
Self {
|
||||||
status: if engine.debugger.is_some() {
|
status: if engine.debugger.is_some() {
|
||||||
DebuggerStatus::STEP
|
DebuggerStatus::Init
|
||||||
} else {
|
} else {
|
||||||
DebuggerStatus::CONTINUE
|
DebuggerStatus::CONTINUE
|
||||||
},
|
},
|
||||||
@ -469,22 +469,27 @@ impl Engine {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let stop = match global.debugger.status {
|
let event = match global.debugger.status {
|
||||||
DebuggerStatus::Next(false, false) => false,
|
DebuggerStatus::Init => Some(DebuggerEvent::Start),
|
||||||
DebuggerStatus::Next(true, false) => matches!(node, ASTNode::Stmt(..)),
|
DebuggerStatus::CONTINUE => None,
|
||||||
DebuggerStatus::Next(false, true) => matches!(node, ASTNode::Expr(..)),
|
DebuggerStatus::NEXT if matches!(node, ASTNode::Stmt(..)) => Some(DebuggerEvent::Step),
|
||||||
DebuggerStatus::Next(true, true) => true,
|
DebuggerStatus::NEXT => None,
|
||||||
DebuggerStatus::FunctionExit(..) => false,
|
DebuggerStatus::INTO if matches!(node, ASTNode::Expr(..)) => Some(DebuggerEvent::Step),
|
||||||
|
DebuggerStatus::INTO => None,
|
||||||
|
DebuggerStatus::STEP => Some(DebuggerEvent::Step),
|
||||||
|
DebuggerStatus::FunctionExit(..) => None,
|
||||||
|
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
|
||||||
};
|
};
|
||||||
|
|
||||||
let event = if stop {
|
let event = match event {
|
||||||
DebuggerEvent::Step
|
Some(e) => e,
|
||||||
} else {
|
None => {
|
||||||
if let Some(bp) = global.debugger.is_break_point(&global.source, node) {
|
if let Some(bp) = global.debugger.is_break_point(&global.source, node) {
|
||||||
DebuggerEvent::BreakPoint(bp)
|
DebuggerEvent::BreakPoint(bp)
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level)
|
self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level)
|
||||||
@ -514,7 +519,7 @@ impl Engine {
|
|||||||
Some(source.as_str())
|
Some(source.as_str())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut context = crate::EvalContext {
|
let context = crate::EvalContext {
|
||||||
engine: self,
|
engine: self,
|
||||||
scope,
|
scope,
|
||||||
global,
|
global,
|
||||||
@ -525,7 +530,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some((.., ref on_debugger)) = self.debugger {
|
if let Some((.., ref on_debugger)) = self.debugger {
|
||||||
let command = on_debugger(&mut context, event, node, source, node.position())?;
|
let command = on_debugger(context, event, node, source, node.position())?;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
DebuggerCommand::Continue => {
|
DebuggerCommand::Continue => {
|
||||||
@ -548,12 +553,12 @@ impl Engine {
|
|||||||
// Bump a level if it is a function call
|
// Bump a level if it is a function call
|
||||||
let level = match node {
|
let level = match node {
|
||||||
ASTNode::Expr(Expr::FnCall(..)) | ASTNode::Stmt(Stmt::FnCall(..)) => {
|
ASTNode::Expr(Expr::FnCall(..)) | ASTNode::Stmt(Stmt::FnCall(..)) => {
|
||||||
context.call_level() + 1
|
level + 1
|
||||||
}
|
}
|
||||||
ASTNode::Stmt(Stmt::Expr(e)) if matches!(e.as_ref(), Expr::FnCall(..)) => {
|
ASTNode::Stmt(Stmt::Expr(e)) if matches!(e.as_ref(), Expr::FnCall(..)) => {
|
||||||
context.call_level() + 1
|
level + 1
|
||||||
}
|
}
|
||||||
_ => context.call_level(),
|
_ => level,
|
||||||
};
|
};
|
||||||
global.debugger.status = DebuggerStatus::FunctionExit(level);
|
global.debugger.status = DebuggerStatus::FunctionExit(level);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -60,6 +60,18 @@ impl<'s, 'ps, 'm, 'pm, 'pt> EvalContext<'_, 's, 'ps, 'm, 'pm, '_, '_, 'pt> {
|
|||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.global.iter_imports()
|
self.global.iter_imports()
|
||||||
}
|
}
|
||||||
|
/// Custom state kept in a [`Dynamic`].
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn tag(&self) -> &Dynamic {
|
||||||
|
&self.global.tag
|
||||||
|
}
|
||||||
|
/// Mutable reference to the custom state kept in a [`Dynamic`].
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn tag_mut(&mut self) -> &mut Dynamic {
|
||||||
|
&mut self.global.tag
|
||||||
|
}
|
||||||
/// _(internals)_ The current [`GlobalRuntimeState`].
|
/// _(internals)_ The current [`GlobalRuntimeState`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -161,7 +161,7 @@ impl Engine {
|
|||||||
level,
|
level,
|
||||||
};
|
};
|
||||||
let var_name = expr.get_variable_name(true).expect("`Expr::Variable`");
|
let var_name = expr.get_variable_name(true).expect("`Expr::Variable`");
|
||||||
match resolve_var(var_name, index, &context) {
|
match resolve_var(var_name, index, context) {
|
||||||
Ok(Some(mut result)) => {
|
Ok(Some(mut result)) => {
|
||||||
result.set_access_mode(AccessMode::ReadOnly);
|
result.set_access_mode(AccessMode::ReadOnly);
|
||||||
return Ok((result.into(), var_pos));
|
return Ok((result.into(), var_pos));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Global runtime state.
|
//! Global runtime state.
|
||||||
|
|
||||||
use crate::{Engine, Identifier};
|
use crate::{Dynamic, Engine, Identifier};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{fmt, marker::PhantomData};
|
use std::{fmt, marker::PhantomData};
|
||||||
@ -9,7 +9,7 @@ use std::{fmt, marker::PhantomData};
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub type GlobalConstants =
|
pub type GlobalConstants =
|
||||||
crate::Shared<crate::Locked<std::collections::BTreeMap<Identifier, crate::Dynamic>>>;
|
crate::Shared<crate::Locked<std::collections::BTreeMap<Identifier, Dynamic>>>;
|
||||||
|
|
||||||
/// _(internals)_ Global runtime states.
|
/// _(internals)_ Global runtime states.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -64,6 +64,8 @@ pub struct GlobalRuntimeState<'a> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub constants: Option<GlobalConstants>,
|
pub constants: Option<GlobalConstants>,
|
||||||
|
/// Custom state that can be used by the external host.
|
||||||
|
pub tag: Dynamic,
|
||||||
/// Debugging interface.
|
/// Debugging interface.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub debugger: super::Debugger,
|
pub debugger: super::Debugger,
|
||||||
@ -95,6 +97,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
constants: None,
|
constants: None,
|
||||||
|
tag: Dynamic::UNIT,
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
debugger: crate::eval::Debugger::new(_engine),
|
debugger: crate::eval::Debugger::new(_engine),
|
||||||
dummy: PhantomData::default(),
|
dummy: PhantomData::default(),
|
||||||
|
@ -841,7 +841,7 @@ impl Engine {
|
|||||||
level,
|
level,
|
||||||
};
|
};
|
||||||
|
|
||||||
match filter(true, info, &context) {
|
match filter(true, info, context) {
|
||||||
Ok(true) => None,
|
Ok(true) => None,
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
Some(Err(
|
Some(Err(
|
||||||
|
@ -10,6 +10,7 @@ use std::prelude::v1::*;
|
|||||||
|
|
||||||
/// A type encapsulating a function callable by Rhai.
|
/// A type encapsulating a function callable by Rhai.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum CallableFunction {
|
pub enum CallableFunction {
|
||||||
/// A pure native Rust function with all arguments passed by value.
|
/// A pure native Rust function with all arguments passed by value.
|
||||||
Pure(Shared<FnAny>),
|
Pure(Shared<FnAny>),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Module defining interfaces to native-Rust functions.
|
//! Module defining interfaces to native-Rust functions.
|
||||||
|
|
||||||
use super::call::FnCallArgs;
|
use super::call::FnCallArgs;
|
||||||
use crate::api::events::VarDefInfo;
|
|
||||||
use crate::ast::FnCallHashes;
|
use crate::ast::FnCallHashes;
|
||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
use crate::eval::{Caches, GlobalRuntimeState};
|
||||||
use crate::plugin::PluginFunction;
|
use crate::plugin::PluginFunction;
|
||||||
@ -9,7 +8,7 @@ use crate::tokenizer::{Token, TokenizeState};
|
|||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult,
|
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult,
|
||||||
RhaiResultOf, StaticVec, ERR,
|
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||||
};
|
};
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -218,6 +217,12 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
pub const fn source(&self) -> Option<&str> {
|
pub const fn source(&self) -> Option<&str> {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
/// Custom state kept in a [`Dynamic`].
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn tag(&self) -> Option<&Dynamic> {
|
||||||
|
self.global.as_ref().map(|g| &g.tag)
|
||||||
|
}
|
||||||
/// Get an iterator over the current set of modules imported via `import` statements
|
/// Get an iterator over the current set of modules imported via `import` statements
|
||||||
/// in reverse order.
|
/// in reverse order.
|
||||||
///
|
///
|
||||||
@ -466,16 +471,16 @@ pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token
|
|||||||
|
|
||||||
/// Callback function for variable access.
|
/// Callback function for variable access.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnVarCallback = dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>>;
|
pub type OnVarCallback = dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>>;
|
||||||
/// Callback function for variable access.
|
/// Callback function for variable access.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnVarCallback =
|
pub type OnVarCallback =
|
||||||
dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
|
dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
|
||||||
|
|
||||||
/// Callback function for variable definition.
|
/// Callback function for variable definition.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnDefVarCallback = dyn Fn(bool, VarDefInfo, &EvalContext) -> RhaiResultOf<bool>;
|
pub type OnDefVarCallback = dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool>;
|
||||||
/// Callback function for variable definition.
|
/// Callback function for variable definition.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnDefVarCallback =
|
pub type OnDefVarCallback =
|
||||||
dyn Fn(bool, VarDefInfo, &EvalContext) -> RhaiResultOf<bool> + Send + Sync;
|
dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool> + Send + Sync;
|
||||||
|
@ -121,9 +121,11 @@ impl Engine {
|
|||||||
ref constants,
|
ref constants,
|
||||||
}) = fn_def.environ
|
}) = fn_def.environ
|
||||||
{
|
{
|
||||||
for (n, m) in imports.iter().cloned() {
|
imports
|
||||||
global.push_import(n, m)
|
.iter()
|
||||||
}
|
.cloned()
|
||||||
|
.for_each(|(n, m)| global.push_import(n, m));
|
||||||
|
|
||||||
(
|
(
|
||||||
if fn_lib.is_empty() {
|
if fn_lib.is_empty() {
|
||||||
lib
|
lib
|
||||||
@ -167,7 +169,6 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
format!("{} @ '{}' < {}", name, src, fn_def.name)
|
format!("{} @ '{}' < {}", name, src, fn_def.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
make_error(fn_name, fn_def, global, err, pos)
|
make_error(fn_name, fn_def, global, err, pos)
|
||||||
}
|
}
|
||||||
// System errors are passed straight-through
|
// System errors are passed straight-through
|
||||||
|
@ -51,6 +51,10 @@ struct OptimizerState<'a> {
|
|||||||
propagate_constants: bool,
|
propagate_constants: bool,
|
||||||
/// An [`Engine`] instance for eager function evaluation.
|
/// An [`Engine`] instance for eager function evaluation.
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
|
/// The global runtime state.
|
||||||
|
global: GlobalRuntimeState<'a>,
|
||||||
|
/// Function resolution caches.
|
||||||
|
caches: Caches,
|
||||||
/// [Module][crate::Module] containing script-defined functions.
|
/// [Module][crate::Module] containing script-defined functions.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
lib: &'a [&'a crate::Module],
|
lib: &'a [&'a crate::Module],
|
||||||
@ -61,7 +65,7 @@ struct OptimizerState<'a> {
|
|||||||
impl<'a> OptimizerState<'a> {
|
impl<'a> OptimizerState<'a> {
|
||||||
/// Create a new State.
|
/// Create a new State.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn new(
|
pub fn new(
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
#[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module],
|
#[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module],
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
@ -71,6 +75,8 @@ impl<'a> OptimizerState<'a> {
|
|||||||
variables: StaticVec::new_const(),
|
variables: StaticVec::new_const(),
|
||||||
propagate_constants: true,
|
propagate_constants: true,
|
||||||
engine,
|
engine,
|
||||||
|
global: GlobalRuntimeState::new(engine),
|
||||||
|
caches: Caches::new(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
lib,
|
lib,
|
||||||
optimization_level,
|
optimization_level,
|
||||||
@ -127,7 +133,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn call_fn_with_constant_arguments(
|
pub fn call_fn_with_constant_arguments(
|
||||||
&self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
@ -138,8 +144,8 @@ impl<'a> OptimizerState<'a> {
|
|||||||
|
|
||||||
self.engine
|
self.engine
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
&mut GlobalRuntimeState::new(&self.engine),
|
&mut self.global,
|
||||||
&mut Caches::new(),
|
&mut self.caches,
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
calc_fn_hash(&fn_name, arg_values.len()),
|
calc_fn_hash(&fn_name, arg_values.len()),
|
||||||
|
@ -49,6 +49,8 @@ pub struct ParseState<'e> {
|
|||||||
interned_strings: StringsInterner,
|
interned_strings: StringsInterner,
|
||||||
/// External [scope][Scope] with constants.
|
/// External [scope][Scope] with constants.
|
||||||
pub scope: &'e Scope<'e>,
|
pub scope: &'e Scope<'e>,
|
||||||
|
/// Global runtime state.
|
||||||
|
pub global: GlobalRuntimeState<'e>,
|
||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
pub stack: Scope<'e>,
|
pub stack: Scope<'e>,
|
||||||
/// Size of the local variables stack upon entry of the current block scope.
|
/// Size of the local variables stack upon entry of the current block scope.
|
||||||
@ -83,6 +85,7 @@ impl<'e> ParseState<'e> {
|
|||||||
allow_capture: true,
|
allow_capture: true,
|
||||||
interned_strings: StringsInterner::new(),
|
interned_strings: StringsInterner::new(),
|
||||||
scope,
|
scope,
|
||||||
|
global: GlobalRuntimeState::new(engine),
|
||||||
stack: Scope::new(),
|
stack: Scope::new(),
|
||||||
block_stack_len: 0,
|
block_stack_len: 0,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -2715,14 +2718,14 @@ impl Engine {
|
|||||||
let context = EvalContext {
|
let context = EvalContext {
|
||||||
engine: self,
|
engine: self,
|
||||||
scope: &mut state.stack,
|
scope: &mut state.stack,
|
||||||
global: &mut GlobalRuntimeState::new(self),
|
global: &mut state.global,
|
||||||
caches: None,
|
caches: None,
|
||||||
lib: &[],
|
lib: &[],
|
||||||
this_ptr: &mut None,
|
this_ptr: &mut None,
|
||||||
level,
|
level,
|
||||||
};
|
};
|
||||||
|
|
||||||
match filter(false, info, &context) {
|
match filter(false, info, context) {
|
||||||
Ok(true) => (),
|
Ok(true) => (),
|
||||||
Ok(false) => return Err(PERR::ForbiddenVariable(name.to_string()).into_err(pos)),
|
Ok(false) => return Err(PERR::ForbiddenVariable(name.to_string()).into_err(pos)),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
|
@ -349,6 +349,7 @@ impl fmt::Debug for Span {
|
|||||||
/// _(internals)_ A Rhai language token.
|
/// _(internals)_ A Rhai language token.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
/// An `INT` constant.
|
/// An `INT` constant.
|
||||||
IntegerConstant(INT),
|
IntegerConstant(INT),
|
||||||
|
@ -55,7 +55,7 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
|
|||||||
state.insert("foo".into(), false.into());
|
state.insert("foo".into(), false.into());
|
||||||
Dynamic::from_map(state)
|
Dynamic::from_map(state)
|
||||||
},
|
},
|
||||||
|context, _, _, _, _| {
|
|mut context, _, _, _, _| {
|
||||||
// Get global runtime state
|
// Get global runtime state
|
||||||
let global = context.global_runtime_state_mut();
|
let global = context.global_runtime_state_mut();
|
||||||
|
|
||||||
|
@ -202,9 +202,15 @@ fn test_var_def_filter() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let ast = engine.compile("let x = 42;")?;
|
let ast = engine.compile("let x = 42;")?;
|
||||||
engine.run_ast(&ast)?;
|
engine.run_ast(&ast)?;
|
||||||
|
|
||||||
engine.on_def_var(|_, info, _| match (info.name, info.nesting_level) {
|
engine.on_def_var(|_, info, mut ctx| {
|
||||||
|
if ctx.tag().is::<()>() {
|
||||||
|
*ctx.tag_mut() = rhai::Dynamic::ONE;
|
||||||
|
}
|
||||||
|
println!("Tag = {}", ctx.tag());
|
||||||
|
match (info.name, info.nesting_level) {
|
||||||
("x", 0 | 1) => Ok(false),
|
("x", 0 | 1) => Ok(false),
|
||||||
_ => Ok(true),
|
_ => Ok(true),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user