Starfish
- The title of this article is not correct because of technical limitations. The correct title is actually *><>.
*><> (pronounced as "starfish") is a stack-based, reflective, two-dimensional esoteric programming language based directly off of ><>. It was created by User:redstarcoder in 2016.
Concepts
*><> is a two-dimensional language, meaning the code is not necessarily executed in a linear manner. Using various instructions, the direction the code is read can be changed to either up, down, left or right. It is also a stack-based language, so all operations are performed on a stack. You can also store and retrieve values in the codebox, so making a proper compiler is very hard, if not impossible.
The goal of *><> is to make ><> more useful, while still keeping the language in the spirit of ><>. Currently *><> adds file i/o, the ability to keep track of time, and a new dive/rise concept. If feasible, some sort of graphical output may be within the scope too.
See ><>#Concepts for more information.
Code execution
See ><>#Code Execution for more information.
Functions (call/ret)
Functions in *><> work similar to call and ret in asm. To use them, put the codebox coordinate for where you want to jump to on the stack (See .
usage for details), and call C
. This will cause the IP to jump to the desired location, saving the previous location on the stack below the current one. So you could actually jump back using .
, but you'd lose the current register:
<;o&C10&a .{{]
Results in:
< ; o *&* C 1 0 & a . { { ] Stack: [] something smells fishy...
R
or "ret", solves this problem by simply jumping to the coordinates on the stack below the current one, then moving the current stack down:
<;o&C10&a R
And this code results in a newline being outputted (a
== 10
== \n
), instead of an error. See "Functions" for a complete example.
Dive / rise
Dive and rise are special instructions which manipulate the IP's ability to execute most instructions, they are u
and O
, respectively. Dive causes the IP to ignore all instructions except movement and mirror instructions (>
<
^
v
/
\
|
_
#
`
x
). Rise causes the IP to behave normally again.
Fisherman
The fisherman instruction is the backtick `
. The first time any fisherman instruction is executed while the IP is moving horizontally, the IP is instructed to move down, the next time, the IP is is instructed to move up. When the IP executes the instruction moving vertically, it is instructed to move in the last horizontal direction it moved in. This allows a swap in the flow of logic, allowing you to more easily do work on a lower line, and have the IP continue back on the line it was originally. Here's a basic program demonstrating that concept to output "Hi" without using the fisherman instruction:
"Hi"rv>; lo<^!?
You can see that to output "Hi" and still execute ;
to exit on the same line uses four instructions. One to instruct the IP in moving each direction (Down, left, up, then right). With the fisherman instruction, this becomes two:
"Hi"r`; l?!`o
The IP here, using just two fisherman instructions, still moves in 4 directions (Down, right, up, then right again).
Stacks
See ><>#Stacks for more information.
Input/output
*><> has three input/output instructions: i
for input and o
/n
for output. The i
instruction simply reads a character from stdin or a file opened with F
. If no more input is available, i
pushes -1
. Note that there is no instruction to input a number, you will have to parse the characters yourself.
While parsing numbers is not very hard, it makes for slow and possibly glitchy programs. Most programs requiring number input reads it from the stack at program start. This is done with an interpreter that supports pre-populating the stack with values. Doing so with the starfish interpreter looks like this:
$ starfish -i 10 -code "2*n;" 20
Output is a bit easier, as you have two instructions: o
and n
. They will pop a value and output it as a character and a number, respectively. The output is naturally written to stdout.
Errors
The following events will cause an error in *><>:
- Division by zero
- Reaching an invalid instruction
- Trying to pop or modify on the stack when it is empty or has too few values
- If the interpreter does not support arbitrary-precision numbers, an overflow results in an error
Although there are multiple reasons an error may occur, there is only one error message: something smells fishy...
Instructions
The following is merely a draft of the instructions in *><>, and is subject to change. Instructions are case-sensitive.
Movement and execution
> < ^ v
|
Change the direction of the instruction pointer (right, left, up, down respectively) |
/ \ | _ #
|
Mirrors; the IP will change direction depending on what direction it already has. |
`
|
Fisherman; the IP will change direction depending on what direction it already has, and what direction it had previously. |
x
|
Random direction. |
!
|
Trampoline - skip the following instruction. |
?
|
Conditional trampoline - pop one value off the stack. The next instruction is only executed if the popped value is non-zero. |
.
|
Jump - pop y and x off the stack, and move the IP to (x,y) in the codebox. The current direction is retained. Note that you have to jump to the cell before the instructions you want to execute, as the IP will move one position next tick before executing.
|
C R
|
Call and ret, respectively. Call jumps like . , but stores the current IP location on a stack below the current one. Ret jumps back to the IP's previous location, consuming the stack below, retaining the current register and stack.
|
u O
|
Dive and rise, respectively. Dive causes all instructions, except directional ones, to be skipped until rise is executed. |
Literals and operators
0-9 a-f
|
Push the corresponding value onto the stack. a = 10, ..., f = 15 |
+ - * , %
|
Addition, subtraction, multiplication, division and modulo, respectively. Pop x and y off the stack, and push y operator x . Division is float division (meaning 94,n; outputs 2.25 and not 2 ). Division by 0 raises an error.
|
=
|
Equals. Pop x and y off the stack, and push 1 if y = x , and 0 otherwise.
|
) (
|
Greater than and less than, respectively. Pop x and y off the stack, and push 1 if y operator x , and 0 otherwise.
|
' "
|
Single and double quote - enable string parsing. String parsing pushes every character found to the stack until it finds a closing quote. |
Stack manipulation
:
|
Duplicate the top value on the stack. |
~
|
Remove the top value from the stack. |
$
|
Swap the top two values on the stack |
@
|
Swap the top three values on the stack, shifting them rightwards (e.g. if your stack is 1,2,3,4 , calling @ results in 1,4,2,3 )
|
} {
|
Shift the entire stack to the right and left, respectively. (e.g. when having 1,2,3,4 , calling } results in 4,1,2,3 while { results in 2,3,4,1 )
|
r
|
Reverse the stack. |
l
|
Push the length of the stack onto the stack. |
[
|
Pop x off the stack and create a new stack, moving x values from the old stack onto the new one. See Stacks.
|
]
|
Remove the current stack, moving its values to the top of the underlying stack. |
I D
|
Increment and decrement, respectively. Changed the selected stack, with I selecting a stack above, and D selecting a stack below. If there is no stack, most operations will cause an error.
|
Input/output
See Input/output.
o n
|
Pop and output as a character and a number, respectively. Output is written to stdout. |
i
|
Read one character from stdin or a file, if open, and push it to the stack. The character should not be shown when read from console. When no more input is available, -1 is pushed.
|
F
|
Open file. Pop x from the stack and then read x values from the stack. If a file isn't open, the values determine the file name. If a file is open, the values are written to the file and the file is closed.
|
Reflection/miscellaneous
&
|
Pop the top value off the stack and put it in the register. Calling & again will take the value in the register and put it back on the stack. See The register.
|
g
|
Pop y and x off the stack, and push the value at x,y in the codebox. Empty cells are equal to 0 .
|
p
|
Pop y , x , and v off the stack, and change the value at x,y to v . E.g. 123p puts 1 at 2,3 in the codebox.
|
S
|
Sleep. Pop x off the stack and sleep for 100ms*x.
|
h m s
|
Hour, minute, and second, respectively. Using the current time push either the hour, minute, or second it currently is to the stack. |
;
|
End execution. |
The space character is simply a NOP and is allowed anywhere.
Examples
All the examples are executed using the go-starfish interpreter.
Hello, world!
"Hello, world!"r>Ool?u!|;
$ starfish helloworld.sf Hello, world!
File i/o
Opens "hello.txt", outputs its contents, and clears it.
"hello.txt"lF\ v!?+1:i< ~ r ov!?l< F0<;
$ starfish fileio.sf Hello *><>!
Digital clock
>s":"m":"hnonon" "ooo1S\ \ ;?+1iod/
$ starfish clock.sf 16:34:33
Functions
Calls the function at 0, 1
, and exits.
<;C10 v"It works!" <oR!?l
$ starfish function.sf It works!
Selecting Stacks
"Hello "r0["World"rDv ov!?l < I o>l?!;
$ starfish stackselect.sf Hello World
See a more in-depth example here.
See the ><> page for more examples.
Interpreters
go-starfish
The "official" interpreter, or the one written by the author. The latest release is available here. For usage information, run it with the -h switch.
js-starfish
An online interpreter, written by User:ApisNecros. It is currently hosted at starfish.vzqk50.com, and its source code is available at git.vzqk50.com. It offers a low-frills interpreter for the language.