Skip to content

Instantly share code, notes, and snippets.

@nikhilr612
Created October 21, 2023 10:12
Show Gist options
  • Save nikhilr612/792fa247cd8dfdd12a42c853b72d206e to your computer and use it in GitHub Desktop.
Save nikhilr612/792fa247cd8dfdd12a42c853b72d206e to your computer and use it in GitHub Desktop.
A simple brainfuck interpreter written in C
#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, &current_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);
}
#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