MUF Programming: Introduction

Welcome to MUCK University's book on MUF Programming!

This "book" is presented as an online resource for anyone wanting to learn the MUF programming language. While I intend it to be accessible to all audiences, I do assume that you have a basic familiarity with mucks, particularly GlowMUCK and FuzzBall mucks. An understanding of the types of muck database items and how database item properties, while not required, will accelerate your learning process. Don't worry if you don't know what any of that means--by the time you complete the material, you will.

This book is written using Drupal's "Book Content" format. You'll find navigating the book easy. At the bottom of each section/webpage, there will be a list of links to the subsections of the current section if you wish to jump to a particular section. In addition to that, there will be two links at the very bottom. On the left will be a link to the previous section and on the right there will be a link to the next section if you wish to simply go through the sections in order.

Things you type into the computer will be printed in a different font, like this: say I'm new to MUF. MUF programs will either be printed in that same font or they will be syntax highlighted like this:

  1. : main
  2.   "me" match "Hello World" ansi_notify
  3. ;

Note that the 1., 2., 3. are shown for our convenience--they are not part of the program.

When the text says that a program example is available under 'programname', you can list the example directly by typing @list $examples/programname on MUCK University. Usually the program will also have a command allowing you to execute the program. The command to execute the program will be given in the relevant section. The program will usually be in "debug" mode which means it will display what the program is doing in addition to its normal output.

If you're new to programming in general, I recommend that you continue on to the next section. If you have programming experience or you are in a computer-centric degree program1, the next section may not say anything you don't already know--but hopefully it says it in a new and interesting way. Be sure to go ahead and read it so you can tell me how wonderful it is...

Speaking of feedback--all of these pages have comments enabled. Feel free to add your own comments and questions.

Programming Language? What's That?

For those that are new to programming, I want to briefly explain what a programming language is.

Computers are fast computation machines but they are limited and inflexible. The only language they truly understand well is composed of numbers, a language with limited vocabulary. When we talk, we can shorten words, we can leave out words, we can compress multiple words into a single word. And yet, whoever we're talking to will understand us. We're capable of adapting and filling-in the missing bits from context and implication. By a computer's standards, human languages have massive vocabularies and grammars of incredible complexity. A computer is extremely literal--every step of an operation must be stated in exact detail with nothing left out.

