Skip to content

Instantly share code, notes, and snippets.

@maxtruxa
Last active November 28, 2017 00:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save maxtruxa/f0725a3a232661336c19 to your computer and use it in GitHub Desktop.
Save maxtruxa/f0725a3a232661336c19 to your computer and use it in GitHub Desktop.
C++ BrainFuck Interpreter
#include <iostream>
#include <fstream>
#include <vector>
#include <Windows.h> // MessageBox
size_t const DATA_SIZE_START = 0x0000400; // 1 KiB
size_t const DATA_SIZE_MAX = 0x1000000; // 16 MiB
enum token_t : char
{
NextField = '>',
PreviousField = '<',
IncrementField = '+',
DecrementField = '-',
Output = '.',
Input = ',',
LoopBegin = '[',
LoopEnd = ']'
};
void DisplayError(char const* message)
{
MessageBoxA(NULL, message, "Error", MB_OK | MB_ICONERROR);
}
int main(int argc, char const* argv[])
{
if (argc != 2)
{
DisplayError("No input file supplied.");
return 1;
}
std::string inputFileName = argv[1];
std::vector<char> contents;
// Extra scope so that ifs will be destructed after being read.
{
std::ifstream ifs;
std::istreambuf_iterator<char> begin;
if (inputFileName.compare("-") == 0)
{
begin = std::cin;
}
else
{
ifs.open(inputFileName);
if (!ifs)
{
DisplayError("Unable to open input file.");
return 1;
}
begin = ifs;
}
contents = std::vector<char>(begin, std::istreambuf_iterator<char>());
}
std::vector<char> data(DATA_SIZE_START);
auto dp = data.begin();
for (auto ip = contents.cbegin(); ip != contents.cend(); ++ip)
{
switch (*ip)
{
case NextField:
if (dp == data.end())
{
if (data.size() == DATA_SIZE_MAX)
{
DisplayError("STACK_OVERFLOW");
return 1;
}
data.resize(std::min(data.size() * 2, DATA_SIZE_MAX));
dp = data.begin();
}
++dp;
break;
case PreviousField:
if (dp == data.begin())
{
DisplayError("ACCESS_VIOLATION");
return 1;
}
--dp;
break;
case IncrementField:
++(*dp);
break;
case DecrementField:
--(*dp);
break;
case Output:
putchar(*dp);
break;
case Input:
*dp = getchar();
break;
case LoopBegin:
if (*dp == 0)
{
size_t level = 1;
for (++ip; ip != contents.cend() && level > 0; ++ip)
{
switch (*ip)
{
case LoopBegin: ++level; break;
case LoopEnd: --level; break;
}
}
if (level > 0)
{
DisplayError("INVALID_LOOP");
return 1;
}
}
break;
case LoopEnd:
if (*dp != 0)
{
size_t level = 1;
for (--ip; ip != contents.cbegin() && level > 0; --ip)
{
switch (*ip)
{
case LoopBegin: --level; break;
case LoopEnd: ++level; break;
}
}
if (level > 0)
{
DisplayError("INVALID_LOOP");
return 1;
}
}
break;
default:
// Unknown tokens are simply ignored.
break;
}
}
return *dp;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment