Ziim
Ziim is a two-dimensional esoteric programming language, invented by Timwi in 2011, consisting entirely of the following Unicode arrow characters:
↑ ↗ → ↘ ↓ ↙ ← ↖ ↕ ⤢ ↔ ⤡
If you cannot see the above arrows, or they are not the same size, then you have a problem. You will need a fixed-width font that includes these characters to display (and edit) Ziim programs properly.
The semantics of a Ziim program depend on which arrows are pointed to by which other arrows, and at what relative angles. With all these arrows pointing at each other, no-one can say this language is pointless! Each instruction is invariant under 45° rotations (although it’s not entirely straight-forward how to rotate an entire program 45° — but it’s certainly possible). The meaning of each arrow depends only on how many other arrows point at it and at what angle (relative to where the arrow itself is pointing). In particular, the distance at which arrows are placed is immaterial.
Apart from these arrows, only spaces (U+0020) and newlines (U+000A, optionally preceded by U+000D) are allowed. Every other character is a syntax error. Lines are allowed to have varying lengths, in which case a compliant interpreter must treat the program as if the lines had been padded with spaces to be equal length.
Semantics
- Ziim programs are heavily multi-threaded. At every point in time there is a finite set of threads running, some of which may be suspended (paused, on halt).
- Each thread is at an arrow — its current instruction. After executing the instruction, the thread moves in the direction the arrow is pointing until it finds another arrow, which becomes its new current instruction.
- Each thread also has a current value, which is a finite-length sequence of bits.
- At the beginning of execution, a new thread is started at each arrow that is not pointed to by any other arrow. All of these threads start out with an initial value of
{ 0 }
, i.e. a sequence containing a single 0-bit. - When a thread encounters an arrow that points to outside the program, the thread’s current value (after executing the instruction) is output to STDOUT. Then the program terminates (not just the thread!). An interpreter is free to interpret the output bit sequence any way it likes; for example, it could decode it as UTF-8 and output as text.
- If a thread reaches an instruction on which another thread is already executing or suspended, the thread must wait. In other words, threads are not allowed to “overtake” each other on any one instruction. If several threads enter an instruction and are suspended, they must be unsuspended in the same order they came in. (However, it is still possible for a thread to “overtake” another by taking a different route.)
- If, at any point, all threads are left in a suspended state, the program terminates.
- The meaning of an instruction depends on the type of arrow (single or double arrow), the number of arrows pointing to it, and the angles at which they point to it (relative to the angle the arrow is pointing to). The table in the following section details the semantics of every instruction. If a program contains an arrow that is pointed to in a combination of angles that is not listed, compliant parsers must issue a syntax error.
Instructions
Ziim | Short | Very short | Detail |
---|---|---|---|
→ |
{ 0 }
|
0
|
Each arrow that has no other arrows pointing to it starts a new thread at the start of execution with the initial value { 0 } .
|
↘ → |
read | R
|
An arrow that has one other arrow pointing to it and that makes a 45° left-turn reads a single bit from STDIN and sets the current value to a single-bit sequence containing that bit. If STDIN is exhausted, the instruction returns the empty sequence ({ } ). (If input is provided as bytes, the most significant bit of the first byte comes first.)
|
→ ↖ |
no-op | N
|
An arrow that has one other arrow pointing to it and that makes a 135° right-turn is a no-op. The thread continues and the current value remains unchanged. |
← ↗ |
invert | I
|
An arrow that has one other arrow pointing to it and that makes a 135° left-turn is an inverter. Every bit in the current value is flipped. The length stays the same.
Apart from the read instruction which reads from STDIN, this is the only instruction that can generate a 1-bit. |
B ↘ → A ↗ |
concat | C
|
An arrow that has two other arrows pointing to it as shown is a concatenator. Every time a thread hits such a concatenator from either side (A or B), it is suspended until the other side is also hit, at which point the current value is set to the concatenation of A’s current value followed by B’s current value. One thread is terminated and execution continues with the concatenated current value. |
↙ → ↖ |
label | L
|
An arrow that has two other arrows pointing to it as shown provides a way for separate paths of execution to merge. Execution simply continues with the current value unchanged. This means it’s effectively a no-op. It enables execution to return to an earlier point (making it possible to construct loops). Think of it like a goto label. |
→↕ |
splitter | S
|
A double-arrow that has one other arrow pointing to it at a 90° angle splits the current thread into two threads with the same current value, which continue their execution separately in opposite directions. |
↕ ↗ |
isZero | Z
|
A double-arrow that is pointed to by one other arrow, forming a 45° left-turn and a 135° right-turn, is an “if” instruction that checks and removes the first bit in the current value. If the first bit was zero, the 45° left-turn is taken. If the first bit was one, the 135° right-turn is taken. In both cases, execution continues with the first bit removed from the current value. If the current value was empty, the thread terminates. |
↔ ↗ |
isEmpty | E
|
A double-arrow that is pointed to by one other arrow, forming a 135° left-turn and a 45° right-turn, is an “if” instruction that checks whether the current value is empty or not. If it is empty, the 45° right-turn is taken, otherwise the 135° left-turn is taken. In both cases, the current value is set to the empty sequence; in other words, if the current value was non-empty, all bits are removed from it.
Apart from the read instruction which returns an empty sequence when STDIN is exhausted, this is the only instruction that can generate an empty sequence. (In theory the isZero instruction can generate an empty sequence too, but in practice you can’t test for it without isEmpty.) |
Hello, World!
The following example program outputs the string “Hello, World!”. It uses only { 0 }
, no-op, inverters, concatenators, splitters and STDOUT. It is “reliable” in the sense that it doesn’t exhibit any race conditions.
Since most systems don’t appear to have the necessary monospace font available for all the arrows to line up correctly, I have also included a graphical representation.
As plain text | As image |
---|---|
↘↓ ↘ ↘ ← ↙ ⤢↘ ↘← ↑ ↘← ↑⤡↙ → ↙ ↓↙↙ ↙ → ↗ ↙ ↘ ← ↙↙↘ ↙↘ ↙ ↑ ↘← ↘ ↑↕ ← ↓↓↑ ↙ ← ↘ ← ↙ ↕← ↗ ↔↙ ↗↓ ↑↑ →↖ ↙← ↓ ↘ → ↘ ↘ ↓↘← ↗ ↙↙ ↙ ↖ ↘ ←↓↙← ↑ ↑↑ ↑ → ↘ → ↙ ↘ ↙ ←↙ ↘ ↕ ↔ ↙↙ ← ↙ ⤡ ↑ ↘ ←↓↓↓ ↓↓↓ ↙↘ ↘→ ↘ ↙ ← ↑ ↓ ↘ → ↘ ↙ ← →↘ ↓ ↓ ↙← ↕← ↗ →↘ ↙← →↗ ↓ ↖← ↖ ↕ ↔ ↙ ↗↔ ↖ ↑↓ ↙↑ ↖← ↑↘ ↙ ↓ →↗→↖↑ ↑ ↑ ↗↖ ↑→ ↙↓ ↙ ↑ ↗ ↔ ↖↙←↙↓↖ ↗↔ ↕ ↑ → ↙ ↘ ←↗ →↖ ↘ ↕ ← ↖ ⤡ ↗ ← ↙ ↑ ↙ ↖ ↓↖← ↖ ↑ ↓ ↓↑↙ → ↘↑↓↖ ↗ ↙← ↑ ↙→ ↕ → ↖ ⤡ ↖ →↗ ↓ ↖ ↑ ↑ ↗↖ |
Useful constructs
Turing completeness
Not sure, but extremely likely.
Interpreter
- Ziim is implemented in Esoteric IDE.