Add breaking at line.

This commit is contained in:
Stephen Chung 2022-01-25 17:29:34 +08:00
parent 9b9cf99a8e
commit efddc7e91d
3 changed files with 185 additions and 42 deletions

View File

@ -77,9 +77,12 @@ fn print_debug_help() {
println!("node => print the current AST node"); println!("node => print the current AST node");
println!("backtrace => print the current call-stack"); println!("backtrace => print the current call-stack");
println!("breakpoints => print all break-points"); println!("breakpoints => print all break-points");
println!("delete <breakpoint#> => delete a break-point"); println!("enable <bp#> => enable a break-point");
println!("disable <bp#> => disable a break-point");
println!("delete <bp#> => delete a break-point");
println!("clear => delete all break-points"); println!("clear => delete all break-points");
println!("break => set a new break-point at the current position"); println!("break => set a new break-point at the current position");
println!("break <line#> => set a new break-point at a line number");
println!("break <func> => set a new break-point for a function call"); println!("break <func> => set a new break-point for a function call");
println!( println!(
"break <func> <#args> => set a new break-point for a function call with #args arguments" "break <func> <#args> => set a new break-point for a function call with #args arguments"
@ -252,6 +255,52 @@ fn main() {
} }
_ => println!("[{}]\n{}", i + 1, bp), _ => println!("[{}]\n{}", i + 1, bp),
}), }),
["enable", n, ..] => {
if let Ok(n) = n.parse::<usize>() {
let range = 1..=context
.global_runtime_state_mut()
.debugger
.break_points()
.len();
if range.contains(&n) {
context
.global_runtime_state_mut()
.debugger
.break_points_mut()
.get_mut(n - 1)
.unwrap()
.enable(true);
println!("Break-point #{} enabled.", n)
} else {
eprintln!("Invalid break-point: {}", n);
}
} else {
eprintln!("Invalid break-point: '{}'", n);
}
}
["disable", n, ..] => {
if let Ok(n) = n.parse::<usize>() {
let range = 1..=context
.global_runtime_state_mut()
.debugger
.break_points()
.len();
if range.contains(&n) {
context
.global_runtime_state_mut()
.debugger
.break_points_mut()
.get_mut(n - 1)
.unwrap()
.enable(false);
println!("Break-point #{} disabled.", n)
} else {
eprintln!("Invalid break-point: {}", n);
}
} else {
eprintln!("Invalid break-point: '{}'", n);
}
}
["delete", n, ..] => { ["delete", n, ..] => {
if let Ok(n) = n.parse::<usize>() { if let Ok(n) = n.parse::<usize>() {
let range = 1..=context let range = 1..=context
@ -278,6 +327,7 @@ fn main() {
let bp = rhai::debugger::BreakPoint::AtFunctionCall { let bp = rhai::debugger::BreakPoint::AtFunctionCall {
name: fn_name.trim().into(), name: fn_name.trim().into(),
args, args,
enabled: true,
}; };
println!("Break-point added for {}", bp); println!("Break-point added for {}", bp);
context context
@ -289,21 +339,44 @@ fn main() {
eprintln!("Invalid number of arguments: '{}'", args); eprintln!("Invalid number of arguments: '{}'", args);
} }
} }
["break", fn_name] => { ["break", param] => {
let bp = rhai::debugger::BreakPoint::AtFunctionName { if let Ok(n) = param.parse::<usize>() {
name: fn_name.trim().into(), // Numeric parameter
}; let range = 1..=lines.len();
println!("Break-point added for {}", bp); if range.contains(&n) {
context let bp = rhai::debugger::BreakPoint::AtPosition {
.global_runtime_state_mut() source: source.unwrap_or("").into(),
.debugger pos: Position::new(n as u16, 0),
.break_points_mut() enabled: true,
.push(bp); };
println!("Break-point added {}", bp);
context
.global_runtime_state_mut()
.debugger
.break_points_mut()
.push(bp);
} else {
eprintln!("Invalid line number: {}", n);
}
} else {
// Function name parameter
let bp = rhai::debugger::BreakPoint::AtFunctionName {
name: param.trim().into(),
enabled: true,
};
println!("Break-point added for {}", bp);
context
.global_runtime_state_mut()
.debugger
.break_points_mut()
.push(bp);
}
} }
["break", ..] => { ["break", ..] => {
let bp = rhai::debugger::BreakPoint::AtPosition { let bp = rhai::debugger::BreakPoint::AtPosition {
source: source.unwrap_or("").into(), source: source.unwrap_or("").into(),
pos, pos,
enabled: true,
}; };
println!("Break-point added {}", bp); println!("Break-point added {}", bp);
context context

View File

@ -40,36 +40,91 @@ pub enum BreakPoint {
/// ///
/// Source is empty if not available. /// Source is empty if not available.
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
AtPosition { source: Identifier, pos: Position }, AtPosition {
source: Identifier,
pos: Position,
enabled: bool,
},
/// Break at a particular function call. /// Break at a particular function call.
AtFunctionName { name: Identifier }, AtFunctionName { name: Identifier, enabled: bool },
/// Break at a particular function call with a particular number of arguments. /// Break at a particular function call with a particular number of arguments.
AtFunctionCall { name: Identifier, args: usize }, AtFunctionCall {
name: Identifier,
args: usize,
enabled: bool,
},
} }
impl fmt::Display for BreakPoint { impl fmt::Display for BreakPoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::AtPosition { source, pos } => { Self::AtPosition {
source,
pos,
enabled,
} => {
if !source.is_empty() { if !source.is_empty() {
write!(f, "{} @ {:?}", source, pos) write!(f, "{} @ {:?}", source, pos)?;
} else { } else {
write!(f, "@ {:?}", pos) write!(f, "@ {:?}", pos)?;
} }
if !*enabled {
f.write_str(" (disabled)")?;
}
Ok(())
}
Self::AtFunctionName {
name: fn_name,
enabled,
} => {
write!(f, "{} (...)", fn_name)?;
if !*enabled {
f.write_str(" (disabled)")?;
}
Ok(())
} }
Self::AtFunctionName { name: fn_name } => write!(f, "{} (...)", fn_name),
Self::AtFunctionCall { Self::AtFunctionCall {
name: fn_name, name: fn_name,
args, args,
} => write!( enabled,
f, } => {
"{} ({})", write!(
fn_name, f,
std::iter::repeat("_") "{} ({})",
.take(*args) fn_name,
.collect::<Vec<_>>() std::iter::repeat("_")
.join(", ") .take(*args)
), .collect::<Vec<_>>()
.join(", ")
)?;
if !*enabled {
f.write_str(" (disabled)")?;
}
Ok(())
}
}
}
}
impl BreakPoint {
/// Is this [`BreakPoint`] enabled?
#[inline(always)]
pub fn is_enabled(&self) -> bool {
match self {
#[cfg(not(feature = "no_position"))]
Self::AtPosition { enabled, .. } => *enabled,
Self::AtFunctionName { enabled, .. } | Self::AtFunctionCall { enabled, .. } => *enabled,
}
}
/// Enable/disable this [`BreakPoint`].
#[inline(always)]
pub fn enable(&mut self, value: bool) {
match self {
#[cfg(not(feature = "no_position"))]
Self::AtPosition { enabled, .. } => *enabled = value,
Self::AtFunctionName { enabled, .. } | Self::AtFunctionCall { enabled, .. } => {
*enabled = value
}
} }
} }
} }
@ -191,22 +246,33 @@ impl Debugger {
} }
/// Does a particular [`AST` Node][ASTNode] trigger a break-point? /// Does a particular [`AST` Node][ASTNode] trigger a break-point?
pub fn is_break_point(&self, src: &str, node: ASTNode) -> bool { pub fn is_break_point(&self, src: &str, node: ASTNode) -> bool {
self.break_points().iter().any(|bp| match bp { self.break_points()
#[cfg(not(feature = "no_position"))] .iter()
BreakPoint::AtPosition { source, pos } => node.position() == *pos && src == source, .filter(|&bp| bp.is_enabled())
BreakPoint::AtFunctionName { name } => match node { .any(|bp| match bp {
ASTNode::Expr(Expr::FnCall(x, _)) | ASTNode::Stmt(Stmt::FnCall(x, _)) => { #[cfg(not(feature = "no_position"))]
x.name == *name BreakPoint::AtPosition { pos, .. } if pos.is_none() => false,
#[cfg(not(feature = "no_position"))]
BreakPoint::AtPosition { source, pos, .. } if pos.is_beginning_of_line() => {
node.position().line().unwrap_or(0) == pos.line().unwrap() && src == source
} }
_ => false, #[cfg(not(feature = "no_position"))]
}, BreakPoint::AtPosition { source, pos, .. } => {
BreakPoint::AtFunctionCall { name, args } => match node { node.position() == *pos && src == source
ASTNode::Expr(Expr::FnCall(x, _)) | ASTNode::Stmt(Stmt::FnCall(x, _)) => {
x.args.len() == *args && x.name == *name
} }
_ => false, BreakPoint::AtFunctionName { name, .. } => match node {
}, ASTNode::Expr(Expr::FnCall(x, _)) | ASTNode::Stmt(Stmt::FnCall(x, _)) => {
}) x.name == *name
}
_ => false,
},
BreakPoint::AtFunctionCall { name, args, .. } => match node {
ASTNode::Expr(Expr::FnCall(x, _)) | ASTNode::Stmt(Stmt::FnCall(x, _)) => {
x.args.len() == *args && x.name == *name
}
_ => false,
},
})
} }
/// Get a slice of all [`BreakPoint`]'s. /// Get a slice of all [`BreakPoint`]'s.
#[inline(always)] #[inline(always)]

View File

@ -260,7 +260,11 @@ impl fmt::Display for Position {
impl fmt::Debug for Position { impl fmt::Debug for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
write!(f, "{}:{}", self.line, self.pos)?; if self.is_beginning_of_line() {
write!(f, "{}", self.line)?;
} else {
write!(f, "{}:{}", self.line, self.pos)?;
}
#[cfg(feature = "no_position")] #[cfg(feature = "no_position")]
f.write_str("none")?; f.write_str("none")?;