From 7b6046745c4df608bc73d3cf4d38c058abcca9ec Mon Sep 17 00:00:00 2001 From: Nilstrieb Date: Fri, 23 Apr 2021 16:54:06 +0200 Subject: [PATCH] o2 start --- bfi-rust/bf.b | 207 +++++----------------- bfi-rust/src/interpreter/mod.rs | 303 ++++++++++++++++++++++++++++++++ bfi-rust/src/main.rs | 199 ++------------------- bfi-rust/src/o2.rs | 0 4 files changed, 358 insertions(+), 351 deletions(-) create mode 100644 bfi-rust/src/interpreter/mod.rs create mode 100644 bfi-rust/src/o2.rs diff --git a/bfi-rust/bf.b b/bfi-rust/bf.b index d9cce41..b2c18e0 100644 --- a/bfi-rust/bf.b +++ b/bfi-rust/bf.b @@ -1,163 +1,44 @@ -* factor an arbitrarily large positive integer -* -* Copyright (C) 1999 by Brian Raiter -* under the GNU General Public License ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>- -* -* read in the number -* -<<<<<<<<<+ -[-[>>>>>>>>>>][-]<<<<<<<<<<[[->>>>>>>>>>+<<<<<<<<<<]<<<<<<<<<<] - >>>>>>>>>>,----------] ->>>>>>>>>>[------------------------------------->>>>>>>>>->] -<[+>[>>>>>>>>>+>]<-<<<<<<<<<<]- -* -* display the number and initialize the loop variable to two -* -[>++++++++++++++++++++++++++++++++++++++++++++++++. - ------------------------------------------------<<<<<<<<<<<] -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. ---------------------------.[-] ->>>>>>>>>>>>++<<<<+ -* -* the main loop -* -[ [-]>> - * - * make copies of the number and the loop variable - * - [>>>>[-]>[-]>[-]>[-] - >[-]>[-] - <<<<<<<[->>>+>+<<<<]>>>>>>>>] - <<<<<<<<<<[>>>>>>[-<<<<+>>>>]<<<<<<<<<<<<<<<<]>>>>>>>>>> - [>[->>>+>>+<<<<<]>>>>>>>>>] - <<<<<<<<<<[>>>>>>[-<<<<<+>>>>>]<<<<<<<<<<<<<<<<]>>>>>>>>>> - * - * divide the number by the loop variable - * - [>>>[-]>>>[-]>[-]>>>] initialize - <<<<<<<<<<[<<<<<<<<<<] - >>>>>>>>>[-]>>>>>>>+<<<<<<<<[+]+ - [ ->> double divisor until above dividend - [>>>>>>[->++<]>>>>]<<<<<<<<<< - [>>>>>>>>[-]>[-] - <<<<[->>>++<<<]<<<<<<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>>[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+< - [->--------->>>>>>>>>+<<<<<<<<<<[->+<]]]]]]]]]]]>>] - <<<<<<<<<<[>>>>>>>>>[-<+<<<+>>>>]<<<<<<<<<<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+> - [-<--------->>>>>>>>>>>+<<<<<<<<<<[-<+>]]]]]]]]]]]>>>] - <<<<<<<<<< - [>>>>[->>>+>>+<<<<<]<<<<<<<<<<<<<<] - >>>>>>>>>>[>>>>>>>[-<<<+>>>]>>>]<<<<<<<<<< - [>>>>>>>>[->-<]> - [<<<<<<<<<[<[-]>>>>>>>>>>[-<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<] - >>>>>>>>>>>>>>>>>>>] - <<<<<<<<<<<<<<<<<<<] - >>>>>>>>>[+[+[+[+[+[+[+[+[+[+[[-]<+>]]]]]]]]]]]< - ] - >>>>>>>> - [ subtract divisor from dividend - <<<<<< - [>>>>>>>>[-]>[-]<<<<<[->>>+>+<<<<]>>>>>>]<<<<<<<<<< - [>>>>>>>>[-<<<<+>>>>]<<<[->>>+>+<<<<]<<<<<<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>>>[-<<<<+>>>>]>]<<<<<<<<<< - [>>>>>>>>[-<->]<<<<<<<<<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+< - [++++++++++[+>-<]>>>>>>>>>>-<<<<<<<<<<]]]]]]]]]]]>>>] - >>>>>>>+ - [ if difference is nonnegative then - [-]<<<<<<<<<<<<<<<<< replace dividend and increment quotient - [>>>>[-]>>>>[-<<<<+>>>>]<<[->>+<<]<<<<<<<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>>[->+<<<+>>]>>]<<<<<<<<<< - [>>>[->>>>>>+<<<<<<]<<<<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>>>[-<<<<<<+>>>>>>[-<<<<<<+>>>>>> - [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> - [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> - [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> - [-<<<<<<+>>>>>>[-<<<<<<--------->>>>>>>>>>>>>>>>+<<<<<<<<<< - [-<<<<<<+>>>>>>]]]]]]]]]]]>] - >>>>>>> - ] halve divisor and loop until zero - <<<<<<<<<<<<<<<<<[<<<<<<<<<<]>>>>>>>>>> - [>>>>>>>>[-]<<[->+<]<[->>>+<<<]>>>>>]<<<<<<<<<< - [+>>>>>>>[-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> - [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> - [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> - [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> - [-<<<<<<<+>>>>>>>]]]]]]]]]<<<<<<< - [->>>>>>>+<<<<<<<]-<<<<<<<<<<] - >>>>>>> - [-<<<<<<<<<<<+>>>>>>>>>>>] - >>>[>>>>>>>[-<<<<<<<<<<<+++++>>>>>>>>>>>]>>>]<<<<<<<<<< - [+>>>>>>>>[-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> - [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> - [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> - [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> - [-<<<<<<<<+>>>>>>>>]]]]]]]]]<<<<<<<< - [->>>>>>>>+<<<<<<<<]-<<<<<<<<<<] - >>>>>>>>[-<<<<<<<<<<<<<+>>>>>>>>>>>>>]>> - [>>>>>>>>[-<<<<<<<<<<<<<+++++>>>>>>>>>>>>>]>>]<<<<<<<<<< - [<<<<<<<<<<]>>>>>>>>>> - >>>>>> - ] - <<<<<< - * - * make copies of the loop variable and the quotient - * - [>>>[->>>>+>+<<<<<]>>>>>>>] - <<<<<<<<<< - [>>>>>>>[-<<<<+>>>>]<<<<<[->>>>>+>>+<<<<<<<]<<<<<<<<<<<<] - >>>>>>>>>>[>>>>>>>[-<<<<<+>>>>>]>>>]<<<<<<<<<< - * - * break out of the loop if the quotient is larger than the loop variable - * - [>>>>>>>>>[-<->]< - [<<<<<<<< - [<<[-]>>>>>>>>>>[-<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<<<<<<<<<] - >>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<] - >>>>>>>>[>-<[+[+[+[+[+[+[+[+[+[[-]>+<]]]]]]]]]]]>+ - [ [-] - * - * partially increment the loop variable - * - <[-]+>>>>+>>>>>>>>[>>>>>>>>>>]<<<<<<<<<< - * - * examine the remainder for nonzero digits - * - [<<<<<<[<<<<[<<<<<<<<<<]>>>>+<<<<<<<<<<]<<<<] - >>>>>>>>>>>>>>>>>>>>[>>>>>>>>>>]<<<<<<<<<<[<<<<<<<<<<] - >>>>- - [ [+] - * - * decrement the loop variable and replace the number with the quotient - * - >>>>>>>>-<<[>[-]>>[-<<+>>]>>>>>>>]<<<<<<<<<< - * - * display the loop variable - * - [+>>[>>>>>>>>+>>]<<-<<<<<<<<<<]- - [>>++++++++++++++++++++++++++++++++++++++++++++++++. - ------------------------------------------------<<<<<<<<<<<<] - ++++++++++++++++++++++++++++++++.[-]>>>> - ] - * - * normalize the loop variable - * - >>>>>> - [>>[->>>>>+<<<<<[->>>>>+<<<<< - [->>>>>+<<<<<[->>>>>+<<<<< - [->>>>>+<<<<<[->>>>>+<<<<< - [->>>>>+<<<<<[->>>>>+<<<<< - [->>>>>+<<<<<[->>>>>--------->>>>>+<<<<<<<<<< - [->>>>>+<<<<<]]]]]]]]]]]>>>>>>>>] - <<<<<<<<<<[>>>>>>>[-<<<<<+>>>>>]<<<<<<<<<<<<<<<<<] - >>>>>>>>> - ]< -]>> -* -* display the number and end -* -[>>>>>>>>>>]<<<<<<<<<<[+>[>>>>>>>>>+>]<-<<<<<<<<<<]- -[>++++++++++++++++++++++++++++++++++++++++++++++++.<<<<<<<<<<<] -++++++++++. \ No newline at end of file +>+++++++++[<+++++++++++>-]<[>[-]>[-]<<[>+>+<<-]>>[<<+>>-]>>> +[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+< +-]<<-<-]+++++++++>[<->-]>>+>[<[-]<<+>>>-]>[-]+<<[>+>-<<-]<<< +[>>+>+<<<-]>>>[<<<+>>>-]>[<+>-]<<-[>[-]<[-]]>>+<[>[-]<-]<+++ ++++++[<++++++<++++++>>-]>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>- +]<<<<<<.>>[-]>[-]++++[<++++++++>-]<.>++++[<++++++++>-]<++.>+ +++++[<+++++++++>-]<.><+++++..--------.-------.>>[>>+>+<<<-]> +>>[<<<+>>>-]<[<<<<++++++++++++++.>>>>-]<<<<[-]>++++[<+++++++ ++>-]<.>+++++++++[<+++++++++>-]<--.---------.>+++++++[<------ +---->-]<.>++++++[<+++++++++++>-]<.+++..+++++++++++++.>++++++ +++[<---------->-]<--.>+++++++++[<+++++++++>-]<--.-.>++++++++ +[<---------->-]<++.>++++++++[<++++++++++>-]<++++.----------- +-.---.>+++++++[<---------->-]<+.>++++++++[<+++++++++++>-]<-. +>++[<----------->-]<.+++++++++++..>+++++++++[<---------->-]< +-----.---.>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>>+++ ++[<++++++>-]<--.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<. +><+++++..--------.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++ +++++++++++++.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<++ ++++++++>-]<--.---------.>+++++++[<---------->-]<.>++++++[<++ ++++++++++>-]<.+++..+++++++++++++.>++++++++++[<---------->-]< +-.---.>+++++++[<++++++++++>-]<++++.+++++++++++++.++++++++++. +------.>+++++++[<---------->-]<+.>++++++++[<++++++++++>-]<-. +-.---------.>+++++++[<---------->-]<+.>+++++++[<++++++++++>- +]<--.+++++++++++.++++++++.---------.>++++++++[<---------->-] +<++.>+++++[<+++++++++++++>-]<.+++++++++++++.----------.>++++ ++++[<---------->-]<++.>++++++++[<++++++++++>-]<.>+++[<-----> +-]<.>+++[<++++++>-]<..>+++++++++[<--------->-]<--.>+++++++[< +++++++++++>-]<+++.+++++++++++.>++++++++[<----------->-]<++++ +.>+++++[<+++++++++++++>-]<.>+++[<++++++>-]<-.---.++++++.---- +---.----------.>++++++++[<----------->-]<+.---.[-]<<<->[-]>[ +-]<<[>+>+<<-]>>[<<+>>-]>>>[-]<<<+++++++++<[>>>+<<[>+>[-]<<-] +>[<+>-]>[<<++++++++++>>>+<-]<<-<-]+++++++++>[<->-]>>+>[<[-]< +<+>>>-]>[-]+<<[>+>-<<-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<>>[<+>-]< +<-[>[-]<[-]]>>+<[>[-]<-]<++++++++[<++++++<++++++>>-]>>>[>+>+ +<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>[-]>[-]++++[<++++++++> +-]<.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<.><+++++..--- +-----.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++++++++++++++ +.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<+++++++++>-]<- +-.---------.>+++++++[<---------->-]<.>++++++[<+++++++++++>-] +<.+++..+++++++++++++.>++++++++[<---------->-]<--.>+++++++++[ +<+++++++++>-]<--.-.>++++++++[<---------->-]<++.>++++++++[<++ +++++++++>-]<++++.------------.---.>+++++++[<---------->-]<+. +>++++++++[<+++++++++++>-]<-.>++[<----------->-]<.+++++++++++ +..>+++++++++[<---------->-]<-----.---.+++.---.[-]<<<] \ No newline at end of file diff --git a/bfi-rust/src/interpreter/mod.rs b/bfi-rust/src/interpreter/mod.rs new file mode 100644 index 0000000..5231ef1 --- /dev/null +++ b/bfi-rust/src/interpreter/mod.rs @@ -0,0 +1,303 @@ +const MEM_SIZE: usize = 0xFFFF; + +type Memory = [u8; MEM_SIZE]; + + +#[derive(Debug, PartialOrd, PartialEq, Clone)] +enum SimpleStatement { + Inc, + Dec, + R, + L, + Out, + In, + Loop(Vec), +} + +fn minify(code: &str) -> String { + let allowed: Vec = vec!['>', '<', '+', '-', '.', ',', '[', ']']; + code.chars().filter(|c| allowed.contains(c)).collect() +} + +fn parse(chars: Vec) -> Vec { + let mut loop_stack = vec![vec![]]; + + for c in chars { + match c { + '+' => loop_stack.last_mut().unwrap().push(SimpleStatement::Inc), + '-' => loop_stack.last_mut().unwrap().push(SimpleStatement::Dec), + '>' => loop_stack.last_mut().unwrap().push(SimpleStatement::R), + '<' => loop_stack.last_mut().unwrap().push(SimpleStatement::L), + '.' => loop_stack.last_mut().unwrap().push(SimpleStatement::Out), + ',' => loop_stack.last_mut().unwrap().push(SimpleStatement::In), + '[' => loop_stack.push(vec![]), + ']' => { + let statement = SimpleStatement::Loop(loop_stack.pop().unwrap()); + loop_stack.last_mut().unwrap().push(statement); + } + _ => () + } + } + + return loop_stack.pop().unwrap(); +} + + +/// +/// # optimization time +/// +/// first parse the bf so that it can be executed faster +/// most importantly: loop jumps should be immediate +/// +pub(crate) mod o1 { + use std::io::{Read, stdin}; + + use crate::interpreter::{MEM_SIZE, Memory, minify, parse, SimpleStatement}; + + pub fn run(pgm: &str) -> String { + let pgm = minify(pgm); + let pgm = parse(pgm.chars().collect()); + let out = interpret(&pgm); + out + } + + fn interpret(pgm: &Vec) -> String { + let mut out = String::new(); + let mut pointer: usize = 0; + let mut mem: [u8; MEM_SIZE] = [0; MEM_SIZE]; + + for s in pgm { + execute(s, &mut mem, &mut pointer, &mut out) + } + + out + } + + fn execute(statement: &SimpleStatement, mem: &mut Memory, pointer: &mut usize, out: &mut String) { + match statement { + SimpleStatement::R => if *pointer == MEM_SIZE - 1 { *pointer = 0 } else { *pointer += 1 }, + SimpleStatement::L => if *pointer == 0 { *pointer = MEM_SIZE - 1 } else { *pointer -= 1 }, + SimpleStatement::Inc => mem[*pointer] = mem[*pointer].wrapping_add(1), + SimpleStatement::Dec => mem[*pointer] = mem[*pointer].wrapping_sub(1), + SimpleStatement::Out => out.push(mem[*pointer] as u8 as char), + SimpleStatement::In => { + let mut in_buffer = [0, 1]; + stdin().read(&mut in_buffer).unwrap(); + mem[*pointer] = in_buffer[0] as u8; + } + SimpleStatement::Loop(vec) => { + while mem[*pointer] != 0 { + for s in vec { + execute(&s, mem, pointer, out); + } + } + } + } + } + + + #[cfg(test)] + mod test { + use crate::interpreter::o1::{execute, run, Statement}; + use crate::interpreter::o1::Statement::{Dec, In, Inc, L, Loop, Out, R}; + use crate::interpreter::parse; + use crate::o1::{execute, parse, run, Statement::{self, Dec, In, Inc, L, Loop, Out, R}}; + + #[test] + fn execute_simple() { + let mut pointer: usize = 0; + let mut mem: [u8; 65535] = [0; 65535]; + let mut out = String::new(); + + execute(&Statement::R, &mut mem, &mut pointer, &mut out); + assert_eq!(pointer, 1); + execute(&Statement::L, &mut mem, &mut pointer, &mut out); + assert_eq!(pointer, 0); + execute(&Statement::Inc, &mut mem, &mut pointer, &mut out); + assert_eq!(mem[pointer], 1); + execute(&Statement::Dec, &mut mem, &mut pointer, &mut out); + assert_eq!(mem[pointer], 0); + } + + #[test] + fn execute_false_loop() { + let statement = Statement::Loop(vec![Statement::Inc, Statement::Inc, Statement::R]); + let mut pointer: usize = 0; + let mut mem: [u8; 65535] = [0; 65535]; + + execute(&statement, &mut mem, &mut pointer, &mut String::new()); + assert_eq!(mem[0], 0); + assert_eq!(mem[1], 0); + } + + #[test] + fn execute_loop() { + let statement = Statement::Loop(vec![Statement::Inc, Statement::Inc, Statement::R]); + let mut pointer: usize = 0; + let mut mem: [u8; 65535] = [0; 65535]; + mem[0] = 1; + + execute(&statement, &mut mem, &mut pointer, &mut String::new()); + assert_eq!(mem[0], 3); + assert_eq!(mem[1], 0); + } + + #[test] + fn run_loop() { + let program = "++++++++++[>++++++++++<-]>."; + let out = run(program); + assert_eq!(out, String::from("d")); + } + + #[test] + fn hello_world() { + let program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; + let out = run(program); + assert_eq!(out, String::from("Hello World!\n")); + } + } +} + +/// +/// # optimization time +/// some better optimizations like set null, repeating and doing more stuff with simplifying stuff +pub(crate) mod o2 { + use std::io::{Read, stdin}; + + use crate::interpreter::{minify, parse, SimpleStatement}; + + const MEM_SIZE: usize = 0xFFFF; + + type Memory = [u8; MEM_SIZE]; + + enum Statement { + Inc, + Dec, + R, + L, + Out, + In, + Loop(Vec), + SetNull, + } + + + pub fn run(pgm: &str) -> String { + let pgm = minify(pgm); + let pgm = parse(pgm.chars().collect()); + let pgm = optimize(&pgm); + let out = interpret(&pgm); + out + } + + fn optimize(code: &Vec) -> Vec { + code.iter().map(|s| { + match s { + SimpleStatement::Loop(v) => { + if let [SimpleStatement::Dec] = v[..] { + Statement::SetNull + } else { + Statement::Loop(optimize(v)) + } + } + SimpleStatement::Inc => Statement::Inc, + SimpleStatement::Dec => Statement::Dec, + SimpleStatement::R => Statement::R, + SimpleStatement::L => Statement::L, + SimpleStatement::Out => Statement::Out, + SimpleStatement::In => Statement::In, + } + }).collect() + } + + fn interpret(pgm: &Vec) -> String { + let mut out = String::new(); + let mut pointer: usize = 0; + let mut mem: [u8; MEM_SIZE] = [0; MEM_SIZE]; + + for s in pgm { + execute(s, &mut mem, &mut pointer, &mut out) + } + + out + } + + 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 }, + Statement::L => if *pointer == 0 { *pointer = MEM_SIZE - 1 } else { *pointer -= 1 }, + Statement::Inc => mem[*pointer] = mem[*pointer].wrapping_add(1), + Statement::Dec => mem[*pointer] = mem[*pointer].wrapping_sub(1), + Statement::SetNull => mem[*pointer] = 0, + Statement::Out => out.push(mem[*pointer] as u8 as char), + Statement::In => { + let mut in_buffer = [0, 1]; + stdin().read(&mut in_buffer).unwrap(); + mem[*pointer] = in_buffer[0] as u8; + } + Statement::Loop(vec) => { + while mem[*pointer] != 0 { + for s in vec { + execute(&s, mem, pointer, out); + } + } + } + } + } + + + #[cfg(test)] + mod test { + use crate::interpreter::o2::{execute, run}; + use crate::interpreter::parse; + use crate::interpreter::SimpleStatement::{Dec, In, Inc, L, Loop, Out, R}; + use crate::o2::{execute, parse, run}; + + #[test] + fn run_loop() { + let program = "++++++++++[>++++++++++<-]>."; + let out = run(program); + assert_eq!(out, String::from("d")); + } + + #[test] + fn hello_world() { + let program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; + let out = run(program); + assert_eq!(out, String::from("Hello World!\n")); + } + } +} + +#[cfg(test)] +mod tests { + use crate::interpreter::parse; + use crate::interpreter::SimpleStatement::{Dec, In, Inc, L, Loop, Out, R}; + + #[test] + fn parse_no_loop() { + let program = "+-<>,."; + let statements = vec![Inc, Dec, L, R, In, Out]; + let result = parse(program.chars().collect()); + + assert_eq!(statements, result); + } + + #[test] + fn parse_simple_loop() { + let program = "+[<<]-"; + let statements = vec![Inc, Loop(vec![L, L]), Dec]; + let result = parse(program.chars().collect()); + + assert_eq!(statements, result); + } + + #[test] + fn parse_complex_loops() { + let program = ">[<[][<[<]>]>[>]]"; + let statements = vec![R, Loop(vec![L, Loop(vec![]), Loop(vec![L, Loop(vec![L]), R]), R, Loop(vec![R])])]; + let result = parse(program.chars().collect()); + + assert_eq!(statements, result); + } +} \ No newline at end of file diff --git a/bfi-rust/src/main.rs b/bfi-rust/src/main.rs index bfe1a5d..da1321a 100644 --- a/bfi-rust/src/main.rs +++ b/bfi-rust/src/main.rs @@ -1,7 +1,9 @@ +mod interpreter; + use std::{env, fs}; -use std::io::{Read, stdin}; use std::time::SystemTime; + fn main() { let path = env::args().skip(1).next(); let path = match path { @@ -18,191 +20,12 @@ fn main() { fn run(path: String) { println!("Path: {}", path); let program = fs::read_to_string(path).unwrap(); - let start = SystemTime::now(); - let out = o1::run(&*program); - println!("{}\nFinished execution in {}ms", out, start.elapsed().unwrap().as_millis()); -} - -/// -/// # optimization time -/// -/// first parse the bf so that it can be executed faster -/// most importantly: loop jumps should be immediate -/// -mod o1 { - use std::io::{stdin, Read, stdout, Write}; - - const MEM_SIZE: usize = 0xFFFF; - - type Memory = [u8; MEM_SIZE]; - - - pub fn run(pgm: &str) -> String { - let pgm = minify(pgm); - let pgm = parse(pgm.chars().collect()); - let out = interpret(&pgm); - out - } - - /// - /// A single Statement, can be an instruction or a nestable loop - #[derive(Debug, PartialOrd, PartialEq)] - enum Statement { - Inc, - Dec, - R, - L, - Out, - In, - Loop(Vec), - } - - fn minify(code: &str) -> String { - let allowed: Vec = vec!['>', '<', '+', '-', '.', ',', '[', ']']; - code.chars().filter(|c| allowed.contains(c)).collect() - } - - fn parse(chars: Vec) -> Vec { - let mut loop_stack = vec![vec![]]; - - for c in chars { - match c { - '+' => loop_stack.last_mut().unwrap().push(Statement::Inc), - '-' => loop_stack.last_mut().unwrap().push(Statement::Dec), - '>' => loop_stack.last_mut().unwrap().push(Statement::R), - '<' => loop_stack.last_mut().unwrap().push(Statement::L), - '.' => loop_stack.last_mut().unwrap().push(Statement::Out), - ',' => loop_stack.last_mut().unwrap().push(Statement::In), - '[' => loop_stack.push(vec![]), - ']' => { - let statement = Statement::Loop(loop_stack.pop().unwrap()); - loop_stack.last_mut().unwrap().push(statement); - } - _ => () - } - } - - return loop_stack.pop().unwrap(); - } - - fn interpret(pgm: &Vec) -> String { - let mut out = String::new(); - let mut pointer: usize = 0; - let mut mem: [u8; MEM_SIZE] = [0; MEM_SIZE]; - - for s in pgm { - execute(s, &mut mem, &mut pointer, &mut out) - } - - out - } - - 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 }, - Statement::L => if *pointer == 0 { *pointer = MEM_SIZE - 1 } else { *pointer -= 1 }, - Statement::Inc => mem[*pointer] = mem[*pointer].wrapping_add(1), - Statement::Dec => mem[*pointer] = mem[*pointer].wrapping_sub(1), - Statement::Out => out.push(mem[*pointer] as u8 as char), - Statement::In => { - let mut in_buffer = [0, 1]; - stdin().read(&mut in_buffer).unwrap(); - mem[*pointer] = in_buffer[0] as u8; - } - Statement::Loop(vec) => { - while mem[*pointer] != 0 { - for s in vec { - execute(&s, mem, pointer, out); - } - } - } - } - } - - - #[cfg(test)] - mod test { - use crate::o1::{parse, Statement::{self, Inc, Dec, R, L, In, Out, Loop}, execute, run}; - - #[test] - fn parse_no_loop() { - let program = "+-<>,."; - let statements = vec![Inc, Dec, L, R, In, Out]; - let result = parse(program.chars().collect()); - - assert_eq!(statements, result); - } - - #[test] - fn parse_simple_loop() { - let program = "+[<<]-"; - let statements = vec![Inc, Loop(vec![L, L]), Dec]; - let result = parse(program.chars().collect()); - - assert_eq!(statements, result); - } - - #[test] - fn parse_complex_loops() { - let program = ">[<[][<[<]>]>[>]]"; - let statements = vec![R, Loop(vec![L, Loop(vec![]), Loop(vec![L, Loop(vec![L]), R]), R, Loop(vec![R])])]; - let result = parse(program.chars().collect()); - - assert_eq!(statements, result); - } - - - #[test] - fn execute_simple() { - let mut pointer: usize = 0; - let mut mem: [u8; 65535] = [0; 65535]; - let mut out = String::new(); - - execute(&Statement::R, &mut mem, &mut pointer, &mut out); - assert_eq!(pointer, 1); - execute(&Statement::L, &mut mem, &mut pointer, &mut out); - assert_eq!(pointer, 0); - execute(&Statement::Inc, &mut mem, &mut pointer, &mut out); - assert_eq!(mem[pointer], 1); - execute(&Statement::Dec, &mut mem, &mut pointer, &mut out); - assert_eq!(mem[pointer], 0); - } - - #[test] - fn execute_false_loop() { - let statement = Statement::Loop(vec![Statement::Inc, Statement::Inc, Statement::R]); - let mut pointer: usize = 0; - let mut mem: [u8; 65535] = [0; 65535]; - - execute(&statement, &mut mem, &mut pointer, &mut String::new()); - assert_eq!(mem[0], 0); - assert_eq!(mem[1], 0); - } - - #[test] - fn execute_loop() { - let statement = Statement::Loop(vec![Statement::Inc, Statement::Inc, Statement::R]); - let mut pointer: usize = 0; - let mut mem: [u8; 65535] = [0; 65535]; - mem[0] = 1; - - execute(&statement, &mut mem, &mut pointer, &mut String::new()); - assert_eq!(mem[0], 3); - assert_eq!(mem[1], 0); - } - - #[test] - fn run_loop() { - let program = "++++++++++[>++++++++++<-]>."; - let out = run(program); - assert_eq!(out, String::from("d")); - } - - #[test] - fn hello_world() { - let program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; - let out = run(program); - assert_eq!(out, String::from("Hello World!\n")); - } - } + let start1 = SystemTime::now(); + let out = interpreter::o1::run(&*program); + let end1 = start1.elapsed().unwrap(); + let start2 = SystemTime::now(); + let out2 = interpreter::o2::run(&*program); + let end2 = start2.elapsed().unwrap(); + assert_eq!(out, out2); + println!("{}\nFinished execution. Took o1: {}ms, o2: {}ms", out, end1.as_millis(), end2.as_millis()); } \ No newline at end of file diff --git a/bfi-rust/src/o2.rs b/bfi-rust/src/o2.rs new file mode 100644 index 0000000..e69de29