|
3.2 Overview: MUF (cont'd)
The Stack:
Everything in MUF is done by manipulating the stack. The
stack is the area in the computer's memory that holds the data for your
program, the information it uses as it does its job. So try thinking of
it as a RAM disk, a storage device. In this case, the
information is stored by putting pieces of information `on top of each
other' instead of in tracks or sectors. Each time the program is used
(each `process'), it gets its own fresh new stack.
The MUF stack is a LIFO stack: `Last in,
first out.' When you add something to the stack, it goes on top of the
stack, and it's the only thing that's immediately accessible. Previous
items are still there, in the computer's memory, but they're
`underneath' whatever you just added, and you'd need to use some stack
handling functions to get them so your program can use them. A good
analogy is a stack of plates. If you put a red plate on top of a blue
plate, and then a white plate on top of the red one, you'd have a
stack:
white plate
red plate
blue plate
All three plates are there, but the only one you can pick up without
rearranging the stack is the white one.
Within a function, MUF reads the code left to right, top
to bottom. We only have one line in our little program, so it reads that
line left to right, coming first to the variable me. We
haven't yet told MUF to do anything with the variable;
we've just supplied it. So it puts that variable on the stack. Here it
is:
me
Our stack is pretty short right now, just one item. Notice that our one
item right now is the variable me , as opposed to the information
stored in that variable (a dbref). Then MUF reads along our
line of code and comes to the @ operator. The operator is
not data. It's an instruction to do something with the data that's
present, in this case the variable me . So the
@ doesn't go on the stack. Instead, it does its little
operation: it fetches the value that's stored inside me .
When it does so, it `uses up' the variable me . Imagine that
the fetch operator picks up the me , opens it like a box,
pulls out what's inside, and throws the box away. So, let's say your
dbref is #123. Now the stack looks like this:
#123
Still pretty short. Then, reading along, MUF comes to
the string "Hello, world!". We haven't told
MUF what to do with this string, we've just supplied it. So
it goes on top of the stack:
"Hello, world!"
#123
Now we have a stack that actually looks like a stack. The string
"Hello, world!" is stacked on top of the dbref
#123. Next MUF comes to the primitive
NOTIFY . Like operators, primitives are instructions to do
things with data, rather than data to be put on the stack.
NOTIFY is a predefined set of instructions that say `Take
the string that's on top of the stack, go find the player with the dbref
that's right underneath that string, and tell her the string. And forget
you ever heard of this string and this dbref'. The program does what
it's told, and the player with dbref #123 suddenly sees
Hello, world! on her screen (she won't see the quotation marks: they're
what define the string as a string, and not part of the string itself).
In the process, NOTIFY `uses up' the two pieces of data
that it requires, and the stack is now empty. Since the program ends at
this point, that's fine. But if we did something else that required data
(like tack another NOTIFY on the end of our line), then
we'd get `stack underflow', and the program would crash.
Watching the Stack with the Debug Mode:
You can see exactly what's going on as your program runs by putting it
in debug mode. A D flag set on a program means `turn on
debugging.'
====================================
> @set tinker.muf = D
Flag set.
> test
Debug> 1 ("") (main)
Debug> 2 ("") V0
Debug> 2 ("", V0) @
Debug> 2 ("", #123) "Hello, world!"
Debug> 2 ("", #123, "Hello, world!") NOTIFY
Hello, world!
Debug> 3 ("") EXIT
====================================
The first bit of these lines, Debug>, simply tells you
that what follows is output from the debugger (as opposed to stuff like
the line - Hello, world! - , which is output from the
program). The number immediately following is the line number of the
program that's currently executing. The items in parentheses, separated
by commas, are the stack: the left end of the line is the `bottom' of
the stack; the right end is the `top'. The last item (after the
parentheses) is what MUF is reading at this exact point in
the program's execution.
On the first line, our stack is "", an empty string (this
is called a "null string"). So our discussion above was slightly
inaccurate. When a program is called, the argument to the trigger action
(whatever was typed after the trigger command) is pushed onto the stack.
We typed just `test' by itself, so nothing was pushed onto the stack. In
this case, `nothing' looks like "", a string with nothing in it. Run the
program again, with an argument this time, to see the difference. Type
`test pickle'. The first line of debugging will now look like...
Debug> 1 ("pickle") (main)
That V0 is our me . We call it me ;
MUF calls it V0 ... V for
`variable', followed by a zero because it's the first variable it
defined and computers start counting at zero. Study what happens to the
stack as each successive item is read by MUF to get an idea
of how the stack works.
You might try adding another line to tinker.muf to see the stack at work
a bit more clearly. Change tinker.muf so that now it reads...
( tinker.muf. A practice program. )
: main
me @ "Hello, world!" notify
"mink" "otter" "linsang"
pop pop pop
;
====================================
> @edit tinker.muf
> 1 99 d
> i
> ( tinker.muf. A practice program. )
>
> : main
>
> me @ "Hello, world!" notify
>
> "mink" "otter" "linsang"
>
> pop pop pop
> ;
> .
> c
> q
> test
Debug> 1 ("") (main)
Debug> 2 ("") V0
Debug> 2 ("", V0) @
Debug> 2 ("", #123) "Hello, world!"
Debug> 2 ("", #123, "Hello, world!") NOTIFY
Hello, world!
Debug> 3 ("") "mink"
Debug> 3 ("", "mink") "otter"
Debug> 3 ("", "mink", "otter") "linsang"
Debug> 3 ("", "mink", "otter", "linsang") POP
Debug> 3 ("", "mink", "otter") POP
Debug> 3 ("", "mink") POP
Debug> 4 ("") EXIT
====================================
The POP primitive takes whatever's on top of the stack
and gets rid of it. POP pops it off the top, into oblivion.
If we added one more POP, the null string would be popped
off the stack right at the end of the program, and it would exit with
nothing on the stack. If we added yet another one, it would try to pop
something that isn't there, and crash.
prev|
toc|
top|
next
|
|