add memory watchers and better output

This commit is contained in:
Crispy 2023-12-06 22:46:06 +01:00
parent 0f2094ede3
commit 6b4b49142d
3 changed files with 137 additions and 45 deletions

9
Cargo.lock generated
View file

@ -5,3 +5,12 @@ version = 3
[[package]] [[package]]
name = "brainfuck" name = "brainfuck"
version = "0.1.0" version = "0.1.0"
dependencies = [
"owo-colors",
]
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"

View file

@ -6,3 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
owo-colors = "3.5.0"

View file

@ -1,7 +1,9 @@
use std::{env, fs, io::stdin, process::exit}; use std::{env, fmt::Display, fs, io::stdin, process::exit};
use owo_colors::OwoColorize;
#[derive(Debug)] #[derive(Debug)]
struct StateMachine { struct BFInterpreter {
memory: Vec<u8>, memory: Vec<u8>,
mem_ptr: usize, mem_ptr: usize,
program: Vec<DebugCommand>, program: Vec<DebugCommand>,
@ -10,6 +12,14 @@ struct StateMachine {
input: Vec<u8>, input: Vec<u8>,
input_ptr: usize, input_ptr: usize,
state: State, state: State,
steps: usize,
watchers: Vec<MemoryWatcher>,
}
#[derive(Debug)]
struct MemoryWatcher {
index: usize,
value: u8,
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]
@ -18,6 +28,7 @@ enum State {
Running, Running,
TooFarLeft, TooFarLeft,
EndOfProgram, EndOfProgram,
StoppedOnMemoryValue,
} }
#[derive(Debug)] #[derive(Debug)]
@ -27,12 +38,6 @@ struct DebugCommand {
column: usize, column: usize,
} }
// #[derive(Debug)]
// struct FastCommand {
// command: Command,
// count: u8,
// }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum Command { enum Command {
Inc, Inc,
@ -43,6 +48,7 @@ enum Command {
Write, Write,
BeginLoop(usize), BeginLoop(usize),
EndLoop(usize), EndLoop(usize),
End,
} }
fn main() { fn main() {
@ -68,34 +74,37 @@ fn main() {
let program = parse(&source); let program = parse(&source);
for c in &program {
print!("{}", c.command.char());
}
println!();
// dbg!(&code_dbg); // dbg!(&code_dbg);
let mut state_machine = StateMachine::new(program, input_data); let mut interpreter = BFInterpreter::new(program, input_data);
loop { loop {
println!("{:?}", state_machine.memory); interpreter.show();
println!("{:?}", state_machine.state);
println!("output: {}", String::from_utf8_lossy(&state_machine.output));
let mut action = String::new(); let mut action = String::new();
stdin().read_line(&mut action).unwrap(); stdin().read_line(&mut action).unwrap();
action = action.trim().to_owned(); let action: Vec<_> = action.trim().split_ascii_whitespace().collect();
if action.starts_with("step ") { match action.as_slice() {
if let Ok(num) = action[5..].trim().parse() { ["step"] => interpreter.step_once(),
state_machine.step(num); ["step", num] => _ = num.parse().map(|n| interpreter.step(n)),
["watch"] => println!("usage: watch [memory index] [value]"),
["watch", _] => println!("usage: watch [memory index] [value]"),
["watch", index, value] => {
if let (Ok(index), Ok(value)) = (index.parse(), value.parse()) {
interpreter.add_watch(index, value)
} else {
println!(
"{}",
"index and value must be valid usize and u8 integers".red()
);
}
} }
} ["run"] => interpreter.run(),
match action.as_str() { ["q" | "exit" | "quit"] => break,
"step" => state_machine.step_once(), [] => interpreter.step_once(),
"run" => state_machine.run(), _ => println!("{}", "unrecognised command".red()),
"exit" | "quit" => break,
_ => (),
} }
} }
} }
impl StateMachine { impl BFInterpreter {
fn new(program: Vec<DebugCommand>, input: Vec<u8>) -> Self { fn new(program: Vec<DebugCommand>, input: Vec<u8>) -> Self {
Self { Self {
memory: vec![0], memory: vec![0],
@ -106,12 +115,59 @@ impl StateMachine {
input, input,
input_ptr: 0, input_ptr: 0,
state: State::Running, state: State::Running,
steps: 0,
watchers: Vec::new(),
} }
} }
fn show(&self) {
for (index, c) in self.program.iter().enumerate() {
if index == self.program_ptr {
print!("{}", c.command.on_cyan());
} else {
print!("{}", c.command);
}
}
println!();
println!(
"source: {}:{}",
self.program[self.program_ptr].line_number, self.program[self.program_ptr].column
);
print!("mem: ");
for (index, cell) in self.memory.iter().enumerate() {
if index == self.mem_ptr {
print!("{:3} ", cell.on_red());
} else {
print!("{:3} ", cell);
}
}
println!();
print!("ind: ");
for i in 0..self.memory.len() {
if i == self.mem_ptr {
print!("{:3} ", i.on_red());
} else {
print!("{:3} ", i);
}
}
println!();
println!("{:?}. steps: {}", self.state, self.steps);
println!("output: {}", String::from_utf8_lossy(&self.output));
// println!("input: {}", String::from_utf8_lossy(&self.input));
}
fn add_watch(&mut self, index: usize, value: u8) {
self.watchers.push(MemoryWatcher { index, value });
}
fn step_once(&mut self) {
self.state = State::Running;
self.step_internal();
}
fn step(&mut self, num: usize) { fn step(&mut self, num: usize) {
for _ in 0..num { for _ in 0..num {
self.step_once(); self.step_internal();
if self.state != State::Running { if self.state != State::Running {
break; break;
} }
@ -120,12 +176,12 @@ impl StateMachine {
fn run(&mut self) { fn run(&mut self) {
while self.state == State::Running { while self.state == State::Running {
self.step_once(); self.step_internal();
} }
} }
fn step_once(&mut self) { fn step_internal(&mut self) {
if self.program_ptr >= self.program.len() { if self.program_ptr + 1 == self.program.len() {
self.state = State::EndOfProgram; self.state = State::EndOfProgram;
} }
if self.state != State::Running { if self.state != State::Running {
@ -133,8 +189,14 @@ impl StateMachine {
} }
let command = self.program[self.program_ptr].command; let command = self.program[self.program_ptr].command;
match command { match command {
Command::Inc => self.memory[self.mem_ptr] = self.memory[self.mem_ptr].wrapping_add(1), Command::Inc => {
Command::Dec => self.memory[self.mem_ptr] = self.memory[self.mem_ptr].wrapping_sub(1), self.memory[self.mem_ptr] = self.memory[self.mem_ptr].wrapping_add(1);
self.update_watchers();
}
Command::Dec => {
self.memory[self.mem_ptr] = self.memory[self.mem_ptr].wrapping_sub(1);
self.update_watchers();
}
Command::Right => { Command::Right => {
self.mem_ptr += 1; self.mem_ptr += 1;
if self.mem_ptr >= self.memory.len() { if self.mem_ptr >= self.memory.len() {
@ -167,9 +229,19 @@ impl StateMachine {
self.program_ptr = start_of_loop; self.program_ptr = start_of_loop;
} }
} }
Command::End => (),
} }
self.program_ptr += 1; self.program_ptr += 1;
self.steps += 1;
}
fn update_watchers(&mut self) {
for watcher in &self.watchers {
if watcher.index == self.mem_ptr && self.memory[watcher.index] == watcher.value {
self.state = State::StoppedOnMemoryValue;
}
}
} }
} }
@ -212,6 +284,11 @@ fn parse(source_text: &str) -> Vec<DebugCommand> {
}); });
} }
} }
out.push(DebugCommand {
command: Command::End,
line_number: 0,
column: 0,
});
if let Some(loop_start_index) = loop_starts.pop() { if let Some(loop_start_index) = loop_starts.pop() {
let loop_start = &out[loop_start_index]; let loop_start = &out[loop_start_index];
println!( println!(
@ -223,17 +300,22 @@ fn parse(source_text: &str) -> Vec<DebugCommand> {
out out
} }
impl Command { impl Display for Command {
fn char(&self) -> char { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { write!(
Command::Inc => '+', f,
Command::Dec => '-', "{}",
Command::Right => '>', match self {
Command::Left => '<', Command::Inc => '+',
Command::Read => ',', Command::Dec => '-',
Command::Write => '.', Command::Right => '>',
Command::BeginLoop(_) => '[', Command::Left => '<',
Command::EndLoop(_) => ']', Command::Read => ',',
} Command::Write => '.',
Command::BeginLoop(_) => '[',
Command::EndLoop(_) => ']',
Command::End => '_',
}
)
} }
} }