diff --git a/bfi-rust/src/interpreter/mod.rs b/bfi-rust/src/interpreter/mod.rs index 9d02624..d72e82e 100644 --- a/bfi-rust/src/interpreter/mod.rs +++ b/bfi-rust/src/interpreter/mod.rs @@ -1,13 +1,16 @@ +use crate::interpreter::optimized::PrintMode; +use std::str::Chars; + pub mod simple; pub mod parsed; pub mod optimized; -const MEM_SIZE: usize = 0xFFFF; +pub const MEM_SIZE: usize = 0xFFFF; -type Memory = [u8; MEM_SIZE]; +pub type Memory = [u8; MEM_SIZE]; #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Clone)] -enum Statement { +pub enum Statement { Inc, Dec, R, @@ -20,11 +23,11 @@ enum Statement { const ALLOWED_CHARS: [char; 8] = ['>', '<', '+', '-', '.', ',', '[', ']']; -fn minify(code: &str) -> String { +pub fn minify(code: &str) -> String { code.chars().filter(|c| ALLOWED_CHARS.contains(c)).collect() } -fn parse(chars: Vec, direct_print: bool) -> Vec { +pub fn parse(chars: Chars, print_mode: PrintMode) -> Vec { let mut loop_stack = vec![vec![]]; for c in chars { @@ -34,10 +37,9 @@ fn parse(chars: Vec, direct_print: bool) -> Vec { '>' => loop_stack.last_mut().unwrap().push(Statement::R), '<' => loop_stack.last_mut().unwrap().push(Statement::L), '.' => { - if direct_print { - loop_stack.last_mut().unwrap().push(Statement::DOut) - } else { - loop_stack.last_mut().unwrap().push(Statement::Out) + match print_mode { + PrintMode::ToString => loop_stack.last_mut().unwrap().push(Statement::Out), + PrintMode::DirectPrint => loop_stack.last_mut().unwrap().push(Statement::DOut) } } ',' => loop_stack.last_mut().unwrap().push(Statement::In), diff --git a/bfi-rust/src/interpreter/optimized/mod.rs b/bfi-rust/src/interpreter/optimized/mod.rs index 8260f61..3a598b0 100644 --- a/bfi-rust/src/interpreter/optimized/mod.rs +++ b/bfi-rust/src/interpreter/optimized/mod.rs @@ -24,7 +24,7 @@ enum ExStatement { Loop(Vec), SetNull, Repeat(Box, usize), - ForLoop(usize, Box), + _ForLoop(usize, Box), } impl From for ExStatement { @@ -46,7 +46,7 @@ impl From for ExStatement { #[derive(Debug)] pub struct BfErr { - msg: &'static str + msg: &'static str, } impl BfErr { @@ -64,10 +64,15 @@ impl Display for BfErr { impl Error for BfErr {} -pub fn run(pgm: &str, direct_print: bool) -> Result { +pub enum PrintMode { + ToString, + DirectPrint, +} + +pub fn run(pgm: &str, print_mode: PrintMode) -> Result { let pgm = minify(pgm); if pgm.is_empty() { return Err(BfErr::new("no program found")); }; - let pgm = parse(pgm.chars().collect(), direct_print); + let pgm = parse(pgm.chars(), print_mode); let pgm = optimize(&pgm); let out = interpret(&pgm); Ok(out) @@ -177,7 +182,7 @@ fn execute(statement: &ExStatement, mem: &mut Memory, pointer: &mut usize, out: } } } - ExStatement::ForLoop(offset, statement) => { + ExStatement::_ForLoop(offset, statement) => { *pointer += offset; while mem[*pointer - offset] != 0 { execute(statement, mem, pointer, out); diff --git a/bfi-rust/src/interpreter/optimized/patterns.rs b/bfi-rust/src/interpreter/optimized/patterns.rs index ffe4bb9..d41e82d 100644 --- a/bfi-rust/src/interpreter/optimized/patterns.rs +++ b/bfi-rust/src/interpreter/optimized/patterns.rs @@ -7,12 +7,12 @@ use crate::interpreter::optimized::ExStatement; /// /// Replace this: `[>>x<<-]` or `[->>x<<]` with `WhileAdd(2, x)` -fn for_loop(to_test: ExStatement) -> ExStatement { +fn _for_loop(to_test: ExStatement) -> ExStatement { match to_test { ExStatement::Loop(v) => { match v[..] { [ExStatement::R, ExStatement::Inc, ExStatement::L, ExStatement::Dec] => { - ExStatement::ForLoop(1, Box::from(ExStatement::Inc)) + ExStatement::_ForLoop(1, Box::from(ExStatement::Inc)) } _ => ExStatement::Loop(v) } @@ -26,18 +26,18 @@ fn for_loop(to_test: ExStatement) -> ExStatement { #[cfg(test)] mod test { - use crate::interpreter::optimized::ExStatement::{Out, Loop, Inc, R, L, Dec, ForLoop}; - use crate::interpreter::optimized::patterns::for_loop; + use crate::interpreter::optimized::ExStatement::{Out, Loop, Inc, R, L, Dec, _ForLoop}; + use crate::interpreter::optimized::patterns::_for_loop; #[test] fn for_loop_false() { let statement = Loop(vec![Out, Inc]); - assert_eq!(statement.clone(), for_loop(statement)); + assert_eq!(statement.clone(), _for_loop(statement)); } #[test] fn for_loop_simplest() { let statement = Loop(vec![R, Inc, L, Dec]); - assert_eq!(ForLoop(1, Box::from(Inc)), for_loop(statement)); + assert_eq!(_ForLoop(1, Box::from(Inc)), _for_loop(statement)); } } \ No newline at end of file diff --git a/bfi-rust/src/interpreter/parsed.rs b/bfi-rust/src/interpreter/parsed.rs index ce0caa5..f30bbaf 100644 --- a/bfi-rust/src/interpreter/parsed.rs +++ b/bfi-rust/src/interpreter/parsed.rs @@ -8,13 +8,16 @@ use std::io::{Read, stdin, Write}; use crate::interpreter::{MEM_SIZE, Memory, minify, parse, Statement}; +use crate::interpreter::optimized::PrintMode; +use crate::repl::BrainfuckState; pub fn run(pgm: &str) -> String { let pgm = minify(pgm); - let pgm = parse(pgm.chars().collect(), false); + let pgm = parse(pgm.chars(), PrintMode::ToString); interpret(&pgm) } + fn interpret(pgm: &[Statement]) -> String { let mut out = String::new(); let mut pointer: usize = 0; @@ -27,6 +30,12 @@ fn interpret(pgm: &[Statement]) -> String { out } +pub fn interpret_with_state(pgm: &[Statement], state: &mut BrainfuckState) { + for s in pgm { + execute(s, &mut state.memory, &mut state.pointer, &mut String::new()) + } +} + fn execute(statement: &Statement, mem: &mut Memory, pointer: &mut usize, out: &mut String) { match statement { Statement::R => if *pointer == MEM_SIZE - 1 { *pointer = 0 } else { *pointer += 1 }, diff --git a/bfi-rust/src/main.rs b/bfi-rust/src/main.rs index 0215baf..d005897 100644 --- a/bfi-rust/src/main.rs +++ b/bfi-rust/src/main.rs @@ -3,37 +3,35 @@ mod repl; use std::{env, fs}; use std::time::SystemTime; +use crate::repl::start_repl; +use crate::interpreter::optimized::{PrintMode}; +use std::error::Error; fn main() { let path = env::args().nth(1); - let path = match path { - Some(p) => p, - None => { - println!("Please specify a path"); - return; - } + + match path { + Some(p) => { + if let Err(why) = run_program(p) { + eprintln!("An error occurred in the program: {}", why) + } + }, + None => start_repl() }; +} + +fn run_program(path: String) -> Result<(), Box> { let program = match fs::read_to_string(path) { Ok(p) => p, Err(e) => { println!("Error reading file: {}", e); - return; + return Err(Box::from(e)); } }; - - run(program); -} - -fn run(program: String) { -/* - let start1 = SystemTime::now(); - let out = interpreter::o1::run(&*program); - let end1 = start1.elapsed().unwrap();*/ - let start2 = SystemTime::now(); - let out2 = interpreter::optimized::run(&*program, false).unwrap(); - let end2 = start2.elapsed().unwrap(); - //assert_eq!(out, out2); - //println!("{}\nFinished execution. Took o1: 18008ms (for hanoi), o2: {}ms", out2/*, end1.as_millis()*/, end2.as_millis()); - println!("{}\nFinished execution. Took {}ms", out2/*, end1.as_millis()*/, end2.as_millis()); + let start_time = SystemTime::now(); + let out = interpreter::optimized::run(&*program, PrintMode::DirectPrint)?; + let duration = start_time.elapsed()?; + println!("{}\nFinished execution. Took {}ms", out, duration.as_millis()); + Ok(()) } \ No newline at end of file diff --git a/bfi-rust/src/o2.rs b/bfi-rust/src/o2.rs deleted file mode 100644 index e69de29..0000000 diff --git a/bfi-rust/src/repl/mod.rs b/bfi-rust/src/repl/mod.rs index e69de29..0cb452c 100644 --- a/bfi-rust/src/repl/mod.rs +++ b/bfi-rust/src/repl/mod.rs @@ -0,0 +1,111 @@ +use std::fmt::{Display, Formatter}; +use std::fmt; +use std::io::{stdin, stdout, Write}; +use crate::interpreter::{minify, parse, parsed, Memory, MEM_SIZE}; +use crate::interpreter::optimized::PrintMode; + +pub struct BrainfuckState { + pub memory: Memory, + pub pointer: usize, +} + +impl Display for BrainfuckState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", display_state(self)) + } +} + +fn display_state(state: &BrainfuckState) -> String { + let start = if state.pointer < 5 { + 0 + } else if state.pointer > MEM_SIZE - 10 { + MEM_SIZE - 10 + } else { + state.pointer - 5 + }; + + format!("{}-\n|{}|\n{}-\n{}|\n{}|\n{}|\n{}-\n {}^^^^", + "----------".repeat(10), + { + let mut out = String::new(); + let end = start + 10; + for i in start..end { + out.push_str(&*format!(" {: >5} ", i)); + } + out + }, + "----------".repeat(10), + "| ".repeat(10), + { + let mut out = String::new(); + let end = start + 10; + for i in start..end { + out.push_str(&*format!("| {: >3} ", state.memory[i])); + } + out + }, + "| ".repeat(10), + "----------".repeat(10), + " ".repeat(state.pointer - start)) +} + +pub fn start_repl() { + println!("Brainfuck REPL"); + + let mut state = BrainfuckState { + memory: [0; MEM_SIZE], + pointer: 0, + }; + + println!("Enter Brainfuck programs and they will be executed immediatly."); + println!("State is kept."); + println!("{}", state); + loop { + print!(">> "); + stdout().flush().unwrap(); + match read_line() { + Ok(s) => { + match &*s { + ":q" => break, + ":?" | "help" | "?" => print_help(), + ":r" => { + reset(&mut state); + println!("{}", state); + }, + _ => { + print!("Output: "); + println!(); + parse_input(s, &mut state); + println!("{}", state); + } + } + } + Err(why) => println!("Error reading input: {}\nPlease try again.", why) + } + } +} + +fn reset(state: &mut BrainfuckState) { + state.pointer = 0; + state.memory = [0; MEM_SIZE]; +} + +fn print_help() { + println!("Brainfuck REPL help + :q => quit + :? => help + :r => reset state"); +} + +fn parse_input(pgm: String, state: &mut BrainfuckState) { + let pgm = minify(&*pgm); + let pgm = parse(pgm.chars(), PrintMode::DirectPrint); + parsed::interpret_with_state(&*pgm, state); +} + +pub fn read_line() -> Result { + let mut buf = String::new(); + stdin().read_line(&mut buf)?; + buf.pop(); + Ok(buf) +} \ No newline at end of file