Skip to content

Instantly share code, notes, and snippets.

@LucasWolschick
Created March 12, 2022 21:34
Show Gist options
  • Save LucasWolschick/7bf85baeb36a912d16e9f7c5e91791a3 to your computer and use it in GitHub Desktop.
Save LucasWolschick/7bf85baeb36a912d16e9f7c5e91791a3 to your computer and use it in GitHub Desktop.
brainfuck interpreter written in C
// Lucas Wolschick (C) 2022
// You should probably not use this as this has not been tested
// Usage: brainfuck [file]
//
// Try it with this code (available at en.wikipedia.org/wiki/Brainfuck, not mine!)
// ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct VM VM;
struct VM {
size_t iptr;
size_t dptr;
uint8_t* data;
};
typedef enum VM_STATUS VM_STATUS;
enum VM_STATUS {
VM_RUNNING,
VM_DONE,
};
VM* vm_new(void) {
VM* vm = malloc(sizeof(VM));
vm->iptr = 0;
vm->dptr = 0;
vm->data = calloc(30*1000, sizeof(uint8_t));
return vm;
}
VM_STATUS vm_run(VM* vm, size_t src_len, const char* src) {
if (vm->iptr >= src_len) {
return VM_DONE;
} else {
switch(src[vm->iptr]) {
case '>':
if (vm->dptr == 30*1000)
// silently ignore
;
else
vm->dptr++;
break;
case '<':
if (vm->dptr > 0)
vm->dptr--;
break;
case '+':
vm->data[vm->dptr]++;
break;
case '-':
vm->data[vm->dptr]--;
break;
case '.':
putc(vm->data[vm->dptr], stdout);
break;
case ',':
vm->data[vm->dptr] = getc(stdin);
break;
case '[':
if (!vm->data[vm->dptr]) {
// jump to after matching ]
size_t idx = vm->iptr + 1;
int counter = 1;
while (idx < src_len && counter > 0) {
if (src[idx] == '[') {
counter++;
} else if (src[idx] == ']') {
counter--;
}
idx++;
}
vm->iptr = idx-1;
}
break;
case ']':
if (vm->iptr < 1)
// we can't have ] at the beginning of the src...
// silently ignore
break;
if (vm->data[vm->dptr] != 0) {
int counter = 1;
size_t idx = vm->iptr-1;
do {
if (src[idx] == ']') {
counter++;
} else if (src[idx] == '[') {
counter--;
}
idx--;
} while (idx > 0 && counter > 0);
vm->iptr = idx+1;
}
break;
// everything else are commentss
}
vm->iptr++;
}
return VM_RUNNING;
}
char* read_to_string(const char* path, size_t* length) {
char* source = 0;
// load file
FILE* src = fopen(path, "rb");
if (!src) {
perror("Could not read brainfuck source");
return source;
}
// get file length
int begin = ftell(src);
if (begin == -1) {
perror("Error seeking file");
goto fail;
}
if (fseek(src, 0, SEEK_END) != 0) {
puts("Error reading file");
goto fail;
}
int len = ftell(src);
if (len == -1) {
perror("Error reading file");
goto fail;
}
if (fseek(src, 0, begin) != 0) {
puts("Error reading file");
goto fail;
};
// allocate and populate string
source = malloc(len);
if (!source) {
puts("Out of memory");
goto fail;
}
int err = fread(source, 1, len, src);
if (err < len) {
if (feof(src)) {
puts("Error: end of file reached");
goto fail;
} else if (ferror(src)) {
perror("Error reading source file");
goto fail;
}
}
fclose(src);
*length = len;
return source;
fail:
fclose(src);
free(source);
return source;
}
int main(int argc, char* argv[]) {
if (argc > 1) {
size_t length = 0;
char* source = read_to_string(argv[1], &length);
if (!source) {
return EXIT_FAILURE;
}
VM* vm = vm_new();
while (vm_run(vm, length, source) == VM_RUNNING) {}
free(source);
return EXIT_SUCCESS;
} else {
puts("Usage: brainfuck <file>");
return EXIT_FAILURE;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment