Navigation
- README
- Pure-Python feature set
- Syntactic macro feature set
- Examples of creating dialects using
mcpyrate - REPL server
- Troubleshooting
- Design notes
- Essays
- Additional reading
- Contribution guidelines
Table of Contents
A BF to Python source-to-source compiler.
Powered by mcpyrate.
from unpythonic.dialects.bf import dialects, BF # noqa: F401
# 'A' via a 5 × 13 multiplication loop
+++++++++++++[>+++++<-]>.- Cell semantics: 8-bit wrapping cells.
- The tape is a
defaultdict[int, int]subclass (unpythonic.dialects.bf.Tape) whose__setitem__masks assigned values to the range0..255. - The pointer is unbounded in either direction (infinite Turing tape); untouched cells read as zero.
- The tape is a
- Folding: consecutive identical commands collapse (
+++→tape[ptr] += 3).- No cancellation of opposites —
+-and><emit both operations, what you wrote is what you get.
- No cancellation of opposites —
- Loops:
[compiles towhile tape[ptr]:plus an indent;]dedents.- An empty loop body gets a
pass.
- An empty loop body gets a
- I/O:
.writeschr(tape[ptr])tostdout;,reads one character fromstdin. On EOF,,stores0in the current cell. - Comments: classical BF treats any non-command character as a no-op.
- The dialect preserves the text — consecutive runs of non-command characters compile into Python
# ...comments, positioned where they appeared in the source. - A leading
#in the BF source is passed through cleanly, so both# real commentand barereal commentcome out as# real commentin the compiled Python.
- The dialect preserves the text — consecutive runs of non-command characters compile into Python
reset: a line whose stripped content is exactlyresetcompiles totape.clear(); ptr = 0. This lets several BF programs share one file.
The same compiler is available as a plain function:
from unpythonic.dialects.bf import bf_compile
print(bf_compile(bf_program_str))bf_compile(src) returns self-contained runnable Python — useful for reading a non-trivial BF program by rewriting it in a language a human can actually read.
For example, the program above compiles to:
from sys import stdin, stdout
from unpythonic.dialects.bf import Tape
tape = Tape()
ptr = 0
tape[ptr] += 13
while tape[ptr]:
ptr += 1
tape[ptr] += 5
ptr -= 1
tape[ptr] -= 1
ptr += 1
stdout.write(chr(tape[ptr])); stdout.flush()BF is a dialect of Python implemented as a whole-module source-to-source transform. The dialect definition lives in unpythonic.dialects.bf. Usage examples can be found in the unit tests.
It's also a minimal example of how to make a source-transforming dialect, the modern equivalent of what old Lisp folks used to call a reader macro. All other dialects in this collection — Lispython, Listhell, Pytkell — are AST-transforming, built on top of unpythonic's macro layer. BF shares none of that machinery: the body of a BF file is not parseable as Python at all, so the compiler runs at the text level, before mcpyrate's AST-level dialect stage.
Source-transforming dialects consume the whole module body, so combining BF with another source-transforming dialect on the same file doesn't really make sense. Composition with AST-transforming dialects is supported: from X import dialects, BF, SomeOptimizer (or on separate from lines) places SomeOptimizer after BF in the transform chain, running its AST pass on the output of the BF compiler.
The mechanism is the mcpyrate.dialects.split_at_dialectimport helper (new in mcpyrate 4.1.0): BF's transform_source uses it to peel off its own dialect-import line while preserving any others for the next round of dialect processing.
Not intended for serious use.
See Wikipedia.