TurtlePost is a simple, interpreted, dynamic, stack-based postfix language.
TurtlePost releases are posted on the releases page with the .NET runtime embedded. You can also build TurtlePost from source using the standard dotnet
CLI commands or a .NET IDE. To start the REPL, run the executable without any arguments. To run a .tpost
script, pass the path as the first argument to the executable.
TurtlePost is designed strictly around stack-based execution. Expressions push and pop values from the main 'user stack' (or simply, 'the stack').
TurtlePost has very simple syntax. All TurtlePost scripts are comprised of one or more value or operation expressions, each separated by spaces.
TurtlePost supports basic types of values:
- Double-precision floating point numbers (
2
,.5
) - Boolean values (
true
andfalse
) - Strings (
"Hello"
,"Alien \U0001F47D"
) - Globals (
&var
) - Labels (
@label
) - Lists (
{ "Hello" "World" 123 }
) - Null (
null
)
A list of all operations and their functions is described below.
Comments are started and ended with a forward slash (/sample comment/
).
The stack is simply a 'last in, first out' buffer of values. Typing a value expression into TurtlePost pushes that value onto the top of the stack. Operations can pop/consume values off of the top of the stack, perform operations on those values, and push new values onto the stack. The stack stores values of any type (even if operations do not work on all types). TurtlePost represents the topmost value on the stack as the one furthest to the right.
> 2 /pushes 2 onto the stack/
2
> 3 /pushes 3 onto the stack, on top of the 2/
2 | 3
Due to the stack-based nature of TurtlePost, operations are said to be postfix, or following their operands. To add two numbers, you would write something like this:
> 2 3 add
5
As we learned, writing 2 3
pushes both 2 and 3 onto the stack. add is an operation that, as may be apparent to you, adds numbers. Specifically, it pops two numbers from the top of the stack, adds the top one to the bottom, and pushes the resulting sum. There are many operations; just to demonstrate some easy ones:
> "hello" /pushes the string 'hello' to the stack/
"hello"
> print /pops the top value on the stack and prints it to the console/
hello
> 5 3 /pushes 5 and 3 to the stack/
5 | 3
> gt /pops the top 2 values, checks if the bottom is greater than the top, and pushes the result/
true
> help /prints a list of all operations/
add sub mul div mod... (remaining omitted for brevity)
> exit /exits the interpreter/
It may be helpful to think of some of these operations in terms of 'ordinary' mathematical notation. For example, 5 3 gt
corresponds to 5 > 3
.
Globals are values that represent references to other values. A global has a name, which always starts with &. When you top use a global, it is declared in memory. Once they are created, they cannot be removed (although, they can be easily reused). Typing in the name of a global pushes the global itself to the stack.
> &var &var2 &var3
/Globals: &var = null, &var2 = null, &var3 = null/
&var | &var2 | &var3
Globals can be written to using the write operation. It pops a global and a value, writing the value into the global. Their value can be retrieved using the read operation, which pops a global and pushes its value. Even if there are no remaining references to a global on the stack, it is still there and its value can be referred to at a later time.
> 5 &var write
/Globals: &var = 5/
> &var read
/Globals: &var = 5/
5
Labels are values that represent their source location. A label has a name, which always begins with @. Labels are declared with a trailing colon (@label:
) and subsequently referred to without the colon (@label
). Before input is interpreted, a quick pass of the source is done to scan for all label declarations. The interpreter also autogenerates a lebal called @end
at the end of each input string.
Like globals, referring to a label pushes it onto the stack. The jump
operation pops a label and unconditionally transfers control of the program to the code starting from the label. Unlike globals, labels are cleared every time an expression is evaluated in the REPL.
> "Hello" println
> 2 3 add println
> @label jump
> "I am skipped" println
>
> @label:
> "Jumped" println
Hello
5
Jumped
Labels can also be used to denote subroutines. A subroutine is similar to a jump, but expected to use the ret
operation to return to where it was called from. The call
operation is used to jump to a subroutine.
4 @powerof2 call
print /prints 16/
@end jump
@powerof2:
dup mul
ret
The call operation pops a label, saves the current source location to the call stack, and unconditionally transfers control of the program to the code starting from the label. When the code hits a ret operation, it will pop the previous source location from the call stack and unconditionally start executing from there.
Note that subroutines will still be executed as normal code if the interpreter reaches the subroutine's code. Since the call stack would be empty at this point, the ret
operation would cause an error. Thus, it is advised to place all subroutines after an @end jump
at the end of the file, so that the interpreter does not attempt to run subroutines with an empty call stack.
Lists are values which themselves contain multiple values. Lists function as both arrays and stacks; specific indices can be read from and written to (get and set), and items can be added and removed from from the top of the list (push and pop).
List literals are declared with a set of curly braces. Initial values can be added to the list literal, each separated by a space. Operations are not allowed inside of a list literal. The first value is added at index 0 and subsequent values are added at increasing indices. Pushing and popping values happens in the same order as they do on the stack.
> { "a" "b" "c" } &var write
> &var read 0 get
"a"
> &var read "d" push &var read
{ "a" "b" "c" "d" }
add sub mul div mod sqrt pow ceil round floor sin cos tan read write concat pop push get set del print println input cls width height cursor dup drop swap over not and or xor eq gt lt gte lte string parse jump call jumpif callif ret typeof exit nop help copying
Name | Operation |
---|---|
add | Pops two numbers, adds the top to the bottom, and pushes the resulting sum. |
sub | Pops two numbers, subtracts the top from the bottom, and pushes the resulting difference. |
mul | Pops two numbers, multiplies the top by the bottom, and pushes the resulting product. |
div | Pops two numbers, divides the top by the bottom, and pushes the resulting quotient. |
mod | Pops two numbers, divides the top by the bottom, and pushes the resulting remainder. |
sqrt | Pops a number, calculates its square root, and pushes the resulting root extraction. |
pow | Pops two numbers, raises the bottom number to the power of the top number, and pushes the resulting exponentiation. |
Name | Operation |
---|---|
ceil | Pops a number and pushes it, rounded up. |
round | Pops a number and pushes it after rounding. |
floor | Pops a numbers and pushes it, rounded down. |
Name | Operation |
---|---|
sin | Pops a number and pushes its sine. |
cos | Pops a number and pushes its cosine. |
tan | Pops a number and pushes its tangent. |
Name | Operation |
---|---|
PI | Pushes π to the stack. |
2PI | Pushes 2π to the stack. |
E | Pushes e to the stack. |
Name | Operation |
---|---|
write | Pops a global and a value, and writes the value into the global. |
read | Pops a global and pushes the value of the global. |
Name | Operation |
---|---|
concat | Pops two strings, concatenates the bottom with the top, and pushes the concatenation. |
Name | Operation |
---|---|
push | Pops and value and a list, and pushes the value to the top of the list. |
pop | Pops a list, removes an item from the top of the list, and pushes the removed item. |
get | Pops a number and a list, truncates the number, and pushes the item in the list at the given index. |
set | Pops a number, an value, and a list, truncates the number, and sets the item in the list at the given index to the given value. |
del | Pops a number and a list, truncates the number, and deletes the item at the given index index from the list. |
Name | Operation |
---|---|
Pops a value and writes it to the standard output. | |
println | Pops a value, writes it to the standard output, and writes an additional newline. |
input | Reads a line of text from the standard input and pushes the string. |
cls | Clears the terminal. |
width | Pushes the amount of characters the current terminal can show horizontally. |
height | Pushes the amount of characters the current terminal can show vertically. |
cursor | Pops two numbers, truncates them, and sets the them as the Y and X coordinates of the terminal cursor respectively. |
Name | Operation |
---|---|
dup | Pops a value and pushes it twice. |
drop | Pops a value, discarding it. |
swap | Pops two values and pushes them back in reverse order. |
over | Pops two values, and pushes the bottom, top, and bottom value again. This effectively pushes the second topmost value on the stack. |
Name | Operation |
---|---|
not | Pops a boolean, computes its logical NOT, and pushes it. |
and | Pops two booleans, computes their logical AND, and pushes it. |
or | Pops two booleans, computes their logical OR, and pushes it. |
xor | Pops two booleans, computes their logical exclusive-OR, and pushes it. |
Name | Operation |
---|---|
eq | Pops two values, checks if the bottom one equals the top, and pushes the result. |
gt | Pops two values, checks if the bottom one is greater than the top, and pushes the result. |
lt | Pops two values, checks if the bottom one is less than the top, and pushes the result. |
gte | Pops two values, checks if the bottom one is greater than or equal to the top, and pushes the result. |
lte | Pops two values, checks if the bottom one is less than or equal to the top, and pushes the result. |
Name | Operation |
---|---|
string | Pops a value, converts it to a string, and pushes the result. |
parse | Pops a string, parses a number from it, and pushes the result. |
Name | Operation |
---|---|
jump | Pops a label and unconditionally transfers control to code starting from the label. |
call | Pops a label, pushes the current source location on the call stack, and unconditionally transfers control to the source code at the label. |
jumpif | Pops a label and a boolean and and transfers control to the source code at the label if the boolean is true. |
callif | Pops a label, pushes the current source location on the call stack, and transfers control to the source code at the label if the boolean is true. |
ret | Pops the previous source location from the call stack and unconditionally transfers control to the source code at that location. |
Name | Operation |
---|---|
typeof | Pops a value and pushes the name of the value's type as a string. |
exit | Exits the process the interpreter is running in with exit code 0. |
nop | Performs no operation. |
help | Prints out a list of all operations the interpreter is configured to use. |
copying | Prints out information about redistribution, warranty, and licensing of TurtlePost. |
A special thanks for these folks for translating TurtlePost:
- Esperanto - Astro
- Dutch - JelleTheWhale
- Portuguese - Vrabbers
- Ukranian - John Tur
- Vietnamese - Victor Tran