I wrote this small Brainfuck interpreter in C++ where the different op codes are handled in one big switch-case statement instead of something like a tokenizer as Brainfuck is very simple in that regard.
Furthermore, I split the logic into namespaces as I wanted to emulate or have the behaviour as if it was a static utility method (like static in Java).
My cells array has a size of 30 000 but is technically an infinite tape as I read somewhere that that size is appropriate for Brainfuck. I also differentiate between handling the input and output as ASCII characters or just numbers in case the user wants to just print Hello Word or compute 1+1 but I feel like there must be a better solution. unsigned char is the datatype I store the current values in as it goes from 0 to 255 which is what Brainfuck specifies I believe.
brainfuck.h
#include <string>
namespace Brainfuck {
void execute(const std::string &input, std::string &output, const char &ascii);
}
brainfuck.cpp
#include "brainfuck.h"
#include <array>
#include <string>
#include <algorithm>
#include <iostream>
namespace Brainfuck {
namespace {
std::array<unsigned char, 30'000> cells;
std::string input_;
int current_index = 0;
int current_char = 0;
void remove_spaces(std::string &input) {
input.erase(std::remove_if(input.begin(), input.end(), ::isspace), input.end());
}
void remove_new_lines(std::string &input) {
input.erase(std::remove(input.begin(), input.end(), '\n'), input.end());
}
int find_matching_end_bracket(int current_char, const std::string &input) {
int new_index = 0;
int expected = 0;
int found = 0;
for (;current_char < input.length(); current_char++) {
if(input[current_char] == '[') expected++;
if(input[current_char] == ']') {
found++;
if(expected == found) {
new_index = current_char;
break;
}
}
}
return new_index;
}
int find_matching_start_bracket(int current_char, const std::string &input) {
int new_index = 0;
int expected = 0;
int found = 0;
for (;current_char >= 0; current_char--) {
if(input[current_char] == ']') expected++;
if(input[current_char] == '[') {
found++;
if(expected == found) {
new_index = current_char;
break;
}
}
}
return new_index;
}
void op_codes(const char &cur, std::string &output, const char &ascii) {
switch (cur)
{
case '>':
if(current_index == cells.size() - 1) {
current_index = 0;
} else {
current_index++;
}
break;
case '<':
if(current_index == 0) {
current_index = cells.size() - 1;
} else {
current_index--;
}
break;
case '+':
cells[current_index]++;
break;
case '-':
cells[current_index]--;
break;
case '.':
if(ascii == 'n') {
output += std::to_string(cells[current_index]);
}
if(ascii == 'y') {
output += cells[current_index];
}
break;
case ',':
{
std::string in;
std::cout << "Enter input between 0 and 255: ";
std::cin >> in;
if(!in.empty() && std::all_of(in.begin(), in.end(), ::isdigit)) {
auto c_in = stoi(in);
if(c_in >= 0 && c_in <= 255) {
cells[current_index] = c_in;
} else {
std::cout << "Not a valid input! Enter a number between 0 and 255!" << std::endl;
}
} else {
std::cout << "Not a valid input!" << std::endl;
}
break;
}
case '[':
if(cells[current_index] == 0) {
current_char = find_matching_end_bracket(current_char, input_);
}
break;
case ']':
if(cells[current_index] != 0) {
current_char = find_matching_start_bracket(current_char, input_);
}
break;
default:
std::cout << "Not a valid Brainfuck program! Unknown symbol at " << current_index << std::endl;
break;
}
}
void reset() {
std::fill(cells.begin(), cells.end(), 0);
current_char = 0;
current_index = 0;
input_ = "";
}
}
void execute(const std::string &input, std::string &output, const char &ascii) {
input_ = input;
remove_spaces(input_);
remove_new_lines(input_);
while(current_char < input_.length()) {
op_codes(input_[current_char], output, ascii);
current_char++;
}
reset();
}
}
main.cpp
#include "brainfuck.h"
#include <iostream>
#include <string>
int main(int, char**) {
std::string brainfuck_code;
std::string output;
char ascii;
std::cout << "Enter your brainfuck code: ";
std::cin >> brainfuck_code;
do {
std::cout << "Do you wish your output to be letters? y/n ";
std::cin >> ascii;
} while(ascii != 'y' && ascii != 'n');
Brainfuck::execute(brainfuck_code, output, ascii);
std::cout << "\n";
std::cout << output << std::endl;
return EXIT_SUCCESS;
}
unsigned charto be larger (not smaller). Useuint8_tif that's exactly what you need. \$\endgroup\$std::uint8_texists, thenunsigned charmust be 0 to 255 (andstd::uint8_tprobably isunsigned char). Ifunsigned charis not 0 to 255, thenstd::uint8_tcan’t exist. If you want to be truly portable, you’d have to usestd::uint_fast8_torstd::uint_least8_t… orunsigned char. (Or, maybe,std::byte.)uint8_tis actually the least portable option. \$\endgroup\$