Skip to content

Instantly share code, notes, and snippets.

@Jookia
Created January 3, 2011 02:25
Show Gist options
  • Save Jookia/763047 to your computer and use it in GitHub Desktop.
Save Jookia/763047 to your computer and use it in GitHub Desktop.

Revisions

  1. Jookia created this gist Jan 3, 2011.
    190 changes: 190 additions & 0 deletions brainfu.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,190 @@
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    // Some defines useful for tweaking the interpreter.
    #define DATA char
    #define CELL unsigned char
    #define CELL_COUNT 30000
    #define LOOP_STACK_SIZE 32

    // data is a string of characters.
    void interpret(DATA* data)
    {
    CELL cells[CELL_COUNT] = {0};

    CELL* currentCell = &cells[0]; // Start at the leftmost cell.
    DATA* currentInstruction = &data[0]; // Start at the beginning of the data.

    // The loop stack is used to store positions for beginnings of loop. Used
    // mainly for loops in loops.
    DATA* loopStack[LOOP_STACK_SIZE] = {0};
    int currentLoop = 0; // Current loop on the stack.
    int loopJumping = 0; // Boolean used if the loop is being jumped over.

    // When jumping through a loop and ignoring everything inside, loopsToJump
    // is incremented and decremented when faced with loops inside the loop that's
    // being jumped through. The purpose of this is to make sure that the end of
    // the jump is the matching loop bracket and not one inside or outside.
    int loopsToJump = 0;

    do
    {
    char operator = *currentInstruction;

    if(loopJumping != 0 && operator != '[' && operator != ']')
    {
    // Ignore anything that isn't brackets for a loop when jumping past
    // loops. Brackets are only acknowledged as to increment or decrement
    // loopsToJump.
    continue;
    }

    switch(operator)
    {
    case '>': ++currentCell; break; // Increment the current cell.
    case '<': --currentCell; break; // Decrement the current cell.

    case '+': ++(*currentCell); break; // Move to the next cell.
    case '-': --(*currentCell); break; // Move to the previous cell.

    case '.': putchar(*currentCell); break; // Print the current cell.
    case ',': // Get input.
    {
    // Just grab the first character from stdin. Will overflow if more than
    // one character is entered in to undefined memory after buffer.

    char buffer;
    fscanf(stdin, "%s", &buffer);

    *currentCell = buffer;

    break;
    }

    case '[': // Iterate over a loop or jump over it if currentCell is 0.
    {
    if(loopJumping != 0)
    {
    // Jumping through a loop, increment loopsToJump as to not mistake
    // a closing loop bracket for the jumping loop's closing bracket.
    ++loopsToJump;
    break;
    }

    if(loopStack[currentLoop] != currentInstruction)
    {
    // Entering a loop. Push the instruction to the loop stack.
    ++currentLoop;
    loopStack[currentLoop] = currentInstruction;
    }

    if(*currentCell == 0)
    {
    // No iterations to do, jump to end of the loop.
    loopJumping = 1;
    }

    break;
    }

    case ']': // Go back to the start of the loop.
    {
    if(loopJumping != 0)
    {
    if(loopsToJump != 0)
    {
    // Jumping through a loop and just passed a loop that's inside a
    // loop, continue jumping to find the real end to this loop and
    // not the end to another.
    --loopsToJump;
    break;
    }

    // Done jumping.
    loopJumping = 0;
    --currentLoop;

    break;
    }

    // Go back to the start of the loop. Take one away as to make up for
    // the current instruction pointer being incremented.
    currentInstruction = loopStack[currentLoop] - 1;

    break;
    }

    // Nothing to do with non-operators.
    default: break;
    }
    }
    // Move to the next instruction in the data. If the instruction is a null
    // terminator, interpreting is done.
    while(*(++currentInstruction) != '\0');
    }

    // Helper function to load a file in to a char array in memory. Don't forget to
    // free the array after use.
    char* loadFile(const char* filename)
    {
    FILE* file = fopen(filename, "r");

    if(file == NULL)
    {
    fprintf(stderr, "Unable to open '%s'.\n", filename);
    return 0;
    }

    if(fseek(file, 0L, SEEK_END) == -1)
    {
    fprintf(stderr, "Unable to seek to the end of the file.\n");
    fclose(file);
    return 0;
    }

    int fileLength = (sizeof(char) * ftell(file));
    char* fileContents = (char*)malloc(fileLength);
    memset(fileContents, 0, fileLength);

    if(fileContents == NULL)
    {
    fprintf(stderr, "Unable to allocate space for the file contents.\n");
    fclose(file);
    return 0;
    }

    rewind(file);
    fread(fileContents, sizeof(char) * fileLength, 1, file);

    fclose(file);

    return fileContents;
    }

    int main(int argc, const char** argv)
    {
    if(argc != 2)
    {
    printf("Usage: %s FILE\n", argv[0]);
    return 0;
    }

    char* fileContents = loadFile(argv[1]);

    if(fileContents == 0)
    {
    return 1;
    }

    // Print BRAINFU to the screen.
    interpret(">+++++++[<++++++++++>-]<----.>++[<++++++++++>-]<----.>--[<+++++++"\
    "+++>-]<+++.>+[<++++++++++>-]<--.+++++.>-[<++++++++++>-]<++.>+[<++++++++++"\
    ">-]<+++++.>>+[<++++++++++>-]<.");

    interpret(fileContents);

    free(fileContents);

    return 0;
    }