pith
Paradigm(s) | Imperative |
---|---|
Designed by | User:ZippyMagician |
Appeared in | 2021 |
Memory system | Stack |
Computational class | Turing-complete |
Reference implementation | pith |
File extension(s) | .ph |
pith is a 3-stack esolang themed around vectors, designed by User:ZippyMagician.
About
pith is a language based around vectors. It features 3 stacks; The left stack I, the right stack J, and the control stack N. Each command is one byte, with two data types also present in the source code. In general, a pith program will look like a sequence of ascii characters, with occasional newlines. All characters not used by pith are ignored, which makes comments using text possible without any delimiter.
Each program written in pith is split into a line-by-line structure, wherein the ,
operator can jump to various other lines. pith is not space sensitive, but is newline sensitive. If a program reaches the end of a line but hasn't halted, it will move onto the next line. If there is no next line, the program will terminate. Similarly, jumping to a line past the last one will terminate the program.
The two data types found in pith are vectors and floats. Floats are just a 64-bit floating point number as defined by the IEEE 754 standard. A vector is an array of two of these floating point numbers, stylized as [F, F]
.
Syntax
Since there are a sizable amount of operators, they are separated into different sections. X represents the argument. If an operation takes an argument, it comes after the operator.
Stack operations
Op | Arg? | Description | Notes |
---|---|---|---|
=
|
N | Construct vector such that the top values of I and J are pushed to N as <I, J> | N/A |
*
|
N | Deconstruct vector such that vector <A, B> sees A being pushed to I and B to J | N/A |
/
|
N | Pop a value from I and push to J | N/A |
\
|
N | Pop a value from J and push to I | N/A |
&
|
Y | Duplicate the top vector of N X times. | default value of X is 1 |
^
|
N | Pop top value from I | N/A |
v
|
N | Pop top value from J | N/A |
Vector operations
Op | Arg? | Description | Notes |
---|---|---|---|
:
|
Y | Construct vector <A, B> based on argument [A, B] and push to N, or construct vector [M, 0] based on argument M | N/A |
.
|
N | Calculate the dot product of the top two items in N and push to I and J | N/A |
+
|
N | Add the top two items in N | N/A |
-
|
N | Subtract the top two items in N | Given the top of N: [<A, B>, <C, D>], replace with: [<A-C, B-D>] |
General math operations
Op | Arg? | Description | Notes |
---|---|---|---|
@
|
N | Calculate the sign of the magnitude of the top item in N and push to I and J | N/A |
%
|
N | Deconstruct the top item in N, take the absolute value of both sides, and push to I and J, respectively | not affected by $
|
~
|
TBD | TBD | TBD |
I/O
Op | Arg? | Description | Notes |
---|---|---|---|
N | Calculate the magnitude of the top item in N and push to stdout as char | N/A | |
_
|
N | Calculate the magnitude of the top item in N and push to stdout | N/A |
#
|
Y | Pop X vector magnitudes from stdin and reconstruct, pushing to N. | default value of X is 1 |
Control flow
Op | Arg? | Description | Notes |
---|---|---|---|
,
|
Y | Pop the top vector of N. If its magnitude is 0, jump to line X | N/A |
!
|
N | Halts the program | N/A |
<
|
N | NOP | N/A |
>
|
N | Jump to the matching <
|
N/A |
Other
Op | Arg? | Description | Notes |
---|---|---|---|
$
|
N | Any operation that pushes the same value to I and J will instead push to one of the following, which cycles on each instance of this op: [I, J, both] | N/A |
Programs
Cat program
<#&,1_> !
Truth Machine
#&,1<&_> _
Hello World
:72|:101|:108&2||:111&|:44|:32|:87||:114||:100|:33|
Division
Only works when the output is a whole number
#2:1*<&*-&,1==:1+*> ==_
Computational Class
pith can be proven to be Turing Complete by translation of the Szewczyk notation for a Minsky Machine. To achieve this, do as follows:
- Initialize the program with a line dedicated to
:0*
, which will push a 0 to both the I and J stacks. Repeat this for the number of registers the program will make use of. Follow it with a:0,
andline# of first instruction
to avoid running any dedicated lines for jumping. - For each command
X -1
, add a line which will use=
to navigate to desired value to modify,:1+
to increment, and then*
to move all reconstructed vectors back into the stack - For each command
X Y
, add a line which use=
to navigate to desired register in stack,&2%=+,
followed byDEDICATED LINE #
, followed by:1-
and then repeated*
to move all vectors back to the stack, and then:0,
followed byline# + 1
Dedicated lines exist so the stack can still be replaced when this jump occurs. To do this, simply make the line contain *
repeated to remove all registers still on the stack, and then :0,
followed by Y + 1 + OFFSET
where OFFSET
is the number of dedicated lines used by jumps.
If compiled by hand, you could use the same dedicated line for multiple jumps if they go to the same place.
While bulky and impractical, this is a simple way of proving Turing-completeness, instead of just hand-waving it due to laziness.
Example
1 -1 1 1 1 1
becomes
:0*:0,2 Initialize registers (only one used) *:0,3 Dedicated line, second and third instructions can share this =:1+* First instruction =&2%=+,1:1-*:0,4 Second instruction =&2%=+,1:1-*:0,5 Third instruction