patterncppMinor
Brainfuck parser
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?
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 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:
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).
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.