prev | toc | next
 

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