brainfuck2scheme/README.md

47 lines
1.6 KiB
Markdown
Raw Normal View History

2024-11-18 18:19:10 -05:00
# Brainfuck2Scheme
A simple compiler from Brainfuck to R5RS. The compiler will output a Scheme
list that is a lambda with two arguments:
* `data`: The data vector.
* `dptr`: The initial data pointer.
To turn the list into an executable Scheme function, just give it to
`eval`.
## How It Works
Brainfuck is a very simple Harvard architecture computer. The data is
stored as a vector `data` and the data pointer is an integer `dptr`.
The big idea is that all the code is compiled to a big lambda form, but
there are some wrinkles due to jumps.
Data access brainfuck instructions are translated like
* `+` -> `(vector-set! data dptr (+ 1 (vector-ref data dptr)))`
* `-` -> `(vector-set! data dptr (+ -1 (vector-ref data dptr)))`
* `>` -> `(set! dptr (+ dptr 1))`
* `<` -> `(set! dptr (- dptr 1))`
* `.` -> `(display (vector-ref data dptr))`
* `,` -> `(vector-set! data dptr (read-char))`
Branches are trickier. Basically, all code that will be executed in a block
is in a lambda. Given `[code...]`, the `code...` will be compiled to a
lambda in a `letrec`, with a conditional at the end that will tail-call the
lambda if the current data pointer is not zero.
The transformation then goes like
`[ CODE ... ] REST ...]` ->
(letrec ((between (lambda ()
(TRANSLATE CODE ...)
(if (not (zero? vector-ref data dptr)
(between))))))
(if (not (zero? (vector-ref data dptr)))
(between)))
(TRANSLATE REST ...)
where `(TRANSLATE CODE ...)` translates `CODE` to Scheme instructions
like above.