Here is an implementation of memcpy:

Yes, you literally pass an image file to the compiler.
The "parser" is based on computer vision and the backend produces C code.
Here's how the parser understands the program (produced if you pass --debug-parser when compiling):

Here's a C program which calls into the function:
#include <stdio.h>
// Supplied by linking with the vizh object file
void memcopy(uint8_t*,uint8_t*,uint8_t*);
int main() {
uint8_t str[] = "Hello!";
uint8_t size = sizeof(str);
uint8_t to[sizeof(str)];
memcopy(&size, str, to);
puts(to);
}
We can compile this into an executable in a single command:
$ vizh memcopy.png main_memcopy.c -o memcopy
Then run it:
$ ./memcopy
Hello!
But what if you make an error, like this?

Well you get the best compiler errors you'll ever see:

The provided implementation is called vizh and compiles to C and can link executables.
You can install vizh directly from PyPi:
$ pip install vizh
vizh depends on OpenCV, cffi, and Tesseract OCR.
You can install OpenCV and cffi with pip:
$ pip install opencv-python cffi
You'll have to install Tesseract OCR separately. See their documentation for instructions.
Usage: vizh [OPTIONS] [INPUTS]...
Options:
--version Show the version and exit.
-c, --compile-only Only compile, don't link.
-o, --output-file PATH Output file for executables or vizh object files.
-q, --quiet Suppress output.
--debug-parser Display how the parser understands your source file.
--help Show this message and exit.
The compiler can take any combination of image files, C sources files, and object files.
You may need to set the TESSDATA_PREFIX environment variable to the folder containing Tesseract data. If you're on Linux this is likely /usr/share/tesseract-ocr/<version>/tessdata.
The vizh abstract machine consists of:
The initial state of the abstract machine is:
See instructions for the valid operations on the abstract machine.
A vizh program consists of a number of functions, each in its own image file. (What image types are allowed? Ideally at least png and jpg)
The entry point to a vizh program is a function called main. (Note that the main function gets mangled as vizh_main. For all other functions the symbol name is the same as the vizh name).
A vizh function is an image file containing:
Function names are alphanumeric: [a-zA-Z][a-zA-Z0-9]*.
The tapes available to a vizh function consist of its tape arguments. On entry to the function the r/w head is initialised to the start of the first tape argument, if any.
A function returns when control flow reaches the end of its instructions.
Any tapes allocated by a function are automatically deallocated when the function exits.
When you call a function subsequent pointer arguments are taken from the currently active tape onwards.
For example, given the following state of the abstract machine where ^ is the last position of the r/w head on that tape and $ is the active tape:
t1 01234
^
$t2 99999
^
t3 00000
^
Then a call to a function that takes two tapes would supply the arguments t2, t3.
The valid instructions in vizh and their encodings are:
110When you move the r/w head up or down, the position it was last at for the previous tape is saved. E.g. given this state of the abstract where ^ is the last position of the r/w head on that tape and $ is the active tape:
$t0 01234
^
t1 01234
^
The sequence of instructions "right right right down" would result in this state:
t0 01234
^
$t1 01234
^
Comments in vizh are anything enclosed in a rectangle. Stick what you want in there.
The vizh standard library is called libv. Much of it is implemented in vizh itself and it is built when you install vizh. It provides the following functions:
readin: read an ASCII character from stdin and write its integral representation into the cell pointed to by the r/w headprint: print the value of the cell pointed to by the r/w head to stout, interpreted as an ASCII characterputstr: write the null-terminated ASCII string starting at the position pointed to by the r/w head to stdout.geta: puts the character a at the current position of the r/w head.getA: puts the character A at the current position of the r/w head.add given tape cells a,b from the r/w head, results in a+b,0.mul given tape cells a,b,c from the r/w head, results in a*b,0,0.zero given tape cell a from the r/w head, results in 0newtape: allocate a new secondary tape underneath the last one currently allocated for this function (or the primary tape if there are no secondary tapes)freetape: deallocate the bottom-most secondary tape for this function (no-op if there are not any)Last modified 14 December 2025