Skip to content

Repository files navigation

TurtlePost

TurtlePost is a simple, interpreted, dynamic, stack-based postfix language.

Running TurtlePost

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 Language

TurtlePost is designed strictly around stack-based execution. Expressions push and pop values from the main 'user stack' (or simply, 'the stack').

Syntax

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 and false)
  • 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

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

Postfix

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

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

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

Subroutines

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

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" }

Operations Reference

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

Arithmetic

NameOperation
addPops two numbers, adds the top to the bottom, and pushes the resulting sum.
subPops two numbers, subtracts the top from the bottom, and pushes the resulting difference.
mulPops two numbers, multiplies the top by the bottom, and pushes the resulting product.
divPops two numbers, divides the top by the bottom, and pushes the resulting quotient.
modPops two numbers, divides the top by the bottom, and pushes the resulting remainder.
sqrtPops a number, calculates its square root, and pushes the resulting root extraction.
powPops two numbers, raises the bottom number to the power of the top number, and pushes the resulting exponentiation.

Rounding

NameOperation
ceilPops a number and pushes it, rounded up.
roundPops a number and pushes it after rounding.
floorPops a numbers and pushes it, rounded down.

Trigonometric

NameOperation
sinPops a number and pushes its sine.
cosPops a number and pushes its cosine.
tanPops a number and pushes its tangent.

Constants

NameOperation
PIPushes π to the stack.
2PIPushes 2π to the stack.
EPushes e to the stack.

Globals

NameOperation
writePops a global and a value, and writes the value into the global.
readPops a global and pushes the value of the global.

Strings

NameOperation
concatPops two strings, concatenates the bottom with the top, and pushes the concatenation.

Lists

NameOperation
pushPops and value and a list, and pushes the value to the top of the list.
popPops a list, removes an item from the top of the list, and pushes the removed item.
getPops a number and a list, truncates the number, and pushes the item in the list at the given index.
setPops 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.
delPops a number and a list, truncates the number, and deletes the item at the given index index from the list.

I/O

NameOperation
printPops a value and writes it to the standard output.
printlnPops a value, writes it to the standard output, and writes an additional newline.
inputReads a line of text from the standard input and pushes the string.
clsClears the terminal.
widthPushes the amount of characters the current terminal can show horizontally.
heightPushes the amount of characters the current terminal can show vertically.
cursorPops two numbers, truncates them, and sets the them as the Y and X coordinates of the terminal cursor respectively.

Stack Manipulation

NameOperation
dupPops a value and pushes it twice.
dropPops a value, discarding it.
swapPops two values and pushes them back in reverse order.
overPops two values, and pushes the bottom, top, and bottom value again. This effectively pushes the second topmost value on the stack.

Boolean Logic

NameOperation
notPops a boolean, computes its logical NOT, and pushes it.
andPops two booleans, computes their logical AND, and pushes it.
orPops two booleans, computes their logical OR, and pushes it.
xorPops two booleans, computes their logical exclusive-OR, and pushes it.

Comparisons

NameOperation
eqPops two values, checks if the bottom one equals the top, and pushes the result.
gtPops two values, checks if the bottom one is greater than the top, and pushes the result.
ltPops two values, checks if the bottom one is less than the top, and pushes the result.
gtePops two values, checks if the bottom one is greater than or equal to the top, and pushes the result.
ltePops two values, checks if the bottom one is less than or equal to the top, and pushes the result.

Conversions

NameOperation
stringPops a value, converts it to a string, and pushes the result.
parsePops a string, parses a number from it, and pushes the result.

Control Flow

NameOperation
jumpPops a label and unconditionally transfers control to code starting from the label.
callPops a label, pushes the current source location on the call stack, and unconditionally transfers control to the source code at the label.
jumpifPops a label and a boolean and and transfers control to the source code at the label if the boolean is true.
callifPops 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.
retPops the previous source location from the call stack and unconditionally transfers control to the source code at that location.

Miscellaneous

NameOperation
typeofPops a value and pushes the name of the value's type as a string.
exitExits the process the interpreter is running in with exit code 0.
nopPerforms no operation.
helpPrints out a list of all operations the interpreter is configured to use.
copyingPrints out information about redistribution, warranty, and licensing of TurtlePost.

Localizations

A special thanks for these folks for translating TurtlePost:

About

Dynamic stack-based programming language

Resources

Stars

1 star

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages