Skip to content

Instantly share code, notes, and snippets.

@p7g
Created February 1, 2022 04:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save p7g/298a4310e7b18493a4dae9d1383e2f39 to your computer and use it in GitHub Desktop.
Save p7g/298a4310e7b18493a4dae9d1383e2f39 to your computer and use it in GitHub Desktop.
My fastest BF interpreter so far, using dynamic dispatch in C
#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