/* ubi3 - Ultimate Brainfuck Interpreter 3 - or - ubi3 - Universal Brainfuck Interpreter 3 This is a bf interpreter based on ubi and Erik Bosman's bf interpreter. The features and polish come from ubi. The linked list and recursive loading (ideas), come from Erik Bosman. All code is original in origin. ubi3 has two priorities: 1) Be featureful and complete. 2) Be fast. ubi3 was written with those two points in mind, and they are listed in order of importance. Erik Bosman's 8-bit bf interpreter is slightly faster for some bf programs and his 16 bit version is decidely faster than ubi3. It's not called "ultimate/universal" for nothing. The purpose behind ubi3 is to run any bf program out there. Granted, there are a lot of bf-like languages out there, and really, ubi3 can only run a small subset of those. If a language or script is 100% bf compatible, ubi3 should be able to run it. ubi3 handles quirks such as cell and array wrapping, multiple EOF styles, and 8, 16, or 32 bit cell sizes. **************************** If you lost my lovely make file, ubi3 can be compiled via: gcc -std=c99 -pedantic -Wall -O3 -ffast-math ubi3.c -o ubi3 To my knowledge, ubi3 is completely c99 compliant and should compile with any c99 compliant compiler. **************************** Exit Codes: 0 = No Errors 1 = Command line error 2 = File error <><><><><><><><><><><><><><><><> TODO */ #define UBI_VERSION "3.0b" #define _GNU_SOURCE #include #include #include #include /* #include #include */ #define CHKOPT(i,v) ((i&v)==v) /* check to see if an option is set. as macro for clarity */ typedef struct { char op; /* operator */ unsigned int amt; /* ammount */ void *loop; /* enter loop */ void *next; /* next command */ } command; struct { char cell_size; short options; char cell_bytes; /* how many bytes in the cell? */ void *sheet_end; /* pointer to end of the sheet. */ unsigned int sheet_size; /* how man cells in a sheet */ } config; char *sheet_8; short *sheet_16; int *sheet_32; char *loadfile(const char *filename) { /* loadfile() takes a filename as a paramter (char pointer) and loads it into memory, stripping out non-command characters */ FILE *fp; char *file_data; char *fd_start; char ch; if (!(fp=fopen(filename,"r"))) { perror("Could not open file"); exit(2); } fseek(fp, 0, SEEK_END); file_data = calloc(ftell(fp)+1, sizeof(char)); fd_start = file_data; fseek(fp, 0, SEEK_SET); while(!feof(fp)) { ch=fgetc(fp); switch(ch) /* switch case is easier and clearer than a bunch of logical ORs */ { case '.': case '>': case '+': case ']': case ',': case '<': case '-': case '[': *(file_data++) = ch; } } /* free possibly unused memory */ fd_start= realloc(fd_start, strlen(fd_start)); return(fd_start); } char *code_optimize(char *cmd_string) { /* optimizes a cmd_string. returns the same pointer passed to it. */ char *found; char ch, match; /* the below do/while loop removes any zero sum ops */ do { ch=0; for ( match=0; match<=12; match+=3 ) { if ( (found = strstr(cmd_string, "+-\0-+\0><\0<>\0[]"+match )) ) /* what can I say? I started */ { /* as a perl hacker. */ *found=0; /* this is my clever way */ strcat(cmd_string, found+2); /* of snipping out the offending code */ ch=1; } } }while( ch ); return(cmd_string); } command *preprocess(char **cmd_string) { /* preprocess() recursively loads a cmd_string into command linked list */ command *cmd = calloc(1, sizeof(command)); command *first = cmd; do { cmd->op = **cmd_string; switch(cmd->op) { case '<': case '>': case '+': case '-': while( cmd->op == *((*cmd_string)++) ) cmd->amt++; (*cmd_string) -= 2; break; case '[': (*cmd_string)++; cmd->loop = preprocess(cmd_string); break; case ']': return(first); break; case '.': case ',': break; } cmd->next = calloc(1, sizeof(command)); cmd = cmd->next; } while( *((*cmd_string)++) ); return(first); } void bf_run_8(command *cmd) { while(cmd->op) { switch(cmd->op) { case '<': sheet_8 -= cmd->amt; break; case '>': sheet_8 += cmd->amt; break; case '-': *sheet_8 -= cmd->amt; break; case '+': *sheet_8 += cmd->amt; break; case '[': while(*sheet_8) bf_run_8(cmd->loop); break; case '.': putchar(*sheet_8); break; case ',': *sheet_8=getchar(); break; case ']': return; break; } cmd = cmd->next; } } void bf_run_16(command *cmd) { while(cmd->op) { switch(cmd->op) { case '<': sheet_16 -= cmd->amt; break; case '>': sheet_16 += cmd->amt; break; case '-': *sheet_16 -= cmd->amt; break; case '+': *sheet_16 += cmd->amt; break; case '[': while(*sheet_16) bf_run_16(cmd->loop); break; case '.': putchar(*sheet_16); break; case ',': *sheet_16=getchar(); break; case ']': return; break; } cmd = cmd->next; } } void bf_run_32(command *cmd) { while(cmd->op) { switch(cmd->op) { case '<': sheet_32 -= cmd->amt; break; case '>': sheet_32 += cmd->amt; break; case '-': *sheet_32 -= cmd->amt; break; case '+': *sheet_32 += cmd->amt; break; case '[': while(*sheet_32) bf_run_32(cmd->loop); break; case '.': putchar(*sheet_32); break; case ',': *sheet_32=getchar(); break; case ']': return; break; } cmd = cmd->next; } } void show_help(void) { fprintf(stderr,"HI\n"); } int main(int argc, char *argv[]) { char ch; /* set defaults */ config.options = 0; config.cell_size = 0; while( (ch = getopt(argc, argv, "Oc:h?V")) != -1 ) { switch(ch) { case 'O': /* toggle optimization */ config.options ^= 1; break; case 'c': /* set cell bit size*/ if (! ( (sscanf(optarg, "%d", (int*)&(config.cell_size)) == 1) && ( config.cell_size < 3 ) && ( config.cell_size > -1) ) ) { fprintf(stderr, "Bad parameter passed to -c. Valid parameters are 0 = 8 bits (Default); 1 = 16 bits; 2 = 32 bits\n"); exit(1); } break; case 's': /* set sheet size */ if ( (sscanf(optarg, "%d", (int*)&(config.sheet_size)) == 1) ) { } break; case 'h': case '?': /* show help, hur hur */ show_help(); /* no break here on purpose! */ case 'V': /* show version */ fprintf(stderr, "ubi version %s.\n\nCopyright (c) 2008 Matthew 'Zashi' Hiles.\n", UBI_VERSION); exit(0); break; } } if (argv[optind] == NULL) { /* no file to run specified? show usage? */ fprintf(stderr, "Error: No source file specified. -h for help.\n"); exit(1); } char *code = loadfile(argv[optind]); if (! CHKOPT(config.options,1)) code_optimize(code); command *prog = preprocess(&code); switch(config.cell_size) { case 0: sheet_8 = calloc( 30000, sizeof(char) ); config.sheet_end = sheet_8 + ( 30000 * sizeof(char) ); bf_run_8(prog); break; case 1: sheet_16 = calloc( 30000, sizeof(short) ); config.sheet_end = sheet_16 + ( 30000 * sizeof(short) ); bf_run_16(prog); break; case 2: sheet_32 = calloc( 30000, sizeof(int) ); config.sheet_end = sheet_32 + ( 30000 * sizeof(int) ); bf_run_32(prog); break; } return(0); }