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`.
|
||||
* 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`.
|
||||
* 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
|
||||
// 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
|
||||
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)));
|
||||
|
||||
let log = logbook.clone();
|
||||
engine.on_debug(move |s, pos| log.write().unwrap().push(
|
||||
format!("DEBUG at {:?}: {}", pos, s)
|
||||
engine.on_debug(move |s, src, pos| log.write().unwrap().push(
|
||||
format!("DEBUG of {} at {:?}: {}", src.unwrap_or("unknown"), pos, s)
|
||||
));
|
||||
|
||||
// Evaluate script
|
||||
@ -49,3 +49,24 @@ for entry in logbook.read().unwrap().iter() {
|
||||
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`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AST {
|
||||
/// Source of the [`AST`].
|
||||
source: Option<ImmutableString>,
|
||||
/// Global statements.
|
||||
statements: Vec<Stmt>,
|
||||
/// Script-defined functions.
|
||||
@ -172,6 +174,7 @@ pub struct AST {
|
||||
impl Default for AST {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
source: None,
|
||||
statements: Vec::with_capacity(16),
|
||||
functions: Default::default(),
|
||||
}
|
||||
@ -186,10 +189,36 @@ impl AST {
|
||||
functions: impl Into<Shared<Module>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
source: None,
|
||||
statements: statements.into_iter().collect(),
|
||||
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.
|
||||
#[cfg(not(feature = "internals"))]
|
||||
#[inline(always)]
|
||||
@ -253,6 +282,7 @@ impl AST {
|
||||
let mut functions: Module = Default::default();
|
||||
functions.merge_filtered(&self.functions, &mut filter);
|
||||
Self {
|
||||
source: self.source.clone(),
|
||||
statements: Default::default(),
|
||||
functions: functions.into(),
|
||||
}
|
||||
@ -262,6 +292,7 @@ impl AST {
|
||||
#[inline(always)]
|
||||
pub fn clone_statements_only(&self) -> Self {
|
||||
Self {
|
||||
source: self.source.clone(),
|
||||
statements: self.statements.clone(),
|
||||
functions: Default::default(),
|
||||
}
|
||||
@ -432,6 +463,7 @@ impl AST {
|
||||
let Self {
|
||||
statements,
|
||||
functions,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let ast = match (statements.is_empty(), other.statements.is_empty()) {
|
||||
@ -445,11 +477,21 @@ impl AST {
|
||||
(true, true) => vec![],
|
||||
};
|
||||
|
||||
let source = if other.source.is_some() {
|
||||
other.source.clone()
|
||||
} else {
|
||||
self.source.clone()
|
||||
};
|
||||
|
||||
let mut functions = functions.as_ref().clone();
|
||||
functions.merge_filtered(&other.functions, &mut filter);
|
||||
|
||||
if let Some(source) = source {
|
||||
Self::new_with_source(ast, functions, source)
|
||||
} else {
|
||||
Self::new(ast, functions)
|
||||
}
|
||||
}
|
||||
/// 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_.
|
||||
|
@ -100,19 +100,21 @@ impl Imports {
|
||||
}
|
||||
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
||||
#[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| {
|
||||
lib.iter()
|
||||
.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.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn iter_raw<'a>(
|
||||
&'a self,
|
||||
) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
|
||||
self.0.iter().flat_map(|lib| lib.iter().rev().cloned())
|
||||
) -> impl Iterator<Item = (&'a ImmutableString, &'a Shared<Module>)> + 'a {
|
||||
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.
|
||||
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.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
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.
|
||||
/// 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.
|
||||
@ -708,11 +712,15 @@ fn default_print(_s: &str) {
|
||||
|
||||
/// Debug to stdout
|
||||
#[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(target_arch = "wasm32"))]
|
||||
if let Some(source) = _source {
|
||||
println!("{} @ {:?} | {}", source, _pos, _s);
|
||||
} else {
|
||||
println!("{:?} | {}", _pos, _s);
|
||||
}
|
||||
}
|
||||
|
||||
/// Search for a module within an imports stack.
|
||||
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||
@ -828,7 +836,7 @@ impl Engine {
|
||||
resolve_var: None,
|
||||
|
||||
print: Box::new(|_| {}),
|
||||
debug: Box::new(|_, _| {}),
|
||||
debug: Box::new(|_, _, _| {}),
|
||||
progress: None,
|
||||
|
||||
optimization_level: if cfg!(feature = "no_optimize") {
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Module that defines the extern API of [`Engine`].
|
||||
|
||||
use crate::dynamic::Variant;
|
||||
use crate::engine::{EvalContext, Imports};
|
||||
use crate::engine::{EvalContext, Imports, State};
|
||||
use crate::fn_native::{FnCallArgs, SendSync};
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::stdlib::{
|
||||
@ -1368,9 +1368,9 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> 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());
|
||||
|
||||
@ -1391,7 +1391,10 @@ impl Engine {
|
||||
mods: &mut Imports,
|
||||
ast: &'a AST,
|
||||
) -> 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()])
|
||||
}
|
||||
/// Evaluate a file, but throw away the result and only return error (if any).
|
||||
@ -1451,9 +1454,12 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let mut mods = self.global_sub_modules.clone();
|
||||
let mut state = Default::default();
|
||||
self.eval_statements_raw(scope, &mut mods, &mut state, ast.statements(), &[ast.lib()])?;
|
||||
let mods = &mut self.global_sub_modules.clone();
|
||||
let state = &mut State {
|
||||
source: ast.clone_source(),
|
||||
..Default::default()
|
||||
};
|
||||
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()])?;
|
||||
Ok(())
|
||||
}
|
||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||
@ -1809,20 +1815,22 @@ impl Engine {
|
||||
///
|
||||
/// // Override action of 'print' function
|
||||
/// let logger = result.clone();
|
||||
/// engine.on_debug(move |s, pos| logger.write().unwrap().push_str(
|
||||
/// &format!("{:?} > {}", pos, s)
|
||||
/// engine.on_debug(move |s, src, pos| logger.write().unwrap().push_str(
|
||||
/// &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(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn on_debug(
|
||||
&mut self,
|
||||
callback: impl Fn(&str, Position) + SendSync + 'static,
|
||||
callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.debug = Box::new(callback);
|
||||
self
|
||||
|
@ -221,20 +221,17 @@ impl Engine {
|
||||
.into(),
|
||||
false,
|
||||
),
|
||||
KEYWORD_DEBUG => (
|
||||
(self.debug)(
|
||||
result.as_str().map_err(|typ| {
|
||||
KEYWORD_DEBUG => {
|
||||
let text = result.as_str().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
pos,
|
||||
)
|
||||
})?,
|
||||
pos,
|
||||
)
|
||||
.into(),
|
||||
false,
|
||||
),
|
||||
})?;
|
||||
let source = state.source.as_ref().map(|s| s.as_str());
|
||||
((self.debug)(text, source, pos).into(), false)
|
||||
}
|
||||
_ => (result, func.is_method()),
|
||||
});
|
||||
}
|
||||
@ -394,7 +391,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
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
|
||||
@ -531,14 +528,16 @@ impl Engine {
|
||||
|
||||
// Script-like function found
|
||||
#[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
|
||||
let func = lib
|
||||
let (func, mut source) = lib
|
||||
.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.packages.get_fn(hash_script))
|
||||
//.or_else(|| mods.get_fn(hash_script))
|
||||
.or_else(|| self.packages.get_fn(hash_script).map(|f| (f, None)))
|
||||
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.clone_id()))))
|
||||
.unwrap();
|
||||
|
||||
if func.is_script() {
|
||||
@ -563,7 +562,10 @@ impl Engine {
|
||||
let result = if _is_method {
|
||||
// Method call of script function - map first argument to `this`
|
||||
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,
|
||||
mods,
|
||||
state,
|
||||
@ -573,17 +575,27 @@ impl Engine {
|
||||
rest,
|
||||
pos,
|
||||
_level,
|
||||
)?
|
||||
);
|
||||
|
||||
// Restore the original source
|
||||
state.source = source;
|
||||
|
||||
result?
|
||||
} else {
|
||||
// Normal call of script function
|
||||
// The first argument is a reference?
|
||||
let mut backup: ArgBackup = Default::default();
|
||||
backup.change_first_arg_to_copy(is_ref, args);
|
||||
|
||||
mem::swap(&mut state.source, &mut source);
|
||||
|
||||
let result = self.call_script_fn(
|
||||
scope, mods, state, lib, &mut None, func, args, pos, _level,
|
||||
);
|
||||
|
||||
// Restore the original source
|
||||
state.source = source;
|
||||
|
||||
// Restore the original reference
|
||||
backup.restore_first_arg(args);
|
||||
|
||||
@ -678,6 +690,7 @@ impl Engine {
|
||||
|
||||
// Evaluate the AST
|
||||
let mut new_state = State {
|
||||
source: state.source.clone(),
|
||||
operations: state.operations,
|
||||
..Default::default()
|
||||
};
|
||||
@ -1162,9 +1175,17 @@ impl Engine {
|
||||
let args = args.as_mut();
|
||||
let new_scope = &mut Default::default();
|
||||
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,
|
||||
)
|
||||
);
|
||||
|
||||
state.source = source;
|
||||
|
||||
result
|
||||
}
|
||||
Some(f) if f.is_plugin_fn() => f
|
||||
.get_plugin_fn()
|
||||
|
@ -360,10 +360,10 @@ pub type OnPrintCallback = Box<dyn Fn(&str) + Send + Sync + 'static>;
|
||||
|
||||
/// A standard callback function for debugging.
|
||||
#[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.
|
||||
#[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.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
|
@ -119,6 +119,8 @@ impl FuncInfo {
|
||||
/// Not available under the `no_module` feature.
|
||||
#[derive(Clone)]
|
||||
pub struct Module {
|
||||
/// ID identifying the module.
|
||||
id: Option<ImmutableString>,
|
||||
/// Sub-modules.
|
||||
modules: HashMap<ImmutableString, Shared<Module>>,
|
||||
/// Module variables.
|
||||
@ -141,6 +143,7 @@ pub struct Module {
|
||||
impl Default for Module {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
modules: Default::default(),
|
||||
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 {
|
||||
write!(
|
||||
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
|
||||
.keys()
|
||||
.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?
|
||||
///
|
||||
/// # Example
|
||||
@ -1338,8 +1381,12 @@ impl Module {
|
||||
/// the hash calculated by [`build_index`][Module::build_index].
|
||||
#[inline]
|
||||
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
||||
if hash_fn == 0 {
|
||||
false
|
||||
} else {
|
||||
self.all_functions.contains_key(&hash_fn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a namespace-qualified function.
|
||||
/// 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].
|
||||
#[inline(always)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Combine another module 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();
|
||||
|
||||
Ok(module)
|
||||
|
@ -163,10 +163,11 @@ impl ModuleResolver for FileModuleResolver {
|
||||
_ => 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))
|
||||
})?;
|
||||
|
||||
m.set_id(Some(path));
|
||||
module = Some(m.into());
|
||||
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()));
|
||||
|
||||
if let Some(mods) = ctx.mods {
|
||||
mods.iter()
|
||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns, m.as_ref()));
|
||||
mods.iter_raw()
|
||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
|
||||
}
|
||||
|
||||
list
|
||||
|
@ -55,17 +55,20 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
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 }")?;
|
||||
|
||||
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);
|
||||
|
||||
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(())
|
||||
}
|
||||
|
@ -13,20 +13,28 @@ fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
engine
|
||||
.on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s)))
|
||||
.on_debug(move |s, pos| {
|
||||
log2.write()
|
||||
.unwrap()
|
||||
.push(format!("DEBUG at {:?}: {}", pos, s))
|
||||
.on_debug(move |s, src, pos| {
|
||||
log2.write().unwrap().push(format!(
|
||||
"DEBUG of {} at {:?}: {}",
|
||||
src.unwrap_or("unknown"),
|
||||
pos,
|
||||
s
|
||||
))
|
||||
});
|
||||
|
||||
// Evaluate script
|
||||
engine.eval::<()>("print(40 + 2)")?;
|
||||
engine.eval::<()>(r#"let x = "hello!"; debug(x)"#)?;
|
||||
engine.consume("print(40 + 2)")?;
|
||||
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
|
||||
assert_eq!(logbook.read().unwrap().len(), 2);
|
||||
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() {
|
||||
println!("{}", entry);
|
||||
|
Loading…
Reference in New Issue
Block a user