Bfc === Brainfuck revived $Id: bfc.txt,v 1.3 2001/02/05 13:07:26 atehwa-u Exp $ For basic language information, see the file "brainfuck.txt". History of this file 2: comments about techniques and extending brainfuck. 1: removed mentions about renaming the language. "brainfunct" is now the name of this particular implementation. Introduction When I spotted the bf language in the archives of Cats Eye Technologies, I got immediately fascinated. I found out, however, that the only implementation left for bf was the interpreter -- which, all respects to Urban, was not too efficient a way to run bf programs. [There are other implementations. bf has been implemented countless times. Many of the implementations are compilers: you can write a compiler for bf in a few lines of macro code. Still, bfc remains the only compiler I know of that does loop reductions, multi-combines instructions, and spots "add-from-to" idioms.] I thought about the task for a while and at last I realised I had to try it -- to make a good, optimising compiler for bf programs. Compiling to C was an obvious choice, not only for gcc optimisations, but also because of portability. Bf idioms, however, are so peculiar even for simple tasks, that I decided to write some own optimisations, too. This has also the advantage that the C programs take much less time to compile. The compiler has no known bugs. If you get to know about one, please inform the author about it. Burden of custom The geek world has many traditions, one of which bf definitely is. There are constantly proposals of how bf could be extended to be more handy; or reduced, to be a more extreme tarpit. I had such ideas, too, when I started writing bfc. After a year or so of explaining these proposals, I realised that none of them would retain the spirit of brainfuck: orthogonal, small, hard-to-do-anything-sensible-with. I think the obscene name is bad humor; but because people usually refer to brainfuck as `bf' anyway, it's not that important. When bfc was first done, I was thinking about renaming brainfuck. I'm not anymore. :) Changes There's no longer any debug command. I'll implement that if I have the time to do that. One useful feature has been added: `,' will set the location to zero on EOF. This should be an acceptable aid, because the value of EOF depends on the environment and testing for EOF in programs would make them unportable. Usage bfc < prog.b > prog.c 2> /dev/null gcc -o prog -O3 prog.c The compiler dumps tons and tons of debug output into stderr. I had no time and no will to make an option not to give the debug output. (What's better, it will irritate the users of the bad'n'broken (t)csh.) The debug output might be useful, actually -- you can learn much about your bugs and common techniques by looking at it. The possible usages include: any mathematical problem, string processing (but see `problems'), entries to IOCCC, etc. Techniques The file named `varia.b' contains some examples of ways to do simple operations in bf. They are not as well-written as one could hope, but they do show the basic ideas. For mathematical problems, it is advisable to use the space like a result stack -- and, maybe, for other problems, too. As a general rule, when you are thinking about how to do something, always remember these things: 1. The only absolute operation is []. The best example of this is CLR [-] or [+]. 2. Flow control always zeroes some location. For this reason, if you want to do a conditional with a value and retain the value, you must DUP it. 3. If you have variable-size data, interleave it with empty locations. This will allow for transfering miscellaneous data over the variable-size data. Constants are usually most easily written with a multiplication of two numbers, and some corrective inc/dec's. If you need many similar constants in sequence, consider using dup or reusing the old constant. Surprisingly complex operations can be achieved by making blocks like [-[-[-[-[-[->+<]]]]]] (the example is a safe (saturating) decrement). Abstraction can be achieved by using the tape as if it was a stack. Subroutines can be simulated by blocks that check whether a given number (their "address") is the current value. Problems While being Turing-complete, brainfuck is not too friendly. There is no way to do subroutine calls, so if you have to do the same thing in different contexts, you have to write the code again. You can circumvent this with defining more complex execution semantics and a standard way of using variables meaning "what to do next". Another problem is that processing of variable-size data and especially of large input chunks is almost unbearably hard. One could argue, of course, that if it _can_ be done, it shouldn't be made too easy. One proposed variant of bf was such that it would allow for easy manipulation of variable-sized data. The point was that the commands would have "indirect-address" semantics, i.e. + --> mem[mem[p]]++; - --> mem[mem[p]]--; > --> p++; < --> p--; and so on... Panu Kalliokoski pkalliok@helsinki.fi