use ::core::convert::TryInto;
use ::std::collections::BTreeSet;
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
#[repr(u16)]
pub(super) enum NopTag {
LoopStart = 0,
LoopArgument,
LoopEnd,
FunctionStart,
FunctionArgument,
Breakpoint,
Unknown,
}
impl NopTag {
#[allow(dead_code)]
pub(super) fn code(&self) -> u16 {
*self as u16
}
pub(super) fn from_code(code: u16) -> Self {
if code >= NopTag::Unknown as u16 {
NopTag::Unknown
} else {
unsafe { ::core::mem::transmute(code) }
}
}
}
fn tagged<'a>(tag: &str, input: &'a str) -> Result<&'a str, ()> {
if input.starts_with(tag) {
Ok(&input[tag.len()..])
} else {
Err(())
}
}
fn parse_base_10_int(input: &str) -> Result<(&str, u64), ()> {
let mut cursor = input.as_bytes();
while let [b'0'..=b'9', rest @ ..] = cursor {
cursor = rest;
}
let len = cursor.as_ptr() as usize - input.as_ptr() as usize;
Ok((
&input[len..],
u64::from_str_radix(&input[..len], 10).unwrap(),
))
}
pub(super) fn debugger_repl<R: ::rand::Rng>(machine: &mut super::Machine, rng: &mut R) {
let stdin = ::std::io::stdin();
let mut buf = String::with_capacity(1024);
let mut breakpoints = BTreeSet::<usize>::new();
while let Ok(_width) = stdin.read_line(&mut buf) {
if let Ok(rest) = tagged("print", &buf) {
if let Ok(rest) = tagged(" ", rest) {
if let Ok(_rest) = tagged("ip", rest) {
println!("IP = {}", machine.instruction_pointer);
} else if let Ok(_rest) = tagged("temp", rest) {
println!("Temporaries: {:?}", machine.value_stack);
} else if let Ok(_rest) = tagged("vars", rest) {
println!("Vars: {:?}", machine.var_stack);
} else if let Ok(rest) = tagged("out", rest) {
if let Ok(_rest) = tagged(" vars", rest) {
println!("Output Vars: {:?}", machine.output_vars);
} else {
println!("Output: {:?}", machine.output);
}
} else if let Ok(_rest) = tagged("fuel", rest) {
println!("Fuel: {}", machine.fuel);
}
} else {
dbg!(&*machine);
}
} else if let Ok(rest) = tagged("fmt", &buf) {
if let [.., top] = &*machine.output {
let buf = if let Ok(_rest) = tagged(" short", rest) {
machine
.stack_top()
.unwrap()
.to_int()
.map(|x| crate::mir::fmt::mbot_format_short(top, x))
} else {
machine
.stack_top()
.unwrap()
.to_int()
.map(|x| crate::mir::fmt::mbot_format_default(top, x))
};
match buf {
Ok(buf) => println!("{}", buf),
Err(e) => println!("Failed to format: {:?}", e),
}
} else {
println!("No structured output to format.");
}
} else if let Ok(_rest) = tagged("bt", &buf) {
println!("Backtrace: {:?}", machine.call_stack);
} else if let Ok(_rest) = tagged("step", &buf) {
let _ = dbg!(machine.step(rng));
} else if let Ok(_rest) = tagged("cont", &buf) {
use super::S;
loop {
let pre_ptr = machine.instruction_pointer;
match machine.step(rng) {
Ok(S::Normal) => {
if breakpoints.contains(&machine.instruction_pointer) {
println!(
"Reached (session) breakpoint at {}",
machine.instruction_pointer
);
break;
}
}
Ok(S::Break) => {
println!("Reached (compiled) breakpoint at {}", pre_ptr);
break;
}
Ok(S::Finished) => {
println!("Reached end of program.");
break;
}
Err(e) => {
println!("Program aborted: {:?}", e);
break;
}
}
}
} else if let Ok(_rest) = tagged("restart", &buf) {
machine.restart();
} else if let Ok(rest) = tagged("break", &buf) {
if let Ok(rest) = tagged(" ", rest) {
match parse_base_10_int(rest) {
Ok((_rest, pos)) => {
if breakpoints.insert(pos.try_into().unwrap()) {
println!("Set breakpoint at position {}.", pos)
} else {
println!("Position {} was already a breakpoint.", pos)
}
}
Err(()) => continue,
}
}
} else if let Ok(rest) = tagged("fuel", &buf) {
if let Ok(rest) = tagged(" ", rest) {
match parse_base_10_int(rest) {
Ok((_rest, amt)) => {
machine.add_fuel(amt);
}
Err(()) => continue,
}
} else {
machine.fuel += 1;
}
} else if let Ok(_rest) = tagged("compile core", &buf) {
#[cfg(feature = "script")]
{
let core = super::super::misp::Core::compile();
dbg!(&core);
}
#[cfg(not(feature = "script"))]
{
println!("this binary was not built with Misp support");
}
} else if let Ok(_rest) = tagged("exit", &buf) {
return;
}
buf.clear();
}
}