Created
May 10, 2011 11:56
-
-
Save migimunz/964338 to your computer and use it in GitHub Desktop.
A simple brainfuck interpreter in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <vector> | |
#include <iterator> | |
#include <sstream> | |
#include <algorithm> | |
#include <iostream> | |
#include <fstream> | |
#include <ctype.h> | |
//typedefs | |
typedef char WORD; | |
typedef std::vector<WORD> instruction_t; | |
typedef instruction_t::iterator instruction_pointer; | |
typedef std::vector<WORD> memory_t; | |
typedef memory_t::iterator memory_pointer; | |
//structs | |
struct environment | |
{ | |
memory_t memory; | |
instruction_t instructions; | |
instruction_pointer ip; | |
memory_pointer mp; | |
environment() : memory(3000) | |
{ | |
clear(); | |
mp = memory.begin(); | |
} | |
void clear() | |
{ | |
instructions.clear(); | |
ip = instructions.begin(); | |
} | |
}; | |
//prototypes | |
void interpret(environment &env); | |
int from_line(environment &env, std::string &line, int open_brackets = 0); | |
int from_stream(environment &env, std::istream &stream, int open_brackets = 0); | |
void interactive_mode(environment &env); | |
void print_word(WORD word); | |
int main(int argc, char** argv) | |
{ | |
environment env; | |
//if no args, read from stdin. else, read from file specified as first argument | |
if(argc == 1) | |
{ | |
interactive_mode(env); | |
} | |
else | |
{ | |
std::ifstream stream = std::ifstream(argv[1]); | |
if(stream.is_open()) | |
{ | |
int open_brackets = from_stream(env, stream); | |
if(open_brackets == 0) | |
interpret(env); | |
else | |
std::cout << "Unmatched brackets!" << std::endl; | |
} | |
else | |
{ | |
std::cout << "File not found : " << argv[1] << std::endl; | |
} | |
} | |
return 0; | |
} | |
/* | |
* Prints a memory cell. If it's not printable, outputs hex representation of it. | |
*/ | |
void print_word(WORD word) | |
{ | |
if(isprint(word)) | |
std::cout << word; | |
else | |
std::cout << "0x" << std::hex << (int)word << std::dec; | |
} | |
/* | |
* Reads bf code from a line, returns number of unmatched brackets (positive for opened, | |
* but not closed, and vice-versa | |
*/ | |
int from_line(environment &env, std::string &line, int open_brackets) | |
{ | |
std::istringstream stream = std::istringstream(line); | |
return from_stream(env, stream, open_brackets); | |
} | |
/* | |
* Reads code from a stream into env.instructions. | |
* '\0' are appended on either side, the interpreter just skips them. | |
*/ | |
int from_stream(environment &env, std::istream &stream, int open_brackets) | |
{ | |
if(open_brackets == 0) | |
env.instructions.push_back('\0'); | |
WORD word; | |
while(true) | |
{ | |
stream >> word; | |
if(!stream) break; | |
switch(word) | |
{ | |
case '<': case '>': case '+': case '-': case ',': case '.': | |
env.instructions.push_back(word); | |
break; | |
case '[': | |
open_brackets++; | |
env.instructions.push_back(word); | |
break; | |
case ']': | |
open_brackets--; | |
env.instructions.push_back(word); | |
break; | |
default: | |
break; | |
} | |
} | |
if(open_brackets == 0) | |
env.instructions.push_back('\0'); | |
return open_brackets; | |
} | |
/* | |
* In interactive mode, the interpreter treats any parsed line of text with all [ and ] | |
* matched as valid code, and interprets it. In case of unmatched brackets, it will wait | |
* for more input. | |
*/ | |
void interactive_mode(environment &env) | |
{ | |
int open_brackets = 0; | |
while(true) | |
{ | |
std::string line; | |
std::getline(std::cin, line); | |
if(!std::cin) break; | |
if(open_brackets == 0) env.clear(); | |
open_brackets = from_line(env, line, open_brackets); | |
if(open_brackets == 0) | |
{ | |
interpret(env); | |
std::cout << ">"; | |
print_word(*env.mp); | |
std::cout << std::endl; | |
} | |
else | |
{ | |
std::cout << "->"; | |
} | |
} | |
} | |
/* | |
* Finds the closing bracket, moves the ip to it | |
*/ | |
void find_closing(environment &env) | |
{ | |
int balance = 1; | |
do | |
{ | |
env.ip++; | |
if(*env.ip == '[') | |
balance++; | |
else if(*env.ip == ']') | |
balance--; | |
}while(balance != 0); | |
} | |
/* | |
* Finds the opening bracket, moves the ip to one cell before it | |
*/ | |
void find_opening(environment &env) | |
{ | |
int balance = 0; | |
do | |
{ | |
if(*env.ip == '[') | |
balance++; | |
else if(*env.ip == ']') | |
balance--; | |
env.ip--; | |
}while(balance != 0); | |
} | |
void interpret(environment &env) | |
{ | |
env.ip = env.instructions.begin(); | |
while(env.ip != env.instructions.end()) | |
{ | |
switch(*env.ip) | |
{ | |
case '+': | |
(*env.mp)++; | |
env.ip++; | |
break; | |
case '-': | |
(*env.mp)--; | |
env.ip++; | |
break; | |
case '>': | |
if(env.mp != (env.memory.end()--)) | |
env.mp++; | |
env.ip++; | |
break; | |
case '<': | |
if(env.mp != env.memory.begin()) | |
env.mp--; | |
env.ip++; | |
break; | |
case '.': | |
print_word(*env.mp); | |
env.ip++; | |
break; | |
case ',': | |
WORD word; | |
std::cin >> word; | |
(*env.mp) = word; | |
env.ip++; | |
break; | |
case '[': | |
if(!(*env.mp)) | |
find_closing(env); | |
env.ip++; | |
break; | |
case ']': | |
find_opening(env); | |
env.ip++; | |
break; | |
case '\0': | |
env.ip++; | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment