5
\$\begingroup\$

Generic Headers

#include <vector>
#include <string>
#include <iostream>
#include <fstream>

typedef std::string const           BFProgram;

BF Interpreter

class BFInterpretor
{
    static const std::size_t MemorySize = 10000;

    std::vector<char>           memory;
    std::vector<std::size_t>    openLoops;
    BFProgram&                  program;
    std::istream&               input;
    std::ostream&               output;
    std::size_t                 dataPointer;
    std::size_t                 progPointer;

    std::size_t scanToEndOfLoop()
    {
        std::size_t nestedLoops = 1;
        std::size_t scanPos     = progPointer;
        for(;nestedLoops != 0;++scanPos)
        {
            switch(program[scanPos])
            {
                case '[':   ++nestedLoops;  break;
                case ']':   --nestedLoops;  break;
                default:                    break;
            }
        }
        return scanPos;
    }

    public:
        BFInterpretor(BFProgram& program, std::istream& input = std::cin, std::ostream& output = std::cout)
            : memory(MemorySize)
            , program(program)
            , input(input)
            , output(output)
            , dataPointer(0)
            , progPointer(0)
        {}

        static bool bfComment(char c)
        {
            switch(c)
            {
                case '>': case '<': case '.': case ',': case '+': case '-': case '[': case ']':
                    return false;
                default:
                    break;
             }
             return true;
        }
        void run()
        {
            while(progPointer < program.size())
            {
                ++progPointer;
                switch(program[progPointer - 1])
                {
                    case '>':   dataPointer = (dataPointer + 1) % MemorySize;break;
                    case '<':   dataPointer = (dataPointer - 1) % MemorySize;break;
                    case '+':   ++(memory[dataPointer]);                     break;
                    case '-':   --(memory[dataPointer]);                     break;
                    case '.':   output.put(memory[dataPointer]);             break;
                    case ',':   memory[dataPointer] = input.get();           break;
                    case '[':   if (memory[dataPointer]) {
                                    openLoops.push_back(progPointer);
                                }
                                else {
                                    progPointer = scanToEndOfLoop();
                                }
                                break;
                    case ']':   if (memory[dataPointer]) {
                                    progPointer = openLoops.back();
                                }
                                else {
                                    openLoops.pop_back();
                                }
                                break;
                    default:    break; /* Ignore all comments */
                }
            }
        }
};

Program Loader

BFProgram loadProgram(std::istream& program)
{
    std::string  programText;

    std::string line;
    while(std::getline(program, line))
    {
        line.erase(std::remove_if(std::begin(line), std::end(line), [](char c){return BFInterpretor::bfComment(c);}), std::end(line));
        programText.append(line);
    }
    return programText;
}
BFProgram loadProgram(std::string const& file)
{
    if (file == "-") {
        return loadProgram(std::cin);
    }
    std::ifstream   program(file);
    return loadProgram(program);
}

Main

int main(int argc, char* argv[])
{
    std::vector<std::string>    args(argv+1 , argv+argc);
    if (args.empty()) {
        args.push_back("-");
    }

    for(std::string const& file: args) {
        std::string     program = loadProgram(file);
        BFInterpretor   interpretor(program);
        interpretor.run();
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Too little to add for a full answer: 1. Are you sure storing a reference to the program instead of a copy ofit is a good idea? Hope noone ever passes an xvalue there. 2. Consider wrapping your lines a little earlier, horizontal scrolling is deadly. \$\endgroup\$ Mar 19, 2015 at 0:07

2 Answers 2

2
\$\begingroup\$
  • Make MemorySize a parameter of the interpreter, or even better make the memory array dynamically adjust its size to the contents.
  • Missing the <algorithm> header for using std::remove_if.
  • Redundant use of lambda [](char c){return BFInterpretor::bfComment(c);}: simple BFInterpretor::bfComment as a predicate would do.
  • I would also declare BFInterpretor's constructor explicit to avoid unnecessary type conversion.
  • For my taste, a bit fishy use of magic "-" literal to treat the special case of std::cin, I would rather store istream-provider functions there.
\$\endgroup\$
1
  • \$\begingroup\$ You get the tick for spotting the bad usage of lambda (which I think was a terrible mistake on my side). Also liked the concept of MemorySize as a parameter \$\endgroup\$ Mar 22, 2015 at 0:59
4
\$\begingroup\$
  • An malformed program (with misbalanced brackets) may cause an UB (e.g. popping back from an empty vector; or running out of program space in scanToEndOfLoops).

  • I recommend implementing step method:

    void run() {
        while (progPointer < program.size()) {
            step();
        }
    }
    

    with an intention to reuse it in a debugger.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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