Piet::Interpreter - Interpreter for the Piet programming language
use Piet::Interpreter;
my $p = Piet::Interpreter->new(image => 'my_code.gif');
$p->run;
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. The language is fully described
at http://www.physics.usyd.edu.au/~mar/esoteric/piet.html. A Piet
program is an image file, usually a gif, which uses a set of 20 colors
and the transitions between blocks of those colors to define a series
of instructions and program flow. See the above URL for more details.
(Note: some sample programs there may not work, as they were
constructed before a working interpreter was available.)
Since Piet is a visual language, an image parsing mechanism is
required. This module uses Image::Magick, so it would be to your
advantage to download, install, and test that module and its
related stuff before trying to use this one.
- my $piet = Piet::Interpreter->new( %args );
-
Instantiates and returns a new Piet::Interpreter object. Valid
arguments are:
- image => 'my_prog.gif'
-
Specifies the program image file to load into the interpreter.
- codel_size => $size
-
Tells the interpreter how large a codel is, in pixels. Defaults to 1.
- nonstandard => ('white'|'black')
-
Sets the behavior of non-standard colored codels to either 'white' or
'black'. Defaults to 'white'.
- debug => (1|0)
-
Turns on debugging information, including warnings.
- warn => (1|0)
-
Turns on warnings only.
- trace => (1|0)
-
Turns on program tracing, which only outputs instructions and values.
- $piet->reset;
-
Resets the PVM (Piet Virtual Machine) back to the default state.
After a reset, the current x and y should both be 0, the DP points to
the right, the CC points to the left, and the stack should be empty.
- $piet->image('myprog.gif');
-
Loads in a program image from the specified file. The interpreter was
designed and tested using gif images, but any format that is supported
by Image::Magick should work just fine. Once the file has been
loaded, it is inspected and processed, creating a run-matrix and
determining some useful properties from the image.
Note: Be sure to set the codel size, if needed, before loading the
image. Otherwise, a size of 1 will be assumed, and the codel columns
and rows will not be calculated correctly, causing pain and
irritation.
- $piet->run;
-
Starts the Piet interpreter running from the upper-left codel.
Program execution is described under ``Language Concepts'', below.
- $done = $piet->step;
-
Performs one ``step'' of a Piet program, where a step is one transition
from one codel block to the next. A failed transition (trying to go
out of bounds, or onto black) is not considered a step, but a slide
into or out of a while block is. Returns the step count number, or
undef if the step terminates the program.
- $piet->debug(1);
-
Turns debugging information on or off.
- $piet->warn(1);
-
Turns warnings on or off.
- $piet->trace(1);
-
Turns program instruction tracing on or off.
- $piet->codel_size(5);
-
Sets or returns the codel size for the program image.
- $piet->nonstandard('white');
-
Sets the behavior of non-standard codels to 'white' or 'black'.
- $rows = $piet->rows;
-
Returns the number of codel rows in the program image.
- $cols = $piet->rows;
-
Returns the number of codel columns in the program image.
- $file = $piet->rows;
-
Returns the name of the file from which the program image was loaded.
- $piet->state(``CHECK'');
-
Prints detailed information about the state of the PVM, with an
optional label. Information reported includes the filename, number of
codel columns and rows, which debugging, warning, or tracing flags are
set, how non-standard colored codels are handled, the step number, the
current x and y position of the pointer, the directions of the DP and
CC, the last color visited, and the values currently on the stack.
- print $piet->to_text;
-
Returns a nicely formatted text version of the program image's codel
matrix, with the filename, codel size, and column/row information.
Piet uses 20 distinct colors, 18 of which are related cyclically in two ways:
Red -> Yellow -> Cyan -> Blue -> Magenta -> Red
Light -> Normal -> Dark -> Light
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 colors (such as orange or brown) may also be used. In the
default case, non-standard colors are treated by the PVM (Piet Virtual
Machine) as the same as white, so may be used freely wherever white is
used. You may also use the nonstandard()
method to tell the PVM to
treat them the same as black.
Piet code takes the form of an image made up of the recognised colors.
Individual pixels of color 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 color 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.
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 the appropriate commands.
The Piet language interpreter begins executing a program in the color
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 color blocks of the program
under the following rules:
-
The interpreter finds the edge of the current color block which is
furthest in the direction of the DP. (This edge may be disjoint if the
block is of a complex shape.)
-
The interpreter finds the codel of the current color block on that
edge which is furthest to the CC's direction of the DP's direction of
travel. (For example, if the DP points downwards, and the CC is to
the left, the interpreter looks for the rightmost codel on the edge.)
-
The interpreter travels from that codel into the color block
containing the codel immediately in the direction of the DP.
The interpreter continues doing this until the program terminates.
Each non-black, non-white color 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.
Black color 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
color block, there is no way out and the program terminates.
White color blocks are ``free'' zones through which the interpreter
passes unhindered. If it moves from a color block into a white area,
the interpreter ``slides'' through the white codels in the direction of
the DP until it reaches a non-white color block. If the interpreter
slides into a black block or an edge, it is considered restricted (see
above), otherwise it moves into the color block so encountered.
Sliding across white blocks does not cause a command to be executed.
Commands are defined by the transition of color from one color 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
below. If the transition between color blocks occurs via a slide
across a white block, no command is executed.
- (0 hue steps, 1 step darker) => push
-
Pushes the value of the color block just exited on to the stack.
Note: values are not automatically pushed onto the stack - the push
operation must be explicitly carried out.
- (0 hue steps, 2 steps darker) => pop
-
Pops the top value off the stack and discards it.
- (1 hue step, 0 steps darker) => add
-
Pops the top two values off the stack, adds them, and pushes the
result back on the stack.
- (1 hue step, 1 step darker) => subtract
-
Pops the top two values off the stack, subtracts the top value from
the second top value, and pushes the result back on the stack.
- (1 hue step, 2 steps darker) => multiply
-
Pops the top two values off the stack, multiplies them, and pushes the
result back on the stack.
- (2 hue steps, 0 steps darker) => divide
-
Pops the top two values off the stack, calculates the integer division
of the second top value by the top value, and pushes the result back
on the stack.
- (2 hue steps, 1 step darker) => mod
-
Pops the top two values off the stack, calculates the second top value
modulo the top value, and pushes the result back on the stack.
- (2 hue steps, 2 steps darker) => not
-
Replaces the top value of the stack with 0 if it is non-zero, and 1 if
it is zero.
- (3 hue steps, 0 steps darker) => greater
-
Pops the top two values off the stack, and pushes 1 on to the stack if
the second top value is greater than the top value, and pushes 0 if it
is not greater.
- (3 hue steps, 1 step darker) => pointer
-
Pops the top value off the stack and rotates the DP clockwise that
many steps, or counterclockwise if it is negative.
- (3 hue steps, 2 steps darker) => switch
-
Pops the top value off the stack and toggles the CC that many times.
- (4 hue steps, 0 steps darker) => duplicate
-
Pushes a copy of the top value on the stack on to the stack.
- (4 hue steps, 1 step darker) => roll
-
Pops the top two values off the stack and ``rolls'' the remaining stack
entries to a depth equal to the second value popped, by a number of
rolls equal to the first value popped. A single roll to depth nis
defined as burying the top value on the stack n deep and bringing all
values above it up by 1 place. A negative number of rolls rolls in the
opposite direction. A negative depth is an error and the command is
ignored.
- (4 hue steps, 2 steps darker) => number_in
-
Reads a character from STDIN as a number, and pushes it on to the stack.
- (5 hue steps, 0 steps darker) => character_in
-
Reads a value from STDIN as a character, and pushes it on to the stack.
- (5 hue steps, 1 step darker) => number_out
-
Pops the top value off the stack and prints it to STDOUT as a number.
- (5 hue steps, 2 steps darker) => character_out
-
Reads a value from STDIN as a character, and pushes it on to the stack.
Any operations which cannot be performed (such as popping values when
not enough are on the stack) are simply ignored.
Marc Majcher (piet-interpreter@majcher.com)
http://www.majcher.com/code/piet
http://www.physics.usyd.edu.au/~mar/esoteric/piet.html