Allow initialization of EvalState tag and separate debugger state into separate variable.

This commit is contained in:
Stephen Chung 2022-05-21 21:44:12 +08:00
parent 5435fdb8c8
commit 1abec0a8a8
10 changed files with 86 additions and 25 deletions

View File

@ -21,6 +21,8 @@ Enhancements
------------
* `EvalAltResult::IndexNotFound` is added to aid in raising errors for indexers.
* `Engine::def_tag`, `Engine::def_tag_mut` and `Engine::set_tag` are added to manage a default value for the custom evaluation state, accessible via `EvalState::tag()` (which is the same as `NativeCallContext::tag()`).
* Originally, the debugger's custom state uses the same state as `EvalState::tag()` (which is the same as `NativeCallContext::tag()`). It is now split into its own variable accessible under `Debugger::state()`.
Version 1.7.0

View File

@ -201,7 +201,7 @@ impl Engine {
scope: &Scope,
scripts: impl AsRef<[S]>,
) -> ParseResult<AST> {
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level())
}
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
///
@ -292,6 +292,6 @@ impl Engine {
let mut peekable = stream.peekable();
let mut state = ParseState::new(self, scope, tokenizer_control);
self.parse_global_expr(&mut peekable, &mut state, self.optimization_level)
self.parse_global_expr(&mut peekable, &mut state, self.optimization_level())
}
}

View File

@ -67,7 +67,7 @@ impl Engine {
let ast = self.compile_with_scope_and_optimization_level(
scope,
&[script],
self.optimization_level,
self.optimization_level(),
)?;
self.eval_ast_with_scope(scope, &ast)
}

View File

@ -30,7 +30,7 @@ pub mod deprecated;
use crate::engine::Precedence;
use crate::tokenizer::Token;
use crate::{Engine, Identifier};
use crate::{Dynamic, Engine, Identifier};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -195,4 +195,23 @@ impl Engine {
Ok(self)
}
/// Get the default value of the custom state for each evaluation run.
#[inline(always)]
#[must_use]
pub fn default_tag(&self) -> &Dynamic {
&self.def_tag
}
/// Get a mutable reference to the default value of the custom state for each evaluation run.
#[inline(always)]
#[must_use]
pub fn default_tag_mut(&mut self) -> &mut Dynamic {
&mut self.def_tag
}
/// Set the default value of the custom state for each evaluation run.
#[inline(always)]
pub fn set_default_tag(&mut self, value: impl Into<Dynamic>) -> &mut Self {
self.def_tag = value.into();
self
}
}

View File

@ -26,7 +26,11 @@ impl Engine {
self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref));
let mut state = ParseState::new(self, scope, tokenizer_control);
let ast = self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?;
let ast = self.parse(
&mut stream.peekable(),
&mut state,
self.optimization_level(),
)?;
self.run_ast_with_scope(scope, &ast)
}

View File

@ -60,7 +60,12 @@ fn print_current_source(
lines: &[String],
window: (usize, usize),
) {
let current_source = &mut *context.tag_mut().write_lock::<ImmutableString>().unwrap();
let current_source = &mut *context
.global_runtime_state_mut()
.debugger
.state_mut()
.write_lock::<ImmutableString>()
.unwrap();
let src = source.unwrap_or("");
if src != current_source {
println!(

View File

@ -132,8 +132,11 @@ pub struct Engine {
/// Language options.
pub(crate) options: LangOptions,
/// Default value for the custom state.
pub(crate) def_tag: Dynamic,
/// Script optimization level.
pub optimization_level: OptimizationLevel,
pub(crate) optimization_level: OptimizationLevel,
/// Max limits.
#[cfg(not(feature = "unchecked"))]
@ -280,6 +283,8 @@ impl Engine {
options: LangOptions::new(),
def_tag: Dynamic::UNIT,
#[cfg(not(feature = "no_optimize"))]
optimization_level: OptimizationLevel::Simple,
#[cfg(feature = "no_optimize")]

View File

@ -253,17 +253,20 @@ pub struct Debugger {
break_points: Vec<BreakPoint>,
/// The current function call stack.
call_stack: Vec<CallStackFrame>,
/// The current state.
state: Dynamic,
}
impl Debugger {
/// Create a new [`Debugger`].
#[inline(always)]
#[must_use]
pub fn new(status: DebuggerStatus) -> Self {
pub fn new(status: DebuggerStatus, state: Dynamic) -> Self {
Self {
status,
break_points: Vec::new(),
call_stack: Vec::new(),
state,
}
}
/// Get the current call stack.
@ -374,6 +377,23 @@ impl Debugger {
pub fn break_points_mut(&mut self) -> &mut Vec<BreakPoint> {
&mut self.break_points
}
/// Get the custom state.
#[inline(always)]
#[must_use]
pub fn state(&self) -> &Dynamic {
&self.state
}
/// Get a mutable reference to the custom state.
#[inline(always)]
#[must_use]
pub fn state_mut(&mut self) -> &mut Dynamic {
&mut self.state
}
/// Set the custom state.
#[inline(always)]
pub fn set_state(&mut self, state: impl Into<Dynamic>) {
self.state = state.into();
}
}
impl Engine {

View File

@ -78,8 +78,6 @@ impl GlobalRuntimeState<'_> {
#[inline(always)]
#[must_use]
pub fn new(engine: &Engine) -> Self {
let _engine = engine;
Self {
#[cfg(not(feature = "no_module"))]
keys: crate::StaticVec::new_const(),
@ -98,21 +96,21 @@ impl GlobalRuntimeState<'_> {
#[cfg(not(feature = "no_function"))]
constants: None,
#[cfg(not(feature = "debugging"))]
tag: Dynamic::UNIT,
tag: engine.default_tag().clone(),
#[cfg(feature = "debugging")]
tag: if let Some((ref init, ..)) = engine.debugger {
debugger: crate::eval::Debugger::new(
if engine.debugger.is_some() {
crate::eval::DebuggerStatus::Init
} else {
crate::eval::DebuggerStatus::CONTINUE
},
if let Some((ref init, ..)) = engine.debugger {
init()
} else {
Dynamic::UNIT
},
#[cfg(feature = "debugging")]
debugger: crate::eval::Debugger::new(if engine.debugger.is_some() {
crate::eval::DebuggerStatus::Init
} else {
crate::eval::DebuggerStatus::CONTINUE
}),
),
dummy: PhantomData::default(),
}

View File

@ -57,10 +57,18 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
},
|mut context, _, _, _, _| {
// Print debugger state - which is an object map
println!("Current state = {}", context.tag());
println!(
"Current state = {}",
context.global_runtime_state_mut().debugger.state()
);
// Modify state
let mut state = context.tag_mut().write_lock::<Map>().unwrap();
let mut state = context
.global_runtime_state_mut()
.debugger
.state_mut()
.write_lock::<Map>()
.unwrap();
let hello = state.get("hello").unwrap().as_int().unwrap();
state.insert("hello".into(), (hello + 1).into());
state.insert("foo".into(), true.into());