Add context source to on_debug.
This commit is contained in:
parent
2955a4ab64
commit
5ac83f0f46
@ -27,7 +27,7 @@ Breaking changes
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Engine::on_progress` now takes `u64` instead of `&u64`.
|
* `Engine::on_progress` now takes `u64` instead of `&u64`.
|
||||||
* The closure for `Engine::on_debug` now takes an additional `Position` parameter.
|
* The closure for `Engine::on_debug` now takes two additional parameters: `source: Option<&str>` and `pos: Position`.
|
||||||
* `AST::iter_functions` now returns `ScriptFnMetadata`.
|
* `AST::iter_functions` now returns `ScriptFnMetadata`.
|
||||||
* The parser function passed to `Engine::register_custom_syntax_raw` now takes an additional parameter containing the _look-ahead_ symbol.
|
* The parser function passed to `Engine::register_custom_syntax_raw` now takes an additional parameter containing the _look-ahead_ symbol.
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ engine.on_print(|x| println!("hello: {}", x));
|
|||||||
|
|
||||||
// Any function or closure that takes a '&str' and a 'Position' argument can be used to
|
// Any function or closure that takes a '&str' and a 'Position' argument can be used to
|
||||||
// override 'debug'.
|
// override 'debug'.
|
||||||
engine.on_debug(|x, pos| println!("DEBUG at {:?}: {}", pos, x));
|
engine.on_debug(|x, src, pos| println!("DEBUG of {} at {:?}: {}", src.unwrap_or("unknown"), pos, x));
|
||||||
|
|
||||||
// Example: quick-'n-dirty logging
|
// Example: quick-'n-dirty logging
|
||||||
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
||||||
@ -37,8 +37,8 @@ let log = logbook.clone();
|
|||||||
engine.on_print(move |s| log.write().unwrap().push(format!("entry: {}", s)));
|
engine.on_print(move |s| log.write().unwrap().push(format!("entry: {}", s)));
|
||||||
|
|
||||||
let log = logbook.clone();
|
let log = logbook.clone();
|
||||||
engine.on_debug(move |s, pos| log.write().unwrap().push(
|
engine.on_debug(move |s, src, pos| log.write().unwrap().push(
|
||||||
format!("DEBUG at {:?}: {}", pos, s)
|
format!("DEBUG of {} at {:?}: {}", src.unwrap_or("unknown"), pos, s)
|
||||||
));
|
));
|
||||||
|
|
||||||
// Evaluate script
|
// Evaluate script
|
||||||
@ -49,3 +49,24 @@ for entry in logbook.read().unwrap().iter() {
|
|||||||
println!("{}", entry);
|
println!("{}", entry);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`on_debug` Callback Signature
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The function signature passed to `Engine::on_debug` takes the following form:
|
||||||
|
|
||||||
|
> `Fn(text: &str, source: Option<&str>, pos: Position) + 'static`
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
| Parameter | Type | Description |
|
||||||
|
| --------- | :------------: | --------------------------------------------------------------- |
|
||||||
|
| `text` | `&str` | text to display |
|
||||||
|
| `source` | `Option<&str>` | source of the current evaluation, if any |
|
||||||
|
| `pos` | `Position` | position (line number and character offset) of the `debug` call |
|
||||||
|
|
||||||
|
The _source_ of a script evaluation is any text string provided to an [`AST`] via the `AST::set_source` method.
|
||||||
|
|
||||||
|
If a [module] is loaded via an [`import`] statement, then the _source_ of functions defined
|
||||||
|
within the module will be the module's _path_.
|
||||||
|
42
src/ast.rs
42
src/ast.rs
@ -163,6 +163,8 @@ impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef {
|
|||||||
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AST {
|
pub struct AST {
|
||||||
|
/// Source of the [`AST`].
|
||||||
|
source: Option<ImmutableString>,
|
||||||
/// Global statements.
|
/// Global statements.
|
||||||
statements: Vec<Stmt>,
|
statements: Vec<Stmt>,
|
||||||
/// Script-defined functions.
|
/// Script-defined functions.
|
||||||
@ -172,6 +174,7 @@ pub struct AST {
|
|||||||
impl Default for AST {
|
impl Default for AST {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
source: None,
|
||||||
statements: Vec::with_capacity(16),
|
statements: Vec::with_capacity(16),
|
||||||
functions: Default::default(),
|
functions: Default::default(),
|
||||||
}
|
}
|
||||||
@ -186,10 +189,36 @@ impl AST {
|
|||||||
functions: impl Into<Shared<Module>>,
|
functions: impl Into<Shared<Module>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
source: None,
|
||||||
statements: statements.into_iter().collect(),
|
statements: statements.into_iter().collect(),
|
||||||
functions: functions.into(),
|
functions: functions.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Create a new [`AST`] with a source name.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new_with_source(
|
||||||
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
|
functions: impl Into<Shared<Module>>,
|
||||||
|
source: impl Into<ImmutableString>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
source: Some(source.into()),
|
||||||
|
statements: statements.into_iter().collect(),
|
||||||
|
functions: functions.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the source.
|
||||||
|
pub fn source(&self) -> Option<&str> {
|
||||||
|
self.source.as_ref().map(|s| s.as_str())
|
||||||
|
}
|
||||||
|
/// Clone the source.
|
||||||
|
pub(crate) fn clone_source(&self) -> Option<ImmutableString> {
|
||||||
|
self.source.clone()
|
||||||
|
}
|
||||||
|
/// Set the source.
|
||||||
|
pub fn set_source<S: Into<ImmutableString>>(&mut self, source: Option<S>) {
|
||||||
|
self.source = source.map(|s| s.into())
|
||||||
|
}
|
||||||
/// Get the statements.
|
/// Get the statements.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -253,6 +282,7 @@ impl AST {
|
|||||||
let mut functions: Module = Default::default();
|
let mut functions: Module = Default::default();
|
||||||
functions.merge_filtered(&self.functions, &mut filter);
|
functions.merge_filtered(&self.functions, &mut filter);
|
||||||
Self {
|
Self {
|
||||||
|
source: self.source.clone(),
|
||||||
statements: Default::default(),
|
statements: Default::default(),
|
||||||
functions: functions.into(),
|
functions: functions.into(),
|
||||||
}
|
}
|
||||||
@ -262,6 +292,7 @@ impl AST {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clone_statements_only(&self) -> Self {
|
pub fn clone_statements_only(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
source: self.source.clone(),
|
||||||
statements: self.statements.clone(),
|
statements: self.statements.clone(),
|
||||||
functions: Default::default(),
|
functions: Default::default(),
|
||||||
}
|
}
|
||||||
@ -432,6 +463,7 @@ impl AST {
|
|||||||
let Self {
|
let Self {
|
||||||
statements,
|
statements,
|
||||||
functions,
|
functions,
|
||||||
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let ast = match (statements.is_empty(), other.statements.is_empty()) {
|
let ast = match (statements.is_empty(), other.statements.is_empty()) {
|
||||||
@ -445,11 +477,21 @@ impl AST {
|
|||||||
(true, true) => vec![],
|
(true, true) => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let source = if other.source.is_some() {
|
||||||
|
other.source.clone()
|
||||||
|
} else {
|
||||||
|
self.source.clone()
|
||||||
|
};
|
||||||
|
|
||||||
let mut functions = functions.as_ref().clone();
|
let mut functions = functions.as_ref().clone();
|
||||||
functions.merge_filtered(&other.functions, &mut filter);
|
functions.merge_filtered(&other.functions, &mut filter);
|
||||||
|
|
||||||
|
if let Some(source) = source {
|
||||||
|
Self::new_with_source(ast, functions, source)
|
||||||
|
} else {
|
||||||
Self::new(ast, functions)
|
Self::new(ast, functions)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
||||||
///
|
///
|
||||||
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
||||||
|
@ -100,19 +100,21 @@ impl Imports {
|
|||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
|
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a str, &'a Module)> + 'a {
|
||||||
self.0.iter().flat_map(|lib| {
|
self.0.iter().flat_map(|lib| {
|
||||||
lib.iter()
|
lib.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(name, module)| (name.clone(), module.clone()))
|
.map(|(name, module)| (name.as_str(), module.as_ref()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn iter_raw<'a>(
|
pub(crate) fn iter_raw<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
|
) -> impl Iterator<Item = (&'a ImmutableString, &'a Shared<Module>)> + 'a {
|
||||||
self.0.iter().flat_map(|lib| lib.iter().rev().cloned())
|
self.0
|
||||||
|
.iter()
|
||||||
|
.flat_map(|lib| lib.iter().rev().map(|(n, m)| (n, m)))
|
||||||
}
|
}
|
||||||
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
||||||
@ -478,6 +480,8 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
|||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
/// Source of the current context.
|
||||||
|
pub source: Option<ImmutableString>,
|
||||||
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
|
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
|
||||||
/// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned.
|
/// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned.
|
||||||
/// When that happens, this flag is turned on to force a scope lookup by name.
|
/// When that happens, this flag is turned on to force a scope lookup by name.
|
||||||
@ -708,11 +712,15 @@ fn default_print(_s: &str) {
|
|||||||
|
|
||||||
/// Debug to stdout
|
/// Debug to stdout
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default_debug(_s: &str, _pos: Position) {
|
fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) {
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
if let Some(source) = _source {
|
||||||
|
println!("{} @ {:?} | {}", source, _pos, _s);
|
||||||
|
} else {
|
||||||
println!("{:?} | {}", _pos, _s);
|
println!("{:?} | {}", _pos, _s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
@ -828,7 +836,7 @@ impl Engine {
|
|||||||
resolve_var: None,
|
resolve_var: None,
|
||||||
|
|
||||||
print: Box::new(|_| {}),
|
print: Box::new(|_| {}),
|
||||||
debug: Box::new(|_, _| {}),
|
debug: Box::new(|_, _, _| {}),
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
||||||
optimization_level: if cfg!(feature = "no_optimize") {
|
optimization_level: if cfg!(feature = "no_optimize") {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Module that defines the extern API of [`Engine`].
|
//! Module that defines the extern API of [`Engine`].
|
||||||
|
|
||||||
use crate::dynamic::Variant;
|
use crate::dynamic::Variant;
|
||||||
use crate::engine::{EvalContext, Imports};
|
use crate::engine::{EvalContext, Imports, State};
|
||||||
use crate::fn_native::{FnCallArgs, SendSync};
|
use crate::fn_native::{FnCallArgs, SendSync};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
@ -1368,9 +1368,9 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let mut mods = self.global_sub_modules.clone();
|
let mods = &mut self.global_sub_modules.clone();
|
||||||
|
|
||||||
let result = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?;
|
let result = self.eval_ast_with_scope_raw(scope, mods, ast)?;
|
||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -1391,7 +1391,10 @@ impl Engine {
|
|||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
ast: &'a AST,
|
ast: &'a AST,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let state = &mut Default::default();
|
let state = &mut State {
|
||||||
|
source: ast.clone_source(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()])
|
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()])
|
||||||
}
|
}
|
||||||
/// 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).
|
||||||
@ -1451,9 +1454,12 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut mods = self.global_sub_modules.clone();
|
let mods = &mut self.global_sub_modules.clone();
|
||||||
let mut state = Default::default();
|
let state = &mut State {
|
||||||
self.eval_statements_raw(scope, &mut mods, &mut state, ast.statements(), &[ast.lib()])?;
|
source: ast.clone_source(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||||
@ -1809,20 +1815,22 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// // Override action of 'print' function
|
/// // Override action of 'print' function
|
||||||
/// let logger = result.clone();
|
/// let logger = result.clone();
|
||||||
/// engine.on_debug(move |s, pos| logger.write().unwrap().push_str(
|
/// engine.on_debug(move |s, src, pos| logger.write().unwrap().push_str(
|
||||||
/// &format!("{:?} > {}", pos, s)
|
/// &format!("{} @ {:?} > {}", src.unwrap_or("unknown"), pos, s)
|
||||||
/// ));
|
/// ));
|
||||||
///
|
///
|
||||||
/// engine.consume(r#"let x = "hello"; debug(x);"#)?;
|
/// let mut ast = engine.compile(r#"let x = "hello"; debug(x);"#)?;
|
||||||
|
/// ast.set_source(Some("world"));
|
||||||
|
/// engine.consume_ast(&ast)?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(*result.read().unwrap(), r#"1:18 > "hello""#);
|
/// assert_eq!(*result.read().unwrap(), r#"world @ 1:18 > "hello""#);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_debug(
|
pub fn on_debug(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&str, Position) + SendSync + 'static,
|
callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.debug = Box::new(callback);
|
self.debug = Box::new(callback);
|
||||||
self
|
self
|
||||||
|
@ -221,20 +221,17 @@ impl Engine {
|
|||||||
.into(),
|
.into(),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
KEYWORD_DEBUG => (
|
KEYWORD_DEBUG => {
|
||||||
(self.debug)(
|
let text = result.as_str().map_err(|typ| {
|
||||||
result.as_str().map_err(|typ| {
|
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
})?,
|
})?;
|
||||||
pos,
|
let source = state.source.as_ref().map(|s| s.as_str());
|
||||||
)
|
((self.debug)(text, source, pos).into(), false)
|
||||||
.into(),
|
}
|
||||||
false,
|
|
||||||
),
|
|
||||||
_ => (result, func.is_method()),
|
_ => (result, func.is_method()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -394,7 +391,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if !fn_def.mods.is_empty() {
|
if !fn_def.mods.is_empty() {
|
||||||
mods.extend(fn_def.mods.iter_raw());
|
mods.extend(fn_def.mods.iter_raw().map(|(n, m)| (n.clone(), m.clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
@ -531,14 +528,16 @@ impl Engine {
|
|||||||
|
|
||||||
// Script-like function found
|
// Script-like function found
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ if self.has_override(Some(mods), lib, 0, hash_script, pub_only) => {
|
_ if hash_script != 0
|
||||||
|
&& self.has_override(Some(mods), lib, 0, hash_script, pub_only) =>
|
||||||
|
{
|
||||||
// Get function
|
// Get function
|
||||||
let func = lib
|
let (func, mut source) = lib
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|&m| m.get_fn(hash_script, pub_only))
|
.find_map(|&m| m.get_fn(hash_script, pub_only).map(|f| (f, m.clone_id())))
|
||||||
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
||||||
.or_else(|| self.packages.get_fn(hash_script))
|
.or_else(|| self.packages.get_fn(hash_script).map(|f| (f, None)))
|
||||||
//.or_else(|| mods.get_fn(hash_script))
|
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.clone_id()))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if func.is_script() {
|
if func.is_script() {
|
||||||
@ -563,7 +562,10 @@ impl Engine {
|
|||||||
let result = if _is_method {
|
let result = if _is_method {
|
||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
let (first, rest) = args.split_first_mut().unwrap();
|
let (first, rest) = args.split_first_mut().unwrap();
|
||||||
self.call_script_fn(
|
|
||||||
|
mem::swap(&mut state.source, &mut source);
|
||||||
|
|
||||||
|
let result = self.call_script_fn(
|
||||||
scope,
|
scope,
|
||||||
mods,
|
mods,
|
||||||
state,
|
state,
|
||||||
@ -573,17 +575,27 @@ impl Engine {
|
|||||||
rest,
|
rest,
|
||||||
pos,
|
pos,
|
||||||
_level,
|
_level,
|
||||||
)?
|
);
|
||||||
|
|
||||||
|
// Restore the original source
|
||||||
|
state.source = source;
|
||||||
|
|
||||||
|
result?
|
||||||
} else {
|
} else {
|
||||||
// Normal call of script function
|
// Normal call of script function
|
||||||
// The first argument is a reference?
|
// The first argument is a reference?
|
||||||
let mut backup: ArgBackup = Default::default();
|
let mut backup: ArgBackup = Default::default();
|
||||||
backup.change_first_arg_to_copy(is_ref, args);
|
backup.change_first_arg_to_copy(is_ref, args);
|
||||||
|
|
||||||
|
mem::swap(&mut state.source, &mut source);
|
||||||
|
|
||||||
let result = self.call_script_fn(
|
let result = self.call_script_fn(
|
||||||
scope, mods, state, lib, &mut None, func, args, pos, _level,
|
scope, mods, state, lib, &mut None, func, args, pos, _level,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Restore the original source
|
||||||
|
state.source = source;
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
backup.restore_first_arg(args);
|
backup.restore_first_arg(args);
|
||||||
|
|
||||||
@ -678,6 +690,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Evaluate the AST
|
// Evaluate the AST
|
||||||
let mut new_state = State {
|
let mut new_state = State {
|
||||||
|
source: state.source.clone(),
|
||||||
operations: state.operations,
|
operations: state.operations,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@ -1162,9 +1175,17 @@ impl Engine {
|
|||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
let new_scope = &mut Default::default();
|
let new_scope = &mut Default::default();
|
||||||
let fn_def = f.get_fn_def().clone();
|
let fn_def = f.get_fn_def().clone();
|
||||||
self.call_script_fn(
|
|
||||||
|
let mut source = module.clone_id();
|
||||||
|
mem::swap(&mut state.source, &mut source);
|
||||||
|
|
||||||
|
let result = self.call_script_fn(
|
||||||
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
|
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
state.source = source;
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
Some(f) if f.is_plugin_fn() => f
|
Some(f) if f.is_plugin_fn() => f
|
||||||
.get_plugin_fn()
|
.get_plugin_fn()
|
||||||
|
@ -360,10 +360,10 @@ pub type OnPrintCallback = Box<dyn Fn(&str) + Send + Sync + 'static>;
|
|||||||
|
|
||||||
/// A standard callback function for debugging.
|
/// A standard callback function for debugging.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnDebugCallback = Box<dyn Fn(&str, Position) + 'static>;
|
pub type OnDebugCallback = Box<dyn Fn(&str, Option<&str>, Position) + 'static>;
|
||||||
/// A standard callback function for debugging.
|
/// A standard callback function for debugging.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnDebugCallback = Box<dyn Fn(&str, Position) + Send + Sync + 'static>;
|
pub type OnDebugCallback = Box<dyn Fn(&str, Option<&str>, Position) + Send + Sync + 'static>;
|
||||||
|
|
||||||
/// A standard callback function for variable access.
|
/// A standard callback function for variable access.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
|
@ -119,6 +119,8 @@ impl FuncInfo {
|
|||||||
/// Not available under the `no_module` feature.
|
/// Not available under the `no_module` feature.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
|
/// ID identifying the module.
|
||||||
|
id: Option<ImmutableString>,
|
||||||
/// Sub-modules.
|
/// Sub-modules.
|
||||||
modules: HashMap<ImmutableString, Shared<Module>>,
|
modules: HashMap<ImmutableString, Shared<Module>>,
|
||||||
/// Module variables.
|
/// Module variables.
|
||||||
@ -141,6 +143,7 @@ pub struct Module {
|
|||||||
impl Default for Module {
|
impl Default for Module {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id: None,
|
||||||
modules: Default::default(),
|
modules: Default::default(),
|
||||||
variables: Default::default(),
|
variables: Default::default(),
|
||||||
all_variables: Default::default(),
|
all_variables: Default::default(),
|
||||||
@ -157,7 +160,12 @@ impl fmt::Debug for Module {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Module(\n modules: {}\n vars: {}\n functions: {}\n)",
|
"Module({}\n modules: {}\n vars: {}\n functions: {}\n)",
|
||||||
|
if let Some(ref id) = self.id {
|
||||||
|
format!("id: {:?}", id)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
},
|
||||||
self.modules
|
self.modules
|
||||||
.keys()
|
.keys()
|
||||||
.map(|m| m.as_str())
|
.map(|m| m.as_str())
|
||||||
@ -220,6 +228,41 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the ID of the module, if any.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Module;
|
||||||
|
///
|
||||||
|
/// let mut module = Module::new();
|
||||||
|
/// module.set_id(Some("hello"));
|
||||||
|
/// assert_eq!(module.id(), Some("hello"));
|
||||||
|
/// ```
|
||||||
|
pub fn id(&self) -> Option<&str> {
|
||||||
|
self.id.as_ref().map(|s| s.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the ID of the module, if any.
|
||||||
|
pub(crate) fn clone_id(&self) -> Option<ImmutableString> {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the ID of the module.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Module;
|
||||||
|
///
|
||||||
|
/// let mut module = Module::new();
|
||||||
|
/// module.set_id(Some("hello"));
|
||||||
|
/// assert_eq!(module.id(), Some("hello"));
|
||||||
|
/// ```
|
||||||
|
pub fn set_id<S: Into<ImmutableString>>(&mut self, id: Option<S>) {
|
||||||
|
self.id = id.map(|s| s.into());
|
||||||
|
}
|
||||||
|
|
||||||
/// Is the module empty?
|
/// Is the module empty?
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -1338,8 +1381,12 @@ impl Module {
|
|||||||
/// the hash calculated by [`build_index`][Module::build_index].
|
/// the hash calculated by [`build_index`][Module::build_index].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
||||||
|
if hash_fn == 0 {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
self.all_functions.contains_key(&hash_fn)
|
self.all_functions.contains_key(&hash_fn)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a namespace-qualified function.
|
/// Get a namespace-qualified function.
|
||||||
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
||||||
@ -1348,8 +1395,12 @@ impl Module {
|
|||||||
/// the hash calculated by [`build_index`][Module::build_index].
|
/// the hash calculated by [`build_index`][Module::build_index].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
||||||
|
if hash_qualified_fn == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
self.all_functions.get(&hash_qualified_fn)
|
self.all_functions.get(&hash_qualified_fn)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Combine another module into this module.
|
/// Combine another module into this module.
|
||||||
/// The other module is consumed to merge into this module.
|
/// The other module is consumed to merge into this module.
|
||||||
@ -1679,6 +1730,7 @@ impl Module {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.set_id(ast.clone_source());
|
||||||
module.build_index();
|
module.build_index();
|
||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
|
@ -163,10 +163,11 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
_ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)),
|
_ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| {
|
let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos))
|
Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
m.set_id(Some(path));
|
||||||
module = Some(m.into());
|
module = Some(m.into());
|
||||||
module_ref = module.clone();
|
module_ref = module.clone();
|
||||||
};
|
};
|
||||||
|
@ -128,8 +128,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> Array {
|
|||||||
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
|
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
|
||||||
|
|
||||||
if let Some(mods) = ctx.mods {
|
if let Some(mods) = ctx.mods {
|
||||||
mods.iter()
|
mods.iter_raw()
|
||||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns, m.as_ref()));
|
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
list
|
list
|
||||||
|
@ -55,17 +55,20 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with(r#"AST { statements: [Block([Const(IdentX { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#));
|
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(IdentX { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#));
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with("AST { statements: [], functions: Module("));
|
assert!(
|
||||||
|
format!("{:?}", ast).starts_with("AST { source: None, statements: [], functions: Module(")
|
||||||
|
);
|
||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
|
|
||||||
let ast = engine.compile("abs(-42)")?;
|
let ast = engine.compile("abs(-42)")?;
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with(r"AST { statements: [Expr(IntegerConstant(42, 1:1))]"));
|
assert!(format!("{:?}", ast)
|
||||||
|
.starts_with(r"AST { source: None, statements: [Expr(IntegerConstant(42, 1:1))]"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -13,20 +13,28 @@ fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine
|
engine
|
||||||
.on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s)))
|
.on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s)))
|
||||||
.on_debug(move |s, pos| {
|
.on_debug(move |s, src, pos| {
|
||||||
log2.write()
|
log2.write().unwrap().push(format!(
|
||||||
.unwrap()
|
"DEBUG of {} at {:?}: {}",
|
||||||
.push(format!("DEBUG at {:?}: {}", pos, s))
|
src.unwrap_or("unknown"),
|
||||||
|
pos,
|
||||||
|
s
|
||||||
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Evaluate script
|
// Evaluate script
|
||||||
engine.eval::<()>("print(40 + 2)")?;
|
engine.consume("print(40 + 2)")?;
|
||||||
engine.eval::<()>(r#"let x = "hello!"; debug(x)"#)?;
|
let mut ast = engine.compile(r#"let x = "hello!"; debug(x)"#)?;
|
||||||
|
ast.set_source(Some("world"));
|
||||||
|
engine.consume_ast(&ast)?;
|
||||||
|
|
||||||
// 'logbook' captures all the 'print' and 'debug' output
|
// 'logbook' captures all the 'print' and 'debug' output
|
||||||
assert_eq!(logbook.read().unwrap().len(), 2);
|
assert_eq!(logbook.read().unwrap().len(), 2);
|
||||||
assert_eq!(logbook.read().unwrap()[0], "entry: 42");
|
assert_eq!(logbook.read().unwrap()[0], "entry: 42");
|
||||||
assert_eq!(logbook.read().unwrap()[1], r#"DEBUG at 1:19: "hello!""#);
|
assert_eq!(
|
||||||
|
logbook.read().unwrap()[1],
|
||||||
|
r#"DEBUG of world at 1:19: "hello!""#
|
||||||
|
);
|
||||||
|
|
||||||
for entry in logbook.read().unwrap().iter() {
|
for entry in logbook.read().unwrap().iter() {
|
||||||
println!("{}", entry);
|
println!("{}", entry);
|
||||||
|
Loading…
Reference in New Issue
Block a user