/***************************************** * Brainf*ck / Ook interpreter and converter * (c) 2007 Rami Chowdhury * This program is free software licensed under the GNU General Public * License. Please see http://www.gnu.org/copyleft/gpl.html for full * details. ******************************************/ #include #include #include #define BF_SIZE 262144 // These globals will be present no matter what we're doing FILE *fstream; char code[BF_SIZE]; // Used only by the converter mode, turning BF to Ook int bf2ook(char* code) { char tmp[BF_SIZE], *ptr; memset(tmp, 0, BF_SIZE); int i; ptr = tmp; for (i = 0; i < (BF_SIZE/10); i++) { switch( code[i] ) { case '>': strncat(ptr, "Ook. Ook? ", 10); break; case '<': strncat(ptr, "Ook? Ook. ", 10); break; case '+': strncat(ptr, "Ook. Ook. ", 10); break; case '-': strncat(ptr, "Ook! Ook! ", 10); break; case ',': strncat(ptr, "Ook. Ook! ", 10); break; case '.': strncat(ptr, "Ook! Ook. ", 10); break; case '[': strncat(ptr, "Ook! Ook? ", 10); break; case ']': strncat(ptr, "Ook? Ook! ", 10); break; default: ptr -= 10; break; // Otherwise it'll put a \0 and C will think the string's finished } ptr += 10; } memset(code, 0, BF_SIZE); for (i = 0; i < BF_SIZE; i++) { code[i] = tmp[i]; } return 0; } // Even when executing, Ook is 'compiled' to BF first, so this is used // in both modes. int ook2bf(char* code) { char tmp[BF_SIZE]; memset(tmp, 0, BF_SIZE); int O = 0, o = 0, k = 0; // Tracks each character ;) int i, ti = 0; // Counter for tmp array for (i = 0; i < BF_SIZE; i++) { // We only care about the punctuation on the Ook keywords, so that's pushed onto tmp if (code[i] == 'O') { O = 1; o = 0; k = 0; continue; } if (O && (code[i] == 'o')) { o = 1; continue; } if (o && (code[i] == 'k')) { k = 1; continue; } if (k) { tmp[ti] = code[i]; ti++; O = 0; o = 0; k = 0; } } // Now, clear out the code array, and put BF code in it from the Ook tokens in tmp memset(code, 0, BF_SIZE); int tj = 0; ti = 0; i = 0; // ti & tj are for the tmp array, i for the code array for (ti = 0; ti < BF_SIZE; ti += 2) { tj = ti + 1; if ( (tmp[ti] == '.') && (tmp[tj] == '?') ) { code[i] = '>'; } else if ( (tmp[ti] == '?') && (tmp[tj] == '.') ) { code[i] = '<'; } else if ( (tmp[ti] == '.') && (tmp[tj] == '.') ) { code[i] = '+'; } else if ( (tmp[ti] == '!') && (tmp[tj] == '!') ) { code[i] = '-'; } else if ( (tmp[ti] == '.') && (tmp[tj] == '!') ) { code[i] = ','; } else if ( (tmp[ti] == '!') && (tmp[tj] == '.') ) { code[i] = '.'; } else if ( (tmp[ti] == '!') && (tmp[tj] == '?') ) { code[i] = '['; } else if ( (tmp[ti] == '?') && (tmp[tj] == '!') ) { code[i] = ']'; } i++; } return 0; } // Conveniently fills the code char* with program code from file or stdin long get_code(FILE *fstream, char* code) { if (!fstream) { return -1; } char tmp[BF_SIZE]; long rbytes = fread(tmp, 1, BF_SIZE, fstream); if (rbytes == -1) { return -1; } int i = 0; for (i = 0; i < rbytes; i++) { code[i] = tmp[i]; } return rbytes; } int convert(char* fname, char* code) { char* ook = strstr(code, "Ook"); char new_fname[128]; memset(new_fname, 0, 128); strncpy(new_fname, fname, 122); if (ook != NULL) { // It's Ook. Write some BF. ook2bf(code); strncat(new_fname, ".b", 2); } else { // It's BF, write Ook bf2ook(code); strncat(new_fname, ".ook", 4); } FILE* output = fopen(new_fname, "w"); if (!output) { fprintf(stderr, "Could not open file %s for writing; could not compile!", fname); return -1; } fwrite(code, 1, strlen(code), output); fclose(output); return 0; } int execute(long psize, char* code) { char mem[BF_SIZE]; memset(mem, 0, BF_SIZE); int p = 0, i = 0, lvl = 0; // p is the memory pointer, i is the counter variable, lvl is the [] nesting counter for (i = 0; ((i > -1) && (i < psize)); i++) { // printf("%c", code[i]); switch (code[i]) { case '>': p++; // Increment pointer if (p >= BF_SIZE) { fprintf(stderr, "Pointer overflowing memory space!"); return -1; } break; case '<': p--; // Decrement pointer if (p < 0) { fprintf(stderr, "Oooh, pointer underflowing memory space!"); return -1; } break; case '+': mem[p]++; break; // Increment byte @ pointer case '-': mem[p]--; break; // Decrement byte @ pointer case '.': putchar(mem[p]); break; // Output byte @ pointer case ',': mem[p] = getchar(); break; // Input byte to position @ pointer case '[': if (mem[p] == 0) { // Jump past the matching ] if byte @ pointer == 0 i++; while ((lvl > 0) || (code[i] != ']')) { if (code[i] == '[') { lvl++; } if (code[i] == ']') { lvl--; } i++; } } break; case ']': if (mem[p] != 0) { // Jump back to matching [ unless byte @ pointer == 0 i--; while ((lvl > 0) || (code[i] != '[')) { if (code[i] == '[') { lvl--; } if (code[i] == ']') { lvl++; } i--; } i--; // makes up for i++ being called by the loop, later } break; case '#': // Nonstandard BF extension from Daniel Cristofani fprintf(stderr, "Pointer position: %d Value at pointer: %d\nCode position: %d of %d bytes\n\n", p, mem[p], i, strlen(code)); break; default: break; // Non-BF characters are comments } } return 0; } void usage() { printf("Brainf*ck / Ook interpreter and converter\n"); printf("Copyright (C) 2007 Rami R Chowdhury\n\n"); printf("bfook is a good Unix application and will accept program code piped to\n"); printf("its standard input, if the program does not require user interaction:\n"); printf(" bfook < [file]\n\n"); printf("The preferred usage is to specify a single filename argument, which bfook\n"); printf("will execute (automatically detecting whether it is Ook or Brainf*ck):\n"); printf(" bfook [file]\n\n"); printf("Using the -c switch puts bfook in converter mode, and it will convert the\n"); printf("input file into the other dialect (if it is in Ook, it will be converted \n"); printf("to Brainf*ck, and vice versa) and write it to a new file with \".b\" appended\n"); printf("if the new file is Brainf*ck, and \".ook\" if it is Ook. (WARNING: bfook will\n"); printf("*NOT* preserve comments or whitespace):\n"); printf(" bfook -c [file]\n\n"); } int main(int argc, char **argv) { int from_file = 0; // Pre-set the code array, just to be on the safe side memset(code, 0, BF_SIZE); if (argc < 2) { fstream = stdin; } else if ( (argc == 2) && (argv[1][0] == '-') && (argv[1][1] == 'h') ) { // -h triggers usage() usage(); return 0; } else { int argn = 1; if (argc == 3) { argn = 2; } fstream = fopen( argv[argn], "r" ); if (!fstream) { fprintf(stderr, "Unable to open file %s! Exiting...\n", argv[argn]); return -1; } from_file = 1; } long psize = get_code(fstream, code); if (psize == -1) { fprintf(stderr, "Error reading program file. Exiting...\n"); return -1; } if (from_file) { fclose(fstream); } if ( (argc == 3) && (argv[1][0] == '-') && (argv[1][1] == 'c') ) { convert(argv[2], code); // argv[1] is '-c' } else { if (strstr(code, "Ook") != NULL) { ook2bf(code); } execute(psize, code); } return 0; }