ec2-54-82-99-24.compute-1.amazonaws.com | ToothyWiki | MoonShadow | RecentChanges | Login | Webcomic
[This] is an assembler for [Piet]. It reads from standard input a file containing Piet instructions (push, pop, add, sub, mul, div, mod, not, gt, ptr, switch, dup, roll, inn, in, outn, out) and assembler directives documented below, and produces on standard output a [netpbm] image of the corresponding Piet program.
Sample assembly program
macro # print - pop string from stack and print it
2 1 roll
macro print 10 out println def
dup 15 mod bz._track_3
dup 3 mod bz._track_1
dup 5 mod bz._track_2
dup outn 10 out
1 add dup 100 brle.0b
"Fizz" println br._track_0
"Buzz" println br._track_0
"FizzBuzz" println br._track_0
This produces the following image (converted to PNG using pnmtopng):
Tested against [Sylvain Tynitillier's interpreter] and npiet.
[This] is a compiler for a C-like high level language. It reads the source from standard input, and produces Piet assembly source suitable for feeding into the assembler above on standard output. It provides all the usual arithmetic and boolean logic operators (though not bitwise manipulation), flow of control (if, for, while) and functions (including arbitrary recursion), as well as an inline assembler, and intrinsics for IO. The only supported data type is integer. TODO: support some form of list / variable length array, and strings as a special case of this. TODO: document syntax.
An implementation of fizzbuzz is [here]; the assembler output looks like [this], and the corresponding image like [this]. (TODO: optimise output more! Compare with hand coded assembly version above...)
For the compiler, you'll need to install Parse::RecDescent? from CPAN if your Perl distribution does not include it.
identifiers consist of the characters a-zA-Z0-9_
- macro ... identifier def
- henceforth, whenever identifier is seen, it is replaced with ... before further parsing. (Macros may consist of multiple lines. def must be the last thing on its line.)
- declares a label
- this instruction causes execution to halt
- br, bz, bnz, bgt, ble
- these are branch instructions. br is an unconditional branch. bz and bnz pop the top item off the stack and branch if it is zero or nonzero respectively, otherwise execution continues. bgt executes a gt instruction and branches if the result is 1; ble, accordingly, 0. The destination label identifier is appended to the instruction, separated by a ., e.g. br.label0, bz.1 etc. If the same label is declared multiple times within the code, the direction of the branch is assumed to be backwards; for numeric labels, the direction may be stated explicitly by appending a b or f as appropriate, e.g. ble.2f A single piet instruction may be appended following another ., e.g. bnz.somewhere.pop - this instruction is executed iff the branch is taken.
- this is a branch table. This must be the first thing on the line. The rest of the line is treated as a list of labels. The top element is popped off the stack; if it is zero, a branch is taken to the first label in the list, otherwise it is decremented and the next label considered, until either a branch is taken or no more labels are left (in which case execution continues with the instruction following the .btbl directive).
- comments out the rest of the line
- integers are pushed onto the stack as they are encountered. When a quoted string is encountered, its characters are pushed onto the stack in reverse order, followed by the length of the string (a macro for printing a string in this form is supplied in the sample above). When a quoted string preceded by @ is encountered, it is printed without affecting the stack; e.g. @"Hello World!\n" will generate a Piet implementation of Hello World.
- the Piet program consists of a series of tracks. Instructions initially go into track 0, with execution flow from left to right along the top of the image. The .track command begins generating a new track. This command must occur on its own line. Tracks after the first are placed vertically in the image, with execution proceeding from top to bottom of each track. A label of the form _track_N is placed in the scope of track 0 for each other track. The label _track_0 is placed in the scope of each track other than 0. Code in track 0 may branch to the start of any other track using the appropriate branch instruction. Similarly, code in the other tracks may branch to track 0. The label _track_0 must be explicitly declared within track 0, exactly once, if this feature is used; then, branches from other tracks will enter at that point. See the sample above for example usage.
There is a github repository [here].
Kudos to [Hugh Satow] for many assorted bugfixes :)
(at what point does this become a statement about art?)