Add context source to on_debug.

This commit is contained in:
Stephen Chung 2020-12-21 22:04:46 +08:00
parent 2955a4ab64
commit 5ac83f0f46
12 changed files with 232 additions and 68 deletions

View File

@ -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.

View File

@ -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_.

View File

@ -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_.

View File

@ -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") {

View File

@ -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

View File

@ -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()

View File

@ -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"))]

View File

@ -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)

View File

@ -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();
}; };

View File

@ -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

View File

@ -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(())
} }

View File

@ -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);