A programming language is a compromise--a language that still must be exact--but it has a greater vocabulary so that a human programmer (that's you!) can express their ideas in a language that a computer program can automatically translate into the computer's language of numbers.

A programming language is a language designed by us to make it easier to instruct a computer what we want it to do.

Even so, the language is still exacting. Sometimes we have to explain what we want the computer to do in excruciating detail. A popular example is the construction of a peanut butter and jelly1 sandwich. If someone was going to prepare some food for me2 it's easy to get a peanut butter and jelly sandwich--I just ask for one.

But what if, for instance, your four-year-old niece wants to make you some food? You know that asking for "lemon meringue pie" is out of her league, but maybe a PBJ is doable. But she's never made one before. So you explain: "Get two slices of bread out. On the first slice, spread some of the jelly on it. On the other slice, spread some peanut butter on it. Slap the slice with the peanut butter on top of the jelly slice. Put it on a saucer and bring it on over for me to try!"

That is what programming is like--except one thing. You have to explain in even more detail. You have to take "Get two slices of bread out" and break it down into its tiniest steps: "Go to the bread box. Remove the bread loaf from the bread box". Wait. That's not detailed enough. We actually need to break "Go to the bread box" down, lets try "Stand up. Walk 17 steps south, walk 8 steps northeast...". That's programming. As programmers we take large tasks, and we carve them up into small tasks, then carve those up into small steps, then divide the steps into very discrete and tiny steps--and then use the programming language's vocabulary to explain the tiniest steps to the computer.

Fortunately, our human crafted programming languages make an effort to make it easier. When we discover that we are doing the same set of tiny steps over and over again in our programs, we extend the language's vocabulary to include a word that does all of those steps for us. Thus, it is frequently much easier than I made it sound when i was talking about PBJs. Let's go find out just how easy it is...as soon as I go make myself a PBJ.

Using the MUF Program Editor

The built-in editor for writing MUF programs is the MUF Program Editor. Written with the constraints of the telnet interface in mind it is a form of text editor known as a line editor. If you've done enough building you have probably used another line editor, lsedit.

While the MUF Program Editor is a pain to use and there are alternate approaches, it is still worth-while to learn the basics of using the built-in editor. There will be times when the alternatives won't be available.

In the next section, we'll dive right in by writing our first version of the classic "Hello World" program.

Starting the Editor and Entering a Program

Starting the MUF Editor is easy. If you wanted to write a program named 'TryIt' you would simply type:

@prog TryIt

and the server would respond with:

Program TryIt(#19117FM3) created.
Entering editor.

Notice it isn't very forthcoming about how to use the editor. Fortunately, you can type help and press <enter> and it will display a help screen. Go ahead and give it a try! Type the following into your favorite muck client while connected to University:

@prog TryIt
help

To get out of the MUF program editor, type quit. Until you do, everything you send to the muck is actually going to the editor which is probably not what you want. After you get out of the editor, I recommend you @recycle the program, @recycle TryIt.

Actually entering a program is easy. After you are in the editor, you type insert to go into insert mode. While in insert mode everything you type and send to the muck is added to your program. The editor will stay in insert mode until you type a single period on a line by itself (followed by an <enter>, of course!).

Try using the MUF program editor to enter the classic "Hello World!" program. Here's a recipe to follow...type this in verbatim:

@prog HelloWorld
insert
: main
  "me" match "Hello World!" ansi_notify
;
.
quit

Don't worry too much about what all of this means. Feel free to make some guesses--but we'll dive into the details later.

If everything went as planned, you should be able to type @list HelloWorld and see the following:
: main
  "me" match "Hello World!" ansi_notify
;
3 lines displayed.

If you ended up with some typing errors, @recycle HelloWorld and try again. Later I'll explain how to fix your mistakes without recycling, but for now...

Okay, by now you should have a "Hello World!" program. The question that should be on your mind is "How do I run my program?". You have to make an action (sometimes called an exit) that is linked to your program. Give the following a try:

@action hello=me
@link hello=HelloWorld

Then, try typing hello! The muck (your program actually) should respond with Hello World!.

Hang on to your HelloWorld program for the next section, where we learn how to use the editor to edit programs.

Editing Programs

You've written your "Hello World!" program. You page the head wizard/staffer and say "Hey, check out my program!". He/she says, "That's cool, but it'd be cooler if it said 'Hello Monkeys!' instead. Try again and get back to me."

You don't want to have to type the WHOLE thing over just to change "Hello World!" to "Hello Monkeys!". So you hop into the MUF program editor by typing @edit HelloWorld. Here's the bad news. There's no handy search and replace function. Instead you can delete lines and you can perform insertions at particular point. So what we'll have to do is delete the line that has "Hello World!" in it and replace it with a line that has "Hello Monkeys!". For that, we'll need to know which line is which.

So our first task when we get in the editor is list the program so we can find out the line number for the line we want to change. If you've not already gone into the editor using @edit HelloWorld, do so now. Then, type 1 100 list. You should see something like the following:

Entering editor.
Line not available for display.
: main
  "me" match "Hello World!" ansi_notify
;
3 lines displayed.

That's our program all right. And its easy for us to count the lines to figure out which line "Hello World!" is on. But. The reason we have computers is so that tedious tasks like 'counting to 3' is done for us. So lets turn on line numbering mode so we don't have to count to find out which line is which. The editor has a simple command for this: numbers. Now try 1 100 list again. This time, we get this kind of output:
  1: : main
  2:   "me" match "Hello World!" ansi_notify
  3: ;
3 lines displayed.

Excellent! No thinking required at all now! Hm. What was it the boss asked us to do? Oh, of course. Change "Hello World!" to "Hello Monkeys!". "Hello World!" is on line 2. So we need to replace that line. First, we delete the line by using the delete command: 2 delete and you should get 1 lines deleted in response. We should check our work, try 1 100 list again. We should see:

  1: : main
  2: ;
2 lines displayed.

Next, we want to go into insert mode, but at a particular point (line 2): 2 insert. This tells the editor we want to insert our new line(s) before line 2. In this case, this has the effect of inserting the new line where the old one was. Type the following:

  "me" match "Hello Monkeys!" ansi_notify
.

Recall that a single period by itself on a line tells the editor we're done inserting. Once done, we should check our work again, using 1 100 list to list the program. If it looks correct, type quit and type hello again to give the new version a try.

Unless you already know the next command I'm going to teach you, the program is going to still display "Hello World!" when you type hello. This is because the program is already compiled.

I'm not going to explain right now what compiling a program actually does. The important idea to take away from this is that your program will exist two forms: 1) uncompiled source code (which is what you typed in) and 2) compiled program code which is created by the server's MUF compiler. The first time you ran your program, the muck saw that you hadn't compiled the program and compiled it for you. Very helpful. But this time, since the program is already compiled, the server doesn't do it again. Unfortunately, the server only executes the compiled version of the program--which means its executing the old version of the program.

To fix this problem, you need to compile your program yourself after making changes to it. Go back into the MUF program editor using @edit HelloWorld. Type the command compile followed by quit. Try hello again, does it show "Hello Monkeys!" now? Excellent.

Before I finish this section, I wanted to include some important tips and notes.

First, you may have noticed that when our commands needed arguments, 1 100 list for example, the arguments came first. The MUF editor uses what's known as postfix notation, where the command follows its arguments. They use the same terminology in math. Writing 1 2 + in postfix is the same as 1 + 2. The way we're used to writing math is called infix notation because the operator is "inside" the operands. It is an idea worth getting used to because the programming language (MUF) uses postfix notation.

Second, if you're like me, you're going to get tired of typing list, insert, compile, numbers, delete, etc. There's good news: all the MUF editor commands can be abbreviated down to just one letter, the first letter. That means you can use l for list, i for insert and so on.

Exercises (MUF Program Editor)

  1. If you haven't already, type in the "Hello World" program. Compile it and create an action to run it.
  2. Modify your Hello World program so that it prints your favorite quotation instead of "Hello World"
  3. Remove different lines or portions of the program and see what sort of errors you get. For instance, what happens when you delete the line that contains only a semi-colon? Such a small change...

Alternatives to the MUF Program Editor

The MUF Program Editor is guaranteed to be available on any TinyMUCK and regardless of what client you use to connect. However, it doesn't match the flexibility and ease-of-use that modern text editors provide.

Most MUD clients have a feature that makes it possible for us to use our favorite text editor instead of the MUF program editor.
IMPORTANT NOTE: A word-processor, such as Microsoft Word, Wordperfect, or Open Office's Writer, is NOT a text editor. If you use a word processor to edit your programs, bad things WILL happen. If you are not familiar with any text editors you like, I'll be including a list and where you can obtain them.

First, I'll explain how this works in general and then I'll cover how to do it with specific clients. Pretend that you have typed up the "Hello World!" program in your favorite text editor and saved it as "HelloWorld.muf" where-ever you store your files on your computer.

That's when the magic starts. You tell your mud client "send this text file to the Muck" the client sends the entire text of the file to the muck as if you were typing whatever was in the file. Pay special attention to that last bit. It's very important: Everything in the text file will be sent to the Muck as if you typed it.

That includes commands. You could, in theory, do all sorts of useful things with this, but I'm only going to cover how it helps us write MUF programs. Consider our old friend, the HelloWorld program:

: main
  "me" match "Hello World!" ansi_notify
;

We could type that up in a text editor. Then connect to the Muck and type the command to create a program, @prog HelloWorld, type insert to go into insert mode. Then tell our mud client to send the file to the muck. Then finish up by entering a period by itself on a line, typing compile and quit and I'd be done.

But wait. If the text file is sent to the muck as if I was sitting at the keyboard typing it, what if I typed the following into my text editor?

@prog HelloWorld
insert
: main
  "me" match "Hello World!" ansi_notify
;
.
compile
quit

Now, when I sent the file to the Muck, not only is the entirety of the program sent, but also the commands required to create, insert, compile, and quit are all sent automatically1.

However, there are some pitfalls you should be aware of when using this technique.

If you are editing a program that is already in place you need to change the text file so that it replaces the program text as part of the operation. To do this you have to use the delete command to delete the entire program so that the new program text replaces the old. Thus, your text file has to look something like this:

@prog HelloWorld
1 10000 delete
insert
: main
  "me" match "Hello World!" ansi_notify
;
.
compile
quit

The 1 10000 delete effectively deletes the entire program (so far I've not seen a program that long--yet.), then we do our insert and the rest of the steps as normal.

I have another pitfall to point out though. If you're not careful with this technique you can end up with multiple copies of the program. It will happen if you the program's database item is not in your inventory or the room when you send the file to the Muck. The muck looks locally for the program when you send/type the @prog2 command. If it finds a matching program, it assumes you want to edit it--but if it doesn't it creates a new one.

What I recommend strongly instead is that you use @register to assign the program register name. For personal programs, you should use @register #me <program's database reference>=<some name you can remember>. For programs to be use by other programmers, wizards, or mortals you may want to register the program in the global registry. You will need to consult with the staff on your muck about how and where it should be registered. Let's assume for a moment that you've registered the HelloWorld program as programs/HelloWorld. Then you would change your text file to look like this:

@teleport $programs/HelloWorld=me
@prog HelloWorld
1 10000 delete
insert
: main
  "me" match "Hello World!" ansi_notify
;
.
compile
quit

All this extra effort may not seem worthwhile right now. But when your programs get longer and you have to make many edits to improve them--that's where the extra effort becomes worth while. You can also do things like make yourself templates for new programs so that you don't have type all of that more than maybe a couple of times. Before you go, I'd like to show you one more variation on the text file...this time with little messages to yourself so you know when the text file starts going to the muck and when it finishes--something that pays off for slow connections and large programs.

whisper me=Beginning upload of Hello World...
@teleport $programs/HelloWorld=me
@prog HelloWorld
1 10000 delete
insert
: main
  "me" match "Hello World!" ansi_notify
;
.
compile
quit
whisper me=Upload Complete

In the next few sections, I'll explain how to do the "send the file to the muck" part for specific clients. Feel free to read them all, but you only need to read the one for your favorite.

Text Editors

There are many many text editors out there. Feel free to use whichever one you like. You can even use notepad--but I don't recommend it.

Probably my favorites are GNU Emacs and Notepad++. I currently don't have a Macintosh/Apple so I can't make very many recommendations there. The list below is in no particular order.

I want to remind everyone that you CANNOT use Microsoft Word, Open Office Writer, Wordperfect, or any other word processor to edit programs. Word processors add extra information to the files (formatting and the like) which the compilers won't understand.

It's also a good idea to set the display font of your editor to a font that is fixed-width/monospace (every letter is the same width). It will make editing programs easier. Trust me.

Apple/Macintosh

Microsoft Windows

Unix/Linux/etc.
A small note: Many *ix accounts and distributions have Pico as an editor. I recommend avoiding using Pico for editing programs, html, etc. Pico's wrapping features have the potential of mangling programs with long lines.

Check your preferred distribution, they typically have at least one or two editors installed by default, and you can optionally install others.

SimpleMU

To send the contents of a text file to the Muck as if you typed it in SimpleMU, you can follow these steps:
Connection MenuConnection Menu

  1. Click on Connection to display its menu as shown in the image.
  2. Then click on "Upload Text" (third one from the bottom).SimpleMU presents the following dialog:
  3. SimpleMU presents the following dialog:
    SimpleMU: Upload TextSimpleMU: Upload Text

  4. Click on the pull down and select "Upload raw text, with no prefixes".
  5. Then click on "Select".
  6. If you stored your filename with the extension ".muf" or left off the extension entirely you'll need to change the "Files of Type" pull-down to "All Files". The dialog might look something like this:
    SimpleMU: Browse DialogSimpleMU: Browse Dialog
  7. Click on "Open" which takes you back to the upload text dialog:
    SimpleMU: Upload Text 2SimpleMU: Upload Text 2
  8. Click on "OK". A few moments later, your program is done.

TinyFugue

To send text to the muck using Tinyfugue can be done in one step. You will need to know the filename of your text file and the path to it. Provided you know those two things, you can send the file to the Muck with the following:

/quote -S '/path/to/myfile

For example, my projects are usually in ~/dev/glow/muf, so for helloworld.muf:

/quote -S '~/dev/glow/muf/helloworld.muf

Words That Stack Part I: Words

A word does not frighten the man who, in acting, feels no fear.-- Sophocles

Writing MUF programs is about defining words. You won't need your favorite dictionary to define these words, since the words and their definitions will be created by you, the programmer.

Since it is not possible to define a word without other words, MUF provides some basic words to define more complex words. These fundamental words are called primitive words. There are more than two hundred primitive words. However, the majority of programs only require a fraction of those. In these first exercises, we will only work with the most common and basic primitives.

There is no need to memorize and master all 219, but you should at least know how to get information about them. The Glowmuck server provides a help system for MUF which is accessible through 'man'. Typing 'man index' will give a list of topics which includes lists of the primitives. Additionally, a complete alphabetical list of the primitives is available at: http://glowmuck.sourceforge.net/mufdocs/primdefs.php.

Defining your own words is pretty simple. Each definition begins with a colon, the word that you are defining, the definition itself, and then a semi-colon to mark the end of the definition. In our example definition below, the word being defined is MyWord and the definition is 10 pop. Don't forget that the program listings in this book include line numbers as an aid to discussion, the line numbers are NOT part of the program.

  1. : MyWord
  2.   10 pop
  3. ;

You can, in turn, use words you have defined to define other words. Using the words you've defined are no different from using the primitive words, simply include them in your definition. In our next example, we have a definition for "FirstWord" and then a definition for "LastWord". LastWord uses FirstWord in its definition.

  1. : FirstWord
  2.   10 pop
  3. ;
  4.  
  5. : LastWord
  6.   "a string" FirstWord
  7. ;

Something important to note here is that words must be defined before they can be used. This means that your definition of a word must be in your program text BEFORE the first word that you use it in.

The very last word definition in your program text is the first one executed when your program is run. The word is typically named 'main' but is not required to be. This 'main' word often resembles a dispatcher's office since in most programs it is mainly composed of executions of the other words in the program, depending on the needs of the user of the program.

It is common for programmers to refer to words as procedures, functions, or methods. This is because words in MUF & FORTH perform the same duties that procedures, functions, or methods do in other languages. If it makes more sense to you to think of the user defined words as functions, procedures, or methods and the primitive words as commands or keywords, feel free to do so.

Words That Stack Part II: The Stack

Before we can begin to define our words, we have to understand how to provide the data that a primitive word or user-defined word needs. The short answer is that the data is provided by the 'stack'. The stack is somewhat difficult to describe in words alone but I've found an analogy that works to help you visualize what the stack is and how it works.

Picture, if you will, a stack of paper on your desk. On each sheet of paper, there is either a number, database reference, or a string. Adding data to the stack is easy: You add another sheet of paper with the data written on the sheet of paper.

MUF adds to its stack in much the same way. As you give it each piece of data it adds that piece of data to the top of the stack, just as if you had written it on a sheet of paper and put that sheet on top of the stack. For example, the following word definition first puts a database reference (#123) on the stack, then a string ("a string"), and then on top an integer value of 23.

  1. : StackData
  2.   #123 "a string" 23
  3. ;

After the 'StackData' word is used, the top three items on the stack will be 23 (on the top), "a string" would be right below the 23, and the database reference #123 would be below "a string". Three sheets of paper added to the stack.

When you are debugging your program, the MUF interpreter will display the stack for you, looking something like this:

Debug> #555 3  (#123, "a string", 23)

The first item inside the parentheses is the bottommost item on the stack, the last item is the topmost item on the stack. Throughout this book we'll use the same format to display the stack when we need to show what order the items on the stack are in.

Once we have the data we need on the stack, we use a word to process the data. Let's assume we needed to add 15 to the 23 put on the stack by 'StackData'. Our program might look something like this:

  1. : StackData
  2.   #123 "a string" 23
  3. ;
  4.  
  5. : AddFifteen
  6.   StackData 15 +
  7. ;

Because AddFifteen is the last word defined in the program it will be the one executed when the program starts. However, AddFifteen's first word (or instruction to the computer) is StackData. So the word StackData executes, which pushes our three pieces of paper are pushed on top of the stack. Then back in 'AddFifteen' another piece of paper with '15' on it is put on top. When the + primitive executes next, it removes the top two pieces of paper (the ones with 23 and 15 on them) and pushes a new piece of paper on the stack with the answer of 38. In debug output, it'd look something like this:

Debug> #555 3  () #123
Debug> #555 3  (#123) "a string"
Debug> #555 3  (#123, "a string") 23
Debug> #555 3  (#123, "a string", 23) 15
Debug> #555 3  (#123, "a string", 23, 15) +
Debug> #555 3  (#123, "a string", 23, 38)

Note that our sheets containing the 23 and 15 values are now gone. Nearly every word will operate in this fashion...any of the data it requires will be popped off the stack and replaced with that word's results.

You may have noticed I that I included a little more information in the debug output. In addition to what's on the stack being between the parentheses, debug output always shows the next thing that it will process. So in the first line of debug output above, the stack is empty (nothing between the parentheses) and #123 is the next thing to be processed. On the second line, we can see #123 is on the stack, and "a string" is about to be added.

Don't worry if some of this doesn't make sense yet. It takes a little while for the concepts to fully sink in. But don't hesitate to ask questions either on MUCK University or using a comment on the website. Your questions are helpful, they help me improve the quality of these explanations!

Our First Program Or An Old Chestnut

In this chapter we'll examine a rather classic program written in MUF. This program will send the text "Hello World!" to whoever runs the program. This example is listable at MUF101/HelloWorld (remember to list use: @list $examples/MUF101/HelloWorld). You can run the example by typing MUF101Ex1. The program is listed below with the line numbers shown:

  1. ( A simple hello world program )
  2. : main
  3.   ( First, we'll pop the unneeded argument string off the stack )
  4.   pop ( s -- )
  5.   ( Next, we will find out the player's dbref )
  6.   "me" match ( s -- d )
  7.   ( Then we'll send the "hello world" to the player )
  8.   "Hello World!" notify ( d s -- )
  9. ;

The first, third, fifth, and seventh lines of this program are merely comments. The compiler ignores the comments which are there for the programmer(s) to easily understand what the program is doing. When commenting your own programs (and you should be commenting your programs!) pay attention to the usage of the parentheses. Every comment must begin with one and only one parenthesis and end with one and only parenthesis. They cannot overlap and you cannot use parentheses in your comments. Any overlaps will confuse the compiler and generate errors.

As we learned in the previous chapter, the ": main" on line 2 begins the definition of a word called "main". The definition of the word follows until we reach the semi-colon on line 9.

We learn three things from line four:

  pop ( s -- )

First is the primitive word "pop". If you remember our "stack of paper" analogy, this is like taking the top piece of paper and throwing it away. "pop" removes the item on the top of the stack.

You may be wondering why we're removing an item from the stack before we've put anything on the stack. The MUF interpreter has put something on the stack for us. Anything that the user typed after the command that ran the program is in a single string on the stack when the program starts. Since our program does not need that string, we're simply removing it when the program starts.

So before the 'pop' the debug output (which includes what is on the stack) looks like this:

Debug> #509 4 ("") POP

And after:
Debug> #509 6 () "me"

As you can see, an empty string between the parentheses has been removed. It is worth looking at the debug output briefly here. The database reference #509 is the database reference number of the program that is running. The number in between the reference and the stack is the line number of the program currently being executed. And after the stack is what 'comes next' So when we were on line 4, 'pop' was next, on line 6, "me" is next.

The last thing we learn from line four is that comments can be anywhere, as long as they're fully enclosed in parentheses. This particular comment is commenting on what is happening with the stack when the word "pop" is executed. Before, there is a string on the stack, represented by an s. After the word, which is represented by --, the string is gone, so there is nothing after the --.

We have the important parts of this program in lines 6 and 8. In line 6:

  "me" match ( s -- d )

The first thing that happens here is that we push a string on to the top of the stack. Continuing our stack of paper analogy, we'd write "me" on a piece of paper and place it on top of our (currently empty) stack of paper.

Then we use the primitive word 'match'. Match removes the string on top of the stack and searches for a database item that matches that string. It begins by searching the player running the program, then the room they are in, and then follows with their environment by checking the room's parent, the parent's parent, and so on until reaches room #0. When it finds a match, it will push the database reference for that database item onto the stack.

Stack Before: ("me")
Stack After: (#555) (where 555 is the reference number)

It is important to understand that when a program uses 'me' as something to match, it will match the player that is using the program. So when you run this program, match will return the your database reference number.

Notice that the comment ( s -- d ) shows us what is happening on the stack. A string is removed and replaced with a database reference.

Finally, in line 8, we have our output:

"Hello World!" notify ( d s -- )

We first push the string "Hello World!" on to the stack. Then the primitive word 'notify' goes to work. It removes both the database reference and the string from the top of the stack. Using the database reference, it sends the string to the player represented by the database reference.

The stack before the notify looks like this:

(#555, "Hello World!")

And after it is empty again: ()

It is worthwhile to run the example program once or twice by typing "MUF101Ex1" or "MUF101Ex1 stufftobeignored" just so you can see the the operation and "Debug>" output yourself.

Roasting The Chestnut Again

In this section we are revisiting the program we examined in the last section and making it a little more flexible. Instead of sending "Hello World!" to only the person using the program, it will send "Hello World, <name>!" to whoever is specified by the user. In the process we'll learn a few more primitives and get our first introduction to libraries.

The first step in the new version of our program is taking the string that will be on top of the stack and determining if it is a valid player name and the database reference of that player.

We could use the primitive word match determine the player's database reference. Match compares the string on top of the stack to all database items 'nearby' to see if the name of those database items contains the string. If it only finds one item, it puts the database reference of that item on the stack.

But what if there is not a item that matches? Or worse, what if there are two items that do match? match uses two special database reference numbers to let you know if it encounters those situations. If it cannot find a match it will put #-1 on the top of the stack. If it finds more than one item it will put #-2 on the top of the stack.

For our program to work correctly we would have to check for these special database reference numbers and send the appropriate messages to the player. This is a process that is needed so frequently that a word to do the work is provided in a library. This word will do the work of matching a player, discovering if it is ambiguous, and informing the user of the result for you. Libraries are collections of words that provide commonly needed functions.

The MATCH library provides a variety of predefined words for matching players, things, etc. much the same way the primitive word 'match' does, but with more options. You cannot find information on library words via 'man', but you can usually find documentation by typing @view <library registered name> or @view #<database reference>. In this case @view $lib/match will reveal the documentation of the MATCH library.

The library word we're interested in is MATCH-noisy_pmatch. The noisy part of its name indicates that it talks to the user directly, rather than letting you determine what you want to tell the player yourself. Since that is what we want, it will be perfect for the task.

Looking at the documentation we see:

MATCH-noisy_pmatch: playername -- playerdbref

The documentation is saying that if you have a player's name on the stack, the noisy_pmatch will replace the name string with the player's database reference.

Further reading reveals that if noisy_pmatch can't find the player it will replace the name string with a #-1, which is a database reference used to represent invalid/not found database items.

At the beginning of our previous program we used 'pop' to remove the string at the top of the stack. This time, since the name that the user types after the command will be in that string, we will start our program by using MATCH-noisy_pmatch. So the program looks like this at the beginning:

  1. ( We tell the compiler we are using the lib-match library )
  2. $include $lib/match
  3. ( Begin our main word )
  4. : main ( s -- )
  5.   MATCH-noisy_pmatch

The first line: $include $lib/match tells the MUF compiler that we'll be using the match library. The next line should be familiar since it is the same as our first program in that begins the definition of the word 'main'. As the last word defined, it will be the first to be executed.

Now if the user of our program provides a name that doesn't exist, or is ambiguous, or otherwise invalid, MATCH-noisy_pmatch will put #-1 on top of the stack. When we use notify later in our program, if #-1 is the database reference it pulls off the stack, our program will fail because notify does not know how to send a message to #-1. Our program will need to check the result to make sure its not #-1 before proceeding.

We can do this using the ok? primitive. ok? pushes a one to the stack if the database reference on the top of the stack is in the valid range of database references (#0 to the highest in the database) and that the database reference does not refer to a garbage (recycled) database item. Otherwise, ok? push a zero on to the stack to indicate the database reference is not valid.

MUF, like many programming languages, uses a zero to represent 'false' and any non- zero value to be 'true'. This becomes important when you use the if primitive. When the value on top of the stack is false, the program will skip from the if to the first instruction after a then Otherwise, it will execute the portion of the program between the if and the then.

In addition, we can have the program execute a different set of instructions if the value on the top of the stack is false. We do this by adding an else. So the form of our if becomes: 'if [things to do when true] else [things to do when false] then'. In this situation, if the value is true, then all the instructions between the if and the else will be executed. If it is false, all the instructions between the else and the then will be executed. Either way, after it is done, execution will resume with the next instruction after the then.

The 'if then' technique allows you to have your program make either/or choices "if this is true, do this, if not, do this other thing.".

For our hello world program we need to skip sending the 'hello world' message when the database reference we have is incorrect. So we place the 'hello world' portion of our program between an if and else primitives.

That portion of our code will end up looking like this:

  1.   ok? ( d d -- d i )
  2.   ( If the dbref is valid... )
  3.   if ( d i -- d )
  4.     ( it is valid, so we go ahead and start building our message )
  5.     "Hello World, " ( d -- d s )
  6.     ( We'll need a copy of the player's dbref so we'll use over )
  7.     over ( d s -- d s d )
  8.     ( We'll use our copy of the dbref to find out the target player's name )
  9.     name ( d s d -- d s s )
  10.     ( We'll now combine the first part of our message with the target player's name )
  11.     strcat ( d s s -- d s )
  12.     ( We also need to add the tail end of our message )
  13.     "!" ( d s -- d s s ) strcat ( d s s -- d s )
  14.     ( And finally, send the string to the target player )
  15.     notify ( d s -- )
  16.   else
  17.     ( The dbref isn't valid, so we pop the dbref off the stack to clean up )
  18.     pop ( d -- )
  19.   then

In line 1, the database reference is checked whether it is a valid reference. If it is, the if on line 3 will allow execution to continue to the next instruction (on line 5). If the reference isn't valid, execution will skip to the instruction after the else on line 16.

Let's assume that the database reference is valid and examine the instructions between the if and the else. First, on line 5, we put "Hello World, " on to the stack. Next on line 7 we use the over primitive. over is similar to the dup primitive, but instead of duplicating the top item on the stack, it duplicates the item below the top item and the duplicate is put on top of the stack. You can think of the copy hopping over the top item in the stack to be on top.

We're using over on line 7 so that we can use the 'name' primitive next (line 9). Name removes a database reference from the stack pushes the name (as a string) of that database item on to the stack.

Since we want to have it say "Hello World, !" we need the target player's name. The next step is adding the string containing the player's name to our "Hello World" string. We use strcat to achieve the task. strcat is stands for "string concatenate" and operates by removing the two strings from the top of the stack and pushing a string on to the stack that is the two combined. The string on the top of the stack is appended to the end of the string right below it on the stack. To complete our string we also push a "!" on to the stack and use another strcat to append it to our message.

On line 15, we use the notify primitive to send the message to the player. Notify takes the message from the top of the stack and then in turn the database reference of the target player, and sends the message to the player. Then we reach the else. Since we're executing the code between if and else (because the database reference was "ok"), we're done. The program will skip down to the next thing after then. In the case of this program, there's nothing left to do, so the program just ends.

However, If for some reason the database reference of the target player is invalid (for instance, you type "Slartibartfast" and there's no player named Slartibartfast), then the code between the else and the then will be executed. In that situation,
the code between else and the then is executed. There, we simply pop the invalid database reference off the stack to clean up. Then we're done, the program will jump to the next instruction after the then, but there aren't any, so the program ends.

End of Completed Material

You've reached the end of what I've gotten written so far. There are a couple more pages which are simply notes on future articles.

Event Driven Programs (Connect, Disconnect, Arrive, Depart, HeartBeat)

Correct way to troubleshoot such a program is to create an action as follows:

  1. @action Queued event.=me
  2. @link Queued event.=<my program to test>

The part "Queued event." is very precise. The case matters if you are going to imitate what the muck sets the command variable to exactly. The period must be included. Okay, then when you need to test the program you must include the event type on the stack as follows:

Event Type Test Command
Connect Event Queued event. Connect
Disconnect Event Queued event. Disconnect
Arrive Event Queued event. Arrive
Depart Event Queued event. Depart
Heartbeat pulse Event Queued event. HeartBeat

Scanning Property Directories

  1.   prog "@propdir/" nextprop ( Start the nextprop cycle. Note that to start it, the string must end in a "/" )
  2.   BEGIN ( Tells the compiler the start of the loop )
  3.     dup "" strcmp WHILE ( we use while so that if the property directory is empty or doesn't exist, the loop will stop immediately and jump to the pop after the REPEAT )
  4.     ( Here we'd put the code that would do whatever we want to do with each property. I recommend it be a separate word for testing )
  5.     prog swap nextprop ( Fetch the next property )
  6.   REPEAT ( Forces program back to the first instruction after the BEGIN )
  7.   pop ( Pop off empty property name that will be left over after coming out of the loop )

muf primitives are case-insensitive. This means I can get away with putting BEGIN, UNTIL, REPEAT, WHILE in all caps. I've found that this makes it much easier to "see" the loop.