diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0de82a1d..42f989ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -576,7 +576,7 @@ Breaking changes
New features
------------
-* Line continuation (via `\`) and multi-line literal strings (wrapped with \`
) support are added.
+* Line continuation (via `\`) and multi-line literal strings (wrapped with `` ` ``) support are added.
* Rhai scripts can now start with a shebang `#!` which is ignored.
Enhancements
diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs
index e501c68d..ca41552d 100644
--- a/src/api/call_fn.rs
+++ b/src/api/call_fn.rs
@@ -190,8 +190,8 @@ impl Engine {
&mut this_ptr,
fn_def,
&mut args,
- Position::NONE,
rewind_scope,
+ Position::NONE,
0,
);
diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs
index 16daa9e5..8cd99b15 100644
--- a/src/eval/chaining.rs
+++ b/src/eval/chaining.rs
@@ -191,8 +191,8 @@ impl Engine {
let fn_name = crate::engine::FN_IDX_SET;
if let Err(err) = self.exec_fn_call(
- global, state, lib, fn_name, hash_set, args, is_ref_mut, true,
- root_pos, None, level,
+ None, global, state, lib, fn_name, hash_set, args, is_ref_mut,
+ true, root_pos, level,
) {
// Just ignore if there is no index setter
if !matches!(*err, ERR::ErrorFunctionNotFound(_, _)) {
@@ -244,8 +244,8 @@ impl Engine {
let fn_name = crate::engine::FN_IDX_SET;
self.exec_fn_call(
- global, state, lib, fn_name, hash_set, args, is_ref_mut, true,
- root_pos, None, level,
+ None, global, state, lib, fn_name, hash_set, args, is_ref_mut,
+ true, root_pos, level,
)?;
}
@@ -351,8 +351,8 @@ impl Engine {
let args = &mut [target.as_mut()];
let (mut orig_val, _) = self
.exec_fn_call(
- global, state, lib, getter, hash, args, is_ref_mut, true, *pos,
- None, level,
+ None, global, state, lib, getter, hash, args, is_ref_mut, true,
+ *pos, level,
)
.or_else(|err| match *err {
// Try an indexer if property does not exist
@@ -392,7 +392,7 @@ impl Engine {
let hash = crate::ast::FnCallHashes::from_native(*hash_set);
let args = &mut [target.as_mut(), &mut new_val];
self.exec_fn_call(
- global, state, lib, setter, hash, args, is_ref_mut, true, *pos, None,
+ None, global, state, lib, setter, hash, args, is_ref_mut, true, *pos,
level,
)
.or_else(|err| match *err {
@@ -405,8 +405,8 @@ impl Engine {
let pos = Position::NONE;
self.exec_fn_call(
- global, state, lib, fn_name, hash_set, args, is_ref_mut, true,
- pos, None, level,
+ None, global, state, lib, fn_name, hash_set, args, is_ref_mut,
+ true, pos, level,
)
.map_err(
|idx_err| match *idx_err {
@@ -429,7 +429,7 @@ impl Engine {
let hash = crate::ast::FnCallHashes::from_native(*hash_get);
let args = &mut [target.as_mut()];
self.exec_fn_call(
- global, state, lib, getter, hash, args, is_ref_mut, true, *pos, None,
+ None, global, state, lib, getter, hash, args, is_ref_mut, true, *pos,
level,
)
.map_or_else(
@@ -537,8 +537,8 @@ impl Engine {
// Assume getters are always pure
let (mut val, _) = self
.exec_fn_call(
- global, state, lib, getter, hash_get, args, is_ref_mut,
- true, pos, None, level,
+ None, global, state, lib, getter, hash_get, args,
+ is_ref_mut, true, pos, level,
)
.or_else(|err| match *err {
// Try an indexer if property does not exist
@@ -585,8 +585,8 @@ impl Engine {
let mut arg_values = [target.as_mut(), val];
let args = &mut arg_values;
self.exec_fn_call(
- global, state, lib, setter, hash_set, args, is_ref_mut,
- true, pos, None, level,
+ None, global, state, lib, setter, hash_set, args,
+ is_ref_mut, true, pos, level,
)
.or_else(
|err| match *err {
@@ -600,8 +600,8 @@ impl Engine {
global.hash_idx_set(),
);
self.exec_fn_call(
- global, state, lib, fn_name, hash_set, args,
- is_ref_mut, true, pos, None, level,
+ None, global, state, lib, fn_name, hash_set,
+ args, is_ref_mut, true, pos, level,
)
.or_else(|idx_err| match *idx_err {
ERR::ErrorIndexingType(_, _) => {
@@ -767,7 +767,7 @@ impl Engine {
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|(mut values, mut pos), expr| -> RhaiResultOf<_> {
let (value, arg_pos) = self.get_arg_value(
- scope, global, state, lib, this_ptr, level, expr, constants,
+ scope, global, state, lib, this_ptr, expr, constants, level,
)?;
if values.is_empty() {
pos = arg_pos;
@@ -813,7 +813,7 @@ impl Engine {
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|(mut values, mut pos), expr| -> RhaiResultOf<_> {
let (value, arg_pos) = self.get_arg_value(
- scope, global, state, lib, this_ptr, level, expr, constants,
+ scope, global, state, lib, this_ptr, expr, constants, level,
)?;
if values.is_empty() {
pos = arg_pos
@@ -1079,7 +1079,7 @@ impl Engine {
let pos = Position::NONE;
self.exec_fn_call(
- global, state, lib, fn_name, hash_get, args, true, true, pos, None, level,
+ None, global, state, lib, fn_name, hash_get, args, true, true, pos, level,
)
.map(|(v, _)| v.into())
}
diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs
index d8a29d9e..f6889c17 100644
--- a/src/eval/debugger.rs
+++ b/src/eval/debugger.rs
@@ -51,6 +51,13 @@ pub enum DebuggerCommand {
FunctionExit,
}
+impl Default for DebuggerCommand {
+ #[inline(always)]
+ fn default() -> Self {
+ Self::Continue
+ }
+}
+
/// The debugger status.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum DebuggerStatus {
@@ -60,6 +67,19 @@ pub enum DebuggerStatus {
FunctionExit(usize),
}
+impl Default for DebuggerStatus {
+ #[inline(always)]
+ fn default() -> Self {
+ Self::CONTINUE
+ }
+}
+
+impl DebuggerStatus {
+ pub const CONTINUE: Self = Self::Next(false, false);
+ pub const STEP: Self = Self::Next(true, true);
+ pub const NEXT: Self = Self::Next(true, false);
+}
+
/// A event that triggers the debugger.
#[derive(Debug, Clone, Copy)]
pub enum DebuggerEvent<'a> {
@@ -231,7 +251,7 @@ impl fmt::Display for CallStackFrame {
#[derive(Debug, Clone, Hash)]
pub struct Debugger {
/// The current status command.
- status: DebuggerStatus,
+ pub(crate) status: DebuggerStatus,
/// The current state.
state: Dynamic,
/// The current set of break-points.
@@ -247,9 +267,9 @@ impl Debugger {
pub fn new(engine: &Engine) -> Self {
Self {
status: if engine.debugger.is_some() {
- DebuggerStatus::Next(true, true)
+ DebuggerStatus::STEP
} else {
- DebuggerStatus::Next(false, false)
+ DebuggerStatus::CONTINUE
},
state: if let Some((ref init, _)) = engine.debugger {
init()
@@ -299,12 +319,6 @@ impl Debugger {
pos,
});
}
- /// Get the current status of this [`Debugger`].
- #[inline(always)]
- #[must_use]
- pub(crate) fn status(&self) -> DebuggerStatus {
- self.status
- }
/// Set the status of this [`Debugger`].
#[inline(always)]
pub(crate) fn reset_status(&mut self, status: Option) {
@@ -476,19 +490,19 @@ impl Engine {
match command {
DebuggerCommand::Continue => {
- global.debugger.status = DebuggerStatus::Next(false, false);
+ global.debugger.status = DebuggerStatus::CONTINUE;
Ok(None)
}
DebuggerCommand::Next => {
- global.debugger.status = DebuggerStatus::Next(false, false);
- Ok(Some(DebuggerStatus::Next(true, false)))
+ global.debugger.status = DebuggerStatus::CONTINUE;
+ Ok(Some(DebuggerStatus::NEXT))
}
DebuggerCommand::StepOver => {
- global.debugger.status = DebuggerStatus::Next(false, false);
- Ok(Some(DebuggerStatus::Next(true, true)))
+ global.debugger.status = DebuggerStatus::CONTINUE;
+ Ok(Some(DebuggerStatus::STEP))
}
DebuggerCommand::StepInto => {
- global.debugger.status = DebuggerStatus::Next(true, true);
+ global.debugger.status = DebuggerStatus::STEP;
Ok(None)
}
DebuggerCommand::FunctionExit => {
@@ -499,6 +513,7 @@ impl Engine {
| ASTNode::Stmt(Stmt::Expr(Expr::FnCall(_, _))) => context.call_level() + 1,
_ => context.call_level(),
};
+ println!("Set FunctionExit to {}", level);
global.debugger.status = DebuggerStatus::FunctionExit(level);
Ok(None)
}
diff --git a/src/eval/expr.rs b/src/eval/expr.rs
index 3b6015b7..79f27d92 100644
--- a/src/eval/expr.rs
+++ b/src/eval/expr.rs
@@ -236,8 +236,8 @@ impl Engine {
);
self.make_function_call(
- scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, pos,
- *capture, level,
+ scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes,
+ *capture, pos, level,
)
}
diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs
index 2adb7829..bb4645da 100644
--- a/src/eval/stmt.rs
+++ b/src/eval/stmt.rs
@@ -153,6 +153,7 @@ impl Engine {
let hash = hash_op_assign;
let args = &mut [lhs_ptr_inner, &mut new_val];
+ let level = level + 1;
match self.call_native_fn(
global, state, lib, op_assign, hash, args, true, true, op_pos, level,
diff --git a/src/func/call.rs b/src/func/call.rs
index cd0d9614..240b050c 100644
--- a/src/func/call.rs
+++ b/src/func/call.rs
@@ -427,7 +427,7 @@ impl Engine {
#[cfg(feature = "debugging")]
if self.debugger.is_some() {
- match global.debugger.status() {
+ match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => {
let scope = &mut &mut Scope::new();
let node = crate::ast::Stmt::Noop(pos);
@@ -591,6 +591,7 @@ impl Engine {
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
pub(crate) fn exec_fn_call(
&self,
+ scope: Option<&mut Scope>,
global: &mut GlobalRuntimeState,
state: &mut EvalState,
lib: &[&Module],
@@ -600,7 +601,6 @@ impl Engine {
is_ref_mut: bool,
is_method_call: bool,
pos: Position,
- scope: Option<&mut Scope>,
level: usize,
) -> RhaiResultOf<(Dynamic, bool)> {
fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> {
@@ -711,8 +711,8 @@ impl Engine {
&mut Some(*first_arg),
func,
rest_args,
- pos,
true,
+ pos,
level,
);
@@ -730,7 +730,7 @@ impl Engine {
}
let result = self.call_script_fn(
- scope, global, state, lib, &mut None, func, args, pos, true, level,
+ scope, global, state, lib, &mut None, func, args, true, pos, level,
);
// Restore the original reference
@@ -812,7 +812,7 @@ impl Engine {
// Map it to name(args) in function-call style
self.exec_fn_call(
- global, state, lib, fn_name, new_hash, &mut args, false, false, pos, None,
+ None, global, state, lib, fn_name, new_hash, &mut args, false, false, pos,
level,
)
}
@@ -852,7 +852,7 @@ impl Engine {
// Map it to name(args) in function-call style
self.exec_fn_call(
- global, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, None,
+ None, global, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos,
level,
)
}
@@ -924,7 +924,7 @@ impl Engine {
args.extend(call_args.iter_mut());
self.exec_fn_call(
- global, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, None,
+ None, global, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos,
level,
)
}
@@ -949,9 +949,9 @@ impl Engine {
state: &mut EvalState,
lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>,
- level: usize,
arg_expr: &Expr,
constants: &[Dynamic],
+ level: usize,
) -> RhaiResultOf<(Dynamic, Position)> {
Ok((
if let Expr::Stack(slot, _) = arg_expr {
@@ -967,7 +967,26 @@ impl Engine {
}
value
} else {
- self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level)?
+ // Do not match function exit for arguments
+ #[cfg(feature = "debugging")]
+ let reset_debugger = match global.debugger.status {
+ crate::eval::DebuggerStatus::FunctionExit(_) => {
+ Some(std::mem::take(&mut global.debugger.status))
+ }
+ _ => None,
+ };
+
+ let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level);
+
+ // Restore function exit if status is not active
+ #[cfg(feature = "debugging")]
+ if self.debugger.is_some()
+ && global.debugger.status == crate::eval::DebuggerStatus::CONTINUE
+ {
+ global.debugger.reset_status(reset_debugger);
+ }
+
+ result?
},
arg_expr.position(),
))
@@ -986,8 +1005,8 @@ impl Engine {
args_expr: &[Expr],
constants: &[Dynamic],
hashes: FnCallHashes,
- pos: Position,
capture_scope: bool,
+ pos: Position,
level: usize,
) -> RhaiResult {
let mut first_arg = first_arg;
@@ -1003,7 +1022,7 @@ impl Engine {
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
let arg = first_arg.unwrap();
let (arg_value, arg_pos) =
- self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
+ self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
if !arg_value.is::() {
return Err(self.make_type_mismatch_err::(
@@ -1038,7 +1057,7 @@ impl Engine {
KEYWORD_FN_PTR if total_args == 1 => {
let arg = first_arg.unwrap();
let (arg_value, arg_pos) =
- self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
+ self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
// Fn - only in function call style
return arg_value
@@ -1053,7 +1072,7 @@ impl Engine {
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
let first = first_arg.unwrap();
let (arg_value, arg_pos) = self
- .get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?;
+ .get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?;
if !arg_value.is::() {
return Err(self.make_type_mismatch_err::(
@@ -1070,7 +1089,7 @@ impl Engine {
.iter()
.try_fold(fn_curry, |mut curried, expr| -> RhaiResultOf<_> {
let (value, _) = self.get_arg_value(
- scope, global, state, lib, this_ptr, level, expr, constants,
+ scope, global, state, lib, this_ptr, expr, constants, level,
)?;
curried.push(value);
Ok(curried)
@@ -1084,7 +1103,7 @@ impl Engine {
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
let arg = first_arg.unwrap();
let (arg_value, _) =
- self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
+ self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
return Ok(arg_value.is_shared().into());
}
@@ -1093,14 +1112,14 @@ impl Engine {
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
let first = first_arg.unwrap();
let (arg_value, arg_pos) = self
- .get_arg_value(scope, global, state, lib, this_ptr, level, first, constants)?;
+ .get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?;
let fn_name = arg_value
.into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?;
let (arg_value, arg_pos) = self.get_arg_value(
- scope, global, state, lib, this_ptr, level, &a_expr[0], constants,
+ scope, global, state, lib, this_ptr, &a_expr[0], constants, level,
)?;
let num_params = arg_value
@@ -1120,7 +1139,7 @@ impl Engine {
KEYWORD_IS_DEF_VAR if total_args == 1 => {
let arg = first_arg.unwrap();
let (arg_value, arg_pos) =
- self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
+ self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
let var_name = arg_value
.into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?;
@@ -1133,7 +1152,7 @@ impl Engine {
let orig_scope_len = scope.len();
let arg = first_arg.unwrap();
let (arg_value, pos) =
- self.get_arg_value(scope, global, state, lib, this_ptr, level, arg, constants)?;
+ self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
let script = &arg_value
.into_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::(typ, pos))?;
@@ -1182,7 +1201,7 @@ impl Engine {
.map(|&v| v)
.chain(a_expr.iter())
.try_for_each(|expr| {
- self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
+ self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten()))
})?;
args.extend(curry.iter_mut());
@@ -1193,7 +1212,7 @@ impl Engine {
return self
.exec_fn_call(
- global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, scope,
+ scope, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos,
level,
)
.map(|(v, _)| v);
@@ -1216,7 +1235,7 @@ impl Engine {
// func(x, ...) -> x.func(...)
a_expr.iter().try_for_each(|expr| {
- self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
+ self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten()))
})?;
@@ -1252,7 +1271,7 @@ impl Engine {
.chain(a_expr.iter())
.try_for_each(|expr| {
self.get_arg_value(
- scope, global, state, lib, this_ptr, level, expr, constants,
+ scope, global, state, lib, this_ptr, expr, constants, level,
)
.map(|(value, _)| arg_values.push(value.flatten()))
})?;
@@ -1262,7 +1281,7 @@ impl Engine {
}
self.exec_fn_call(
- global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, None, level,
+ None, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, level,
)
.map(|(v, _)| v)
}
@@ -1305,7 +1324,7 @@ impl Engine {
arg_values.push(Dynamic::UNIT);
args_expr.iter().skip(1).try_for_each(|expr| {
- self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
+ self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten()))
})?;
@@ -1335,7 +1354,7 @@ impl Engine {
} else {
// func(..., ...) or func(mod::x, ...)
args_expr.iter().try_for_each(|expr| {
- self.get_arg_value(scope, global, state, lib, this_ptr, level, expr, constants)
+ self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten()))
})?;
args.extend(arg_values.iter_mut());
@@ -1385,7 +1404,7 @@ impl Engine {
mem::swap(&mut global.source, &mut source);
let result = self.call_script_fn(
- new_scope, global, state, lib, &mut None, fn_def, &mut args, pos, true,
+ new_scope, global, state, lib, &mut None, fn_def, &mut args, true, pos,
level,
);
diff --git a/src/func/native.rs b/src/func/native.rs
index c5787386..43de5bcb 100644
--- a/src/func/native.rs
+++ b/src/func/native.rs
@@ -321,6 +321,7 @@ impl<'a> NativeCallContext<'a> {
self.engine()
.exec_fn_call(
+ None,
&mut global,
&mut state,
self.lib,
@@ -330,7 +331,6 @@ impl<'a> NativeCallContext<'a> {
is_ref_mut,
is_method_call,
Position::NONE,
- None,
self.level + 1,
)
.map(|(r, _)| r)
diff --git a/src/func/script.rs b/src/func/script.rs
index 9c10e76e..66132cc3 100644
--- a/src/func/script.rs
+++ b/src/func/script.rs
@@ -31,8 +31,8 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>,
fn_def: &ScriptFnDef,
args: &mut FnCallArgs,
- pos: Position,
rewind_scope: bool,
+ pos: Position,
level: usize,
) -> RhaiResult {
#[inline(never)]
@@ -73,6 +73,11 @@ impl Engine {
return Err(ERR::ErrorStackOverflow(pos).into());
}
+ #[cfg(feature = "debugging")]
+ if self.debugger.is_none() && fn_def.body.is_empty() {
+ return Ok(Dynamic::UNIT);
+ }
+ #[cfg(not(feature = "debugging"))]
if fn_def.body.is_empty() {
return Ok(Dynamic::UNIT);
}
@@ -136,6 +141,13 @@ impl Engine {
(lib, None)
};
+ #[cfg(feature = "debugging")]
+ if self.debugger.is_some() {
+ println!("Level = {}", level);
+ let node = crate::ast::Stmt::Noop(fn_def.body.position());
+ self.run_debugger(scope, global, state, lib, this_ptr, &node, level)?;
+ }
+
// Evaluate the function
let mut _result = self
.eval_stmt_block(
@@ -172,7 +184,7 @@ impl Engine {
#[cfg(feature = "debugging")]
if self.debugger.is_some() {
- match global.debugger.status() {
+ match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) if n >= level => {
let node = crate::ast::Stmt::Noop(pos);
let node = (&node).into();