use std::{
collections::HashMap,
io::BufRead,
io::{self, Write},
};
use crate::{
operation::RegisterOperation,
parser::{parse, Instruction, Line, RegisterContents, ResultValue, Value},
runtime_state::Register,
};
struct Debugger {
registers: HashMap<char, Register>,
lines: Vec<(usize, Line)>,
found_labels: HashMap<u32, usize>,
}
fn ellipsis(source: String, len: usize) -> String {
source
.get(..len)
.map(|sub| format!("{sub}..."))
.unwrap_or(source.to_string())
}
impl Debugger {
fn get_register(&mut self, name: char) -> Result<&mut Register, String> {
self.registers
.get_mut(&name)
.ok_or(format!("Register {name} not defined"))
}
fn resolve(&self, value: Value) -> Result<u32, String> {
match value {
Value::Register(name) => self
.registers
.get(&name)
.map(|reg| reg.get())
.ok_or(format!("Register {name} not defined")),
Value::Number(value) => Ok(value.into()),
}
}
pub fn debug(mut self) -> Result<(), String> {
let mut pc = 0;
let mut target: Option<usize> = None;
let mut ops_since_target_set = 0;
let mut sorted_register_names = self
.registers
.iter()
.map(|(name, _)| name.clone())
.collect::<Vec<char>>();
sorted_register_names.sort();
loop {
let mut jumped = false;
let (line_num, line) = match self.lines.get(pc).cloned() {
Some(val) => val,
None => return Ok(()),
};
println!("{line_num}: {line}");
match &line.instruction {
Instruction::Register(name, op, value) => {
let num = self.resolve(value.clone())?;
let register = self.get_register(name.clone())?;
match op {
RegisterOperation::Right => register.right(num),
RegisterOperation::Left => register.left(num),
RegisterOperation::Add => register.add(num.try_into().unwrap()),
RegisterOperation::Subtract => register.sub(num),
}
}
Instruction::Result(value) => match value {
ResultValue::All => println!("="),
ResultValue::Register(reg) => println!("= {}", self.get_register(*reg)?.get()),
ResultValue::Value(val) => println!("= {}", self.resolve(*val)?),
},
Instruction::Jump(reg, to) => {
if reg
.map(|r| -> Result<bool, String> { Ok(self.get_register(r)?.get() > 0) })
.unwrap_or(Ok(true))?
{
pc = self
.found_labels
.get(to)
.ok_or("Label {to} not defined")?
.to_owned();
jumped = true;
}
}
Instruction::Take(to, from) => {
let val = self.get_register(*from)?.remove() + self.get_register(*to)?.get();
self.get_register(*to)?.insert(val);
}
}
for name in sorted_register_names.iter() {
println!(
"{name}:{}",
ellipsis(format!("{}", self.registers.get(name).unwrap()), 80)
)
}
if let Some(to) = target {
if line_num == to {
target = None
}
}
if ops_since_target_set > 1000 {
target = None
}
io::stdout().flush().unwrap();
if target.is_none() {
target = io::stdin()
.lock()
.lines()
.next()
.map(|s| s.unwrap().parse().ok())
.unwrap_or_default();
ops_since_target_set = 0
} else {
println!("");
ops_since_target_set += 1
}
if !jumped {
pc += 1;
}
}
}
fn new(source: &str) -> Result<Self, String> {
let (lines, register_definitions) =
parse(source).map_err(|e| format!("{}:{}", e.line, e.message))?;
let registers = register_definitions
.into_iter()
.map(|(_, def)| {
(
def.name,
Register {
content: match def.contents {
RegisterContents::Size(size) => {
let mut content = Vec::new();
content.resize(size.into(), 0);
content
}
RegisterContents::Values(values) => {
values.into_iter().map(|i| i as u32).collect()
}
},
cursor: 0,
},
)
})
.collect::<HashMap<char, Register>>();
let found_labels = lines
.iter()
.enumerate()
.filter_map(|(num, (line_num, line))| line.label.map(|name| (name, num, line_num)))
.try_fold(
HashMap::<u32, usize>::new(),
|mut m, (name, offset, line_num)| {
if m.get(&name).is_some() {
Err(format!("{line_num}: Label {} already defined", name))
} else {
m.insert(name.try_into().unwrap(), offset);
Ok(m)
}
},
)?;
Ok(Self {
registers,
lines,
found_labels,
})
}
}
pub fn debug(source: &str) -> Result<(), String> {
Debugger::new(source)?.debug()
}