SLINKY actually stands for Stack based Low overhead INterpreter for Knowledge Yield, and is based on an actual language, FORTH.
The stack calculator that we have seen in class and section forms the basis of the Slinky language. In particular, we add the following functionality to the stack calculator:
1234
"hello"
"this is weird"
1345 "this is interesting"
1345
is an Integer. The second name, "this is interesting"
,
is a string.1234 5678
"He said his name was
"John"
"
within a String.
It must be used to designate where the beginning of the string is, and the last. If you
wish to have "
within you string, use the C++-style notation \"
.
So, we should change the above to "He said his name was \"John"
in order for this to become a legal string.{ hello }
{}
.{ hello { "my" } name is }
{
and
}
within it, as well as double quotes. This particular block contains
4 names.hello
1+
1+
is not an Integer,
due to the '+' sign. It is also not a String, as it is not enclosed in double
quotes. Finally, it is also not a Block, as it is not enclosed with the braces {
}
.hello
and hello
the same thing?ostrich
and emu
the same thing?hello
and "hello"
the same thing?hello
is a reference, and "hello"
is a string. They do share the
same name, however. This is a very important distinction! Two names are equal to each
other if they share both the same type and name.First, we must define the notation that we will use to describe the stack and stack operations. This is the same notation that is used by the Slinky stack calculator.
"a" "b" | Push the string "a" onto the stack, and then push the string "b" |
1 2 3 | Push the numbers 1, 2, and 3 onto the stack, in that order |
"a" 1 "b" 2 | Push the string "a", the number 1, the string "b", and the number 2 onto the stack. |
"a" 1 { 3 5 6 } "1" | Push the string "a", the number 1, the block { 3 5 6 }, and the string "1" onto the stack. |
{3 5 6 { hello } 7 } | Push the block { 3 5 6 { hello } 7 } onto the stack. |
Due to the use of this notation, we can read what a stack contains by reading from left to right. The right-most item listed in our notation corresponds with the top of the stack. For example, the stack 1 { 3 5 6 { "hello" } 7 } "I am the king!" looks graphically like (where the top of the table is the top of the stack):
"I am the King!" |
{ 3 5 6 { "hello" } 7 } |
1 |
It is important to note that in our notation, while the head of the stack is clearly delineated as the right-most name in our list, there is no direct indication as to what the height of the stack really is. So, for instance, 1 { 3 5 6 { "hello" } 7 } "I am the king!" clearly shows that "I am the king!" is at the top of the stack. However, there may be other data items that may be to the left of the left-most member in the list. In our list from before, there may be a string "I am lower" that is below (or to the left of) the number 1
All commands are written in our stack notation as References. As noted earlier, our Slinky stack calculator cannot push References directly onto the stack. So what happens to them? it would probably be useful to describe what the slinky stack calculator does.
The Slinky stack calculator monitors input from the user (or a file), and determines the type and the name of a particular input. If a String, Integer, or Block is entered, then simply push the item onto the stack, and continue reading from the user or file. If a Reference is entered, the Slinky calculator determines if the Reference is a valid command by first looking at global memory to see if the user has previously defined the Reference. If the Reference is found, then push the value that the Reference refers to onto the stack, and evaluate it. Otherwise, the calculator checks to see if the method is built in to the calculator itself. If the reference isn't a built-in command, and the user has not previously defined the reference, then our Slinky stack calculator warns the user that the Reference was not found.
For example, here is a table that shows some sample input, and what our Slinky stack calculator does as a result of the input. For this example, we assume that the Slinky's stack is initially empty:
User Input | Stack Calculator does: | Stack looks like afterwards: |
1 | 1 is an Integer, so push it onto the stack | 1 |
2 | 2 is also an Integer, so push it onto the stack | 1 2 |
3 | 3 is also an Integer, so push it onto the stack | 1 2 3 |
+ | + is a Reference, which has a meaning associated with it. In this case, the meaning is to replace the top two items on the stack with the added result. | 1 5 |
- | - is also a Reference, meaning to replace the top two items with the subtraction of the two. | -4 |
The following table enumerates almost all the commands that our Slinky stack calculator understands:
Command | Meaning | Stack before executing (example) | Stack After executing (example) |
+ | Add the top two stack items together. Both stack items must be Integers. | 3 4 5 | 3 9 |
- | Subtract the top stack item from the item right under it. Both stack items must be Integers. | 3 4 5 | 3 -1 |
1+ | Add 1 to the top stack item. The top stack item must be an Integer. | 3 4 5 | 3 4 6 |
1- | Subtract 1 from the top stack item. The top stack item must be an Integer. | 3 4 5 | 3 4 4 |
* | Multiply the top two stack items together. Both stack items must be Integers. | 3 4 5 | 3 20 |
/ | Divide the top stack item from the item right under it. Both stack items must be Integers. | 3 4 2 | 3 2 |
neg | Negate the top stack item. The top stack item must be an Integer. | 3 4 5 | 3 4 -5 |
== | Pop out and compare the top two stack items. If they have the same name and the same type, push 1 onto the stack, else push 0. The two stack items can be of any type. | 3 4 4 3 4 5 3 "hello" { hello } |
3 1 3 0 3 0 |
< | Pop out and compare the top two stack items. If the bottom item is less than the top item, push 1, else push 0. Both stack items must be Integers. | 3 4 5 3 5 4 |
3 1 3 0 |
> | Pop out and compare the top two stack items. if the bottom item is greater than the top item, push 1, else push 0. Both stack items must be Integers. | 3 4 5 3 5 4 |
3 0 3 1 |
not | Pop out the top item. If the item is equal to the Integer 0, then push 1, else push 0. | 3 4 0 3 4 "hello" |
3 4 1 3 4 0 |
and | Pop out the top two items. If the items are both not equal to the Integer 0, then push 1, else push 0. | 3 "hello" 4 3 1 0 3 0 1 3 0 0 |
3 1 3 0 3 0 3 0 |
or | pop out the top two items. If both items are qual to the Integer 0, then push 0, else push 1. | 3 1 1 3 1 0 3 0 1 3 0 0 |
3 1 3 1 3 1 3 0 |
dup | Push a duplicate of the top item on the stack onto the stack. | "a" "b" "c" | "a" "b" "c" "c" |
drop | Pop the top item off the stack. | "a" "b" "c" | "a" "b" |
swap | Swap the top two items on the stack. | "a" "b" "c" | "a" "c" "b" |
rot | Rotate the top three items on the stack left by one place. | "a" "b" 3 4 | "a" 3 4 "b" |
depth | Push the stack depth as an Integer onto the stack. | "a" "b" 3 5 | "a" "b" 3 5 4 |
top | Print out to the screen the top item on the stack. | "a" "b" 3 5 | "a" "b" 3 5 (no change) |
. | Print out to the screen the entire stack. | "a" "b" 3 5 | "a" "b" 3 5 (no change) |
load | Pop off the top item of the stack, and use the name as a file name. Load in and interpret the contents of that file. The file name must be a String. | "a" "b" "commands.txt" | (results from interpretting commands.txt) |
if | Pop off the top two items off the stack. If the bottom item is not equal to the Integer 0, then evaluate the top item (see eval). the two items can be any type. | 3 1 { 2 3 + } 3 0 { 2 3 + } |
3 5 3 |
ifelse | Pop off the top three items off the stack. If the bottom item is not equal to the Integer 0, then evaluate the middle item. Otherwise, evaluate the top item. See eval for details on evaluating an item. | 3 1 { 2 3 + } { 2 3 - } 3 0 { 2 3 + } { 2 3 - } |
3 5 3 -1 |
set | Pop off the top two items off the stack. The top item must be a String. Convert the String into the Reference, keeping its name intact. Store the Reference, along with the bottom item (which is now the Reference's value) into global memory. Note that this is analogous in C++ to declaring that a variable or function exists, and defining its contents (the value) | 2 4 { + + } "2add" 3 5 "five" |
2 4 3 |
eval | Pop off the top item off the stack and evaluate it. If the top item of the stack is a String or an Integer, then simply push the top item back onto the stack. Otherwise, the item must be a Block (remember that our Stack does not hold References). Interpret each of the individual items within the block as if the user had typed them in. | 6 2 4 5 { + + } 6 2 4 5 "hi there!" 6 2 4 5 5 |
6 11 6 2 4 5 "hi there!" 6 2 4 5 5 |
SLINKY also has comments. They are denoted as //, and work in exactly the same way as the C++ comments. Numerous examples of commenting appear in the succeeding sections.
{ * 2 / } "tri-area" set
{ swap // Flip top two items
dup // Duplicate top
rot // Rotate the original top item back to the top
dup // Duplicate top
rot // Rotate the item we originally duplicated back to the top
swap // Swap the two top items so that they look exactly like before
} "2dup" set
{ dup 0 > // Duplicate the iterator and see if it's greater than 0
{ swap // Put the item we wish to duplicate on top
dup // Duplicate it
rot // Put the iterator back on top
1- // Subtract one from the iterator
dupmult // Call self again, with the smaller iterator.
}
{ drop } // Else clause, remove the iterator.
ifelse // Test to see if the iterator's greater than 0, and execute true body if it is, else
// execute false body (which simply drops the iterator from the stack)
} "dupmult" set