brainfuck interpreter
brainfuck cli rust
5

Configure Feed

Select the types of activity you want to include in your feed.

feat: refine runtime error types with specific variants

- split DataPointerOutOfBounds into DataPointerOutsideTapeLength and
DataPointerNegative
- split CellOutOfBounds into CellUnderflow and CellOverflow
- split IoError into IoReadError and IoWriteError
- removed a leftover comment

digi.rip (Jun 7, 2026, 2:46 PM EDT) 7a8f74f7 f4106f7f

+42 -28
+42 -28
src/evaluator.rs
··· 7 7 8 8 #[derive(Debug)] 9 9 pub enum RuntimeError { 10 - DataPointerOutOfBounds(usize, usize), 11 - CellOutOfBounds(usize, usize), 10 + DataPointerOutsideTapeLength(usize, usize, usize), 11 + DataPointerNegative(usize, usize), 12 + CellUnderflow(usize, usize), 13 + CellOverflow(usize, usize), 12 14 InvalidToken(u8, usize, usize), 13 - IoError(std::io::Error), 15 + IoReadError(std::io::Error), 16 + IoWriteError(std::io::Error), 14 17 } 15 18 16 19 impl fmt::Display for RuntimeError { 17 20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 21 match self { 19 - RuntimeError::DataPointerOutOfBounds(line, col) => { 22 + RuntimeError::DataPointerOutsideTapeLength(tape_length, line, col) => { 20 23 write!( 21 24 f, 22 - "Data pointer out of bounds at line {:?}, col {:?}", 23 - line, col 25 + "Data pointer moved past position {} (end of tape) at line {}, col {}", 26 + tape_length, line, col 24 27 ) 25 28 } 26 - RuntimeError::CellOutOfBounds(line, col) => { 29 + RuntimeError::DataPointerNegative(line, col) => { 27 30 write!( 28 31 f, 29 - "Cell value out of bounds at line {:?}, col {:?}", 32 + "Data pointer went left of position 0 at line {}, col {}", 30 33 line, col 31 34 ) 32 35 } 33 - RuntimeError::InvalidToken(line, col, token) => { 36 + RuntimeError::CellUnderflow(line, col) => { 37 + write!(f, "Cell value went below 0 at line {}, col {}", line, col) 38 + } 39 + RuntimeError::CellOverflow(line, col) => { 40 + write!(f, "Cell value exceeded 255 at line {}, col {}", line, col) 41 + } 42 + RuntimeError::InvalidToken(token, line, col) => { 34 43 write!( 35 44 f, 36 - "Invalid token {:?} at at line {:?}, col {:?}, check tokenizer!", 37 - token, line, col 45 + "Invalid token {} at line {}, col {}", 46 + char::from(*token), 47 + line, 48 + col 38 49 ) 39 50 } 40 - RuntimeError::IoError(err) => { 41 - write!(f, "{}", err) 51 + RuntimeError::IoReadError(err) => { 52 + write!(f, "Failed while reading stdin: {}", err) 53 + } 54 + RuntimeError::IoWriteError(err) => { 55 + write!(f, "Failed while writing stdout: {}", err) 42 56 } 43 57 } 44 58 } ··· 151 165 fn move_right(&mut self, current: &Token) -> Result<(), RuntimeError> { 152 166 if self.data_pointer + 1 >= self.tape.len() { 153 167 if self.data_oob == OOBBehavior::Error { 154 - return Err(RuntimeError::DataPointerOutOfBounds( 168 + return Err(RuntimeError::DataPointerOutsideTapeLength( 169 + self.tape.len(), 155 170 current.line, 156 171 current.col, 157 172 )); ··· 168 183 fn move_left(&mut self, current: &Token) -> Result<(), RuntimeError> { 169 184 if self.data_pointer == 0 { 170 185 if self.data_oob == OOBBehavior::Error { 171 - return Err(RuntimeError::DataPointerOutOfBounds( 172 - current.line, 173 - current.col, 174 - )); 186 + return Err(RuntimeError::DataPointerNegative(current.line, current.col)); 175 187 } else if self.data_oob == OOBBehavior::Wrap { 176 188 self.data_pointer = self.tape.len() - 1; 177 189 } ··· 189 201 self.tape[self.data_pointer] = val; 190 202 } 191 203 None => { 192 - return Err(RuntimeError::CellOutOfBounds(current.line, current.col)); 204 + return Err(RuntimeError::CellOverflow(current.line, current.col)); 193 205 } 194 206 } 195 207 } else if self.cell_oob == OOBBehavior::Wrap { ··· 206 218 self.tape[self.data_pointer] = val; 207 219 } 208 220 None => { 209 - return Err(RuntimeError::CellOutOfBounds(current.line, current.col)); 221 + return Err(RuntimeError::CellUnderflow(current.line, current.col)); 210 222 } 211 223 } 212 224 } else if self.cell_oob == OOBBehavior::Wrap { ··· 221 233 222 234 input 223 235 .read_exact(&mut buffer) 224 - .map_err(RuntimeError::IoError)?; 236 + .map_err(RuntimeError::IoReadError)?; 225 237 self.tape[self.data_pointer] = buffer[0]; 226 238 227 239 loop { 228 240 match input.fill_buf() { 229 - Err(e) => return Err(RuntimeError::IoError(e)), 241 + Err(e) => return Err(RuntimeError::IoReadError(e)), 230 242 Ok(r) if r.is_empty() => break, 231 243 Ok(r) if r[0].is_ascii_alphanumeric() => input.consume(1), 232 244 Ok(_) => { ··· 242 254 fn write_ascii<W: std::io::Write>(&mut self, output: &mut W) -> Result<(), RuntimeError> { 243 255 output 244 256 .write_all(&[self.tape[self.data_pointer]]) 245 - .map_err(RuntimeError::IoError)?; 257 + .map_err(RuntimeError::IoWriteError)?; 246 258 247 259 Ok(()) 248 260 } ··· 261 273 Ok(0) => break, // eof 262 274 Ok(_) if single[0].is_ascii_digit() => buffer.push(single[0] as char), 263 275 Ok(_) => break, 264 - Err(e) => return Err(RuntimeError::IoError(e)), 276 + Err(e) => return Err(RuntimeError::IoReadError(e)), 265 277 } 266 278 } 267 279 ··· 269 281 if buffer.len() == 3 { 270 282 loop { 271 283 match input.fill_buf() { 272 - Err(e) => return Err(RuntimeError::IoError(e)), 284 + Err(e) => { 285 + return Err(RuntimeError::IoReadError(e)); 286 + } 273 287 Ok(r) if r.is_empty() => break, 274 288 Ok(r) if r[0].is_ascii_digit() => input.consume(1), 275 289 Ok(_) => { ··· 282 296 283 297 match buffer.parse::<u16>() { 284 298 Ok(val) if val > 255 && self.cell_oob == OOBBehavior::Error => { 285 - return Err(RuntimeError::CellOutOfBounds(current.line, current.col)); 299 + return Err(RuntimeError::CellOverflow(current.line, current.col)); 286 300 } 287 301 Ok(val) => self.tape[self.data_pointer] = val as u8, 288 302 Err(_) => self.tape[self.data_pointer] = 0, ··· 292 306 } 293 307 294 308 fn write_raw<W: std::io::Write>(&mut self, output: &mut W) -> Result<(), RuntimeError> { 295 - write!(output, "{} ", self.tape[self.data_pointer]).map_err(RuntimeError::IoError)?; 309 + write!(output, "{} ", self.tape[self.data_pointer]) 310 + .map_err(|e| RuntimeError::IoWriteError(e))?; 296 311 297 312 Ok(()) 298 313 } ··· 336 351 337 352 b',' => { 338 353 if self.tracing { 339 - // std::io::stdout().flush().ok(); 340 354 print!("\nInput: "); 341 355 std::io::stdout().flush().ok(); 342 356 }