| title | General-Purpose Brainfuck Transpiler 'lgbt.py' |
|---|---|
| tags | Brainf*ck Python C Terminal Transpiler |
| author | fygar256 |
| slide | false |
Literal General Brainfuck Transpiler
This is 'lgbt.py', a Literal General Brainfuck Transpiler that converts Brainfuck to various languages.
I want to clarify that, just because the name is LGBT, there might be some misunderstanding, so I'm normal in terms of sexuality. Alan Turing, who died young, was homosexual.
I believe it can convert to languages that jump to any label (for example, assembly language) and almost any language with while loops. It can also be applied to languages that use line numbers to indicate jump destinations, like early BASIC.
The source code of the conversion result is output to standard output, so please use redirection with lgbt.py suffix file.bf>filename.ext.
Generally, transpiling from a high-level language to a lower-level language is difficult, but transpiling from a lower-level language to a higher-level language is easy if you can tolerate some redundancy. Brainfuck is the lowest-level language, so it can be translated into virtually any language. Furthermore, it is Turing complete, possessing computational versatility; almost everything except special operations can be written in Brainfuck. Though it's not very practical.
It also automatically handles indentation, making conversion to Python possible. Even if a single Brainfuck instruction spans multiple lines in the target language, it can be written as a JSON list.
For languages that use labels, such as assembly, openlabel and closelabel in the map file's target string are replaced with the labels immediately before and after the corresponding '[',']' positions, respectively.
In languages that use line numbers to indicate jump destinations, such as early BASIC, openline and closeline are replaced with the line numbers at the position of the corresponding [ and ], respectively.
If the openline token exists in the map file, line numbers will be automatically added to the output.
To run lgbt.py, you need a mapfile, headerfile, and tailorfile for each language.
The naming convention for each file is as follows:
map.suffix.json # mapfile
header.suffix # headerfile
tailor.suffix # tailorfile
For sample and practical use, headers, footers, and map files for C, Python, Ruby, bwbasic, GAME language, assembly of x86_64 FreeBSD and so on are included.
The command-line arguments and their interpretation are as follows:
lgbt.py <suffix> <file.bf>
The following Brainfuck sources are bundled so that every example below can be run as written:
hello.bf # prints "Hello, world!"
alphabet.bf # prints A-Z followed by a newline (exercises nested loops)
echo.bf # copies stdin to stdout until EOF (exercises ',')
mandelbrot.bf is a well-known third-party benchmark and is not bundled here;
if you want to try it, fetch it separately and drop it in this directory.
Map file:map.c.json
Header:header.c
Tailor:tailor.c
lgbt.py c alphabet.bf >out.c # Convert to C
cc out.c # Compile
a.out # Execution
Map file:map.py.json
Header file:header.py
Tailor file:nothing
lgbt.py py hello.bf > hello.py
python hello.py
Map file:map.rb.json
Header file:header.rb
Tailor file:nothing
lgbt.py rb hello.bf > hello.rb
ruby hello.rb
header:header.lisp
tailor:tailor.lisp
map:map.lisp.json
lgbt.py lisp hello.bf > hello.lisp
sbcl --script hello.lisp
installation of sbcl required.
header:header.pl
tailor:tailor.pl
map:map.pl.json
lgbt.py pl hello.bf >hello.pl
swipl hello.pl
installation of swi-prolog required.
Map file:map.hs.json
Header:header.hs
Tailor:tailor.hs
lgbt.py hs alphabet.bf >out.hs # Convert to Haskell
ghc out.hs -o out # Compile
out # Execution
map.s.json / tailor.s use the FreeBSD system call numbers
(write=4, read=3, exit=1).
lgbt.py s hello.bf > hello.s # Transform
as hello.s -o hello.o # Assemble
ld hello.o -o hello # Link
./hello # Execute
map.s.linux.json / header.s.linux / tailor.s.linux are the same target
with the Linux system call numbers (write=1, read=0, exit=60).
lgbt.py s.linux hello.bf > hello.s
as hello.s -o hello.o
ld hello.o -o hello
./hello
map.aarch64.json and tailor.aarch64 use the Linux aarch64 system call
numbers (write=64, read=63, exit=93), so assemble them for a Linux target:
lgbt.py aarch64 alphabet.bf > alphabet.aarch64
llvm-mc -triple=aarch64-unknown-linux -filetype=obj alphabet.aarch64 -o alphabet.aarch64.o
ld alphabet.aarch64.o -o alphabet
./alphabet
Map file
{
">": "ptr=ptr+1",
"<": "ptr=ptr-1",
"+": "array(ptr)=(array(ptr)+1) mod 256",
"-": "array(ptr)=(array(ptr)+255) mod 256",
".": "print chr$(array(ptr));",
",": [ "do",
" a$=inkey$",
"loop while a$=\"\"",
"array(ptr)=asc(a$)" ],
"[": "if array(ptr)=0 then goto closeline",
"]": "goto openline"
}
Header file
dim array(30000)
ptr=0
Tailor file
end
Execution
lgbt.py bwbasic hello.bf >hello.bwbasic
bwbasic hello.bwbasic
Hello world!
Also, it seems that bwbasic can have issues like this. (Gemini)
- [Important] Bugs specific to bwbasic (empty PRINT and consecutive execution) A bug has been reported in bwbasic (especially older versions and certain builds) where, if certain processing is performed immediately after a PRINT statement, or if PRINT is repeated rapidly within a loop, extra line breaks (empty lines) are inserted due to a problem with the internal buffer processing.
- Cell wrap-around. Brainfuck cells are 8 bit and wrap. If the target
language does not wrap by itself, do it in the map, e.g.
& 0xFF,mod 256, orand 255. Beware of languages whosemodcan return a negative value: write(x + 255) mod 256for-instead of(x - 1) mod 256. - Byte in, byte out.
.must emit exactly one byte, not one character. In Python,chr(200)is written out as two UTF-8 bytes, so usesys.stdout.buffer.write(bytes([...]))instead. Likewise,should read one byte from stdin. - End of input. The convention used by the bundled map files is that
,stores 0 when the input is exhausted. - Empty loops.
[]is legal Brainfuck. In a language where an empty block is a syntax error (Python, Common Lisp, COBOL), put a no-op such aspass,(values)orCONTINUEin the[entry so that the body is never empty. closelineand the tailor file.closelineresolves to the line after the matching]. If the program ends with]and there is no tailor file, that line does not exist; lgbt.py warns about this. Provide a tailor file (even just a program terminator) for line-numbered targets.
By applying this, I think it can be used in a wide range of languages.
Ook and other languages are also supported.