a simple brainfuck interpreter in rust
Find a file
2026-02-09 18:20:57 +01:00
src src/main.rs: simplify imports from std::io. 2026-02-09 18:20:57 +01:00
.gitignore initial commit with tested run_program{,_once} functions 2026-02-05 15:16:41 +01:00
Cargo.lock initial commit with tested run_program{,_once} functions 2026-02-05 15:16:41 +01:00
Cargo.toml initial commit with tested run_program{,_once} functions 2026-02-05 15:16:41 +01:00
README.md src/lib.rs: retain indices during loop delimiter balance check and reuse during interpretation to avoid linear scans of the program source each time a loop delimiter is interpreted. 2026-02-09 17:05:10 +01:00

my first brainfuck interpreter

a simple brainfuck interpreter. based off of the esolang wiki page for the language. written as a learning exercise, not intended for production use (though when is brainfuck intended for production use?)

todo

  • implement the , command/instruction (read a single ASCII character from input)
  • increase minimum tape size to 30k cells
  • wrap on cell overflow
  • wrap on cell underflow
  • make the interpreter print output to screen "when it happens". Currently the interpreter waits until the program it is interpreting terminates, and then prints out the concatenated output from the entire run.
  • when no input bytes can be read, instead of crashing, don't change the contents of the cell that is currently pointed at.
  • improve memory use: don't allocate a new String for each instruction interpreted. this might be solved by addressing the previous todo.
  • check that loop delimiters are balanced (each start has a matching end, and vice-versa) before starting execution of the program itself
  • add the tests from https://www.brainfuck.org/tests.b to the repo, and make sure that the interpreter passes them: see next section
  • optimization: avoid linear search for matching loop delimiter on each [ and ] interpretation by retaining computed loop bound indices from initial balance check
  • allow specifying a file as program input via command line arg (separately from the program's source). ex: interpreter program.b --input=input.txt
  • allow specifying longer tape length (via command line arg?)
  • allow specifying cell size larger than 8 bits.
  • Depends on previous item: make the . command/instruction (print the pointed cell's value as ASCII) robust by applying modulo 256 to the cell's value (i.e. don't attempt to directly convert a value greater than 255 to an ASCII character)
  • allow toggling tape/pointer wrapping (e.g. left of position #0 -> right end of tape)
  • implement a visualizer of the tape's cells and pointer head

tests from https://www.brainfuck.org/tests.b

  • ,>+++++++++,>+++++++++++[<++++++<++++++<+>>>-]<<.>.<<-.>.>.<<.:
    This is for testing i/o; give it a return followed by an EOF. (Try it both with file input--a file consisting only of one blank line--and with keyboard input, i.e. hit return and then ctrl-d (Unix) or ctrl-z (Windows).)
    It should give two lines of output; the two lines should be identical, and should be lined up one over the other. If that doesn't happen, ten is not coming through as newline on output.
    The content of the lines tells how input is being processed; each line should be two uppercase letters.
    Anything with O in it means newline is not coming through as ten on input.
    LK means newline input is working fine, and EOF leaves the cell unchanged (which I recommend).
    LB means newline input is working fine, and EOF translates as 0.
    LA means newline input is working fine, and EOF translates as -1.
    Anything else is fairly unexpected.
  • ++++[>++++++<-]>[>+++++>+++++++<<-]>>++++<[[>[[>>+<<-]<]>>>-]>-[>+>+<<-]>]+++++[>+++++++<<++>-]>.<<.:
    Goes to cell 30000 and reports from there with a #. (Verifies that the array is big enough.)
  • bounds checking:
    These next two test the array bounds checking. Bounds checking is not essential, and in a high-level implementation it is likely to introduce extra overhead. In a low-level implementation you can get bounds checking for free by using the OS's own memory protections; this is the best solution, which may require making the array size a multiple of the page size.
    Anyway. These two programs measure the "real" size of the array, in some sense, in cells left and right of the initial cell respectively. They output the result in unary; the easiest thing is to direct them to a file and measure its size, or (on Unix) pipe the output to wc. If bounds checking is present and working, the left should measure 0 and the right should be the array size minus one.
    • +[<+++++++++++++++++++++++++++++++++.]: left (start)
    • +[>+++++++++++++++++++++++++++++++++.]: right (end)
  • []++++++++++[>>+>+>++++++[<<+<+++>>>-]<<<<-]"A*$";?@![#>>+<<]>[>>]<<<<[>++<[-]]>.>.:
    Tests for several obscure problems. Should output an H.
  • +++++[>+++++++>++<<-]>.>.[:
    Should ideally give error message "unmatched [" or the like, and not give any output. Not essential.
  • +++++[>+++++++>++<<-]>.>.][:
    Should ideally give error message "unmatched ]" or the like, and not give any output. Not essential.

see also https://www.brainfuck.org/brainfuck.html, https://www.brainfuck.org/epistle.html, and https://www.brainfuck.org in general.