- #include <unordered_map> 
- #include <stdio.h> 
- #include <string> 
- #include <vector> 
- using std::unordered_map; 
- using std::string; 
- using std::vector; 
- const int TAPE_LEN = 30*1000; 
- struct Jumps { 
- // forward maps from a forward brace index 
- // to the matching closing brace. 
- unordered_map<int, int> forward; 
- // backward maps from closing brace index 
- // to the matching opening brace 
- unordered_map<int, int> backward; 
- };
- Jumps build_jumps(const string& prog) { 
- /* 
- build tables to lookup the matching
- brace for a program.
- */
- Jumps result;
- vector<int> stack; 
- int i = 0; 
- while(i < prog.size()) { 
- if (prog[i] == '[') { 
- stack.push_back(i); 
- } else if (prog[i] == ']') { 
- if (stack.size() == 0) { 
- puts("no [ on stack."); 
- exit(1); 
- }
- int match = stack.back(); stack.pop_back(); 
- result.backward[i] = match; 
- result.forward[match] = i; 
- }
- i += 1; 
- }
- if (stack.size() > 0) { 
- puts("jump stack not empty"); 
- exit(1); 
- }
- return result; 
- }
- int jump(const unordered_map<int, int>& jumps, 
- int index) { 
- auto it = jumps.find(index); 
- if (it != jumps.end()) { 
- return it->second; 
- } else { 
- printf("no jumps for %d\n", index); 
- exit(1); 
- }
- }
- void bf(const string& prog) { 
- vector<uint8_t> tape;
- tape.resize(TAPE_LEN); 
- Jumps jumps = build_jumps(prog); 
- int ptr = 0; 
- int pc = 0; 
- int len = prog.length(); 
- while(pc < len && ptr >= 0 && ptr < tape.size()) { 
- // printf("pc: %d\n", pc); 
- char c = prog[pc]; 
- switch(c) { 
- case '+': 
- tape[ptr]++;
- break; 
- case '-': 
- tape[ptr]--;
- break; 
- case '>': 
- ptr += 1; 
- break; 
- case '<': 
- ptr -= 1; 
- break; 
- case '[': 
- if(tape[ptr] == 0) 
- pc = jump(jumps.forward, pc); 
- break; 
- case ']': 
- if(tape[ptr] != 0) 
- pc = jump(jumps.backward, pc); 
- break; 
- case '.': 
- printf("%c", (char)tape[ptr]); 
- // fflush(stdout); // flush for debugging 
- break; 
- case ',': 
- puts(", unimplemented"); 
- exit(1); 
- break; 
- default: 
- printf("unhandled instn: '%c'\n", c); 
- exit(1); 
- }
- pc += 1; 
- }
- }
- string slurp(const string& path) { 
- string output;
- FILE* f = fopen(path.c_str(), "rb"); 
- if (!f) { 
- perror("failed to open file"); 
- exit(1); 
- }
- char buffer[1024]; 
- int goal = sizeof(buffer)-1; 
- while(true) { 
- size_t n = fread(buffer, 1, goal, f); 
- if (n < 0) { 
- perror("file read error"); 
- exit(1); 
- }
- buffer[n] = 0; 
- output.append(buffer, n); 
- if (n < goal) 
- break; 
- }
- return output; 
- }
- string clean(const string& input) { 
- string program;
- program.reserve(input.length()); 
- int i = 0; 
- while (i < input.length()) { 
- char c = input[i]; 
- if (c == ';') { 
- // drop until newline 
- while (i < input.length() && 
- input[i] != '\n') { 
- i++;
- }
- continue; 
- }
- switch(c) { 
- case '\n': 
- [[fallthrough]];
- case ' ': 
- break; 
- default: 
- program.push_back(c); 
- }
- i++;
- }
- return program; 
- }
- int main(int argc, char** argv) { 
- if (argc < 2) { 
- puts("usage: ./bf file.bf"); 
- return 0; 
- }
- string program = slurp(argv[1]); 
- program = clean(program); 
- bf(program); 
- return 0; 
- }