The background of Stacked
The last couple of weeks I've been playing around with creating a stack-based programming language - modelled after
FORTH and
PostScript. The result is the
Stacked programming language, which I present here in this post.
It all started when I casually mentioned to a college, that one, due to the simplistic nature of the FORTH environment, probably would be able to implement a functioning FORTH interpreter in a day or so.
A saturday some weeks later, I started writing what evolved into the
Stacked programming language. (The hypothesis was proven true, as I had a functioning
Stacked interpreter up an running after just a few hours. Mostly because of the extremely simple syntax of, and as a result, parser for, FORTH-like languages.)
The language I did end up implementing/designing do not conform to any standard version of FORTH. In actuality, I would say that, the
Stacked language have more in common with PostScript than it has with FORTH.
The main purpose for me to creating
Stacked was to have fun and to think about different types of programming languages.
Some words about the syntax
The parsing/tokenizing scheme of
Stacked differ from that of FORTH. In
Stacked, strings are enclosed between
', like
'this'. Identifiers can either be multi-character alpha-numeric words (
dup,
stack,
execute_debug, ...) or single-character non-alpha-numeric words (
+,
{,
\, ...). White-spaces is only needed for marking token limits if both tokens are numbers or multi-character words. Single character words can be placed beside any other token without any whitespace in between.
Introduction through examples
Hello World!
Lets start with the old trusty
Hello World! example.
Output:
Hello World!
As
Stacked is a stack-based language, the
stack is a central concept, used by functions for receiving parameters and for outputting results.
So, what is happening here? First, the string
'Hello World!' is pushed to the stack. Then the function
. is called, popping the upper element from the stack and outputting it.
Basic arithmetic
As you can see from the
Hello World! example, the operator (
.) comes
after the parameter (
'Hello World!'). This is a common trait among stack-based programming languages, as it plays well with stacks. You formal language aficionados will know this as
reverse Polish notation.
Now, lets take a look at this example.
Output:
50
To read
Stacked code, begin from the left and read to the right. Literals (strings, numbers, ...) will be put on the stack, and identifiers (procedures, operators, ...) will be executed. In this case, first
10 and then
5 is pushed to the stack. Then the multiplication operator
* is executed, popping the two upper elements from the stack, multiplying them and pushing the result (
50) on the stack. Then,
. is executed, popping the uppermost element from the stack, and outputting it. Pretty straightforward actually, isn't it.
Stack manipulation
Output:
16
First
4 is pushed to the stack. Then the
dup operator is called, pushing a copy (duplicate) of the uppermost element of the stack, to the stack. The
* operator multiplies the two fours from the stack, and pushes the result
16 to the stack. Lastly, the
. operator pops the
16 from the stack and outputs it.
Global name binding
It is possible to bind values to
global names. This feature can be used to create globally accessible values.
\pi 3.141592653589793 ;
@pi .
Output:
3.141592653589793
The
; operator binds a value (
3.141592653589793) to a name (
pi). The
@ operator can be used to push a named value to the stack. The
\ operator is used to keep the name
pi from being executed, and instead simply pushed to the stack.
Please note that the
\ and
@ operators operate on the identifier to the
right of the operator.
Sequences
Sometimes you want to have a collection of things, a list. In
Stacked this is called a
sequence.
Curly brackets are used to denote the beginning and end of a sequence.
Executing sequences
In
Stacked, sequences of items can both be used as a data structure (a list), or as something that resembles procedures in other languages.
\hello {'Hello!'.};
hello
Output:
In this example, first the name
hello is bound to the sequence by the mean of the
; operator. Then the
hello sequence is executed.
Passing values to and from sequence calls
Parameters are passed to sequence calls by putting them on the stack. Which aso can be used by sequences to pass return values.
\squared {dup *};
5 squared .
Output:
First we bind the sequence to the global name
squared by the
; operator. The we put
5 on the stack, calls the sequence, and output the uppermost element of the stack (pushed there by the
* operator in the sequence) through the
. operator.
Serializing
{1 'string' {'A sequence'} \something} serialize .
Output:
{1 'string' {'A sequence'} \ something}
The
serialize function transforms the uppermost element of the stack into its string representation.
Inspecting the stack
Sometimes, especially while debugging, one want to see the content of the stack.
stack:
10
stack:
2 3
stack:
+
stack:
*
stack:
Output:
{}
{10}
{10 2 3}
{10 5}
{50}
The
stack function pushes the entire content of the stack as a sequence to the stack. The
: operator pops and serializes the uppermost element of the stack, and outputs the result.
The conditional operator
true 'TRUE' 'FALSE' ? .
false 'TRUE' 'FALSE' ? .
Output:
The conditional operator,
?, works in a similar way as the
?: operator in many other programming languages. It pops three element from the stack. If the first is
true the second element is pushed back to the stack, else the third element is pushed to the stack.
The execute operator
Output:
The
! operator executes the uppermost element of the stack. If this element is a sequence it will simply execute the sequence. If the element is a string the operator will execute the content of the string as
Stacked source code.
Local named values
\a 'GLOBAL';
@a.
{@a. \a 'LOCAL', @a.}!
@a.
Output:
GLOBAL
GLOBAL
LOCAL
GLOBAL
The
, operator binds values to local names - bounded to the scope of a called sequence. When getting a named value by the use of the
@ operator, local names have higher priority than global names.
Bulk local named value binding
The main hassle, as a human, in using a stack based language, is to mangle the stack content - to put the parameters in the right order before a call. To make this a bit more hassle free in
Stacked, one can use bulk binding of local names.
\t { {a b}#
'a: ' @a +
' - b: ' @b +
+.
};
'A' 55 t
}
Output:
The
# operator pops a sequence of identifiers, and binds local names given through those identifiers to the top elements of the stack.
Conditionals
The
? and
! operators can used together fill the function of the
if-else-endif construct in other languages.
\is_positive {{n}#
@n 0 gt
{@n ' is positive'+.}
{@n ' is NOT positive'+.}
?!
};
-5 is_positive
0 is_positive
7 is_positive
Output:
-5 is NOT positive
0 is NOT positive
7 is positive
The sequence in the example above could be rewritten more compactly as:
{dup 0 gt ' is' ' is NOT'?+' positive'+.}
But that would just be me trying to show off.
A more involved example
Putting the stuff we talked about so far together.
\fibonacci {{n}#
~'Calculate the n-th fibonacci number. (n -- fib)'
@n 2 lte
{1}
{@n 1 - fibonacci @n 2 - fibonacci +}
?!
};
1 fibonacci.
2 fibonacci.
3 fibonacci.
4 fibonacci.
5 fibonacci.
Output:
This defines the sequence
fibonacci, which when called, calculates the n-th
fibonacci number. The
~ operator lets one define a description for a sequence.
Getting help
Sometimes you want to get information about a function.
Output:
dup: External function -
Duplicate the upper element of the stack. (a -- a a)
The
help function pushes a string containing information about the given functions onto the stack.
Test it yourself
As
Stacked is implemented in JavaScript, you can test it out right here in your browser. Just type in your
Stacked code in the grey box and click
Execute. For example, write
get_global_name_list: to get the entire list of registered global names.
Have fun!