vizh

From Esolang
Jump to navigation Jump to search

vizh is a visual esoteric programming language that takes image files as input and is based on a multi-tape Turing machine. It is designed for compatibility with C and was created by User:TartanLlama in 2021.

The "parser" has to use computer vision and OCR to process the user's code. Here is an implementation of memcpy:

Memcopy vizh.png

Here's how the parser understands the program (produced if you pass --debug-parser when compiling):

Memcopy vizh parser output.png

More complex programs can be written by splitting the symbols across multiple lines, such as this function which counts nested square braces:

Seekclosbr.png

Abstract Machine

The vizh abstract machine consists of:

  • Some number of tapes which are contiguous groups of 8-bit unsigned integers
  • A read/write head with storage for a single 8-bit unsigned integer

The initial state of the abstract machine is:

  • A single tape of size 4096 is allocated with all cells initialised to 0
  • The read/write head is initialised to the left-most cell of this tape

Programs and Functions

A vizh program consists of a number of functions, each in its own image file.

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:

  • The name of the function at the top left of the image
  • The number of arguments (tapes) it takes at the top right of the image
  • A sequence of instructions in a horizontal lines

Function names are alphanumeric: [a-zA-Z][a-zA-Z0-9]*.

The tapes available to a vizh function consist of its tape arguments. 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.

Instruction Set

Instruction Description
Left arrow Move the r/w head left.
Right arrow Move the r/w head right.
Up arrow Move the r/w head to the tape above the current one.
Down arrow Move the r/w head to the tape below the current one.
Function name in a circle Call the named function.
Plus Increment the value pointed to by the r/w head by 1.
Minus Decrement the value pointed to by the r/w head by 1.
Equilateral triangle with the point at the top Read the cell pointed to by the r/w head into the r/w head storage.
Equilateral triangle with the point at the bottom Write the value stored in r/w head storage into the cell pointed to by the r/w head.
[<instructions>] Loop over the instructions between the brackets until the value pointed to by the r/w head at the start of the loop is 0.

When 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.

Standard Library

The vizh standard library is called libv and the majority of it can be implemented in pure vizh.

Function Description
readin Read an ASCII character from stdin and write its integral representation into the cell pointed to by the r/w head.
print Print the value of the cell pointed to by the r/w head to stout, interpreted as an ASCII character.
putstr 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.
minus 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 0.
compl Given tape cells a,b from the r/w head, results in 1,0 if a==0, otherwise 0,0.
equal Given tape cells a,b from the r/w head, results in a==b?1:0,0.
newtape Allocate a new tape underneath the last one currently allocated for this function.
freetape Deallocate the bottom-most secondary tape for this function (no-op if there are not any).

Computability and Usability

Since vizh is essentially a multi-tape Turing machine, it is Turing-complete.

The ability to allocate multiple tapes and call functions makes it much more useable than something like Brainfuck. You can find an implementation of a Brainfuck interpreter written in vizh on GitHub.

Implementation

An implementation written in Python which uses OpenCV and Tesseract OCR to parse code and outputs C is available on GitHub.