1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
//! mindjuice //! ========= //! //! Mindjuice is a simple and easy-to-use brainfuck interpreter! //! //! Usage //! ===== //! //! Mindjuice parses and runs brainfuck programs in two stages. First, it converts an input string into a `Vec<Instruction>`, then it can run that instruction vector to produce output. //! //! You can pass anything which implements `Iterator<char>` to the parse function. //! //! For example, parsing and executing static string: //! //! ```rust //! extern crate mindjuice; //! //! use std::io; //! //! // parse_instructions will return Err() if there are any unmatched left or right brackets. //! // Because we know this program doesn't have any unmatched brackets, using `.unwrap()` is fine. //! let instructions = mindjuice::parse_instructions("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".chars()).unwrap(); //! //! let mut buffer = Vec::new(); //! //! // Execute the instructions! //! mindjuice::execute_brainfuck(instructions, // Instructions vec //! &mut buffer, // io::Write to send output to //! io::empty(), // io::Read to get input from //! 30000000u64 // Maximum program iterations to run before returning //! ).unwrap(); //! //! assert_eq!(&buffer[..], b"Hello World!\n"); //! ``` //! //! Note: Because the hello world program example doesn't use the `,` input command, we can use //! `io::empty()` as the input. However, if we provided `io::empty()` for a program which did use //! `,`, `execute_brainfuck()` would loop indefinitely waiting for input. use std::fmt; use std::iter; use std::io; const MEMORY_SIZE: usize = 32768usize; #[derive(Debug)] pub enum Error { /// A right bracket was found with no unmatched left brackets preceding it. UnbalancedRightBracket, /// The input ended before right brackets were found to match all left brackets. UnbalancedLeftBracket, } #[derive(Debug)] pub enum ExecutionTerminationCondition { /// The maximum number of iterations was reached. MaximumIterationsReached, /// The program finished executing all instructions AllInstructionsFinished, } impl fmt::Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { &Error::UnbalancedRightBracket => { write!(formatter, "Expected matching `[` before `]`, found lone `]` first.") }, &Error::UnbalancedLeftBracket => { write!(formatter, "Unbalanced `[`. Expected matching `]`, found end of file.") }, } } } impl fmt::Display for ExecutionTerminationCondition { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { &ExecutionTerminationCondition::MaximumIterationsReached => { write!(formatter, "Maximum iterations reached.") }, &ExecutionTerminationCondition::AllInstructionsFinished => { write!(formatter, "Finished normally.") }, } } } #[derive(Debug)] pub enum Instruction { /// Increment the memory pointer by one MoveRight, /// Decrement the memory pointer by one MoveLeft, /// Increment the memory value at the memory pointer by one Increment, /// Decrement the memory value at the memory pointer by one Decrement, /// Output the value of the current memory pointer as a char Output, /// Set the memory value at the current memory pointer to a char read from stdin. Input, /// This is the left side of a loop. /// If the memory value at the memory pointer is zero, set the next instruction to the /// contained value. JumpToLeft(usize), /// This is the right side of a loop. /// If the memory value at the memory pointer is non-zero, set the next instruction to the /// contained value. JumpToRight(usize), } pub fn parse_instructions<T>(input: T) -> Result<Vec<Instruction>, Error> where T: iter::Iterator<Item=char> { // Vec of opening jumps waiting for a closing jump to find // each u16 is a position in the instructions vec. let mut waiting_opening_jumps = Vec::new(); // Output vec of instructions let mut instructions = Vec::new(); // Main loop to parse for c in input { // Match on the next character let instruction = match c { '>' => Instruction::MoveRight, '<' => Instruction::MoveLeft, '+' => Instruction::Increment, '-' => Instruction::Decrement, '.' => Instruction::Output, ',' => Instruction::Input, '[' => { // instructions.len() is the position where the JumpToLeft is going to be inserted // in the instructions vec. waiting_opening_jumps.push(instructions.len()); // This is a placeholder, this is guaranteed to be replaced when the // corresponding `]` is found, or an error will be thrown. Instruction::JumpToLeft(0usize) }, ']' => { // This pops of the position of the last `[` found. match waiting_opening_jumps.pop() { Some(left_jump) => { // instructions.len() is the position where the JumpToRight is going to be // inserted in the instructions vec. instructions[left_jump] = Instruction::JumpToLeft(instructions.len()); // We can then just construct our JumpToRight using the position of the // JumpToLeft in the instructions vec. Instruction::JumpToRight(left_jump) }, None => { // We found a right bracket where no left brackets were waiting! return Err(Error::UnbalancedRightBracket); } } }, // If we find a character we don't know about, just ignore it and continue. _ => continue, }; instructions.push(instruction); } // Check to make sure there are no more opening left brackets which didn't find a matching // right bracket. if !waiting_opening_jumps.is_empty() { // If there are, throw an error. return Err(Error::UnbalancedLeftBracket); } // Return the instructions generated! return Ok(instructions); } pub fn execute_brainfuck<O, I>(instructions: Vec<Instruction>, mut output: O, mut input: I, maximum_iterations: u64) -> io::Result<ExecutionTerminationCondition> where O: io::Write, I: io::Read { // Program memory, max size is 2^15 let mut memory = [0u8; MEMORY_SIZE]; // Current position in memory let mut memory_position = 0usize; // Next instruction to run let mut next_instruction = 0usize; // Buffer used for reading input let mut read_buf = [0u8; 1]; // u32::MAX as a limit to the number of iterations to run for a single program. for _ in 0..maximum_iterations { if next_instruction >= instructions.len() { // We've reached the end of the instructions return Ok(ExecutionTerminationCondition::AllInstructionsFinished); } match instructions[next_instruction] { Instruction::MoveRight => { // Increment the position by one, and make sure it still fits into memory_size memory_position += 1; memory_position %= MEMORY_SIZE; }, Instruction::MoveLeft => { // Decrement the position by one, and make sure it still fits into memory_size memory_position -= 1; memory_position %= MEMORY_SIZE; }, // Increment the memory value at the current position Instruction::Increment => memory[memory_position] += 1, // Decrement the memory value at the current position Instruction::Decrement => memory[memory_position] -= 1, // Writ the memory value at the current position to the given output Instruction::Output => try!(write!(&mut output, "{}", &(memory[memory_position] as char))), Instruction::Input => { // TODO: More efficient implementation of this perhaps? loop { if try!(input.read(&mut read_buf)) >= 1 { // If we've read at least 1 character, break. break; } } memory[memory_position] = read_buf[0]; } Instruction::JumpToLeft(target_position) => { if memory[memory_position as usize] == 0 { next_instruction = target_position; continue; // this avoids the automatic incrementing of next_instruction below. } }, Instruction::JumpToRight(target_position) => { if memory[memory_position as usize] != 0 { next_instruction = target_position; continue; // this avoids the automatic incrementing of next_instruction below. } }, } next_instruction += 1; } // We reached the maximum iteration count return Ok(ExecutionTerminationCondition::MaximumIterationsReached); }