Factoid:
beeswax is a self-modifying 2D esoteric programming language created by Manuel Lohmann, based on a 2-dimensional hexagonal grid. Every cell in a beeswax program (the honeycomb) has 6 neighbors.
2 — 1
/ \ / \
3 — β — 0
\ / \ /
4 — 5
Actual layout:
21
3β0
45
Instruction pointers (called bees) travel around on the honeycomb. Bees can pick up values from any location on the honeycomb, or drop values on it, potentially changing its size and content. Every bee carries a stack (local stack/lstack), with a fixed length of 3. Bees can interact with a global stack (gstack) of unlimited length that’s accessible by all bees. The gstack only allows basic stack operations like rotating up, down (similar to how Piet handles the stack) and pushing and popping values on or off the stack. All arithmetic or bitwise manipulation of data has to be done by bees.
All values in beeswax are unsigned 64-bit integers.
GitHub repository to a beeswax interpreter written in Julia
All snippets in reverse order:
Length 15 snippet (introducing the print toggle switch)
The good old plain “Hello, World!”
*`Hello, World!
The backtick character `
is a toggle switch to print every character encountered after the switch to STDOUT until a second backtick toggles the output off again or until the bee leaves the honeycomb or the program ends, whichever occurs first.
Length 14 snippet
in the works
Length 13 snippet
in the works
Length 12 snippet II
p{N<P{*
>~+d
Another 12 bytes long example. This calculates and outputs the fibonacci sequence, part of my solution to the Fibonacci function or sequence challenge.
Finally a program doing something more useful again.
Explanation:
lstack output
* [0 0 0]• create bee
{ 0 output lstack 1st as integer to STDOUT
P [0 0 1]• increment lstack 1st
< (1) redirect to left
N \n output newline to STDOUT
{ 1 output lstack 1st as integer to STDOUT
p redirect to lower left
>~ [0 1 0]• redirect to right, flip lstack 1st and 2nd
+d [0 1 1]• lstack 1st=1st+2nd, redirect to upper right
< redirect to left, loop back to (1)
N \n utput newline to STDOUT
{ 1
p
>~ [0 1 1]•
+d [0 1 2]•
<
N \n
{ 2
p
>~+d [0 2 3]•
N< \n
p{ 3
>~+d [0 3 5]•
N< \n
p{ 5
...
...
This outputs the fibonacci sequence, but only up to the 93rd element, after which 64bit-wraparound causes the sequence to produce wrong values:
...
4660046610375530309
7540113804746346429
12200160415121876738 ← 93rd Fibonacci number, last correct value
1293530146158671551 ← 1st. case of 64-bit overflow/wraparound
13493690561280548289
...
Implementing a longer word length is possible, of course. Maybe I can implement such a fibonacci sequence program in one of the next examples.
Length 12 snippet
This is my contribution to the Code that executes only once challenge.
You have to save this program in a file named !
.
This program does not add any new fancy ideas—it is just a slightly modified realization of the idea shown in snippet length 10.
_8F+++P]f1Fw
Explanation
lstack gstack
_8F+++P [0x08,0x08,0x21]•
] [0x08,0x08,0x2100000000000000]• rotate bits of lstack 1st by lstack 2nd steps to the right
f1 [0x08,0x08,0x01]• [0x2100000000000000]• push lstack 1st on gstack, set lstack 1st to 1
Fw [0x01,0x01,0x01]• write file named "!" (Char(0x21)) with content 0x00 (1 byte).
So, during execution the program overwrites its own file, so it can’t be executed again.
Length 11 snippet (clear screen using ANSI escape sequence)
_3F..}`[2J`
This is my beeswax example for the task Terminal control—Clear the sreen on rosettacode, which can be found here.
• marks top of stack
lstack
_ [0 0 0]• create bee
3 [0 0 3]• lstack 1st=1
F [3 3 3]• all lstack = 1st
. [3 3 9]• 1st=1st*2nd
. [3 3 27]• 1st=1st*2nd
} output lstack 1st as char to STDOUT
`[2J` output `[2J` to STDOUT
This program uses the ANSI escape sequence ESC[2J
, as shown here.
27
is the ASCII code for the control character ESC
, [2J
is the rest of the ANSI escape sequence to clear the screen.
Length 10 snippet (introducing the file write instruction)
_Z~8~]f1Fw
This program writes a file named “Z”, containing one byte of information: 0
lstack gstack
_ [0,0,0]• create bee
Z [0,0,90]• pick up value from relative address lstack(1st,2nd), see length 7 snippet.
~ [0,90,0]• flip lstack 1st,2nd
8 [0,90,8]• lstack 1st=8
~ [0,8,90]• flip lstack 1st,2nd
] [0,8,6485183463413514240]• rotate bits of lstack 1st by lstack 2nd steps to the right.
f [0,8,6485..4240]• [6485183463413514240]• push lstack 1st on gstack
1 [0,8,1]• lstack 1st=1
F [1,1,1]• set all lstack to 1st value
w write file [-,filebytes,namebytes]•
The “magic” becomes obvious if we look at the hex values of the stack contents:
lstack gstack
_ [0x0000000000000000,0x0000000000000000,0x0000000000000000]•
Z [0x0000000000000000,0x0000000000000000,0x000000000000005a]•
~ [0x0000000000000000,0x000000000000005a,0x0000000000000000]•
8 [0x0000000000000000,0x000000000000005a,0x0000000000000008]•
~ [0x0000000000000000,0x0000000000000008,0x000000000000005a]•
] [0x0000000000000000,0x0000000000000008,0x5a00000000000000]•
f [0x0000000000000000,0x0000000000000008,0x5a00000000000000]• [0x5a00000000000000]•
1 [0x0000000000000000,0x0000000000000008,0x0000000000000001]•
F [0x0000000000000001,0x0000000000000001,0x0000000000000001]•
w
At instruction w
lstack is [1,1,1]•
. This means, lstack 1st bytes (1 byte) of gstack are used for the file name, and lstack 2nd bytes (1 byte) are used for the file content. The first byte of gstack is 0x5a
, or 90
in decimal. This is the ASCII code for the character Z
. The next byte is 0x00
,which is the file content.
So, this program creates a file Z
with the content 0x00
.
Length 9 snippet
in the works...
Length 8 snippet II (truth machine, introducing all conditional operators)
My solution to the challenge Implement a Truth Machine. My full explanation can be found there.
_T> "{'j
It’s not a real showcase without a truth machine, right? ;)
This example also showcases two different conditional jump instructions that are working hand in hand in their functionality.
If lstack is [0,0,0]•
(the user entered 0
) then the bee only visits the instructions:
_T> "{'
and jumps outside the honeycomb, which terminates the program after printing out one 0
to STDOUT.
If lstack is [0,0,1]•
(the user enters 1
) then the case gets more interesting:
_T> " 'j'{"> " 'j'{"> " 'j'{">....
which lets the program output an infinite stream of 1
s to STDOUT.
Instruction j
reflects the direction of the IP horizontally (see the list at the length 4 snippet) to run the check over and over again.
Beeswax has 4 conditional and one unconditional “skip next” instructions:
'
skip next instruction if lstack 1st value = 0
.
"
skip next instruction if lstack 1st value > 0
.
K
skip next instruction if lstack 1st value = 2nd value
.
L
skip next instruction if lstack 1st value > 2nd value
.
Q
skip next instruction unconditionally.
Length 8 snippet (introducing absolute addressing)
_4F(@0@D
Explanation:
lstack
_ [0,0,0]• create bee
4 [0,0,4]• lstack 1st=4
F [4,4,4]• lstack=lstack 1st
( [4,4,64]• 1st=1st<<2nd (arithmetic shift left)
@ [64,4,4]• flip lstack 1st/3rd
0 [64,4,0]• lstack 1st=0
@ [0,4,64]• flip back
D drop lstack 1st at row,column = lstack 2nd,3rd
Result:
_4F(@0@D
@
The 3 instructions D
(drop value at cell), G
(get value from cell) and J
(jump to cell) use absolute addressing. Beeswax programs use 1-indexed coordinates, the y coordinate pointing “downwards”, with the origin at the upper left corner of the honeycomb.
Instruction D
has the ability to change the size of the honeycomb by dropping values outside the current honeycomb area. Growth in positive direction (down and right) is only limited by the UInt64
number range, growth in negative direction is only possible if values get dropped to the coordinate (0,n)
, (n,0)
or (0,0)
.
The example above drops the value 64
(ASCII for @
) at (row,column)=(4,0)
Column 0
is the (imaginary) column right at the left border of the honeycomb. If the honeycomb grows in negative direction, then the origin of the new honeycomb gets reset to the new upper left corner. That means, in the example above, the new origin (1,1)
changes to the coordinate left of the _
. Negative growth is only possible in steps of 1 because unlike the relative addressing instructions, D
does not recognize 2’s complements as negative numbers.
A little word of advice: Don’t try to drop values at too high addresses because you’ll run out of memory very quickly. An area of roughly 11,600x11,600 cells already needs at least 1 GB of memory (if the cells only contain 8-bit values and no multibyte characters).
length 7 snippet (introducing relative addressing)
_T";@Z}
The program above is not really useful, but it demonstrates how the code manipulation instructions that address cells locally work in beeswax.
There are two of these instructions that address cells locally: Y
and Z
.
Instruction Y
drops values to a cell that’s addressed relative to the position of Y
in the program.
Instruction Z
picks up a value from a cell that’s addressed relative to the position of the Z
instruction. As all values in beeswax are unsigned 64 bit integers, there is a problem. You can’t address any cells at relative addresses lower than (row,column)=(0,0)
. But bees aren’t stupid, so they figured out how to solve that problem by using the two’s complement of the addresses for the cells in question, meaning cells located at the left or below (the coordinate system of the honeycomb is flipped upside down). The two’s complement of a 64 bit integer n
is simply 2^64-n
.
The addressing works identical for instruction Y
.
At instruction Z
in the example, relative address
(0,0)
is the cell of the instruction itself, returning the value 90, the ASCII value of Z
.
(0,1)
returns 125, the ASCII value of }
(0,2)
returns 0, the default return value if the relative address is outside the honeycomb.
(0,-1)
becomes (0,18446744073709551615)
and returns 64
, the ASCII value for @
, and so on.
Z picks up values at the relative address lstack[column,row,-]•
and puts the value that’s found at that address on top of the lstack.
Program flow:
_ create bee
T read in integer from STDIN (enter column of character to be read)
" skip next instruction if lstack 1st>0
; terminate program (if user entered 0)
@ flip lstack 1st and 3rd values
Z pick up value from cell at lstack[column,row,-]3
} output lstack 1st as char to STDOUT
Examples:
julia> beeswax("snippet7.bswx")
i0
Program finished!
julia> beeswax("snippet7.bswx")
i1
}
Program finished!
julia> beeswax("snippet7.bswx")
i2
�
Program finished!
julia> beeswax("snippet7.bswx")
i18446744073709551615
@
Program finished!
julia> beeswax("snippet7.bswx")
i18446744073709551614
;
Program finished!
julia> beeswax("snippet7.bswx")
i18446744073709551613
"
Program finished!
Length 6 snippet
(Introducing lstack I/O)
*T~T+{
Introducing new instructions: T
and ~
Explanation:
(•
marks top of stack)
lstack
*T [0,0,a]• Create bee. Get integer from STDIN, store as lstack 1st value.
~ [0,a,0]• Flip lstack 1st and 2nd values.
T [0,a,b]• Get integer from STDIN, store as lstack 1st value.
+ [0,a,a+b]• lstack 1st = lstack 1st + lstack 2nd.
{ [0,a,a+b]• Output lstack 1st to STDOUT
This program adds two positive integers given by the user.
There are 4 I/O operators that interact only with the lstack:
T
Get integer value from STDIN, store as lstack top value.
,
Get character from STDIN, store its value as lstack top value.
{
Output lstack top value as integer to STDOUT.
}
Output lstack top value as Character to STDOUT.
Just for convenience, during program execution T
and ,
give different output for the input request, so the user knows what is wanted by the program.
T
outputs an i
to remind the user that an integer is requested.
,
outputs a c
to remind the user that a character is requested.
The program above looks like this during execution:
julia> beeswax("A+B.bswx")
i3
i5
8
Program finished!
Length 5 snippet
_9FB{
This program ouputs 387420489
to STDOUT, which is the result of 9^9
.
Explanation (•
marks top of stack):
step lstack
_9 [0,0,9]• Create bee, set lstack top value to 9.
F [9,9,9]• Set all lstack values to the first value.
B [9,9,387420489]• lstack top = top^2nd.
{ [9,9,387420489]• output lstack top to STDOUT.
In beeswax, the numbers 0...9
set the top of lstack to the appropriate integer value.
F
sets all lstack values equal to the topmost value.
The other operator setting all lstack values to the same value is z
(not used here), which sets all lstack values to 0
.
This is the first example that uses an arithmetic operator, B
, which raises lstack 1st to the power of lstack 2nd value.
Length 4 snippet
(Introducing redirection and reflection instructions)
>_{j
This program outputs an infinite string of zeros.
time state output
tick 0: >_{j
tick 1: >α{j _ creates two bees:
the first (α) moving right.
the second (β) moving left.
tick 2: β_αj 0 α executes { and outputs its topmost lstack value to STDOUT.
β gets redirected to move to the right.
tick 3: >β{α α arrives at j, gets mirrored back to the left.
β moves to the right, ignoring the _ instruction.
tick 4: >_αj 00 α and β arrive at the { instruction,
both output their top lstack value to SDTOUT, first α, then β.
tick 5: >α{β α moves on, β gets reflected.
tick 6: α_βj 0 α gets redirected to the right
β outputs top lstack value to STDOUT.
tick 7: >α{j α and β arrive at _ and move on.
This state is identical to the state at tick 1.
tick 8: β_αj 0 Identical to state at tick 2.
. . . .
. . . .
. . . .
beeswax has 6 direct redirection instructions: < b d > q p
, redirecting to the left, upper left, upper right, right, lower right and lower left, respectively, as shown in the diagram below (α showing the bee, the numbers the direction):
b d
2 1
< 3 α 0 >
4 5
p q
Indirect redirections
Here is a table with all mirroring instructions and their resulting reflected direction.
a
and x
turn the direction one step clockwise and counterclockwise.
O
reflects all directions in the opposite direction.
s
,t
,u
reflect along the main axes \
(2-5),/
(1-4),—
(0-3).
j
,k
,l
reflect along the half axes |
(between 1-4 and 2-5),/
(between 0-3 and 1-4),\
(between 0-3 and 2-5).
╔════════════════════════════════════╗
║incoming a x s t u j k l O ║
╠════════════════════════════════════╣
║ 0 1 5 4 2 0 3 1 5 3 ║
║ 1 2 0 3 1 5 2 0 4 2 ║
║ 2 3 1 2 0 4 1 5 3 1 ║
║ 3 4 2 1 5 3 0 4 2 0 ║
║ 4 5 3 0 4 2 5 3 1 5 ║
║ 5 0 4 5 3 1 4 2 0 4 ║
╚════════════════════════════════════╝
Length 3 snippet
Cat program.
_,}
Introducing two new instructions:
,
reads a character from STDIN and pushes its value on top of lstack.
}
returns lstack top value as character to STDOUT.
Length 2 snippet
*{
Introducing the {
instruction, which outputs the integer value of the top of the lstack to STDOUT. The lstacks of all created IPs/bees are initialized to [0,0,0]
at program start, so this program just ouputs 0
to STDOUT.
Length 1 snippet
The shortest valid beeswax program contains at least 1 of 4 instructions to create bees at the start of the program.
A program only containing one of these instructions does not accomplish anything; the bees get destroyed as soon as they leave the honeycomb. A program that loses all its bees during runtime gets automatically terminated.
*
creates 6 bees, each moving in one of the 6 possible directions
in the following order of creation: 0, 1, 2, 3, 4, 5.
\
creates 2 bees in the following order: first bee moving to the
upper left (dir. 2), second bee moving to the lower right (dir. 5).
/
creates 2 bees in the following order: first bee moving to the
upper right (dir. 1), second bee moving to the lower left (dir. 4).
_
creates 2 bees in the following order: first bee moving to the
right(dir. 0), second bee moving to the left (dir. 3).
During program initialization the honeycomb is scanned for these 4 instructions column by column, starting in the upper left corner and ending in the lower right corner of the honeycomb.
A beeswax program may contain an arbitrary amount of these 4 instructions. All created bees are pushed on an IP stack, so the first bee executing code after initialization is the last bee that got pushed onto the IP stack.
After initialization, the creation instructions have no effect on program execution anymore and get ignored by the bees if they encounter these instructions.
Length 0 snippet
Invalid program. Beeswax demands at least one instruction for IP creation, no matter how large the program is. The interpreter stops with an error message.
julia> beeswax("invalid program.bswx")
ERROR: No starting point found. Not a valid beeswax program.