HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Brainfuck parser

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
brainfuckparserstackoverflow

Problem

Since I have little to no knowledge about native languages, I tried once more to write a simple application in C++, this time with a Brainfuck parser.

It works, kind of, verified with the Hello World and Cat examples given in the Esolang wiki. So...what did I just break?

#include 
#include 

using namespace std;

#define INC_POINTER '>'
#define DEC_POINTER '<'
#define INC_VALUE '+'
#define DEC_VALUE '-'
#define OUTPUT_ASCII '.'
#define INPUT_ASCII ','
#define IF_ZERO '['
#define IF_NOT_ZERO ']'

int main(int argc, char* argv[]) {
    if (argc <= 1) {
        cout << "Input-File required..." << endl;
        return 0;
    }

    ifstream input;
    input.open(argv[1], ios::in | ios::binary | ios::ate);

    ifstream::pos_type programLength = input.tellg();
    char* program = new char[programLength];

    input.seekg(0, ios::beg);
    input.read(program, programLength);

    input.close();

    // I'm still thinking about a good way to pre-calculate
    // the size.
    char* data = new char[30000];
    fill_n(data, 30000, 0);

    for (int idx = 0; idx < programLength; idx++) {
        if (program[idx] == INC_POINTER) {
            ++data;
        } else if (program[idx] == DEC_POINTER) {
            --data;
        } else if (program[idx] == INC_VALUE) {
            ++*data;
        } else if (program[idx] == DEC_VALUE) {
            --*data;
        } else if (program[idx] == OUTPUT_ASCII) {
            cout << *data;
        } else if (program[idx] == INPUT_ASCII) {
            cin.read(data, 1);
        } else if (program[idx] == IF_ZERO) {
            if(*data == 0) {
                while(program[idx] != IF_NOT_ZERO) {
                    idx++;
                }
            }
        } else if (program[idx] == IF_NOT_ZERO) {
            if(*data != 0) {
                do {
                    idx--;
                } while(program[idx] != IF_ZERO);
            }
        }
    }

    return 0;
}

Solution

You don't need to read the whole program into memory.

You use a stream to represent the program.

Reading a character automatically moves you to the next location. If you need additional control of the stream you can use seekg() to move around the stream a bit more.

Rather than multiple if statements. This is the perfect situation for a switch:

If there is an error you should return 1 rather than 0 so that the inclosing shell notices there was an error.

There is a small bug in your back seek:

If you have code like this:

[XX[]XX]XXXX
       ^


If you are at the point marked with ^ you will seek back to the wrong point (the second '[' in the program rather than the first.

Here is what I would do.

Just copying and modifying your code (not reading how BF works).

#include 
#include 

using namespace std;

int main(int argc, char* argv[])
{
    std::vector  jumpPoint;

    if (argc  dataStore(30000, '\0');
    char* data = &dataStore[0];

    unsigned char next;
    while(input >> noskipws >> next)
    {
        switch(next)
        {
            case '>': // INC_POINTER 
                ++data; if (data > &dataStore[29999]) { data = &dataStore[0];}
                break;
            case '<': // DEC_POINTER
                --data; if (data < &dataStore[0]) { data = &dataStore[29999];}
                break;
            case '+': // INC_VALUE
                ++*data;
                break;
            case '-': // DEC_VALUE
                --*data;
                break;
            case '.': // OUTPUT_ASCII
                cout << *data;
                break;
            case ',': // INPUT_ASCII
                cin.read(data, 1);
                break;
            case '[': // IF_ZERO
                if(*data == 0) {
                    // Seek forward to the point after ']'
                    std::string ignore;
                    std::getline(input, ignore, ']');
                }
                else {
                    // Otherwise push the current point so we can rewind
                    // if required when we hit the next ']'
                    jumpPoint.push_back(input.tellg());
                }
                break;
            case ']': // IF_NOT_ZERO
                if(*data != 0) {
                    // Move the read point back to the last '[' that we saved
                    input.seekg(jumpPoint.back());
                }
                else {
                    // Since this is a close and we did not seek back pop the last
                    // saved point off the stack
                    input.pop_back();
                }
                break;
            default:
                // ignore;
                break;
        }
    }

    return 0;
}

Code Snippets

[XX[]XX]XXXX
       ^
#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char* argv[])
{
    std::vector<std::streampos>  jumpPoint;

    if (argc <= 1) {
        cout << "Input-File required..." << endl;
        return 1;
    }

    ifstream input(argv[1], ios::in | ios::binary);
    if (!input) {
        cout << "Failed to open: " << argv[1] << endl;
        return 1;
    }

    std::vector<char> dataStore(30000, '\0');
    char* data = &dataStore[0];

    unsigned char next;
    while(input >> noskipws >> next)
    {
        switch(next)
        {
            case '>': // INC_POINTER 
                ++data; if (data > &dataStore[29999]) { data = &dataStore[0];}
                break;
            case '<': // DEC_POINTER
                --data; if (data < &dataStore[0]) { data = &dataStore[29999];}
                break;
            case '+': // INC_VALUE
                ++*data;
                break;
            case '-': // DEC_VALUE
                --*data;
                break;
            case '.': // OUTPUT_ASCII
                cout << *data;
                break;
            case ',': // INPUT_ASCII
                cin.read(data, 1);
                break;
            case '[': // IF_ZERO
                if(*data == 0) {
                    // Seek forward to the point after ']'
                    std::string ignore;
                    std::getline(input, ignore, ']');
                }
                else {
                    // Otherwise push the current point so we can rewind
                    // if required when we hit the next ']'
                    jumpPoint.push_back(input.tellg());
                }
                break;
            case ']': // IF_NOT_ZERO
                if(*data != 0) {
                    // Move the read point back to the last '[' that we saved
                    input.seekg(jumpPoint.back());
                }
                else {
                    // Since this is a close and we did not seek back pop the last
                    // saved point off the stack
                    input.pop_back();
                }
                break;
            default:
                // ignore;
                break;
        }
    }

    return 0;
}

Context

StackExchange Code Review Q#13767, answer score: 3

Revisions (0)

No revisions yet.