Skip to content

Instantly share code, notes, and snippets.

@gastrodon
Last active November 17, 2019 08:36
Show Gist options
  • Save gastrodon/0f4ee9e21d9c6a2c30e57501a0ff4802 to your computer and use it in GitHub Desktop.
Save gastrodon/0f4ee9e21d9c6a2c30e57501a0ff4802 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define U_SMALL 65535
char *get_source(char *filename) {
FILE *source = fopen(filename, "r");
if (source == NULL) {
printf("Error reading %s\n", filename);
exit(-1);
}
int length = 1 + lseek(fileno(source), 0, SEEK_END);
rewind(source);
unsigned int index = 0;
char *code = (char*) malloc((length + 1) * sizeof(char));
while(index < length) {
code[index++] = fgetc(source);
}
code[length + 1] = '\0';
return code;
}
void lint(char *code) {
int sym_ptr, block_sum = 0;
for (sym_ptr = 0; code[sym_ptr] != '\0'; sym_ptr++) {
switch (code[sym_ptr]) {
case '[':
++block_sum;
break;
case ']':
--block_sum;
break;
}
}
if (!block_sum) {
return;
}
if (block_sum > 0) {
printf("Error: unmatched [\n");
exit(1);
}
if (block_sum < 0) {
printf("Error: unmatched ]\n");
exit(1);
}
}
int main(int argc, char *argv[]) {
char *code;
if (argc == 2) {
code = get_source(argv[1]);
} else {
char buffer[U_SMALL];
unsigned short index, length = 0;
fgets(buffer, U_SMALL, stdin);
length = strlen(buffer);
code = (char*) malloc((length + 1) * sizeof(char));
while(length--){
code[index] = buffer[index];
index++;
}
}
int sym_ptr;
lint(code);
int cell_pointer = 0;
int block_counter = 0;
unsigned short int cells_seen = 0;
unsigned char cells[U_SMALL];
for(sym_ptr = 0; code[sym_ptr] != '\0'; sym_ptr++) {
if (cell_pointer == cells_seen && cells_seen < U_SMALL) {
cells[++cells_seen] = 0;
}
switch (code[sym_ptr]) {
case '-':
--cells[cell_pointer];
break;
case '+':
++cells[cell_pointer];
break;
case '<':
--cell_pointer;
break;
case '>':
++cell_pointer;
break;
case ',':
scanf("%c", &cells[cell_pointer]);
break;
case '.':
printf("%c", cells[cell_pointer]);
break;
case '[':
if (cells[cell_pointer] == 0) {
++block_counter;
while(block_counter) {
++sym_ptr;
if (code[sym_ptr] == '[') {
++block_counter;
}
if (code[sym_ptr] == ']') {
--block_counter;
}
if (code[sym_ptr] == '\0') {
printf("Error: unmatched evaluated [\n");
return 1;
}
}
}
break;
case ']':
if(cells[cell_pointer] != 0) {
--block_counter;
while (block_counter) {
--sym_ptr;
if (code[sym_ptr] == '[') {
++block_counter;
}
if (code[sym_ptr] == ']') {
--block_counter;
}
if (sym_ptr < 0) {
printf("Error: unmatched evaluated ]\n");
return 1;
}
}
}
break;
}
if (cell_pointer < 0 || cell_pointer > U_SMALL) {
printf("Error: cell pointer out of bounds (0 to %d)\n", U_SMALL);
return 2;
}
}
printf("\n");
return 0;
}

This was only tested with gcc on Arch (WSL)

What this interpreter doesn't do (on purpose)

  • Cell wrapping. A negative cell index or cell index > U_SMALL will throw an exception
  • Accomodate for mismatched parenthesis. Mismatched parenthesis will throw an exception

Limitations

  • Each cell is an unsigned char per this spec
  • The maximum codesize and number of cells total is 65535, macro'd to U_SMALL. This is to make a reasonably sized cells and code arrays, since this implementation does not want do deal with actual pointers pointing to actual memory.
  • The interpreter lazily deals with new cells being defaulted to zero. This hasn't broken yet, but I usually don't trust what I write.

Usage

I compile with gcc gcc -o bf main.c, and use it on some file with valid code ./bf test.bf, or send code in stdin echo "++[>++<-]." | ./bf. Errors will be detailed at interpret time if they are present (sorry adox, I disagree with you here)

Errors

  • unmatched evaluated ]
  • unmatched evaluated [
  • cell pointer out of bounds (0 to 65535)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment