This is my learning project to practice building compiler and interpreter with LLVM C API.
The project is too complext for someone, who just want to implement a simple interpreter for Brainfuck. This snippet is a better alternative, which implements the interpreter with one C file only.
There are so many LLVM tutorials in C++, however, I'd like to build a compiler and interpreter with LLVM in C. Therefore, I created this project to build a compiler and JIT interpreter for Brainfuck. Brainfuck language is simple enough, and it designed for implementing the smallest possible compiler.
-
Generating Makefile with GNU Autoconf. - Generating Makefile with CMake.
- Building project with GNU Make.
- Parsing Command line options with getopt.
- Lexical analysis with flex.
- Syntax analysis with bison.
- Creating LLVM IR file with LLVM C API.
- Creating native object file with LLVM C API.
- Creating executable file with linker.
- Running script file with LLVM MCJIT.
- Deploying with docker.
- Linking with lld.
- Static linking with musl.
- Embedding C runtime library.
The program can only working on Linux for now.
wget https://github.com/redraiment/brainfuck/releases/download/v0.5.0/brainfuck-0.5.0-x86_64.gz
gunzip brainfuck-0.5.0.x86_64.gz
sudo mv brainfuck-0.5.0.x86_64 /usr/local/bin/brainfuck
brainfuck -v
You can see below version information if the above command run success.
brainfuck v0.5.0
Home page: <https://github.com/redraiment/brainfuck/>.
E-mail bug reports to: <redraiment@gmail.com>.
Example for creating executable file and run it then:
brainfuck hello-world.bf
./hello-world
You can find the hello-world.bf
in test folder.
An example on Ubuntu:
sudo apt install --no-install-recommends -y flex bison clang-15 lld-15 liblld-15-dev llvm-15 llvm-15-dev llvm-15-tools zlib1g-dev libtinfo-dev binutils-dev musl-dev xxd make cmake
git clone --depth=1 https://github.com/redraiment/brainfuck.git
cd brainfuck
cmake 'Unix Makefiles' -B build .
cmake --build build
brainfuck [OPTIONS] <source-file>
It will create an executable file default.
-c/--compile
: only run preprocess, compile and assemble steps, then emit native object (.o
) to output file. By default, the object file name for a source file is made by replacing the extension with.o
.-r/--representation
: emit LLVM representation (.ll
) to standard output.-s/--script
: run source file as Brainfuck script.-m/--enable-single-line-comment
: enable single line comment command#
. It's useful used with Shebang.-o/--output <output-file>
: write output to file. This applies to whatever sort of output is being produced, whether it be an executable file, an object file, an IR file. If-o
is not specified, the default executable file name for a source file is made by removing the extension.-h/--help
: show this help and exit.-v/--version
: show version and exit.
- Creating an executable file:
brainfuck helloworld.bf
- Running a file as scripting:
brainfuck -s helloworld.bf
- Using with Shebang:
#!/usr/local/bin/brainfuck -ms
- Creating native object file:
brainfuck -c helloworld.bf
- Creating LLVM representation file:
brainfuck -p helloworld.bf
Here are some key behaviors:
- Memory size: 30,000 bytes, and initialized to zero.
- Data pointer initialized to point to the leftmost byte of the array.
- Two streams of bytes for input and output.
- End-of-file behavior: setting the cell to 0.
- Use "\n" for end-of-line.
Character | Meaning |
---|---|
> |
Increment the data pointer (to point to the next cell to the right). |
< |
Decrement the data pointer (to point to the next cell to the left). |
+ |
Increment (increase by one) the byte at the data pointer. |
- |
Decrement (decrease by one) the byte at the data pointer. |
. |
Output the byte at the data pointer. |
, |
Accept one byte of input, storing its value in the byte at the data pointer. |
[ |
If the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching ] command. |
] |
If the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching [ command. |
# |
Single line comment. Disabled default. |
others | Comments |
HINT: Single line comment command (#
) is an extra command to ignore the text until end-of-line. It was added to avoid command of shebang conflict with Brainfuck commands. For example, there is -
in #!/bin/brainfuck -s
, which is backward command of Brainfuck.
Here some Brainfuck snippets for testing.
From Wikipedia. It will write "Hello world" to standard output.
++++++++++
[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
It will read data from standard input and write to standard output directly, until end of file.
,[.,]
from brainfuck.org. the standard (line and) word (and character) count utility.
>>>+>>>>>+>>+>>+[<<],[
-[-[-[-[-[-[-[-[<+>-[>+<-[>-<-[-[-[<++[<++++++>-]<
[>>[-<]<[>]<-]>>[<+>-[<->[-]]]]]]]]]]]]]]]]
<[-<<[-]+>]<<[>>>>>>+<<<<<<-]>[>]>>>>>>>+>[
<+[
>+++++++++<-[>-<-]++>[<+++++++>-[<->-]+[+>>>>>>]]
<[>+<-]>[>>>>>++>[-]]+<
]>[-<<<<<<]>>>>
],
]+<++>>>[[+++++>>>>>>]<+>+[[<++++++++>-]<.<<<<<]>>>>>>>>]
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (git checkout -b feature/AmazingFeature)
- Commit your Changes (git commit -m 'Add some AmazingFeature')
- Push to the Branch (git push origin feature/AmazingFeature)
- Open a Pull Request
Distrubuted under the GPLv3 License. See LICENSE
for more information.
- Zhang, Zepeng - @redraiment - redraiment@gmail.com