pb
pb is an esoteric programming language created by User:undergroundmonorail in 2015. It is a 2D language, but not in the sense of Befunge or ><>. Instead, the terminal is thought of as a 2D "canvas". The programmer moves their "paintbrush" to whatever location they wish to output to, and do so. This makes pb difficult to use for most purposes, as a string must be printed character by character, with the brush being moved between each one. However, it does allow for easier text placement, without having to fool around with newlines and spaces to adjust a character's position. pb's interpreter, pbi, can be found in the github repo for the language.
By convention, pb programs use the file type .pb
. But don't let me tell you what to do. The interpreter accepts anything; go nuts.
Palette
A painter is nothing without their palette. In pb, the palette is a set of predefined variables. No additional variables may be defined.
Var | Definition |
---|---|
X
|
The brush's X position on the canvas (starts at 0) |
Y
|
The brush's Y position on the canvas (starts at 0) |
P
|
The current output colour (Initialized to 0, colour IDs defined below) |
B
|
The character on the canvas at the brush's current location as an ASCII value (Initialized to 0 at every point on the canvas) |
C
|
The colour of the character at the brush's current location (Initialized to 0 everywhere, colour IDs defined below) |
T
|
Initialized to 0 |
Anywhere that a colour is referred to, they are represented by these numbers:
# | Colour |
---|---|
0 | White |
1 | Red |
2 | Green |
3 | Yellow |
4 | Blue |
5 | Magenta |
6 | Cyan |
7 | Black |
Commands
pb understands the following commands:
Cmd | Definition |
---|---|
#
|
Ignore the rest of this line. (Any character pb doesn't understand is ignored by default, but this allows for comments containing ones that it does.) |
^
|
Move the cursor up by 1. Similarly, < , > , and v all move the cursor in their respective direction. May optionally be followed by a pair of square brackets containing a number or expression, causing the cursor to move that many spaces.
|
c
|
Set P to (P+1)%8
|
t
|
Must be followed by square brackets containing a number or expression. Sets T to that number or the result of that expression.
|
b
|
Must be followed by a number or expression. That number or the result of that expression is converted to an ASCII character and placed onto the canvas at the brush's current coordinates in the colour P
|
w
|
See "while", below. This one's a doozy. |
while
The only conditional branch/loop structure possible in pb is the while loop. A while loop is created with the w
command and is followed by a pair of square brackets and a pair of curly braces, like so: w[]{}
. The square brackets contain the condition for the while loop, and the curly braces contain pb code to be executed until the condition is no longer true. The condition is made up of three parts: Two numbers and/or expressions, separated by the character =
or !
. If the character in the middle is =
, the code will be run until both sides evaluate to different things. If it's !
, the code will be run until they evaluate to the same thing.
Canvas
The canvas is an infinite 2D plane represented by the terminal, starting in the upper left corner at (0, 0). The canvas spreads in all four directions. If the b
command is used while the brush is at a negative X coordinate, a negative Y coordinate, or both, the resulting character will not be printed to the screen. However, it will be saved in the program's memory. Going back to that same coordinate later will set the B
variable to whatever you printed.
Input
Assume the user enters a string of input that is n characters long. Input is kept at (0, -1), (1, -1)... (n, -1). All input is white.
Input is collected all at once at pbi's initialization, whether it's required by the program or not. When I stop being so lazy I'll update pbi to assume no input at first, then ask for it if the program tries to read a character at Y=-1. It will still all be collected at once.
Example Programs
cat.pb
^w[B!0]{t[B]vb[T]^>}
^ # Go to the first byte of input w[B!0]{ # While B != 0 t[B] # Store B in the variable T vb[T] # Go down to Y=0 and write the contents of T ^> # Go to the next byte of input. } # Repeat if B != 0
reverse.pb
^w[B!0]{w[B!0]{>}<t[B]b[0]<[X]vw[B!0]{>}b[T]<[X]^}
^ # Go to the first byte of input w[B!0]{ # While B != 0 w[B!0]{>} # Go right until B = 0 < # Go left to find the last place where B is not 0 t[B]b[0] # Store B in T, then write 0 <[X]v # Go to to (0, 0) w[B!0]{>} # Go right until B = 0 b[T] # Write the contents of B <[X]^ # Go to (0, -1) } # Repeat if B != 0
rainbow.pb
This program takes the first byte of input and prints it in every colour in the language, including black (invisible, but it's there I promise). Here's an example:
^t[B]vb[T]cw[P!0]{>b[T]c}
^t[B]v # Set T to the first byte of input b[T] # Write T at (0, 0) c # Cycle the output colour w[P!0]{ # While the output colour != 0 (white) >b[T] # Move right and write T again c # Cycle the output colour } # Repeat if the output colour != 0 (white)
Computational class
pb is Turing-complete, as it can simulate a 2-counter Minsky machine (with X and Y of the cursor as counters). Comparison with T (0) can be used for conditional state transitions.