brainfuck/src/bf_compiler_tinycc.c

134 lines
3.5 KiB
C

#include "bf_compiler.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <libtcc.h>
struct str_buffer {
char *data;
size_t len;
size_t capacity;
};
static void str_buffer_init(struct str_buffer *buffer, size_t capacity)
{
buffer->data = malloc(capacity);
assert(buffer->data != NULL);
buffer->len = 0;
buffer->capacity = capacity;
}
static void str_buffer_deinit(struct str_buffer *buffer)
{
free(buffer->data);
buffer->data = NULL;
buffer->capacity = 0;
buffer->len = 0;
}
static void str_buffer_append_with_len(struct str_buffer *buffer, const char *text, size_t text_len)
{
if (buffer->len + text_len > buffer->capacity) {
while (buffer->len + text_len > buffer->capacity) {
buffer->capacity *= 2;
}
buffer->data = realloc(buffer->data, buffer->capacity);
assert(buffer->data != NULL);
}
memcpy(buffer->data + buffer->len, text, text_len);
buffer->len += text_len;
}
static void str_buffer_append(struct str_buffer *buffer, const char *text)
{
str_buffer_append_with_len(buffer, text, strlen(text));
}
static int bf_compiler_compile_tinycc(struct bf_compiler *compiler, const char *output_filename, const char *program, size_t program_len)
{
struct str_buffer c_program = { 0 };
str_buffer_init(&c_program, 128);
int rc = -1;
TCCState *state = tcc_new();
if (!state) {
goto err;
}
const char *cell_type = NULL;
if (compiler->cell_size == 8) {
cell_type = "uint8_t";
} else if (compiler->cell_size == 16) {
cell_type = "uint16_t";
} else if (compiler->cell_size == 32) {
cell_type = "uint32_t";
} else {
return -1;
}
char line_buffer[256] = { 0 };
str_buffer_append(&c_program, "#include <inttypes.h>\n");
str_buffer_append(&c_program, "#include <stdlib.h>\n");
str_buffer_append(&c_program, "#include <assert.h>\n");
snprintf(line_buffer, sizeof(line_buffer), "typedef %s cell_t;\n", cell_type);
str_buffer_append(&c_program, line_buffer);
str_buffer_append(&c_program, "int main()\n");
str_buffer_append(&c_program, "{\n");
snprintf(line_buffer, sizeof(line_buffer), "size_t data_len = %d;\n", compiler->data_len);
str_buffer_append(&c_program, line_buffer);
str_buffer_append(&c_program, "cell_t data_pointer = 0;\n");
str_buffer_append(&c_program, "cell_t *data = calloc(data_len, sizeof(cell_t));\n");
str_buffer_append(&c_program, "assert(data != NULL);\n");
for (int i = 0; i < program_len; i++) {
char inst = program[i];
if (inst == '>') {
str_buffer_append(&c_program, "data_pointer++;\n");
} else if (inst == '<') {
str_buffer_append(&c_program, "data_pointer--;\n");
} else if (inst == '+') {
str_buffer_append(&c_program, "data[data_pointer]++;\n");
} else if (inst == '-') {
str_buffer_append(&c_program, "data[data_pointer]--;\n");
} else if (inst == '.') {
str_buffer_append(&c_program, "putchar(data[data_pointer]); fflush(0);\n");
} else if (inst == ',') {
str_buffer_append(&c_program, "data[data_pointer] = getchar();\n");
} else if (inst == '[') {
str_buffer_append(&c_program, "while (data[data_pointer] != 0) {\n");
} else if (inst == ']') {
str_buffer_append(&c_program, "}\n");
}
}
str_buffer_append(&c_program, "free(data);\n");
str_buffer_append(&c_program, "return 0;\n");
str_buffer_append(&c_program, "}\n");
str_buffer_append_with_len(&c_program, "\0", 1);
tcc_set_output_type(state, TCC_OUTPUT_EXE);
tcc_set_options(state, "-w");
if (tcc_compile_string(state, c_program.data) == -1) {
goto err;
}
if (tcc_output_file(state, output_filename)) {
goto err;
}
rc = 0;
err:
tcc_delete(state);
str_buffer_deinit(&c_program);
return rc;
}