1. #include <unordered_map>
    
  2. #include <stdio.h>
    
  3. #include <string>
    
  4. #include <vector>
    
  5. 
    
  6. using std::unordered_map;
    
  7. using std::string;
    
  8. using std::vector;
    
  9. 
    
  10. const int TAPE_LEN = 30*1000;
    
  11. struct Jumps {
    
  12.     // forward maps from a forward brace index
    
  13.     // to the matching closing brace.
    
  14.     unordered_map<int, int> forward;
    
  15.     // backward maps from closing brace index
    
  16.     // to the matching opening brace
    
  17.     unordered_map<int, int> backward;
    
  18. };
    
  19.     
    
  20. Jumps build_jumps(const string& prog) {
    
  21.     /*
    
  22.     build tables to lookup the matching
    
  23.     brace for a program.
    
  24.     */
    
  25.     Jumps result;
    
  26.     vector<int> stack;
    
  27.     int i = 0;
    
  28.     while(i < prog.size()) {
    
  29.         if (prog[i] == '[') {
    
  30.             stack.push_back(i);
    
  31.         } else if (prog[i] == ']') {
    
  32.             if (stack.size() == 0) {
    
  33.                 puts("no [ on stack.");
    
  34.                 exit(1);
    
  35.             }
    
  36.             int match = stack.back(); stack.pop_back();
    
  37.             result.backward[i] = match;
    
  38.             result.forward[match] = i;
    
  39.         }
    
  40.         i += 1;
    
  41.     }
    
  42.     if (stack.size() > 0) {
    
  43.         puts("jump stack not empty");
    
  44.         exit(1);
    
  45.     }
    
  46.     return result;
    
  47. }
    
  48. 
    
  49. int jump(const unordered_map<int, int>& jumps,
    
  50.     int index) {
    
  51.     auto it = jumps.find(index);
    
  52.     if (it != jumps.end()) {
    
  53.         return it->second;
    
  54.     } else {
    
  55.         printf("no jumps for %d\n", index);
    
  56.         exit(1);
    
  57.     }
    
  58. }
    
  59. void bf(const string& prog) {
    
  60.     vector<uint8_t> tape;
    
  61.     tape.resize(TAPE_LEN);
    
  62.     Jumps jumps = build_jumps(prog);
    
  63.     int ptr = 0;
    
  64.     int pc = 0;
    
  65.     int len = prog.length();
    
  66.     while(pc < len && ptr >= 0 && ptr < tape.size()) {
    
  67.         // printf("pc: %d\n", pc);
    
  68.         char c = prog[pc];
    
  69.         switch(c) {
    
  70.         case '+':
    
  71.             tape[ptr]++;
    
  72.             break;
    
  73.         case '-':
    
  74.             tape[ptr]--;
    
  75.             break;
    
  76.         case '>':
    
  77.             ptr += 1;
    
  78.             break;
    
  79.         case '<':
    
  80.             ptr -= 1;
    
  81.             break;
    
  82.         case '[':
    
  83.             if(tape[ptr] == 0)
    
  84.                 pc = jump(jumps.forward, pc);
    
  85.             break;
    
  86.         case ']':
    
  87.             if(tape[ptr] != 0)
    
  88.                 pc = jump(jumps.backward, pc);
    
  89.             break;
    
  90.         case '.':
    
  91.             printf("%c", (char)tape[ptr]);
    
  92.             // fflush(stdout);  // flush for debugging
    
  93.             break;
    
  94.         case ',':
    
  95.             puts(", unimplemented");
    
  96.             exit(1);
    
  97.             break;
    
  98.         default:
    
  99.             printf("unhandled instn: '%c'\n", c);
    
  100.             exit(1);
    
  101.         }
    
  102.         pc += 1;
    
  103.     }
    
  104. }
    
  105. 
    
  106. string slurp(const string& path) {
    
  107.     string output;
    
  108.     FILE* f = fopen(path.c_str(), "rb");
    
  109.     if (!f) {
    
  110.         perror("failed to open file");
    
  111.         exit(1);
    
  112.     }
    
  113.     char buffer[1024];
    
  114.     int goal = sizeof(buffer)-1;
    
  115.     while(true) {
    
  116.         size_t n = fread(buffer, 1, goal, f);
    
  117.         if (n < 0) {
    
  118.             perror("file read error");
    
  119.             exit(1);
    
  120.         } 
    
  121.         buffer[n] = 0;
    
  122.         output.append(buffer, n);
    
  123.         if (n < goal)
    
  124.             break;
    
  125.     }
    
  126.     return output;
    
  127. }
    
  128. 
    
  129. string clean(const string& input) {
    
  130.     string program;
    
  131.     program.reserve(input.length());
    
  132.     int i = 0;
    
  133.     while (i < input.length()) {
    
  134.         char c = input[i];
    
  135.         if (c == ';') {
    
  136.             // drop until newline
    
  137.             while (i < input.length() && 
    
  138.                 input[i] != '\n') {
    
  139.                 i++;
    
  140.             }
    
  141.             continue;
    
  142.         }
    
  143. 
    
  144.         switch(c) {
    
  145.             case '\n':
    
  146.                 [[fallthrough]];
    
  147.             case ' ':
    
  148.                 break;
    
  149.             default:
    
  150.                 program.push_back(c);
    
  151.         }
    
  152.         i++;
    
  153.     }
    
  154.     return program;
    
  155. }
    
  156.     
    
  157. int main(int argc, char** argv) {
    
  158.     if (argc < 2) {
    
  159.         puts("usage: ./bf file.bf");
    
  160.         return 0;
    
  161.     }
    
  162.     string program = slurp(argv[1]);
    
  163.     program = clean(program);
    
  164.     bf(program);
    
  165.     return 0;
    
  166. }