-
-
Save p7g/298a4310e7b18493a4dae9d1383e2f39 to your computer and use it in GitHub Desktop.
My fastest BF interpreter so far, using dynamic dispatch in C
This file contains hidden or 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 <stdlib.h> | |
#include <stdio.h> | |
struct tape { | |
unsigned pos, capacity; | |
unsigned char *data; | |
}; | |
void tape_init(struct tape *t) | |
{ | |
t->pos = 0; | |
t->capacity = 1; | |
t->data = calloc(1, sizeof(unsigned char)); | |
} | |
void tape_deinit(struct tape *t) | |
{ | |
free(t->data); | |
} | |
unsigned char tape_get(const struct tape *t) | |
{ | |
return t->data[t->pos]; | |
} | |
void tape_inc(struct tape *t, int amount) | |
{ | |
t->data[t->pos] += amount; | |
} | |
void tape_move(struct tape *t, int amount) | |
{ | |
unsigned i; | |
t->pos += amount; | |
while (t->pos >= t->capacity) { | |
t->capacity <<= 1; | |
t->data = realloc(t->data, t->capacity * sizeof(unsigned char)); | |
for (i = t->capacity >> 1; i < t->capacity; i += 1) | |
t->data[i] = 0; | |
} | |
} | |
struct op; | |
struct op_list { | |
unsigned size, capacity; | |
struct op *items; | |
}; | |
union op_data { | |
int amount; | |
struct op_list ops; | |
}; | |
struct op { | |
union op_data data; | |
void (*run)(const union op_data *data, struct tape *); | |
void (*free)(union op_data *data); | |
}; | |
void op_list_init(struct op_list *l) | |
{ | |
l->size = l->capacity = 0; | |
l->items = 0; | |
} | |
struct op *op_list_reserve(struct op_list *l) | |
{ | |
if (l->size < l->capacity) | |
return &l->items[l->size++]; | |
if (l->capacity == 0) | |
l->capacity = 4; | |
else | |
l->capacity <<= 1; | |
l->items = realloc(l->items, l->capacity * sizeof(struct op)); | |
return &l->items[l->size++]; | |
} | |
void op_list_deinit(struct op_list *l) | |
{ | |
unsigned i; | |
struct op *op; | |
for (i = 0; i < l->size; i += 1) { | |
op = &l->items[i]; | |
if (op->free) | |
op->free(&op->data); | |
} | |
if (l->items != 0) | |
free(l->items); | |
} | |
void op_free_fn_list(union op_data *data) { | |
op_list_deinit(&data->ops); | |
} | |
void op_run_fn_inc(const union op_data *data, struct tape *tape) | |
{ | |
tape_inc(tape, data->amount); | |
} | |
void op_run_fn_move(const union op_data *data, struct tape *tape) | |
{ | |
tape_move(tape, data->amount); | |
} | |
void op_run_fn_print(const union op_data *data, struct tape *tape) | |
{ | |
(void) data; | |
putchar((char) tape_get(tape)); | |
fflush(stdout); | |
} | |
void bf_run(const struct op_list *ops, struct tape *tape); | |
void op_run_fn_loop(const union op_data *data, struct tape *tape) | |
{ | |
while (tape_get(tape) != 0) | |
bf_run(&data->ops, tape); | |
} | |
void bf_parse(FILE *f, struct op_list *ops) | |
{ | |
struct op *op; | |
for (;;) { | |
switch (getc(f)) { | |
case '+': | |
op = op_list_reserve(ops); | |
op->run = op_run_fn_inc; | |
op->free = 0; | |
op->data.amount = 1; | |
break; | |
case '-': | |
op = op_list_reserve(ops); | |
op->run = op_run_fn_inc; | |
op->free = 0; | |
op->data.amount = -1; | |
break; | |
case '>': | |
op = op_list_reserve(ops); | |
op->run = op_run_fn_move; | |
op->free = 0; | |
op->data.amount = 1; | |
break; | |
case '<': | |
op = op_list_reserve(ops); | |
op->run = op_run_fn_move; | |
op->free = 0; | |
op->data.amount = -1; | |
break; | |
case '.': | |
op = op_list_reserve(ops); | |
op->run = op_run_fn_print; | |
op->free = 0; | |
break; | |
case '[': | |
op = op_list_reserve(ops); | |
op->run = op_run_fn_loop; | |
op->free = op_free_fn_list; | |
op_list_init(&op->data.ops); | |
bf_parse(f, &op->data.ops); | |
break; | |
case ']': | |
case EOF: | |
return; | |
} | |
} | |
} | |
void bf_run(const struct op_list *ops, struct tape *tape) | |
{ | |
unsigned i; | |
struct op *op; | |
for (i = 0; i < ops->size; i += 1) { | |
op = &ops->items[i]; | |
op->run(&op->data, tape); | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
FILE *f; | |
struct op_list ops; | |
struct tape tape; | |
if (argc < 2) { | |
fputs("Expected filename arg\n", stderr); | |
return 1; | |
} | |
f = fopen(argv[1], "r"); | |
if (!f) { | |
perror("fopen:"); | |
return 1; | |
} | |
op_list_init(&ops); | |
tape_init(&tape); | |
bf_parse(f, &ops); | |
bf_run(&ops, &tape); | |
op_list_deinit(&ops); | |
tape_deinit(&tape); | |
fclose(f); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment