Created
July 30, 2017 21:16
-
-
Save euhmeuh/c2b1abd37c7048e9cb8e2377d11b0782 to your computer and use it in GitHub Desktop.
Brainfuck interpreter in C
This file contains 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
/* | |
Brainfuck interpreter by euhmeuh | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <errno.h> | |
#include <string.h> | |
#define MEMORY_SIZE 1024 | |
#define CODE_BUFFER_SIZE 256 | |
#define JUMP_BUFFER_SIZE 8 | |
/* --- DynamicArray --- */ | |
typedef struct { | |
int* string; | |
unsigned int buffer_size; | |
unsigned int size; | |
unsigned int cursor; | |
} DynamicArray; | |
DynamicArray* array_create(unsigned int buffer_size) { | |
DynamicArray* array = malloc(sizeof(DynamicArray)); | |
array->string = malloc(sizeof(int) * buffer_size); | |
array->buffer_size = buffer_size; | |
array->size = buffer_size; | |
array->cursor = 0; | |
return array; | |
} | |
void array_append(DynamicArray* array, int c) { | |
array->string[array->cursor] = c; | |
array->cursor++; | |
if (array->cursor >= array->size) { | |
array->size += array->buffer_size; | |
array->string = realloc(array->string, sizeof(int) * array->size); | |
} | |
} | |
int array_pop(DynamicArray* array) { | |
array->cursor--; | |
return array->string[array->cursor]; | |
} | |
void array_destroy(DynamicArray* array) { | |
free(array->string); | |
free(array); | |
} | |
/* --- Interpreter --- */ | |
/* | |
Evaluate the current character under the codeptr. | |
'+' and '-' will change the data in memory. | |
'>' and '<' will change the dataptr. | |
'.' and ',' will print and read a character respectively. | |
'[' and ']' will jump the codeptr around. | |
Any other character is ignored. | |
*/ | |
void | |
eval_character(DynamicArray* code, | |
unsigned int* codeptr, | |
int* memory, | |
unsigned int* dataptr, | |
unsigned int* jump_table) | |
{ | |
switch(code->string[*codeptr]) | |
{ | |
case '+': | |
memory[*dataptr]++; | |
break; | |
case '-': | |
memory[*dataptr]--; | |
break; | |
case '>': | |
(*dataptr)++; | |
if(*dataptr >= MEMORY_SIZE) { | |
*dataptr = 0; | |
} | |
break; | |
case '<': | |
(*dataptr)--; | |
if(*dataptr < 0) { | |
*dataptr = MEMORY_SIZE - 1; | |
} | |
break; | |
case '.': | |
fprintf(stdout, "%c", memory[*dataptr]); | |
break; | |
case ',': | |
memory[*dataptr] = getchar(); | |
break; | |
case '[': | |
if (memory[*dataptr] == 0) { | |
*codeptr = jump_table[*codeptr] + 1; | |
return; | |
} | |
break; | |
case ']': | |
if (memory[*dataptr] != 0) { | |
*codeptr = jump_table[*codeptr] + 1; | |
return; | |
} | |
break; | |
default: | |
// noop | |
break; | |
} | |
(*codeptr)++; | |
} | |
/* | |
Generates a registers that indexes all jump destinations in the code | |
*/ | |
unsigned int* index_jumps(DynamicArray* code) | |
{ | |
unsigned int* jump_table = malloc(sizeof(unsigned int) * code->cursor); | |
unsigned int codeptr = 0; | |
DynamicArray* stack = array_create(JUMP_BUFFER_SIZE); | |
for (codeptr; codeptr < code->cursor; codeptr++) { | |
if (code->string[codeptr] == '[') { | |
array_append(stack, codeptr); | |
} else if (code->string[codeptr] == ']') { | |
jump_table[codeptr] = array_pop(stack); | |
jump_table[jump_table[codeptr]] = codeptr; | |
} | |
} | |
array_destroy(stack); | |
return jump_table; | |
} | |
/* | |
Execute a piece of code | |
*/ | |
void execute(DynamicArray* code) | |
{ | |
int memory[MEMORY_SIZE] = {0}; | |
unsigned int dataptr = 0; | |
unsigned int codeptr = 0; | |
unsigned int* jump_table = index_jumps(code); | |
while(codeptr < code->cursor) { | |
eval_character(code, &codeptr, memory, &dataptr, jump_table); | |
} | |
free(jump_table); | |
} | |
DynamicArray* parse_file(FILE* input) | |
{ | |
int c; | |
DynamicArray* array = array_create(CODE_BUFFER_SIZE); | |
while(c != EOF) { | |
c = fgetc(input); | |
array_append(array, c); | |
} | |
return array; | |
} | |
FILE* get_input_from_args(int argc, char** argv) | |
{ | |
if (argc > 1) { | |
if (!strcmp(argv[1], "-")) { | |
return stdin; | |
} else { | |
return fopen(argv[1], "r"); | |
} | |
} else { | |
return stdin; | |
} | |
} | |
int main(int argc, char** argv) | |
{ | |
FILE* input; | |
DynamicArray* code = NULL; | |
input = get_input_from_args(argc, argv); | |
if (input == NULL) { | |
fprintf(stderr, "Unable to open '%s': %s\n", argv[1], strerror(errno)); | |
exit(EXIT_FAILURE); | |
} | |
code = parse_file(input); | |
fclose(input); | |
execute(code); | |
array_destroy(code); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment