add memory watchers and better output
This commit is contained in:
parent
0f2094ede3
commit
6b4b49142d
3 changed files with 137 additions and 45 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
152
src/main.rs
152
src/main.rs
|
@ -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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match action.as_str() {
|
["run"] => interpreter.run(),
|
||||||
"step" => state_machine.step_once(),
|
["q" | "exit" | "quit"] => break,
|
||||||
"run" => state_machine.run(),
|
[] => interpreter.step_once(),
|
||||||
"exit" | "quit" => break,
|
_ => println!("{}", "unrecognised command".red()),
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,8 +300,11 @@ 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 {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
match self {
|
match self {
|
||||||
Command::Inc => '+',
|
Command::Inc => '+',
|
||||||
Command::Dec => '-',
|
Command::Dec => '-',
|
||||||
|
@ -234,6 +314,8 @@ impl Command {
|
||||||
Command::Write => '.',
|
Command::Write => '.',
|
||||||
Command::BeginLoop(_) => '[',
|
Command::BeginLoop(_) => '[',
|
||||||
Command::EndLoop(_) => ']',
|
Command::EndLoop(_) => ']',
|
||||||
}
|
Command::End => '_',
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue