I wrote brainfuck interpreter in order to prepare myself for a C job. I try to write the code as clear and as defensively as I can. Can somebody take a look at the code and give me some hints for improvement?
// tape.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct Tape {
long pointer;
long capacity;
unsigned short *data;
} Tape;
void initializeTape(Tape *tape);
void growTape(Tape *tape);
void incrementPointer(Tape *tape);
void decrementPointer(Tape *tape);
void incrementValue(Tape *tape);
void decrementValue(Tape *tape);
void read(Tape *tape);
void get(Tape *tape);
void freeTape(Tape *tape);
long interpret(Tape *tape, const char *source_code, long source_code_size, long position);
The implementation of tape.h
// tape.c
#include "tape.h"
void initializeTape(Tape *tape) {
tape->pointer = 0;
tape->capacity = 8;
tape->data = (unsigned short *) calloc( tape->capacity, sizeof(unsigned short));
if (tape->data == NULL) {
fprintf(stderr, "Out of memory error.\n");
exit(1);
}
}
void growTape(Tape *tape) {
tape->capacity *= 2;
tape->data = (unsigned short *) realloc(tape->data, tape->capacity);
if (tape->data == NULL) {
fprintf(stderr, "Out of memory error.\n");
exit(1);
}
}
void incrementPointer(Tape *tape) {
if (tape->pointer >= tape->capacity) {
growTape(tape);
}
tape->pointer++;
}
void decrementPointer(Tape *tape) {
if (tape->pointer == 0) {
fprintf(stderr, "Syntax error. Negative pointer detected.");
exit(1);
}
tape->pointer--;
}
void incrementValue(Tape *tape) {
tape->data[tape->pointer]++;
}
void decrementValue(Tape *tape) {
tape->data[tape->pointer]--;
}
void read(Tape *tape) {
putchar(tape->data[tape->pointer]);
}
void get(Tape *tape) {
tape->data[tape->pointer] = (char) getchar();
}
void freeTape(Tape *tape) {
free(tape->data);
tape->pointer = 0;
tape->capacity = 0;
}
long interpret(Tape *tape, const char *source_code, long source_code_size, long position) {
char c = source_code[position];
switch (c) {
case '>':
incrementPointer(tape);
break;
case '<':
decrementPointer(tape);
break;
case '+':
incrementValue(tape);
break;
case '-':
decrementValue(tape);
break;
case '.':
read(tape);
break;
case ',':
get(tape);
break;
case '[':
if (tape->data[tape->pointer] == (char) 0) {
int stack = 1;
long j = position + 1;
for (; j < source_code_size && stack > 0 && tape->pointer < source_code_size; j++) {
char _c = source_code[j];
if (_c == '[') {
++stack;
} else if (_c == ']') {
--stack;
}
}
if (stack != 0) {
fprintf(stderr, "Syntax error. Missing closing ].\n");
exit(1);
} else {
position = j + 1;
}
}
break;
case ']':
if (tape->data[tape->pointer] != (char) 0) {
int stack = 1;
long j = position - 1;
for (; j >= 0 && stack > 0 && tape->pointer >= 0; j--) {
char _c = source_code[j];
if (_c == '[') {
--stack;
} else if (_c == ']') {
++stack;
}
}
if (stack != 0) {
fprintf(stderr, "Syntax error. Missing opening [.\n");
exit(1);
} else {
position = j + 1;
}
}
break;
default:
break;
}
return ++position;
}
And the main
file:
// main.c
#include "tape.h"
int main(int argc, char **argv) {
FILE *file;
if (argc < 2) {
file = fopen("helloworld.bf", "r");
} else {
file = fopen(argv[1], "r");
}
if (file == NULL) {
fprintf(stderr, "Can not open file %s\n", argv[1]);
return 1;
}
if (fseek(file, 0L, SEEK_END) != 0) {
fprintf(stderr, "Fail to fseek file %s\n", argv[1]);
return 1;
}
long filesize = ftell(file);
if (filesize < 0) {
fprintf(stderr, "Fail to read file's size\n");
return 1;
}
rewind(file);
char source_code[filesize];
size_t result = fread(source_code, 1, filesize, file);
if (fclose(file) != 0) {
fprintf(stderr, "Can not close file %s\n", argv[1]);
return 1;
}
if (result != filesize) {
fprintf(stderr, "Can not read file. Corrupt\n");
}
Tape tape;
initializeTape(&tape);
long i = 0;
while(i < filesize) {
i = interpret(&tape, source_code, filesize, i);
}
freeTape(&tape);
return 0;
}
void *newptr = realloc(...); if ( newptr ) data = newptr; else handle_nomemory();
\$\endgroup\$