From 02797b9b868e9be43ecf72c5ee9e586fd50ea99f Mon Sep 17 00:00:00 2001 From: Ian McLinden Date: Sun, 19 Nov 2023 09:45:01 -0600 Subject: [PATCH] Add help, load to interactive mode --- .gitignore | 3 +++ src/lib.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 71ab9a4..171bae4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ debug/ target/ +# Test files +bf/ + # These are backup files generated by rustfmt **/*.rs.bk diff --git a/src/lib.rs b/src/lib.rs index f5616ca..2ee5bae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use std::{ fmt::Display, + fs::File, io::{ErrorKind, Read, Write}, thread, time::Duration, @@ -9,9 +10,6 @@ use std::{ use clap::{crate_name, crate_version}; -const TAPE_SIZE: usize = 30_000; -const QUIT_SYMBOLS: &[&str] = &["q", "quit"]; - pub type Result = std::result::Result; #[derive(Clone, PartialEq)] @@ -20,6 +18,7 @@ pub enum BFMachineError { UnmatchedBracket, OutOfBounds, ReadError, + FileError(String), WriteError, Quit, Unknown, @@ -31,6 +30,7 @@ impl std::fmt::Display for BFMachineError { BFMachineError::InvalidCommand(c) => write!(f, "Invalid command: '{}'", *c as char), BFMachineError::UnmatchedBracket => write!(f, "Unmatched brackets"), BFMachineError::OutOfBounds => write!(f, "Out of bounds"), + BFMachineError::FileError(n) => write!(f, "Could not read file '{}'", n), BFMachineError::ReadError => write!(f, "Read error"), BFMachineError::WriteError => write!(f, "Write error"), BFMachineError::Quit => write!(f, "Quitting"), @@ -128,7 +128,7 @@ pub struct BfMachine { impl Default for BfMachine { fn default() -> Self { - Self::new(TAPE_SIZE) + Self::new(Self::TAPE_SIZE) } } @@ -139,6 +139,10 @@ impl Drop for BfMachine { } impl BfMachine { + const TAPE_SIZE: usize = 30_000; + const QUIT_SYMBOLS: &'static [&'static str] = &["quit", "q"]; + const HELP_SYMBOLS: &'static [&'static str] = &["help", "h"]; + pub fn new(tape_size: usize) -> Self { Self { data: vec![0; tape_size], @@ -412,11 +416,55 @@ impl BfMachine { self.run(&mut std::io::stdin(), &mut std::io::stdout(), program) } + fn print_interactive_help() { + let col1 = "\x1b[16G"; + println!("{} {}", crate_name!(), crate_version!()); + println!("\x1b[4;1mCommands:\x1b[0m"); + println!(" load [FILE]{col1}Load a brainfuck program from FILE"); + println!( + " {}{col1}Print this help message", + Self::HELP_SYMBOLS + .iter() + .map(|s| s.to_string()) + .collect::>() + .join(", ") + ); + println!( + " {}{col1}Exit the interactive terminal", + Self::QUIT_SYMBOLS + .iter() + .map(|s| s.to_string()) + .collect::>() + .join(", ") + ); + } + + fn try_load_file(filename: &str) -> Result { + if filename.is_empty() { + return Err(BFMachineError::FileError(filename.to_string())); + } + let mut prog_file = + File::open(filename).map_err(|_| BFMachineError::FileError(filename.to_string()))?; + let mut program = String::new(); + prog_file + .read_to_string(&mut program) + .map_err(|_| BFMachineError::FileError(filename.to_string()))?; + Ok(program) + } + pub fn interactive(&mut self) -> Result<()> { println!("{} {}", crate_name!(), crate_version!()); + println!( + "Type {} for help", + Self::HELP_SYMBOLS + .iter() + .map(|s| format!("'{s}'")) + .collect::>() + .join(", ") + ); println!( "Ctrl+C, or type {} to quit.", - QUIT_SYMBOLS + Self::QUIT_SYMBOLS .iter() .map(|s| format!("'{s}'")) .collect::>() @@ -442,11 +490,28 @@ impl BfMachine { .map_err(|_| BFMachineError::ReadError)?; let program = program.trim(); - if QUIT_SYMBOLS.contains(&program) { + if Self::QUIT_SYMBOLS.contains(&program) { return Ok(()); } - if let Err(e) = self.execute(program) { + if Self::HELP_SYMBOLS.contains(&program) { + Self::print_interactive_help(); + continue; + } + + let args = program.split_whitespace().collect::>(); + let mut program = program.to_string(); + if args.first().is_some_and(|a| a == &"load") { + match Self::try_load_file(args.get(1).unwrap_or(&"")) { + Ok(prog) => program = prog, + Err(e) => { + eprintln!("Error: {e}"); + continue; + } + } + } + + if let Err(e) = self.execute(&program) { eprintln!("Error: {e}"); } }