StackStacks

From Esolang
Jump to navigation Jump to search
Not to be confused with Stackstack.

StackStacks is an esoteric programming language designed and implemented by User:Oj742 in 2014, taking inspiration from Forth. It doesn't store any data (excluding two flags); instead, all stacks contain stacks within themselves, so that a theoretically unlimited level of recursion can be reached.

It is likely that if you read this article in its entirety, you will become sick of the word stack.

Overview

The stacks in StackStacks don't hold data, but instead hold other stacks. There are four variables in the language:

  • The root stack, the first stack from which all other stacks are made within.
  • The working stack, which is actually just a pointer to a stack. All instructions are local to the working stack.
  • The FAIL flag, which is set whenever a failable instruction is executed; 0 if the instruction succeeded, and 1 if it fails.
  • The TEST flag, which is used by conditional branching instructions.

Since there needs to be some data for i/o, stacks can be translated into data and vice versa. Translation is as follows:

  • Stack -> Data : The size of the stack becomes the value of the data (an integer/character/boolean).
  • Data -> Stack : Create a stack with x empty stacks inside of it, where x is the value of the data (integer/character/boolean).

At the beginning of execution, the working stack is set to the root stack; you can think of the stacks like directories in a file system. You can move the working stack up, to its parent, or down, to its head.

StackStacks uses functions, in a similar way to C (though without any parameters). Functions are declared by a name outside any code block, followed by a code block. Execution begins at the function called 'main'. Code blocks are also similar to those in C.

Comments are made like this:

/* a comment */

While inline comments are made like this:

//a comment

Instructions

Data

Instruction Stack Description
num -- num push a "number" onto the stack
"string" -- a{string.last .. string.first} push a stack filled with "characters" onto the stack
'string' -- string.last .. string.first push each "character" in the string onto the stack, in reverse order (so that the first character becomes the top item)
push -- 0 push an empty stack onto the stack
pop a -- remove the top stack from the stack
pop2 a b --
tuck a -- 0 a add an empty stack to the stack as the second item
nip a b -- a remove the second stack from the stack
clear a .. z -- remove all stacks from the stack
.size -- working.size pushes the size of the working stack onto the stack
.level -- working.level pushes the level of the working stack onto the stack (the root stack is at level 0)
.fail -- FAIL pushes the FAIL flag onto the stack
.test -- TEST pushes the TEST flag onto the stack
.leaf -- leaf.level pushes the level of the lowest, currently accessible stack onto the stack

Basic stack manipulation

Instruction Stack Description
dup a -- a a.size
cdup a -- a a push a complete copy of the top stack onto the stack
dup2 a b -- a b a.size b.size
swap a b -- b a
swap2 a b c d -- c d a b
over a b -- a b a.size
-over a b -- b.size a b
cover a b -- a b a
-cover a b -- b a b
over2 a b c d -- a b c d a.size b.size
-over2 a b c d -- c.size d.size a b c d
rot a b c -- c a b
-rot a b c -- b c a
cycle a .. z -- z a .. pop the top stack and send it to the bottom of the stack
-cycle a .. z -- .. z a remove the bottom stack and push it to the stack

Interstack manipulation

Instruction Stack Description
pack a{} b -- a{b} pop the top stack and push it onto the stack after it
-pack a b{} -- b{a}
unpack a{A} -- a{} A pop the top stack off the top stack and push it onto the stack
inc a{} -- a{0} push an empty stack to the top stack
dec a{A} -- a{} pop the top stack off the top stack
shftl a{} b{A} -- a{A} b{} pop the top stack off the top stack and push it to the second stack
shftr a{A} b{} -- a{} b{A} pop the top stack off the second stack and push it to the top stack
xchg a{A} b{B} -- a{B} b{A} exchange the top stacks of the top two stacks with each other
add a{A..} b{B..} -- a{B.. A..}
cat a{A..} b{B..} -- a{A.. B..}
take a b{A .. Z} -- b{Z .. [a.size]} c{[a.size-1] .. A}

Comparison

