mice 0.11.1

messing with dice
Documentation
//! Basic debugging facilities for programs running on [`stack`](super).
//!
//! Breakpoints, steps, and printing information about the running program.
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 {
            // Safety: We've verified that `code` is in bounds of `NopTag`.
            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) {
            // TODO: parse a number of steps to take
            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();
    }
}