What This is Not
Acus4 is not a BF compiler: it is a C++ library for the development of such compilers. This chapter is not a guide on how to use Acus. For examples and extensive API details, please visit the GitHub repository at [10]. Instead, this chapter goes through the most important concepts, methods and abstractions that allow for the translation of high-level programming concepts (like function calls and flow control) into BF.
Motivation and History
Up to this point, almost every BF program used to test the computer was found online and used verbatim. On some occasions, simple BF routines were written to test very specific functions of the computer, but these were borderline trivial to produce. The next goal, therefore, was to create a compiler that takes human-readable source code and turns that into BF, which in turn can be assembled into the binary format understood by Synapse-191.
The author has attempted to create such a language and compiler in the past: BrainFix [9] is a compiler that takes a C-style language and turns it into BF. This compiler works reasonably well but has some serious limitations:
No (true) function calls: all functions are inlined. Recursion is therefore not possible.
Dynamic array accesses were implemented in a way that limits the maximum array size to 256 elements.
Only 8-bit numbers are supported (assuming the BF tape stores 8-bit integers in its cells).
No signed integers.
No pointers or function pointers.
These limitations derive from naive choices during early development of the compiler, where the focus was on parsing and language design: features were bolted on rather than planned for in the early stages of development. In this second, more mature attempt to build a compiler, we started out by building a backend rather than a full compiler: Acus. Acus is a C++ library that exposes a set of elementary assembly-like instructions (like arithmetic operations, conditional expressions and jumps) that are compiled into BF. Acus is meant to be used as a compiler backend: a layer that accepts higher-level operations and emits BF code. A frontend can then link with Acus, parse an actual programming language, call the necessary Acus functions and produce the corresponding BF. Acus can also be used on its own, since its library functions were designed to feel a bit like an actual language, as can be seen from the Hello World snippet of Listing 35
#include <iostream>
#include "acus/assembler/assembler.h"
using namespace acus;
int main() {
Assembler a;
a.program("hello", "main").begin(); {
a.function("main").begin(); {
a.print(literal::string("Hello, World!\n"));
a.returnFromFunction();
} a.endFunction();
} a.endProgram();
std::cout << a.brainfuck("hello") << '\n';
}Challenges and Design Goals
The main goal was to make Acus capable enough that a frontend for a language like C would have no problem implementing most of its features. These include:
8-bit and 16-bit integer data types;
signed integers (both 8-bit and 16-bit);
arrays and strings;
user-defined structured types (structs);
arithmetic operators (
+, -, *, /, %);logic operators (
and, or, not, xor);function calls and recursion;
pointers and pointer arithmetic;
labels and conditional jumps (
goto);global data.
Familiar features that were not implemented include:
floating/fixed-point numbers;
bitwise operators;
member functions;
control-flow mechanisms like
for, while, switch, if.
Many of the above can be implemented in terms of the features provided by Acus; it was a deliberate choice not to make these part of the library in order for the library to remain as fundamental as possible.