Update
This commit is contained in:
parent
d5e4b36718
commit
4bff69ec4a
12 changed files with 2056 additions and 1813 deletions
191
brainfuck.md
191
brainfuck.md
|
@ -35,18 +35,18 @@ Brainfuck source code files usually have *.bf* or *.b* extension.
|
|||
|
||||
## Implementation
|
||||
|
||||
This is a very simple [C](c.md) implementation of brainfuck:
|
||||
This is a very simple [C](c.md) implementation of Brainfuck interpreter:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
#define CELLS 30000
|
||||
|
||||
const char program[] = ",[.-]"; // your program here
|
||||
|
||||
#define CELLS 30000
|
||||
char tape[CELLS];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char tape[CELLS];
|
||||
unsigned int cell = 0;
|
||||
const char *i = program;
|
||||
int bDir, bCount;
|
||||
|
@ -89,12 +89,68 @@ int main(void)
|
|||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
TODO: brainfuck to C translator
|
||||
|
||||
TODO: comun implementation
|
||||
|
||||
Advanced Brainfuck implementations may include [optimizations](optimization.md), for example things like `>>><<>` may be reduced to `>>` etc.
|
||||
|
||||
And here is a Brainfuck to C transpiler, written in C, which EVEN does the above simple optimization of grouping together additions, subtractions and shifts. It will allow you to compile Brainfuck to native executables. The code is possibly even simpler than the interpreter:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int c, cNext;
|
||||
|
||||
puts("#include <stdio.h>\nunsigned char m[1024];\n"
|
||||
"char *c = m;\nint main(void) {");
|
||||
|
||||
#define NEXT { c = cNext; cNext = getchar(); }
|
||||
|
||||
NEXT NEXT
|
||||
|
||||
while (c != EOF)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '>': case '<': case '+': case '-':
|
||||
{
|
||||
unsigned int n = 1;
|
||||
|
||||
while (cNext == c)
|
||||
{
|
||||
NEXT
|
||||
n++;
|
||||
}
|
||||
|
||||
printf(" %s %c= %u;\n",(c == '<' || c == '>') ? "c" : "*c",
|
||||
(c == '>' || c == '+') ? '+' : '-',n);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '.': puts(" putchar(*c);"); break;
|
||||
case ',': puts(" *c = getchar();"); break;
|
||||
case '[': puts(" while (*c) {"); break;
|
||||
case ']': puts(" }"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
NEXT
|
||||
}
|
||||
|
||||
puts("return 0; }");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Programs
|
||||
|
||||
Here are some simple programs in brainfuck.
|
||||
|
@ -113,6 +169,131 @@ Read two 0-9 numbers (as ASCII digits) and add them:
|
|||
|
||||
TODO: more
|
||||
|
||||
## Making Brainfuck Usable: Defining Macrofucker
|
||||
|
||||
{ There probably exist BF derivatives in this spirit, it's very natural, I didn't bother checking too much, here I just want to derive this from scratch myself, for educational purposes. ~drummyfish }
|
||||
|
||||
What if we want to actually write a more complex program in Brainfuck? How do we tame the beast and get out of the Turing tarpit? We may build a metalanguage on top of Brainfuck that will offer more convenient constructs and will compile to Brainfuck, and maybe we'll learn something about building and [bootstrapping](bootstrap.md) computing environments along the way :) We may do this e.g. with a simple system of [preprocessing](preprocessing.md) [macros](macro.md), i.e. we will create a language with more advanced commands that will be replaced by plain Brainfuck commands -- on the level of source code -- before it gets executed. This turns out to be a quite effective approach that enables us to create sort of a [Forth](forth.md)-like language in which we may program quite complex things with the stack-based computing [paradigm](paradigm.md).
|
||||
|
||||
Hmmm okay, what name do we give the language? Let's call it **Macrofucker**. It will work like this:
|
||||
|
||||
- Vanilla Brainfuck commands work normally, they'll be simply copied.
|
||||
- Additionally we introduce macros. A macro will be defined as: `:M<commands>;`. `:` and `;` are simply keywords separating the macro definition, `M` is the macro name, which we'll for simplicity sake limit to single uppercase letters only (so we won't be able to make more macros than there are letters), and `<commands>` are just commands that will be copy-pasted wherever the macro is used.
|
||||
- A macro will be used by simply writing its name, i.e. if we have macro `M` defined (anywhere in the source code), we can use it by simply writing `M`. Optionally we may call it with numeric parameter as `MX`, where `X` is a decimal number. If no parameter is given, we consider it 0. Macro may be invoked even inside another macro.
|
||||
- Inside a macro definition we may use the symbol `$` that will repeat the next character by the macro's parameter number of times.
|
||||
|
||||
For example consider the following piece of code:
|
||||
|
||||
```
|
||||
:X[-]$+; >X10 >X11 >X12 >X13
|
||||
```
|
||||
|
||||
We first define macro called `X` that serves for storing constants in cells. The macro first zeroes the cell (`[-]`) and then repeats the character `+` the argument number of times. Then we use the macro 4 times, with constants 10, 11, 12 and 13. We also shift right before each macro invocation so it's as if we're pushing the constants on the stack. This code will compile to:
|
||||
|
||||
```
|
||||
>[-]++++++++++>[-]+++++++++++>[-]++++++++++++>[-]+++++++++++++
|
||||
```
|
||||
|
||||
If we examine and run the code, we indeed find that we end up with the values 10, 11, 12 and 13 on the tape:
|
||||
|
||||
```
|
||||
0 10 11 12 13
|
||||
^
|
||||
```
|
||||
|
||||
Implementing the preprocessor is about as simple as implementing Brainfuck itself: pretty easy. As soon as we have the preprocessor, we may start implementing a "[library](library.md)" of macros, i.e. we may expand Brainfuck by adding quite powerful commands -- the [beauty](beauty.md) of it is we'll be expanding the language in Macrofucker itself from now on, no more C code is required beyond writing the simple preprocessor. This is a very cool, [minimalist](minimalism.md) approach of building complex things by adding simple but powerful extensions to very simple things, the kind of incremental programming approach that's masterfully applied in languages such as [Forth](forth.md) and [Lisp](lisp.md).
|
||||
|
||||
So here it is, the Macrofucker preprocessor in C, along with embedded code of the program it processes -- here we include simple library that even includes things such as division, modulus and printing and reading decimal values:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
const char program[] =
|
||||
// the library (WARNING: cells to the right may be changed):
|
||||
":Z[-];" // zero: c[0] = 0
|
||||
":L$<;" // left: c -= N
|
||||
":R$>;" // right: c += N
|
||||
":I$+;" // inc: c[0] += N
|
||||
":D$-;" // dec: c[0] -= N
|
||||
":XZ$+;" // const: c[0] = N
|
||||
":N>Z+<[Z>-<]>[<$++>Z]<;" // not: c[0] = c[0] == 0 ? N + 1 : 0
|
||||
":CZ>Z<<$<[-$>>+>+<$<<]$>>>[-<$<<+>>$>]<;" // copy: c[0] = c[-(N + 1)]
|
||||
":M>C<Z>[-<$-->]<;" // minus: c[0] *= -(N + 1)
|
||||
":F>Z<[->+<]<$<[->$>+<$<]$>>>[-<<$<+>>$>]<;" // flip: SWAP(c[0],c[-(N + 1)])
|
||||
":A>C1[-<+>]<;" // add: c[0] += c[-1]
|
||||
":S>C1[-<->]<;" // subtract: c[0] -= c[-1]
|
||||
":T>C1>C1>Z<<-[->>A<<]>>[-L3+R3]L3;" // times: c[0] *= c[-1]
|
||||
":EC1>C1[-<->]<N;" // equals: c[-2] == c[-1] ? 1 : 0
|
||||
":GZ>C2>C2+<[->->CN[L3+R3Z]<<]<;" // greater: c[-1] > c[0] ? 1 : 0
|
||||
":B>C1>C1<<Z>>>GN[L3+>>S>GN]<F<;" // by: c[1] = c[0] % c[-1]; c[0] = c[0] / c[-1]; c++
|
||||
":P>X100>C1BF>X48A.L3X10>BF>X48A.<F>X48A.L4;" // print: print byte as decimal
|
||||
":VX48>,SFX100T>X48>,SFX10TF<A>X48>,SF<AF2L3;" // value: reads decimal number of three digits
|
||||
// the main program itself:
|
||||
"Z>V>C[>C1BN[L4+R4Z]<<-]<<P>X10.X2>E[X112.X114.X105.X109.X101.X10.Z]"
|
||||
;
|
||||
|
||||
void process(const char *c, int topLevel)
|
||||
{
|
||||
char f = *c; // macro name to search
|
||||
unsigned int n = 0; // macro argument
|
||||
|
||||
if (!topLevel)
|
||||
{
|
||||
// read argument
|
||||
c++;
|
||||
|
||||
while (*c >= '0' && *c <= '9')
|
||||
{
|
||||
n = 10 * n + *c - '0';
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
c = program;
|
||||
|
||||
while (*c) // search for the macro
|
||||
{
|
||||
if (topLevel || (c[0] == ':' && c[1] == f))
|
||||
{
|
||||
c += topLevel ? 0 : 2; // skip the beginning macro chars
|
||||
|
||||
while (*c && *c != ';')
|
||||
{
|
||||
if (*c == ':')
|
||||
while ((*++c) != ';'); // skip macro definitions
|
||||
else if (*c == '+' || *c == '-' || *c == '<' || *c == '>' ||
|
||||
*c == '[' || *c == ']' || *c == '.' || *c == ',')
|
||||
putchar(*c); // normal BF commands
|
||||
else if (*c >= 'A' && *c <= 'Z')
|
||||
process(c,0); // macro
|
||||
else if (*c == '$')
|
||||
{
|
||||
c++;
|
||||
for (unsigned int i = 0; i < n; ++i)
|
||||
putchar(*c);
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
process(program,1);
|
||||
putchar(0); // allows separating program on stdin from program input
|
||||
// puts("013"); // program input may go here
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The main program we have here is the example program from the [algorithm](algorithm.md) article: it reads a number, prints the number of its divisors and says if the number is [prime](prime.md). Code of the Brainfuck program will be simply printed out on standard output and it can then be run using our Brainfuck interpreter above. Unlike "hello world" this is already a pretty cool problem we've solved with Brainfuck, and we didn't even need that much code to make it happen. Improving this further could allow us to make a completely usable (though, truth be said, probably slow) language. Isn't this just beautiful? Yes, it is :)
|
||||
|
||||
## Variants
|
||||
|
||||
TODO
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue