Vim as a Turing Machine: A Deep Dive into Pure Command-Line Computation
- Stephanie Domas
- Jul 15
- 4 min read
Updated: Jul 16
You know Vim, right? That venerable text editor we all love. But how well do you really know it? Today, I want to pull back the curtain and show you how we can push Vim far beyond mere text editing, transforming it into a fully functional Turing machine. And no, I'm not talking about Vimscript. That would be too easy. We're going vanilla, sticking to the raw power of normal Vim commands: yank, put, delete, search, and replace.
The Grand Design: BrainF**k in Vim
To truly demonstrate Vim's computational majesty, we'll implement an interpreter for BrainFuck (BF)2. Our "machine" is a simple text file that, when opened in Vim and kicked off with a few key presses, interprets BF code purely through these copy/paste/search/replace style Vim commands.
Let's break down the architecture:
Memory Tape: Our data tape is represented directly in the text file as a series of zeros. We start with ten cells, easily located by searching for _t.
BrainFuck Code: The BF program itself is embedded within the file. I've included a Fibonacci number generator as a demonstration.
Input/Output & ASCII Lookup: We set aside sections for input (i:) and output (o:). For output, we need to convert numeric memory cells to ASCII. This is handled by an ASCII lookup table directly in the file, cleverly arranged with underscores and spaces to aid navigation via Vim commands.
Navigation Marks: To simplify navigation within our sprawling "program," we set Vim marks at key points: the start of the file (mh), the memory tape (mt), the BF code (mp), and the input, output, and ASCII tables (mo, mi, ma).
The Execution Engine: Self-Modifying Key Presses
This is where the magic truly happens. Instead of manually typing commands repeatedly, we embed the necessary key presses directly into our Vim file. The genius lies in self-modification: a line of commands is yanked (yy) and executed (@"). If that line then moves to another line and repeats the process, execution continues indefinitely, without any further user interaction. It's a pure, self-perpetuating Vim machine.
Our initial launch command gg2yy@" kicks off the entire process, by moving to the first line, yanking it, and executing its content. This first line contains a sequence of commands that handles setting up the machine and then begins the actual BF instruction interpretation loop.
The core execution loop is quite elegant: it moves to the BF program ('p), yanks a single BF instruction (yl), then jumps to our command table ('c). It searches for the yanked BF instruction in this table, navigates to its corresponding Vim commands, yanks those commands, and executes them. This cycle repeats for every BF instruction.
Implementing BrainF**k Instructions
Let's look at how each of the eight BrainF**k(BF) instructions is translated into a sequence of normal Vim commands:
> (Move Pointer Right): We move to the memory tape ('t), advance one cell (w), mark the new position (mt), then return to the BF program ('p), move past the executed instruction, and prepare for the next.
< (Move Pointer Left): Similar to > but moving backward (b) on the memory tape.
+ (Increment Cell): We move to the current memory cell ('t) and increment it using ^A. While ^A is technically a visual mode command and a slight deviation from our "copy/paste/search/replace" purity, it could be replaced with a more elaborate lookup table mechanism for true purists.
- (Decrement Cell): Analogous to +, but using ^X to decrement the current cell.
. (Output Cell): This is a bit more involved. We yank the current memory cell (yw), move to our ASCII table ('a), search for the numeric value (or uuu if out of range), yank the corresponding ASCII character, move to the output section ('o), paste it (p), mark the new output position (mo), and then resume BF execution.
, (Input Cell): This is handled by a reverse lookup process where we yank a memory cell, navigate to "assist commands" ('i), search for either the yanked cell or ^R"\n, and execute commands to store the input.
[ (Conditional Jump Forward): This instruction means "if the current cell is zero, jump the instruction pointer forward to the command after the matching ] command." We achieve this with a conditional search on the memory cell, which directs execution to either advance the BF program normally (if non-zero) or jump ahead (if zero).
] (Conditional Jump Backward): The counterpart to [, it moves the instruction pointer backward to the command after the matching [ instruction. This also uses conditional logic based on the cell's value.
Witness the Power!
With all eight BF instructions implemented, our Vim execution engine is complete! It's a true testament to the raw power lurking beneath Vim's seemingly simple interface. We've even added a convenience search and replace sequence at the start to allow for easy insertion of control characters without direct entry, resulting in a pure ASCII file.
To experience this for yourself, simply copy the entire machine definition, paste it into a vanilla Vim instance, launch with gg2yy@", and watch as our benevolent editor calculates prime numbers up to 100. It's mind-bending to see a text editor perform arbitrary computation with nothing but its native commands.
If you're eager to dig into the source or play around, check out the repository:
It's been a wild ride pushing Vim to its absolute limits, and I hope this peek behind the curtain inspires you to explore the hidden depths of your favorite tools!
Christopher Domas (@xoreaxeaxeax)

