4
\$\begingroup\$

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 <iostream>
#include <fstream>

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;
}
\$\endgroup\$
3
\$\begingroup\$

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 <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;
}
\$\endgroup\$
  • \$\begingroup\$ It looks like you lost the definition of data. \$\endgroup\$ – Winston Ewert Jul 17 '12 at 22:39
  • \$\begingroup\$ @WinstonEwert: Thanks Fixed. You made me read the language spec. Ugg. \$\endgroup\$ – Martin York Jul 18 '12 at 19:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.