An optimizing compiler for brainfuck, written in Rust for Unix-like systems.
Output ELF executables that uses Linux system calls for I/O. Currently, it has x64_64, arm64, i386, s390x, and riscv64 backends.
I was trying to get a better understanding of C, so I wrote an optimizing brainfuck compiler in C. While working on that project, C clicked for me in a way it hadn't previously. I have tried to learn Rust a few times, but it never clicked. I hoped that this rewrite in Rust does the same for the language I wanted to love. It did.
- Usage
- Supported platforms
- Building and Installing
- Development Process and Standards
- Dependencies
- Legal Stuff and Code Origins
The most basic way to use eambfc-rs is to simply run it, passing files to
compile as command-line arguments:
eambfc-rs foo.bfThat will compile foo.bf into an ELF executable file named foo.
It expects brainfuck source files to use the file extension .bf.
If passed multiple files to compile, eambfc-rs will compile them in the
provided order, stopping on the first error.
Compiled files have a tape size of 8 4-KiB blocks, for a total of 32 KiB, so any program that works with Urban Müller's original implementation's 30 KB tape should work fine.
| short | long | effect |
|---|---|---|
-h |
--help |
Display basic usage information, then exit |
-V |
--version |
Display version and copyright information, then exit |
-j |
--json |
Write JSON-formatted error messages to stdout |
-q |
--quiet |
Don't write error messages to stderr |
-O |
--optimize |
Perform basic optimizations |
-c |
--continue |
Continue to the next file instead of aborting on failure |
-A |
--list-targets |
Display info about supported targets, and exit |
-k |
--keep-failed |
Don't delete files after failed compilation |
-t count |
--tape-size=count |
Use count 4-KiB blocks instead of the default 8 |
-e ext |
--source-extension=ext |
Use ext instead of .bf as the source extension |
-a arch |
--target-arch=arch |
Use the arch backend instead of the default |
-s suf |
--output-suffix=suf |
Append suf to the ends of output filenames |
Portability to non-Linux target platforms is outside of the scope of this
project, as Linux is unique in providing a stable syscall ABI. That said, it
should be possible to compile a properly-working build of eambfc-rs itself for
any fully-supported Rust target, though the test suite has non-portable
dependencies, and non-unix, non-wasm targets are not able to handle non-Unicode
arguments properly, and non-unix platforms can't mark the output binaries as
executable.
Development is done primarily on the current stable release of Rust, without any effort to ensure compatibility with previous Rust versions, so there's no guaranteed minimum supported Rust version beyond the stable release at the time of the most recent commit.
# install with cargo
cargo install --path .This rewrite was done more-or-less linearly, with no git branches other than
main. Originally, the function signatures from the C version's headers were
used as the basis for function declarations in Rust. As the actual
implementation began, it became clear that that would force me to fight against
Rust's semantics rather than embrace them, so a new approach was needed.
The goal was to support all of the C version's features and pass the C version's
test suite, and work on any Rust target that provides std::os::unix, and that
goal has been met.
Since then, I've used feature branches for large refactors or new features, and dev branches for new versions.
The test suite is run with cargo test, as is typical of Rust projects. The
tests require the llvm_sys crate, which has a bit of a complicated setup
process - see its docs
for more info. In order for it to build properly on my system
(Debian Trixie with llvm-19-dev), I had to include the following
in .cargo/config.toml:
[env]
LLVM_SYS_191_PREFIX = "/usr/lib/llvm-19"
LLVM_SYS_201_PREFIX = "/usr/lib/llvm-20"
LLVM_SYS_211_PREFIX = "/usr/lib/llvm-21"
LLVM_SYS_221_PREFIX = "/usr/lib/llvm-22"(.cargo/config.toml is for local configuration and is not supposed to be
checked into version control, so if you need to do something similar, that's the
place to do it).
Some tests will only run if it's possible for the host system to directly run
the binaries that this compiler outputs, either because they're native, or
because there's a compaitibility layer that's automatically invoked, such as
Linux's binfmt_misc with QEMU binfmt support set up, or FreeBSD's
"Linuxulator" system call translation layer.
On non-cfg(unix) platforms, tests that use the llvm-sys crate to disassemble
compiled code will be skipped, as will tests that require Unix-specific
functionality. Around half of the test suite is skipped on platforms that are
both non-unix and lacking in support for the binaries compiled by eambfc-rs.
The longopts feature requires the lexopt crate, and the
eambfc-rs as a whole is licensed under the GNU General Public License
version 3. Some individual components of the source code, infrastructure, and
test assets are licensed under the Zero-Clause BSD license, a
public-domain-equivalent license.
Files have licensing information encoded in accordance with the REUSE Specification, using SPDX headers to encode the copyright info in a manner that is both human and machine readable.
Some brainfuck test programs include snippets of sample code taken from the esolangs.org pages brainfuck algorithms and brainfuck constants, which are available under the CC0 public-domain-equivalent license.
One test uses a modified form of the brainfuck implementation of colortest, which is a different hobby project of mine.
An algorithm to pick a RISC-V instruction sequence to set a register to an immediate is adapted from the LLVM project.
Other than that, all content in this repository is my original work, either
created for eambfc-rs, or adapted from the the original eambfc project in C.
All licenses used in any part of this repository are in the LICENSES/ directory,
and every file has an SPDX License header identifying the license(s) it's under,
either near the top of the file, or in an associated .license file.
No "AI" tools have been used in the creation of the code or documentation in this repository.