Brainfxcker
Brainfuck to x86-64 compilerongoing
- Period
- Apr 2026 – present
- Context
- personal
- Team
- solo
- C
- Brainfuck
- Assembly
- NASM x86-64 Intel
- Compiler
- Low-level
- Imperative programming
A real little compiler: it reads a Brainfuck program, passes it through a lexer, a parser and an intermediate representation, then writes x86-64 assembly that NASM and ld turn into an ELF binary.
Why Brainfuck
Brainfuck has only eight instructions, acting on a tape of memory cells and a pointer. The language is trivial: all the interest is in the translation. It was the perfect excuse to understand assembly and the stages of a compiler, without getting lost in the grammar of a complex language.
The stages
The lexer only keeps the eight meaningful characters. The parser builds a tree in which each [ ] loop holds its own block. That tree is then flattened into intermediate instructions: ADD, MOVE, SET, OUTPUT, INPUT, LOOP_BEG, LOOP_END.
An optimisation pass merges repeated instructions (+++ becomes ADD 3, >> becomes MOVE 2) and spots reset loops such as [-]. The code generator finally writes NASM code: the 30,000-byte tape is reserved in .bss, the pointer lives in rbx, and input / output go straight through the read, write and exit system calls.
The details
Instructions are stored in a hand-written “paged vector”: an array of 64-element pages that grows without ever moving the elements already stored. Since I work on a Mac, the compiler runs in a linux/amd64 Docker container to produce and run real x86-64 binaries.
What I took away
- A compiler is mostly a series of representations, each one closer to the machine.
- The Linux system call convention (
rax,rdi,rsi,rdx, thensyscall). - What the assembler and the linker actually do, and what is inside an ELF file.