#include <algorithm>
#include <fstream>
#include <iostream>
#include <stack>
#include <vector>
#include "parser.h"
#include "utils.h"
constexpr int MEMORY_SIZE = 30000;
enum class BfOpKind {
INVALID_OP = 0,
INC_PTR,
DEC_PTR,
INC_DATA,
DEC_DATA,
READ_STDIN,
WRITE_STDOUT,
LOOP_SET_TO_ZERO,
LOOP_MOVE_PTR,
LOOP_MOVE_DATA,
JUMP_IF_DATA_ZERO,
JUMP_IF_DATA_NOT_ZERO
};
const char* BfOpKind_name(BfOpKind kind) {
switch (kind) {
case BfOpKind::INC_PTR:
return ">";
case BfOpKind::DEC_PTR:
return "<";
case BfOpKind::INC_DATA:
return "+";
case BfOpKind::DEC_DATA:
return "-";
case BfOpKind::READ_STDIN:
return ",";
case BfOpKind::WRITE_STDOUT:
return ".";
case BfOpKind::JUMP_IF_DATA_ZERO:
return "[";
case BfOpKind::JUMP_IF_DATA_NOT_ZERO:
return "]";
case BfOpKind::LOOP_SET_TO_ZERO:
return "s";
case BfOpKind::LOOP_MOVE_PTR:
return "m";
case BfOpKind::LOOP_MOVE_DATA:
return "d";
case BfOpKind::INVALID_OP:
return "x";
}
return nullptr;
}
struct BfOp {
BfOp(BfOpKind kind_param, int64_t argument_param)
: kind(kind_param), argument(argument_param) {}
void serialize(std::string* s) const {
*s += BfOpKind_name(kind) + std::to_string(argument);
}
BfOpKind kind = BfOpKind::INVALID_OP;
int64_t argument = 0;
};
std::vector<BfOp> optimize_loop(const std::vector<BfOp>& ops,
size_t loop_start) {
std::vector<BfOp> new_ops;
if (ops.size() - loop_start == 2) {
BfOp repeated_op = ops[loop_start + 1];
if (repeated_op.kind == BfOpKind::INC_DATA ||
repeated_op.kind == BfOpKind::DEC_DATA) {
new_ops.push_back(BfOp(BfOpKind::LOOP_SET_TO_ZERO, 0));
} else if (repeated_op.kind == BfOpKind::INC_PTR ||
repeated_op.kind == BfOpKind::DEC_PTR) {
new_ops.push_back(
BfOp(BfOpKind::LOOP_MOVE_PTR, repeated_op.kind == BfOpKind::INC_PTR
? repeated_op.argument
: -repeated_op.argument));
}
} else if (ops.size() - loop_start == 5) {
if (ops[loop_start + 1].kind == BfOpKind::DEC_DATA &&
ops[loop_start + 3].kind == BfOpKind::INC_DATA &&
ops[loop_start + 1].argument == 1 &&
ops[loop_start + 3].argument == 1) {
if (ops[loop_start + 2].kind == BfOpKind::INC_PTR &&
ops[loop_start + 4].kind == BfOpKind::DEC_PTR &&
ops[loop_start + 2].argument == ops[loop_start + 4].argument) {
new_ops.push_back(
BfOp(BfOpKind::LOOP_MOVE_DATA, ops[loop_start + 2].argument));
} else if (ops[loop_start + 2].kind == BfOpKind::DEC_PTR &&
ops[loop_start + 4].kind == BfOpKind::INC_PTR &&
ops[loop_start + 2].argument == ops[loop_start + 4].argument) {
new_ops.push_back(
BfOp(BfOpKind::LOOP_MOVE_DATA, -ops[loop_start + 2].argument));
}
}
}
return new_ops;
}
std::vector<BfOp> translate_program(const Program& p) {
size_t pc = 0;
size_t program_size = p.instructions.size();
std::vector<BfOp> ops;
std::stack<size_t> open_bracket_stack;
while (pc < program_size) {
char instruction = p.instructions[pc];
if (instruction == '[') {
open_bracket_stack.push(ops.size());
ops.push_back(BfOp(BfOpKind::JUMP_IF_DATA_ZERO, 0));
pc++;
} else if (instruction == ']') {
if (open_bracket_stack.empty()) {
DIE << "unmatched closing ']' at pc=" << pc;
}
size_t open_bracket_offset = open_bracket_stack.top();
open_bracket_stack.pop();
std::vector<BfOp> optimized_loop =
optimize_loop(ops, open_bracket_offset);
if (optimized_loop.empty()) {
ops[open_bracket_offset].argument = ops.size();
ops.push_back(
BfOp(BfOpKind::JUMP_IF_DATA_NOT_ZERO, open_bracket_offset));
} else {
ops.erase(ops.begin() + open_bracket_offset, ops.end());
ops.insert(ops.end(), optimized_loop.begin(), optimized_loop.end());
}
pc++;
} else {
size_t start = pc++;
while (pc < program_size && p.instructions[pc] == instruction) {
pc++;
}
size_t num_repeats = pc - start;
BfOpKind kind = BfOpKind::INVALID_OP;
switch (instruction) {
case '>':
kind = BfOpKind::INC_PTR;
break;
case '<':
kind = BfOpKind::DEC_PTR;
break;
case '+':
kind = BfOpKind::INC_DATA;
break;
case '-':
kind = BfOpKind::DEC_DATA;
break;
case ',':
kind = BfOpKind::READ_STDIN;
break;
case '.':
kind = BfOpKind::WRITE_STDOUT;
break;
default: { DIE << "bad char '" << instruction << "' at pc=" << start; }
}
ops.push_back(BfOp(kind, num_repeats));
}
}
return ops;
}
void optinterp3(const Program& p, bool verbose) {
std::vector<uint8_t> memory(MEMORY_SIZE, 0);
size_t dataptr = 0;
Timer t1;
const std::vector<BfOp> ops = translate_program(p);
if (verbose) {
std::cout << "* translation [elapsed " << t1.elapsed() << "s]:\n";
for (size_t i = 0; i < ops.size(); ++i) {
std::cout << " [" << i << "] " << BfOpKind_name(ops[i].kind) << " "
<< ops[i].argument << "\n";
}
}
size_t ops_size = ops.size();
for (size_t pc = 0; pc < ops_size; ++pc) {
BfOp op = ops[pc];
BfOpKind kind = op.kind;
switch (kind) {
case BfOpKind::INC_PTR:
dataptr += op.argument;
break;
case BfOpKind::DEC_PTR:
dataptr -= op.argument;
break;
case BfOpKind::INC_DATA:
memory[dataptr] += op.argument;
break;
case BfOpKind::DEC_DATA:
memory[dataptr] -= op.argument;
break;
case BfOpKind::READ_STDIN:
for (int i = 0; i < op.argument; ++i) {
memory[dataptr] = std::cin.get();
}
break;
case BfOpKind::WRITE_STDOUT:
for (int i = 0; i < op.argument; ++i) {
std::cout.put(memory[dataptr]);
}
break;
case BfOpKind::LOOP_SET_TO_ZERO:
memory[dataptr] = 0;
break;
case BfOpKind::LOOP_MOVE_PTR:
while (memory[dataptr]) {
dataptr += op.argument;
}
break;
case BfOpKind::LOOP_MOVE_DATA: {
if (memory[dataptr]) {
int64_t move_to_ptr = static_cast<int64_t>(dataptr) + op.argument;
memory[move_to_ptr] += memory[dataptr];
memory[dataptr] = 0;
}
break;
}
case BfOpKind::JUMP_IF_DATA_ZERO:
if (memory[dataptr] == 0) {
pc = op.argument;
}
break;
case BfOpKind::JUMP_IF_DATA_NOT_ZERO:
if (memory[dataptr] != 0) {
pc = op.argument;
}
break;
case BfOpKind::INVALID_OP:
DIE << "INVALID_OP encountered on pc=" << pc;
break;
}
}
}
int main(int argc, const char** argv) {
bool verbose = false;
std::string bf_file_path;
parse_command_line(argc, argv, &bf_file_path, &verbose);
Timer t1;
std::ifstream file(bf_file_path);
if (!file) {
DIE << "unable to open file " << bf_file_path;
}
Program program = parse_from_stream(file);
if (verbose) {
std::cout << "[>] Running optinterp3:\n";
}
Timer t2;
optinterp3(program, verbose);
if (verbose) {
std::cout << "[<] Done (elapsed: " << t2.elapsed() << "s)\n";
}
return 0;
}