I’m currently away on a family vacation, and as soon as vacation is over, I’m off on a business trip for a week. And along the way, I’ve got some deadlines for my book. So to fill in, I’m recycling some old posts. I decided that it’s been entirely too long since there was any pathological programming ’round these parts, so I’m going to repost some of my favorites.
As long-time readers know by now, in real life, I’m not a mathematician; I’m a computer scientist. I’m still a math geek, mind you, but what I really do is very much in the realm of applied math, working on building systems to help people build programs.
One of my pathological obsessions is programming languages. Since I first got exposed to TRS-80 Model 1 BASIC back in middle school, I’ve been absolutely nuts programming languages. Last time I counted, I’d learned about 150 different languages; and I’ve picked up more since then. I’ve written programs most of them. Like I said, I’m nuts.
These pathological programming language posts are my way of inflicting my obsession on you in a (hopefully) amusing way. You see, in this screwed up world of ours, there are lots and lots of thoroughly crazy people out there. In the world of geekery, many of those crazy people like to invent programming languages. Some very small number of them try to design good languages and succeed; a much larger number try to design good languages and fail; and then there are ones whose work I’m writing about. The ones who deliberately set out to design strange, warped, twisted, and nearly unusable languages, and succeed brilliantly. Most of the people who design them call them “esoteric” programming languages. I call them evil.
Today, the beautiful grand-daddy of the esoteric language family: the one, the only, the truly and deservedly infamous: Brainfuck, designed by Urban Müller. (There are a number of different implementations available; just follow the link.)
Only 8 commands – including input and output – all written using symbols. And yet it’s Turing complete. In fact, it’s one-up on just being Turing complete – it’s actually been formally specified with a complete formal theoretical design, called P”. And it’s even been implemented in hardware!.
BrainFuck is based on something very much like a twisted cross between a
Turing machine and a Minsky machine: it’s got a tape, like a turing machine. But unlike the turing machine, each cell of the tape can store an arbitrary number, which can be incremented or decremented — like a Minsky machine’s registers. Also like a Minsky machine, it’s got a notion of control flow based on zero-tests.
The 8 brainfuck instructions are:
>: move the tape head one cell forward.
<: move the tape head one cell backward.
+: increment the value on the current tape cell.
-: decrement the value on the current tape cell.
.: output the value on the current tape cell as a character.
,: input a character and write its numeric value onto the current tape cell.
[: compare and jump forward: compare the current tape cell to 0. If it’s zero, jump forward to the first instruction after the matching “
]“; otherwise, go on to the next instruction.
]: compare and jump backward: if the current tape cell is not zero, then jump backward to the matching “[“.
- Any character which is not one of those eight instruction characters is a no-op – that is, it does nothing – execution will skip forward to the next command character. This means that you don’t need any special syntax to write comments in brainfuck – you just intersperse them with the program instructions. (But you need to do it carefully; if you use punctuation, you’ll probably accidentally create instructions which break your program. So Brainfuck makes it impossible to write grammatical comments!)
Here’s a hello-world program in BF:
++++++++[>+++++++++<-]>.<+++++[>++++++<-]>-. +++++++..+++.<++++++++[>>++++<<-]>>. <<++++[>------<-]>.<++++[>++++++<-]>. +++.------.--------.>+.
Let’s pull that apart just a bit so that we can hope to understand.
++++++++“: store the number “8” in the current tape cell. We’re going to use that as a loop index, so the loop is going to repeat 8 times.
[>+++++++++<-]“: Run a loop: using the tape cell after the loop index, add “9” to it. Then go back to the loop index, decrement it, and branch back to the beginning of the loop if it’s not zero. So we wind up with the number 72 in the second cell. That’s the ascii code for “H”.
>.“: go to the cell after the loop index, and output what’s there. That outputs the “72” as a character: “H”.
<+++++“: back to the loop index. This time store 5 in it.
[>++++++<-]“: same idea as the loop to generate the “H”: this time, we’re going to add 5 * 6 to the value in the second cell. (Remember that we didn’t get rid of the value in that cell – so it’s still 72.) So now the second cell contains 102.
>-.“: Advance past the index, subtract one, and output. That’s 101, or “e”.
After that, it continues in pretty much the same vein, using a couple of tape cells, and running loops to generate the values of the characters. It’s quite beautiful in its way. But at the same time, that’s an astonishingly complicated way of just printing out “Hello world”! Remarkably, isn’t it?
If that didn’t seem impressive enough,here is a really gorgeous implementation of a fibonacci sequence generator, complete with documentation. The BF compiler used to write this ignores any character other than the 8 commands, so the comments don’t need to be marked in any way; they just need to be really careful not to use punctuation.
+++++++++++ number of digits to output > #1 + initial number >>>> #5 ++++++++++++++++++++++++++++++++++++++++++++ (comma) > #6 ++++++++++++++++++++++++++++++++ (space) <<<<<< #0 [ > #1 copy #1 to #7 [>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-] < divide #7 by 10 (begins in #7) [ > ++++++++++ set the divisor #8 [ subtract from the dividend and divisor -<- if dividend reaches zero break out copy dividend to #9 [>>+>+<<<-]>>>[<<<+>>>-] set #10 + if #9 clear #10 <[>[-]<[-]] if #10 move remaining divisor to #11 >[<<[>>>+<<<-]>>[-]] jump back to #8 (divisor possition) << ] if #11 is empty (no remainder) increment the quotient #12 >>> #11 copy to #13 [>>+>+<<<-]>>>[<<<+>>>-] set #14 + if #13 clear #14 <[>[-]<[-]] if #14 increment quotient >[<<+>>[-]] <<<<<<< #7 ] quotient is in #12 and remainder is in #11 >>>>> #12 if #12 output value plus offset to ascii 0 [++++++++++++++++++++++++++++++++++++++++++++++++.[-]] subtract #11 from 10 ++++++++++ #12 is now 10 < #11 [->-<] > #12 output #12 even if it's zero ++++++++++++++++++++++++++++++++++++++++++++++++.[-] <<<<<<<<<<< #1 check for final number copy #0 to #3 <[>>>+>+<<<<-]>>>>[<<<<+>>>>-] <- #3 if #3 output (comma) and (space) [>>.>.<<<[-]] << #1 [>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<- ]