Created
October 21, 2023 10:12
-
-
Save nikhilr612/792fa247cd8dfdd12a42c853b72d206e to your computer and use it in GitHub Desktop.
A simple brainfuck interpreter written 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 "brainfuck.h" | |
#define CHECK_DP(interp) ((interp -> data_pointer) < (interp -> memsize)) | |
int bf_init(bf_Interpreter* interp, const char* fpath, size_t memsize) { | |
// Allocate memory | |
unsigned char* mem_alloc; | |
mem_alloc = (unsigned char*) calloc(memsize, sizeof(unsigned char)); | |
if (mem_alloc == NULL) { | |
printf("Failed to allocate %d chars of memory\n", memsize); | |
return FAILURE; | |
} | |
// Open file | |
FILE* fh = fopen(fpath, "r"); | |
if (fh == NULL) { | |
printf("Failed to open file %s\n", fpath); | |
return FAILURE; | |
} | |
// Fill buffer with zeroes for Safety. | |
for (size_t i = 0; i < BUFFER_SIZE; i++) { | |
(interp -> buffer)[i] = 0; | |
} | |
// Set fields. | |
interp -> buflen = 0; | |
interp -> bufstart=0; | |
interp -> offset = 0; | |
interp -> data_pointer = 0; | |
interp -> memory = mem_alloc; | |
interp -> file = fh; | |
interp -> memsize = memsize; | |
interp -> stack_p = 0; | |
return SUCCESS; | |
} | |
int new_buffer(bf_Interpreter* interp) { | |
long loc = ftell(interp -> file); | |
interp -> offset = 0; | |
if (loc == -1) { | |
fprintf(stderr, "Failed to read position of file pointer.\n"); | |
return FAILURE; | |
} | |
interp -> bufstart = loc; | |
size_t newlen = fread(interp -> buffer, sizeof(char), BUFFER_SIZE, (interp -> file)); | |
if (newlen == 0) { | |
// No characters were read. EOF was reached. | |
return FAILURE; | |
} | |
interp -> buflen = newlen; | |
return SUCCESS; | |
} | |
// Read next character from file. | |
int read_char(bf_Interpreter* interp, char* outptr) { | |
// If buffer has been read fully, re-fill it and reset offset. | |
if ((interp -> offset) >= (interp -> buflen)) { | |
if (new_buffer(interp) == FAILURE) { | |
return FAILURE; | |
} | |
} | |
// Read character from buffer, and increment offset. | |
*outptr = (interp -> buffer)[(interp -> offset)]; | |
interp -> offset += 1; | |
return SUCCESS; | |
} | |
// Read characters until matching ']' is encountered. | |
int skip_forward(bf_Interpreter* interp) { | |
char ch; | |
size_t count = 0; // Track all '[' encountered. | |
while (read_char(interp, &ch)) { | |
if (ch == '[') { | |
count += 1; | |
} else if (ch == ']') { | |
if (count == 0) { | |
return SUCCESS; | |
} else { | |
count -= 1; | |
} | |
} | |
} | |
return FAILURE; | |
} | |
// Function for mainpulating the stack, from here ... | |
int get_last_loc(bf_Interpreter* interp, long* outptr) { | |
if (interp -> stack_p) { | |
*outptr = (interp -> stack)[(interp -> stack_p) -1]; | |
return SUCCESS; | |
} else return FAILURE; | |
} | |
int save_loc(bf_Interpreter* interp) { | |
long loc = (interp -> bufstart) + (interp -> offset); // Get current location | |
if (loc == -1) { | |
fprintf(stderr, "Failed to read position of file pointer.\n"); | |
return FAILURE; | |
} | |
if ((interp -> stack_p) < STACK_SIZE) { | |
(interp -> stack)[(interp -> stack_p)++] = loc; | |
return SUCCESS; | |
} else { | |
return FAILURE; | |
} | |
} | |
int erase_last_loc(bf_Interpreter* interp) { | |
if (interp -> stack_p) { | |
(interp -> stack)[--(interp -> stack_p)] = 0; | |
return SUCCESS; | |
} | |
return FAILURE; | |
} | |
int move_to_loc(bf_Interpreter* interp, long location) { | |
if ((location >= (interp -> bufstart)) && (location <= (interp -> bufstart) + (interp -> buflen))) { | |
// If location lies within the buffer, just change offset. | |
interp -> offset = (size_t)(location - (interp -> bufstart)); | |
return SUCCESS; | |
} | |
// Seek to location | |
if (!fseek(interp->file, location, SEEK_SET)) { | |
fprintf(stderr, "Failed to move to location %d in stream.\n", location); | |
return FAILURE; | |
} | |
// Reset buffer | |
return new_buffer(interp); | |
} | |
/// ... till here | |
int bf_execute(bf_Interpreter* interp) { | |
char current_char; | |
while (read_char(interp, ¤t_char) == SUCCESS) { | |
switch (current_char) { | |
case '>': | |
interp -> data_pointer += 1; | |
break; | |
case '<': | |
// Prevent underflow | |
if CHECK_DP(interp) interp -> data_pointer -= 1; | |
break; | |
case '+': | |
// Check bounds; ignore otherwise | |
if CHECK_DP(interp) (interp->memory)[interp->data_pointer] += 1; | |
break; | |
case '-': | |
// Check bounds; ignore otherwise | |
if CHECK_DP(interp) (interp->memory)[interp->data_pointer] -= 1; | |
break; | |
case '.': | |
if CHECK_DP(interp) { | |
char ch = (char)(interp->memory)[interp->data_pointer]; | |
printf("%c", ch); | |
} | |
break; | |
case ',': | |
int retval = fgetc(stdin); | |
if (retval == EOF) { | |
fprintf(stderr, "Error: Couldn't read char from stdin.\n"); | |
return FAILURE; | |
} | |
unsigned char ch = (unsigned char) retval; | |
if CHECK_DP(interp) (interp->memory)[interp->data_pointer] = ch; | |
break; | |
case '[': | |
if CHECK_DP(interp) { | |
unsigned char ch = (interp -> memory)[interp -> data_pointer]; | |
if (ch == 0) { | |
if (skip_forward(interp) == FAILURE) { | |
fprintf(stderr, "Syntax Error: Mismatched '[]', orphan '['\n"); | |
return FAILURE; | |
} | |
} else { | |
if (save_loc(interp) == FAILURE) { | |
fprintf(stderr, "Stack Overflow: Too many '['\n"); | |
return FAILURE; | |
} | |
} | |
} | |
break; | |
case ']': | |
if CHECK_DP(interp) { | |
unsigned char ch = (interp -> memory)[interp -> data_pointer]; | |
if (ch == 0) { | |
// If we don't jump back, discard the last return addr. | |
if (erase_last_loc(interp) == FAILURE) { | |
fprintf(stderr, "Syntax Error: Mismatched '[]', orphan ']'\n"); | |
return FAILURE; | |
} | |
} else { | |
long last_loc; | |
if (get_last_loc(interp, &last_loc) == FAILURE) { | |
fprintf(stderr, "Syntax Error: Mismatched '[]', orphan ']'\n"); | |
return FAILURE; | |
} | |
if (move_to_loc(interp, last_loc) == FAILURE) { | |
fprintf(stderr, "Error: Failed to jump to last '['\n"); | |
return FAILURE; | |
} | |
} | |
} | |
break; | |
default: | |
// Ignore | |
break; | |
} | |
} | |
return SUCCESS; | |
} | |
void bf_release(bf_Interpreter* interp) { | |
free(interp -> memory); | |
fclose(interp -> file); | |
} | |
void bf_inspect(const bf_Interpreter* interp, size_t offset, size_t length, unsigned char* outbuf) { | |
if (offset >= interp -> memsize) return; | |
if ((offset + length) >= interp -> memsize) length = interp -> memsize; | |
for (size_t i = 0; i < length; i++) { | |
outbuf[i] = (interp -> memory)[offset + i]; | |
} | |
} | |
void main() { | |
bf_Interpreter interp; | |
if (bf_init(&interp, "./test1.txt", MIN_MEMORY) == FAILURE) { | |
fprintf(stderr, "Failed to initialize interpreter.\n"); | |
return; | |
} | |
if (bf_execute(&interp) == FAILURE) { | |
fprintf(stderr, "\nTerminated with error.\n"); | |
} | |
bf_release(&interp); | |
} |
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
#ifndef BRAINFUCK_H | |
#define BRAINFUCK_H | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define BUFFER_SIZE 512 | |
#define STACK_SIZE 128 | |
#define SUCCESS 0 | |
#define FAILURE 1 | |
#define MIN_MEMORY 32768 | |
#endif | |
// Main struct | |
typedef struct Interpreter { | |
// Program memory | |
size_t memsize; | |
unsigned char* memory; | |
unsigned int data_pointer; | |
// Program data | |
FILE* file; | |
// File buffer | |
char buffer[BUFFER_SIZE]; | |
size_t buflen; | |
size_t offset; | |
long bufstart; | |
// Stack to store 'return address' of '[' for ']', | |
// For '[' just read until matching ']' | |
long stack[STACK_SIZE]; | |
size_t stack_p; | |
} bf_Interpreter; | |
// Initialize the interpreter with a file. | |
int bf_init(bf_Interpreter* interp, const char* fpath, size_t memsize); | |
// Execute instructions. | |
int bf_execute(bf_Interpreter* interp); | |
// Read data stored in memory segment starting from offset | |
void bf_inspect(const bf_Interpreter* interp, size_t offset, size_t length, unsigned char* outbuf); | |
// Free memory, and release the file. | |
void bf_release(bf_Interpreter* interp); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment