BF interpreter in C++

Posted on 23th June 2015


BF is an amusingly named, impractical, Turing complete programming language. It is very like an actual Turing machine: you have an infinite (originally, one-sided, 30K long) array of (8-bit) values, with commands to increase/decrease (+/-) the cell, or move left/right (). Input (,) and output (.) are handled. Loops are given by [] and implement a "while cell is non-zero" loop. So a trivial mapping to C is possible (see the Wikipedia article).

I thought it would be fun to give an object oriented implementation, for which I used C++ for a change. My implementation decouples the parsing of the programme from running it. The parse class reads the input in a single pass, using a stack for dealing with nested loops. It translates the input into a list of commands, represented by an abstract base class with an execute method, overloaded for the various different commands. This is the "command" pattern (closely related to the "strategy" pattern).

I then have a separate class to represent the machine which can run the commands. This is again an abstract base class, which leaves input and output to be implemented in a derived class. The commands act on the machine (I think you could consider this an example of "dependency inversion principle": both the commands and the machine act on abstractions, so there is loose coupling between them).

Currently I just have one concrete machine, which uses vectors to store input and output. As an example of the loose coupling, there are two parsers and associated command classes, which can work with the same machine. The first parser does no translation (and, internally, I use unique_ptrs a lot to avoid memory management). The second parser coalesces multiple commands (so, for example, "++++" becomes "+ times 4" internally) and uses RAII to handle memory management.

The parser and the machine need to communicate the list of instructions. This is mediated by an abstract base class which functions like a specialised vector. The concrete implementation actually uses a vector of (smart) pointers but one could also implement this as a raw interpreter with no parse stage, I guess. To avoid copying the list, I used shared_ptr aka reference counting. A subtle point here is that in the constructor, we shouldn't have a naked pointer because we may throw an exception if the input is not well-formed. The unique_ptr release() method comes in handy here to pass off ownership to a shared_ptr.

On GitHub

Update: To be correct, in the 2nd case, we need to delete the copy constructor and assignment operators (as the object "owns" the allocated memory, a copy should not be allowed, unless we make a deep copy, which we have no need for.) I actually rather like the philosophy of "Role of Zero": use automatic containers and unique_ptr or shared_ptr to manage all members. These then automatically provide copy/move operators, or disallow them (in the case of unique_ptr). (As ever, try this with g++, and you get an incomprehensible error message though!)

The article goes on to talk about how we can in fact avoid the need for virtual destructors if we consistently use shared_ptr, because a side-effect of storing a destructor is that shared_ptr<base> p = make_shared<derived>() means that when the reference count of p becomes 0, the destructor of derived is (correctly) called even if the destructor is not virtual. A bit of work with Google reveals the following standards suggestion: n3974.pdf which will allow something similar with make_unique.


Categories
Recent posts