Yet Another Brain F*ck Interpreter

-- See project here

What is BF?

Brain F*ck, also known as BF and brain-lang, is an esoteric programming language. If you are unfamiliar this video is a great primer. With only 8 symbols +-[].,>< the language is deceptively simple, almost unusable. At its core BF has a static sized buffer. This buffer stores a byte, initialized with 0. To interact with the buffer you use +- to increment and decrement the buffer at the current pointer. >< shifts the pointer to the right and left. . prints the current value at the pointer as an ascii character. , requests a number from STDIN and places it in the pointer position.

What is YABFI?

Yet Another Brain F*ck Interpreter is just another interpreter for BF, written in Rust. While the world didn't need ANOTHER Rust powered BFI, I really wanted to write one. I figured since I'm still learning Rust it would make for another awesome project.

Constraints

As will all of my projects I started with constraints and project steps. The constraints were as follows...

Challenges

Ultimately this project went smoother than I expect, because Brain-lang is very well documented. So to not spoil the fun I avoided looking at other BFI implementations.

The first issue I ran into was supporting wrap around addition and subtraction. In BF if I execute 0-1 it should return 255, which does not play well with Rust's u8 type, it immediately throws an overflow error. So I considered switching to i16 to abuse the signed bit, but there were many issues with this and the Rust compile caught me out. After digging through the Rust docs I found wrapping_add() (Read more here). This allows me to do that math but ignore the carrying digit. Additionally the wrapping_sub() does the same for me going in the opposite direction.

The final challenge was figuring out how to handle the loops. BF Loops are pretty simple. When you encounter a [ if the current pointer value is 0 you skip to the matching ]. When you encounter a ] you jump back to the matching [. The way I handled this was actually pretty simple.

I do this with a single Vec<usize>. All I do it I push() the program cursor position to the stack when I see a [. When I see a ] I pop() the loop stack and store that. If the buf[pointer] value is 0 I move forward exiting the loop otherwise I skip the cursor back the popped value. The stack of course is so I can have multiple nested loops and keep track of them all.

match program[cursor] as char {
  // -- snip --
  '[' => stack.push(cursor),
  ']' => {
      let start = stack.pop().unwrap();
      // Set the cursor back to the start of the loop
      if arr[p] != 0 {
          cursor = start;
          continue;
      }
  }
}

Conclusion

YABFI is a silly little program that was written in about 3 hours. I had a lot of fun writing it and was given an opportunity to dive into Rust's primitive type methods. I think the idea of creating a Brain-lang CLI interpreter with a consistent buffer is fairly novel. 10/10 would write again.