Instruction Stack Description
not a -- !a.size
eq a b -- (b.size==a.size)
ls a b -- (b.size<a.size)
grt a b -- (b.size>a.size)
neq a b -- (b.size!=a.size)
lseq a b -- (b.size<=a.size)
grteq a b -- (b.size>=a.size)
or a b -- (b.size||a.size)
and a b -- (b.size&&a.size)
xor a b -- (b.size xor a.size)
test a -- pop the top stack off the stack and set the TEST flag to bool(a.size)

I/O

Instruction Stack Description
outi a -- output the top stack's size as an integer
outc a -- output the top stack's size as a character
outa a .. z -- output each stack's size as a character
outs a -- output each stack's size in the top stack as a character
geta -- input.last .. input.first push all "characters" from input to the stack in reverse order (so that the first character ends up on the top of the stack)
gets -- a{input.last .. input.first}
endl output a newline

Stack traversal

Instruction Stack Description
\up move the working stack up a level (into the parent stack)
\down move the working stack down a level (into the top stack)
\root move the working stack to the root stack
\leaf move the working stack to the lowest, currently accessible stack
\goto a -- move the working stack to the a.size level (if it cannot be moved to that level, push the popped stack back onto the stack)

Branch

Instruction Description
?skip skip the next instruction if TEST == true
?do skip the next instruction if TEST == false
?loop goto the beginning of the current code block if TEST == true
?exit exit the current code block if TEST == true
@foo call the function foo

Debug

Instruction Description
debug display the contents of the working stack (and all stacks within and below it)
debuga display the contents of the root stack (and all stacks within and below it)
debuge display the TEST and FAIL flags, and the level of the working stack
debugc display the call stack

Examples

Note that these examples don't really take advantage of StackStacks 'stacks within stacks'.

Hello World

main
{
   'Hello World!\n' outa //'"Hello World!\n" outs' is also viable
}

Cat

main
{
   geta outa //As with hello world, 'gets outs' would also work
}

99 bottles of beer

main
{
	1 99 { @bottles "of beer on the wall, " outs @bottles "of beer.\n" outs @takedown
	dup not not test swap ?skip not dup test swap ?loop }
}	
bottles
{
	dup
	1 eq test ?do { //one bottle
		"1 bottle " } ?skip {
	dup 0 eq test ?do { //zero bottles
		"No more bottles "
	} ?skip { //more than one bottle
		dup outi " bottles "
	} } outs //output
}
takedown
{
	dup 0 neq test ?skip {
	"Go to the store, buy some more, 99 bottles " outs
	} ?do {
	"Take one down, pass it around, " outs
	dec dup //take one down
	@bottles
	} "of beer on the wall.\n\n" outs
}

Quine

main
{
	"main"
	cdup outs endl 123 outc endl 9 outc 34 pack 0 34 pack cat outs endl 1
	"cdup outs endl 123 outc endl 9 outc 34 pack 0 34 pack cat outs endl 1"
	"swap { 9 outc -cycle dup test dec cycle cdup ?do { outs endl 9 outc 34 pack 0 34 pack cat outs } ?skip { 34 pack 0 34 pack cat outs endl 9 outc outs } endl ?loop 125 outc endl }"
	swap { 9 outc -cycle dup test dec cycle cdup ?do { outs endl 9 outc 34 pack 0 34 pack cat outs } ?skip { 34 pack 0 34 pack cat outs endl 9 outc outs } endl ?loop 125 outc endl }
}

Truth-machine

main
{
        geta dup '0' eq test ?do outc ?exit '1' eq test ?do { 1 outi ?loop }
}

Turing-completeness

Turing-complete by reduction from brainfuck. If you start the program with

main { 0 0 0

and end it with

}

then the following reduction is true:

Brainfuck StackStacks Equivalent
> swap -cycle pack \down cycle .size 3 eq test ?skip { 0 0 }
< -cycle \up .fail test ?skip unpack cycle ?skip swap
+ inc
- dec
[ dup test ?do {
] dup test ?loop }
. dup outc
, -cycle dup test ?skip { pop gets 1 cat } unpack swap cycle nip

External resources

Oj742's (Crude) Interpreter