[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2 MOO Language Statements

Statements are MOO constructs that, in contrast to expressions, perform some useful, non-value-producing operation. For example, there are several kinds of statements, called `looping constructs', that repeatedly perform some set of operations. Fortunately, there are many fewer kinds of statements in MOO than there are kinds of expressions.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.1 Errors While Executing Statements

Statements do not return values, but some kinds of statements can, under certain circumstances described below, generate errors. If such an error is generated in a verb whose ‘d’ (debug) bit is not set, then the error is ignored and the statement that generated it is simply skipped; execution proceeds with the next statement.

Note: This error-ignoring behavior is very error prone, since it affects all errors, including ones the programmer may not have anticipated. The ‘d’ bit exists only for historical reasons; it was once the only way for MOO programmers to catch and handle errors. The error-catching expression and the try-except statement are far better ways of accomplishing the same thing.

If the ‘d’ bit is set, as it usually is, then the error is raised and can be caught and handled either by code surrounding the expression in question or by verbs higher up on the chain of calls leading to the current verb. If the error is not caught, then the server aborts the entire task and, by default, prints a message to the current player. See the descriptions of the error-catching expression and the try-except statement for the details of how errors can be caught, and the chapter on server assumptions about the database for details on the handling of uncaught errors.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.2 Simple Statements

The simplest kind of statement is the null statement, consisting of just a semicolon:

 
;

It doesn't do anything at all, but it does it very quickly.

The next simplest statement is also one of the most common, the expression statement, consisting of any expression followed by a semicolon:

 
expression;

The given expression is evaluated and the resulting value is ignored. Commonly-used kinds of expressions for such statements include assignments and verb calls. Of course, there's no use for such a statement unless the evaluation of expression has some side-effect, such as changing the value of some variable or property, printing some text on someone's screen, etc.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.3 Statements for Testing Conditions

The ‘if’ statement allows you to decide whether or not to perform some statements based on the value of an arbitrary expression:

 
if (expression)
  statements
endif

Expression is evaluated and, if it returns a true value, the statements are executed in order; otherwise, nothing more is done.

One frequently wants to perform one set of statements if some condition is true and some other set of statements otherwise. The optional ‘else’ phrase in an ‘if’ statement allows you to do this:

 
if (expression)
  statements-1
else
  statements-2
endif

This statement is executed just like the previous one, except that statements-1 are executed if expression returns a true value and statements-2 are executed otherwise.

Sometimes, one needs to test several conditions in a kind of nested fashion:

 
if (expression-1)
  statements-1
else
  if (expression-2)
    statements-2
  else
    if (expression-3)
      statements-3
    else
      statements-4
    endif
  endif
endif

Such code can easily become tedious to write and difficult to read. MOO provides a somewhat simpler notation for such cases:

 
if (expression-1)
  statements-1
elseif (expression-2)
  statements-2
elseif (expression-3)
  statements-3
else
  statements-4
endif

Note that ‘elseif’ is written as a single word, without any spaces. This simpler version has the very same meaning as the original: evaluate expression-i for i equal to 1, 2, and 3, in turn, until one of them returns a true value; then execute the statements-i associated with that expression. If none of the expression-i return a true value, then execute statements-4.

Any number of ‘elseif’ phrases can appear, each having this form:

 
elseif (expression) statements

The complete syntax of the ‘if’ statement, therefore, is as follows:

 
if (expression)
  statements
zero-or-more-elseif-phrases
an-optional-else-phrase
endif

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.4 Statements for Looping

MOO provides three different kinds of looping statements, allowing you to have a set of statements executed (1) once for each element of a given list, (2) once for each integer or object number in a given range, and (3) over and over until a given condition stops being true.

To perform some statements once for each element of a given list, use this syntax:

 
for variable in (expression)
  statements
endfor

The expression is evaluated and should return a list; if it does not, E_TYPE is raised. The statements are then executed once for each element of that list in turn; each time, the given variable is assigned the value of the element in question. For example, consider the following statements:

 
odds = {1, 3, 5, 7, 9};
evens = {};
for n in (odds)
  evens = {@evens, n + 1};
endfor

The value of the variable ‘evens’ after executing these statements is the list

 
{2, 4, 6, 8, 10}

To perform a set of statements once for each integer or object number in a given range, use this syntax:

 
for variable in [expression-1..expression-2]
  statements
endfor

The two expressions are evaluated in turn and should either both return integers or both return object numbers; E_TYPE is raised otherwise. The statements are then executed once for each integer (or object number, as appropriate) greater than or equal to the value of expression-1 and less than or equal to the result of expression-2, in increasing order. Each time, the given variable is assigned the integer or object number in question. For example, consider the following statements:

 
evens = {};
for n in [1..5]
  evens = {@evens, 2 * n};
endfor

The value of the variable ‘evens’ after executing these statements is just as in the previous example: the list

 
{2, 4, 6, 8, 10}

The following loop over object numbers prints out the number and name of every valid object in the database:

 
for o in [#0..max_object()]
  if (valid(o))
    notify(player, tostr(o, ": ", o.name));
  endif
endfor

The final kind of loop in MOO executes a set of statements repeatedly as long as a given condition remains true:

 
while (expression)
  statements
endwhile

The expression is evaluated and, if it returns a true value, the statements are executed; then, execution of the ‘while’ statement begins all over again with the evaluation of the expression. That is, execution alternates between evaluating the expression and executing the statements until the expression returns a false value. The following example code has precisely the same effect as the loop just shown above:

 
evens = {};
n = 1;
while (n <= 5)
  evens = {@evens, 2 * n};
  n = n + 1;
endwhile

Fine point: It is also possible to give a `name' to a ‘while’ loop, using this syntax:

 
while name (expression)
  statements
endwhile

which has precisely the same effect as

 
while (name = expression)
  statements
endwhile

This naming facility is only really useful in conjunction with the ‘break’ and ‘continue’ statements, described in the next section.

With each kind of loop, it is possible that the statements in the body of the loop will never be executed at all. For iteration over lists, this happens when the list returned by the expression is empty. For iteration on integers, it happens when expression-1 returns a larger integer than expression-2. Finally, for the ‘while’ loop, it happens if the expression returns a false value the very first time it is evaluated.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.5 Terminating One or All Iterations of a Loop

Sometimes, it is useful to exit a loop before it finishes all of its iterations. For example, if the loop is used to search for a particular kind of element of a list, then it might make sense to stop looping as soon as the right kind of element is found, even if there are more elements yet to see. The ‘break’ statement is used for this purpose; it has the form

 
break;

or

 
break name;

Each ‘break’ statement indicates a specific surrounding loop; if name is not given, the statement refers to the innermost one. If it is given, name must be the name appearing right after the ‘for’ or ‘while’ keyword of the desired enclosing loop. When the ‘break’ statement is executed, the indicated loop is immediately terminated and executing continues just as if the loop had completed its iterations normally.

MOO also allows you to terminate just the current iteration of a loop, making it immediately go on to the next one, if any. The ‘continue’ statement does this; it has precisely the same forms as the ‘break’ statement:

 
continue;

or

 
continue name;

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.6 Returning a Value from a Verb

The MOO program in a verb is just a sequence of statements. Normally, when the verb is called, those statements are simply executed in order and then the integer 0 is returned as the value of the verb-call expression. Using the ‘return’ statement, one can change this behavior. The ‘return’ statement has one of the following two forms:

 
return;

or

 
return expression;

When it is executed, execution of the current verb is terminated immediately after evaluating the given expression, if any. The verb-call expression that started the execution of this verb then returns either the value of expression or the integer 0, if no expression was provided.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.7 Handling Errors in Statements

Normally, whenever a piece of MOO code raises an error, the entire task is aborted and a message printed to the user. Often, such errors can be anticipated in advance by the programmer and code written to deal with them in a more graceful manner. The try-except statement allows you to do this; the syntax is as follows:

 
try
  statements-0
except variable-1 (codes-1)
  statements-1
except variable-2 (codes-2)
  statements-2
…
endtry

where the variables may be omitted and each codes part is either the keyword ANY or else a comma-separated list of expressions, just like an argument list. As in an argument list, the splicing operator (‘@’) can be used here. There can be anywhere from 1 to 255 except clauses.

First, each codes part is evaluated, yielding a list of error codes that should be caught if they're raised; if a codes is ANY, then it is equivalent to the list of all possible MOO values.

Next, statements-0 is executed; if it doesn't raise an error, then that's all that happens for the entire try-except statement. Otherwise, let E be the error it raises. From top to bottom, E is searched for in the lists resulting from the various codes parts; if it isn't found in any of them, then it continues to be raised, possibly to be caught by some piece of code either surrounding this try-except statement or higher up on the verb-call stack.

If E is found first in codes-i, then variable-i (if provided) is assigned a value containing information about the error being raised and statements-i is executed. The value assigned to variable-i is a list of four elements:

 
{code, message, value, traceback}

where code is E, the error being raised, message and value are as provided by the code that raised the error, and traceback is a list like that returned by the ‘callers()’ function, including line numbers. The traceback list contains entries for every verb from the one that raised the error through the one containing this try-except statement.

Unless otherwise mentioned, all of the built-in errors raised by expressions, statements, and functions provide tostr(code) as message and zero as value.

Here's an example of the use of this kind of statement:

 
try
  result = object:(command)(@arguments);
  player:tell("=> ", toliteral(result));
except v (ANY)
  tb = v[4];
  if (length(tb) == 1)
    player:tell("** Illegal command: ", v[2]);
  else
    top = tb[1];
    tb[1..1] = {};
    player:tell(top[1], ":", top[2], ", line ", top[6], ":",
                v[2]);
    for fr in (tb)
      player:tell("... called from ", fr[1], ":", fr[2],
                  ", line ", fr[6]);
    endfor
    player:tell("(End of traceback)");
  endif
endtry

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.8 Cleaning Up After Errors

Whenever an error is raised, it is usually the case that at least some MOO code gets skipped over and never executed. Sometimes, it's important that a piece of code always be executed, whether or not an error is raised. Use the try-finally statement for these cases; it has the following syntax:

 
try
  statements-1
finally
  statements-2
endtry

First, statements-1 is executed; if it completes without raising an error, returning from this verb, or terminating the current iteration of a surrounding loop (we call these possibilities transferring control), then statements-2 is executed and that's all that happens for the entire try-finally statement.

Otherwise, the process of transferring control is interrupted and statments-2 is executed. If statements-2 itself completes without transferring control, then the interrupted control transfer is resumed just where it left off. If statements-2 does transfer control, then the interrupted transfer is simply forgotten in favor of the new one.

In short, this statement ensures that statements-2 is executed after control leaves statements-1 for whatever reason; it can thus be used to make sure that some piece of cleanup code is run even if statements-1 doesn't simply run normally to completion.

Here's an example:

 
try
  start = time();
  object:(command)(@arguments);
finally
  end = time();
  this:charge_user_for_seconds(player, end - start);
endtry

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.9 Executing Statements at a Later Time

It is sometimes useful to have some sequence of statements execute at a later time, without human intervention. For example, one might implement an object that, when thrown into the air, eventually falls back to the ground; the ‘throw’ verb on that object should arrange to print a message about the object landing on the ground, but the message shouldn't be printed until some number of seconds have passed.

The ‘fork’ statement is intended for just such situations and has the following syntax:

 
fork (expression)
  statements
endfork

The ‘fork’ statement first executes the expression, which must return a integer; call that integer n. It then creates a new MOO task that will, after at least n seconds, execute the statements. When the new task begins, all variables will have the values they had at the time the ‘fork’ statement was executed. The task executing the ‘fork’ statement immediately continues execution. The concept of tasks is discussed in detail in the next section.

By default, there is no limit to the number of tasks any player may fork, but such a limit can be imposed from within the database. See the chapter on server assumptions about the database for details.

Occasionally, one would like to be able to kill a forked task before it even starts; for example, some player might have caught the object that was thrown into the air, so no message should be printed about it hitting the ground. If a variable name is given after the ‘fork’ keyword, like this:

 
fork name (expression)
  statements
endfork

then that variable is assigned the task ID of the newly-created task. The value of this variable is visible both to the task executing the fork statement and to the statements in the newly-created task. This ID can be passed to the kill_task() function to keep the task from running and will be the value of task_id() once the task begins execution.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated by Roger Crew on March, 27 2010 using texi2html 1.78.