PureStack

From Esolang
Jump to navigation Jump to search

PureStack is an esoteric, stack-based, mostly higher level programming language created by User:iownpants in 2009. It is designed with the intention of satisfying two principles: basing the language on only stacks and allowing no pointer or array type manipulation by the basic instructions, as well as maintaining an esoteric style while providing a number of useful, higher-level capabilities for programming. It is currently not implemented, and some of the specification is unclear. It requires further revision in order to function properly under all conditions.

Stacks

PureStack programs are created with four stacks automatically, none of which may be destroyed by the program. These are the Instruction Stack, Default Stack, Context Stack, and Routine Stack. The Default stack has no unique features, it is a data stack that exists automatically, but the others are used for various functions.

The Instruction stack contains all of the programs instructions. At each step of the program, an instruction is popped and executed. It is the default value of Routine.

The Context stack is used to identify which data stack instructions are operating on. Pushing a new stack name onto the context stack will change the scope of your instructions, and popping them will move back to your previous stack. An empty Context automatically uses the Default data stack.

The Routine stack is used to keep track of nested subroutines. When empty it refers to the Instruction stack. If an exception is called it is popped onto Routine and treated like a regular subroutine, except that it will not be exited until the code in its body has been completed (this allows exceptions to manipulate the Routine stack without killing themselves).

Creating New Stacks

New data stacks are created with the following syntax:

StackName|

This new stack is automatically pushed onto Context, allowing data write to begin. Pushing a stack onto Context is done by writing its name, followed by whitespace if another stack name immediately follows.

StackName AnotherStack
ThisStackHasAReallyLongName

To empty a stack of all its contents prefix it with the pop character and enclose it in single quotes. A stack may be deleted completely by using the declaration function, but with the stack enclosed in single quotes.

~'StackName'
'StackName'|

The values in a stack may be either integers or strings. Instruction and subroutines can include instructions values, context and routine can include stack names as values.

Basic Operations

Comments are surrounded by parentheses.

(This is a comment)

Pushing and popping are simple. To push a value onto the current stack, write the number desired to push. Pop a value by writing the pop character (tilde). Popping is useless without a destination. Prefix a context switch with this instruction to push the character onto the new stack, or use it with input/output.

54 7 2369 (Push three numbers onto the current stack)
"Hello there" "Foobar" (Push strings onto the current stack)
~ (removes top of stack, foobar)
~AnotherStack (removes hello there and places it onto anotherstack)

A value may be popped off of the context stack, or the routine stack, using the following functions. Note that a subroutine will exit if it pops itself.

, (pop the current context)
. (pop the current routine)

The context instruction will also modify itself if grouped with a pop function, to do a context switch and pop the previous top to the top of the new stack.

~,

Values can be duplicated with the duplicate character (grave) as follows. Additionally the swap character (hash) is used to swap the top two values of a stack.

1 ` (The current stack now displays 1 1)
5 4 # (reads 4 5)

All math is done to the top two values, replacing them with the result. Math operations include addition, subtraction, multiplication, division, modulo, and powers. There are also two operators for bit rotations.

1 2 + (3)
2 3 - (-1)
1 3 * (3)
6 2 / (3)
5 4 % (1)
3 2 ^ (9)
> (bit shift right)
< (bit shift left)

The input and output operations are prefixed onto a pop to push the value onto or off an i/o stack.

@~"" @~ (receive string input, then integer)
!~ (output a value, type depends on the element popped)

Lastly control is done through the if construct. If will skip the next instruction unless a condition is met. It can be modified to either apply to constants, compare the top two values of a stack, or compare the top value of the special stacks.

?=5 (if the top of stack is 5)
?=$ (if the top two values are the same)
?,=SomeStack (checks the current context)
?.=SomeRoutine (checks the current subroutine)
?/= (not equals) ?> ?< ?>= ?<= (other comparisons)

Subroutines and Exceptions

Subroutines and exceptions are themselves stacks of instructions that have been popped off of the Instruction stack, or included using a special preprocessor instruction. To create a new subroutine stack, write the name followed by its code enclosed in brackets. Note that the name must not be separated by the opening bracket with whitespace, otherwise the name will be pushed onto context and an error occurs when the program tries to create a stack with no name.

SubRoutine[ (Code) ]
SubRoutine [] (ILLEGAL!!)

To call a subroutine, push it onto the Routine stack by writing its name and then colon. The same syntax is used to manually throw an exception.

SubRoutine:
Exception:

Since a program cannot backtrack into its Instruction stack, subroutines are the only way to modify the procedure of programs. Quitting a subroutine is done by popping Routine, or reaching the end of its code. Subroutine and exception code is the only nondestructive element when read. They can be deleted by declaring them inside single quotes, like other stacks.

'SubRoutine': (Deletes the subroutine)

Exceptions are somewhat more complicated. On the surface they act as regular subroutines, however they have many special properties. For one, an exception can be pushed onto Routine by the runtime, and handle errors. For another exception code is not exited until it has been finished, unless something gets really messed up. They are also capable of holding several instructions that are not valid while in the normal program. To declare an exception, declare a subroutine with braces instead of brackets.

Exception{ (code) } (remember no whitespace between name and open brace)
OverflowException{ (code) } (an exception can be mapped to those found in the runtime)

The following instructions work only while handling an exception.

~[] (pops an instruction from Instruction without executing it)
~[5] (as above, five times)
,'' (completely empties Context)
.'' (completely empties Routine)
'Exception': (deletes an exception, very dangerous if used wrong)

Finally the ampersand is used to import preexisting code. It can only import subroutines/exceptions to used. It may also be used to specify an implementation.

&'Program.SubRoutine'
&'Standard'
&'Wacky Version'

Examples

Hello World

"Hello, World!"
!~

Cat

Cat[
  @~
  ?/=0 (or some other value for end of file)
    !~
  ?=0
    EOF:
  Cat: ]
RoutineOverflow{
  .''
  Cat: }
EOF{
  .'' }
Cat:

99 Bottles of Beer

Bottles| 99,
Phrases| "Bottles of beer on the wall" "Take one down, pass it around" "Bottles of beer" "Bottles of beer on the wall",
TakeOneDown[
	Bottles
	1 # - , ]
GetBottle[
	Bottles
	` ~, !~ ]
GetPhrase[
	Phrases 
	~, ` !~ ]
Loop[
	,
	GetBottle:
	GetPhrase:
	GetBottle:
	GetPhrase:
	TakeOneDown:
	GetPhrase:
 	GetBottle:
	GetPhrase:
	~Phrases, ~Phrases, ~Phrases, ~Phrases,
	Bottles ?/=0
		Loop: ]
Bottles Loop:

Computational Class

Whether or not PureStack is Turing Complete is unknown, but it should be fairly simple to produce a Brainfuck interpreter.