Composition with Red, Yellow and Blue. 1921, Piet Mondrian. |
Piet is a programming language in which programs look like abstract paintings. The language is named after Piet Mondrian, who pioneered the field of geometric abstract art. I would have liked to call the language Mondrian, but someone beat me to it with a rather mundane-looking scripting language. Oh well, we can't all be esoteric language writers I suppose.
Notes:
#FFC0C0 light red |
#FFFFC0 light yellow |
#C0FFC0 light green |
#C0FFFF light cyan |
#C0C0FF light blue |
#FFC0FF light magenta |
#FF0000 red |
#FFFF00 yellow |
#00FF00 green |
#00FFFF cyan |
#0000FF blue |
#FF00FF magenta |
#C00000 dark red |
#C0C000 dark yellow |
#00C000 dark green |
#00C0C0 dark cyan |
#0000C0 dark blue |
#C000C0 dark magenta |
#FFFFFF white | #000000 black |
Piet uses 20 distinct colours, as shown in the table at right. The 18 colours in the first 3 rows of the table are related cyclically in the following two ways:
Note that "light" is considered to be one step "darker" than "dark", and vice versa. White and black do not fall into either cycle.
Additional colours (such as orange, brown) may be used, though their effect is implementation-dependent. In the simplest case, non-standard colours are treated by the language interpreter as the same as white, so may be used freely wherever white is used. (Another possibility is that they are treated the same as black.)
Piet code takes the form of graphics made up of the recognised colours. Individual pixels of colour are significant in the language, so it is common for programs to be enlarged for viewing so that the details are easily visible. In such enlarged programs, the term "codel" is used to mean a block of colour equivalent to a single pixel of code, to avoid confusion with the actual pixels of the enlarged graphic, of which many may make up one codel.
The basic unit of Piet code is the colour block. A colour block is a contiguous block of any number of codels of one colour, bounded by blocks of other colours or by the edge of the program graphic. Blocks of colour adjacent only diagonally are not considered contiguous. A colour block may be any shape and may have "holes" of other colours inside it, which are not considered part of the block.
Piet uses a stack for storage of all data values. Data values exist only as integers, though they may be read in or printed as Unicode character values with appropriate commands.
The stack is notionally infinitely deep, but implementations may elect to provide a finite maximum stack size. If a finite stack overflows, it should be treated as a runtime error, and handling this will be implementation dependent.
DP | CC | Codel chosen |
---|---|---|
right | left | uppermost |
right | lowermost | |
down | left | rightmost |
right | leftmost | |
left | left | lowermost |
right | uppermost | |
up | left | leftmost |
right | rightmost |
The Piet language interpreter begins executing a program in the colour block which includes the upper left codel of the program. The interpreter maintains a Direction Pointer (DP), initially pointing to the right. The DP may point either right, left, down or up. The interpreter also maintains a Codel Chooser (CC), initially pointing left. The CC may point either left or right. The directions of the DP and CC will often change during program execution.
As it executes the program, the interpreter traverses the colour blocks of the program under the following rules:
The interpreter continues doing this until the program terminates.
Each non-black, non-white colour block in a Piet program represents an integer equal to the number of codels in that block. Note that non-positive integers cannot be represented, although they can be constructed with operators. When the interpreter encounters a number, it does not necessarily do anything with it. In particular, it is not automatically pushed on to the stack - there is an explicit command for that (see below).
The maximum size of integers is notionally infinite, though implementations may implement a finite maximum integer size. An integer overflow is a runtime error, and handling this will be implementation dependent.
Black colour blocks and the edges of the program restrict program flow. If the Piet interpreter attempts to move into a black block or off an edge, it is stopped and the CC is toggled. The interpreter then attempts to move from its current block again. If it fails a second time, the DP is moved clockwise one step. These attempts are repeated, with the CC and DP being changed between alternate attempts. If after eight attempts the interpreter cannot leave its current colour block, there is no way out and the program terminates.
White colour blocks are "free" zones through which the interpreter passes unhindered. If it moves from a colour block into a white area, the interpreter "slides" through the white codels in the direction of the DP until it reaches a non-white colour block. If the interpreter slides into a black block or an edge, it is considered restricted (see above), otherwise it moves into the colour block so encountered. Sliding across white blocks into a new colour does not cause a command to be executed (see below). In this way, white blocks can be used to change the current colour without executing a command, which is very useful for coding loops.
Sliding across white blocks takes the interpreter in a straight line until it hits a coloured pixel or edge. It does not use the procedure described above for determining where the interpreter emerges from non-white coloured blocks.
Precisely what happens when the interpeter slides across a white block and hits a black block or an edge was not clear in the original specification. My interpretation follows from a literal reading of the above text:
Lightness change | |||
---|---|---|---|
Hue change | None | 1 Darker | 2 Darker |
None | push | pop | |
1 Step | add | subtract | multiply |
2 Steps | divide | mod | not |
3 Steps | greater | pointer | switch |
4 Steps | duplicate | roll | in(number) |
5 Steps | in(char) | out(number) | out(char) |
Commands are defined by the transition of colour from one colour block to the next as the interpreter travels through the program. The number of steps along the Hue Cycle and Lightness Cycle in each transition determine the command executed, as shown in the table at right. If the transition between colour blocks occurs via a slide across a white block, no command is executed. The individual commands are explained below.
Any operations which cannot be performed (such as popping values when not enough are on the stack) are simply ignored, and processing continues with the next command.
Note on the mod command: In the original specification of Piet the result of a modulo operation with a negative dividend (the second top value popped off the stack) was not explicitly defined. I assumed that everyone would assume that the result of (p mod q) would always be equal to ((p + Nq) mod q) for any integer N. So:
The mod command is thus identical to floored division in Wikipedia's page on the modulus operation.