[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Expressions are those pieces of MOO code that generate values; for example, the MOO code
3 + 4 |
is an expression that generates (or “has” or “returns”) the value 7. There are many kinds of expressions in MOO, all of them discussed below.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Most kinds of expressions can, under some circumstances, cause an error to be
generated. For example, the expression x / y
will generate the error
E_DIV
if y
is equal to zero. When an expression generates an
error, the behavior of the server is controlled by setting of the ‘d’
(debug) bit on the verb containing that expression. If the ‘d’ bit is not
set, then the error is effectively squelched immediately upon generation; the
error value is simply returned as the value of the expression that generated
it.
Note: This error-squelching 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, both described below, 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] | [ ? ] |
The simplest kind of expression is a literal MOO value, just as described in the section on values at the beginning of this document. For example, the following are all expressions:
17 #893 "This is a character string." E_TYPE {"This", "is", "a", "list", "of", "words"} |
In the case of lists, like the last example above, note that the list expression contains other expressions, several character strings in this case. In general, those expressions can be of any kind at all, not necessarily literal values. For example,
{3 + 4, 3 - 4, 3 * 4} |
is an expression whose value is the list {7, -1, 12}
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As discussed earlier, it is possible to store values in properties on objects; the properties will keep those values forever, or until another value is explicitly put there. Quite often, though, it is useful to have a place to put a value for just a little while. MOO provides local variables for this purpose.
Variables are named places to hold values; you can get and set the value in a given variable as many times as you like. Variables are temporary, though; they only last while a particular verb is running; after it finishes, all of the variables given values there cease to exist and the values are forgotten.
Variables are also “local” to a particular verb; every verb has its own set of them. Thus, the variables set in one verb are not visible to the code of other verbs.
The name for a variable is made up entirely of letters, digits, and the underscore character (‘_’) and does not begin with a digit. The following are all valid variable names:
foo _foo this2that M68000 two_words This_is_a_very_long_multiword_variable_name |
Note that, along with almost everything else in MOO, the case of the letters in variable names is insignificant. For example, these are all names for the same variable:
fubar Fubar FUBAR fUbAr |
A variable name is itself an expression; its value is the value of the named
variable. When a verb begins, almost no variables have values yet; if you try
to use the value of a variable that doesn't have one, the error value
E_VARNF
is raised. (MOO is unlike many other programming languages in
which one must `declare' each variable before using it; MOO has no such
declarations.) The following variables always have values:
INT FLOAT OBJ STR LIST ERR player this caller verb args argstr dobj dobjstr prepstr iobj iobjstr NUM |
The values of some of these variables always start out the same:
INT
an integer, the type code for integers (see the description of the function
typeof()
, below)
NUM
the same as INT
(for historical reasons)
FLOAT
an integer, the type code for floating-point numbers
LIST
an integer, the type code for lists
STR
an integer, the type code for strings
OBJ
an integer, the type code for objects
ERR
an integer, the type code for error values
For others, the general meaning of the value is consistent, though the value itself is different for different situations:
player
an object, the player who typed the command that started the task that involved running this piece of code.
this
an object, the object on which the currently-running verb was found.
caller
an object, the object on which the verb that called the currently-running verb was found. For the first verb called for a given command, ‘caller’ has the same value as ‘player’.
verb
a string, the name by which the currently-running verb was identified.
args
a list, the arguments given to this verb. For the first verb called for a given command, this is a list of strings, the words on the command line.
The rest of the so-called “built-in” variables are only really meaningful for the first verb called for a given command. Their semantics is given in the discussion of command parsing, above.
To change what value is stored in a variable, use an assignment expression:
variable = expression |
For example, to change the variable named ‘x’ to have the value 17, you would write ‘x = 17’ as an expression. An assignment expression does two things:
Thus, the expression
13 + (x = 17) |
changes the value of ‘x’ to be 17 and returns 30.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
All of the usual simple operations on numbers are available to MOO programs:
+ - * / % |
These are, in order, addition, subtraction, multiplication, division, and remainder. In the following table, the expressions on the left have the corresponding values on the right:
5 + 2 ⇒ 7 5 - 2 ⇒ 3 5 * 2 ⇒ 10 5 / 2 ⇒ 2 5.0 / 2.0 ⇒ 2.5 5 % 2 ⇒ 1 5.0 % 2.0 ⇒ 1.0 5 % -2 ⇒ 1 -5 % 2 ⇒ -1 -5 % -2 ⇒ -1 -(5 + 2) ⇒ -7 |
Note that integer division in MOO throws away the remainder and that the result of the remainder operator (‘%’) has the same sign as the left-hand operand. Also, note that ‘-’ can be used without a left-hand operand to negate a numeric expression.
Fine point: Integers and floating-point numbers cannot be mixed in any particular use of these arithmetic operators; unlike some other programming languages, MOO does not automatically coerce integers into floating-point numbers. You can use the
tofloat()
function to perform an explicit conversion.
The ‘+’ operator can also be used to append two strings. The expression
"foo" + "bar" |
has the value
"foobar" |
Unless both operands to an arithmetic operator are numbers of the same kind
(or, for ‘+’, both strings), the error value E_TYPE
is raised. If
the right-hand operand for the division or remainder operators (‘/’ or
‘%’) is zero, the error value E_DIV
is raised.
MOO also supports the exponentiation operation, also known as “raising to a power,” using the ‘^’ operator:
3 ^ 4 ⇒ 81 3 ^ 4.5 error--> E_TYPE 3.5 ^ 4 ⇒ 150.0625 3.5 ^ 4.5 ⇒ 280.741230801382 |
Note that if the first operand is an integer, then the second operand must also be an integer. If the first operand is a floating-point number, then the second operand can be either kind of number. Although it is legal to raise an integer to a negative power, the result is unlikely to be terribly useful.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Any two values can be compared for equality using ‘==’ and ‘!=’. The first of these returns 1 if the two values are equal and 0 otherwise; the second does the reverse:
3 == 4 ⇒ 0 3 != 4 ⇒ 1 3 == 3.0 ⇒ 0 "foo" == "Foo" ⇒ 1 #34 != #34 ⇒ 0 {1, #34, "foo"} == {1, #34, "FoO"} ⇒ 1 E_DIV == E_TYPE ⇒ 0 3 != "foo" ⇒ 1 |
Note that integers and floating-point numbers are never equal to one another, even in the `obvious' cases. Also note that comparison of strings (and list values containing strings) is case-insensitive; that is, it does not distinguish between the upper- and lower-case version of letters. To test two values for case-sensitive equality, use the ‘equal’ function described later.
Warning: It is easy (and very annoying) to confuse the equality-testing operator (‘==’) with the assignment operator (‘=’), leading to nasty, hard-to-find bugs. Don't do this.
Numbers, object numbers, strings, and error values can also be compared for ordering purposes using the following operators:
< <= >= > |
meaning “less than,” “less than or equal,” “greater than or equal,” and “greater than,” respectively. As with the equality operators, these return 1 when their operands are in the appropriate relation and 0 otherwise:
3 < 4 ⇒ 1 3 < 4.0 error--> E_TYPE #34 >= #32 ⇒ 1 "foo" <= "Boo" ⇒ 0 E_DIV > E_TYPE ⇒ 1 |
Note that, as with the equality operators, strings are compared
case-insensitively. To perform a case-sensitive string comparison, use the
‘strcmp’ function described later. Also note that the error values are
ordered as given in the table in the section on values. If the operands to
these four comparison operators are of different types (even integers and
floating-point numbers are considered different types), or if they are lists,
then E_TYPE
is raised.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There is a notion in MOO of true and false values; every value is one or the other. The true values are as follows:
0.0
,
All other values are false:
0.0
and -0.0
,
There are four kinds of expressions and two kinds of statements that depend upon this classification of MOO values. In describing them, I sometimes refer to the truth value of a MOO value; this is just true or false, the category into which that MOO value is classified.
The conditional expression in MOO has the following form:
expression-1 ? expression-2 | expression-3 |
First, expression-1 is evaluated. If it returns a true value, then expression-2 is evaluated and whatever it returns is returned as the value of the conditional expression as a whole. If expression-1 returns a false value, then expression-3 is evaluated instead and its value is used as that of the conditional expression.
1 ? 2 | 3 ⇒ 2 0 ? 2 | 3 ⇒ 3 "foo" ? 17 | {#34} ⇒ 17 |
Note that only one of expression-2 and expression-3 is evaluated, never both.
To negate the truth value of a MOO value, use the ‘!’ operator:
! expression |
If the value of expression is true, ‘!’ returns 0; otherwise, it returns 1:
! "foo" ⇒ 0 ! (3 >= 4) ⇒ 1 |
The negation operator is usually read as “not.”
It is frequently useful to test more than one condition to see if some or all of them are true. MOO provides two operators for this:
expression-1 && expression-2 expression-1 || expression-2 |
These operators are usually read as “and” and “or,” respectively.
The ‘&&’ operator first evaluates expression-1. If it returns a true value, then expression-2 is evaluated and its value becomes the value of the ‘&&’ expression as a whole; otherwise, the value of expression-1 is used as the value of the ‘&&’ expression. Note that expression-2 is only evaluated if expression-1 returns a true value. The ‘&&’ expression is equivalent to the conditional expression
expression-1 ? expression-2 | expression-1 |
except that expression-1 is only evaluated once.
The ‘||’ operator works similarly, except that expression-2 is evaluated only if expression-1 returns a false value. It is equivalent to the conditional expression
expression-1 ? expression-1 | expression-2 |
except that, as with ‘&&’, expression-1 is only evaluated once.
These two operators behave very much like “and” and “or” in English:
1 && 1 ⇒ 1 0 && 1 ⇒ 0 0 && 0 ⇒ 0 1 || 1 ⇒ 1 0 || 1 ⇒ 1 0 || 0 ⇒ 0 17 <= 23 && 23 <= 27 ⇒ 1 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Both strings and lists can be seen as ordered sequences of MOO values. In the
case of strings, each is a sequence of single-character strings; that is, one
can view the string "bar"
as a sequence of the strings "b"
,
"a"
, and "r"
. MOO allows you to refer to the elements of lists
and strings by number, by the index of that element in the list or
string. The first element in a list or string has index 1, the second has
index 2, and so on.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The indexing expression in MOO extracts a specified element from a list or string:
expression-1[expression-2] |
First, expression-1 is evaluated; it must return a list or a string (the
sequence). Then, expression-2 is evaluated and must return an
integer (the index). If either of the expressions returns some other type
of value, E_TYPE
is returned. The index must be between 1 and the
length of the sequence, inclusive; if it is not, then E_RANGE
is raised.
The value of the indexing expression is the index'th element in the sequence.
Anywhere within expression-2, you can use the symbol $
as an
expression returning the length of the value of expression-1.
"fob"[2] ⇒ "o" "fob"[1] ⇒ "f" {#12, #23, #34}[$ - 1] ⇒ #23 |
Note that there are no legal indices for the empty string or list, since there are no integers between 1 and 0 (the length of the empty string or list).
Fine point: The
$
expression actually returns the length of the value of the expression just before the nearest enclosing[…]
indexing or subranging brackets. For example:
"frob"[{3, 2, 4}[$]] ⇒ "b"
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
It often happens that one wants to change just one particular slot of a list or string, which is stored in a variable or a property. This can be done conveniently using an indexed assignment having one of the following forms:
variable[index-expr] = result-expr object-expr.name[index-expr] = result-expr object-expr.(name-expr)[index-expr] = result-expr $name[index-expr] = result-expr |
The first form writes into a variable, and the last three forms write into a
property. The usual errors (E_TYPE
, E_INVIND
, E_PROPNF
and E_PERM
for lack of read/write permission on the property) may be
raised, just as in reading and writing any object property; see the
discussion of object property expressions below for details. Correspondingly,
if variable does not yet have a value (i.e., it has never been assigned
to), E_VARNF
will be raised.
If index-expr is not an integer, or if the value of variable or the
property is not a list or string, E_TYPE
is raised. If
result-expr is a string, but not of length 1, E_INVARG
is
raised. Now suppose index-expr evaluates to an integer k. If
k is outside the range of the list or string (i.e. smaller than 1 or
greater than the length of the list or string), E_RANGE
is raised.
Otherwise, the actual assignment takes place. For lists, the variable or the
property is assigned a new list that is identical to the original one except at
the k-th position, where the new list contains the result of
result-expr instead. For strings, the variable or the property is
assigned a new string that is identical to the original one, except the
k-th character is changed to be result-expr.
The assignment expression itself returns the value of result-expr. For
the following examples, assume that l
initially contains the list
{1, 2, 3}
and that s
initially contains the string "foobar":
l[5] = 3 error--> E_RANGE l["first"] = 4 error--> E_TYPE s[3] = "baz" error--> E_INVARG l[2] = l[2] + 3 ⇒ 5 l ⇒ {1, 5, 3} l[2] = "foo" ⇒ "foo" l ⇒ {1, "foo", 3} s[2] = "u" ⇒ "u" s ⇒ "fuobar" s[$] = "z" ⇒ "z" s ⇒ "fuobaz" |
Note that the $
expression may also be used in indexed assignments with
the same meaning as before.
Fine point: After an indexed assignment, the variable or property contains a new list or string, a copy of the original list in all but the k-th place, where it contains a new value. In programming-language jargon, the original list is not mutated, and there is no aliasing. (Indeed, no MOO value is mutable and no aliasing ever occurs.)
In the list case, indexed assignment can be nested to many levels, to work on
nested lists. Assume that l
initially contains the list
{{1, 2, 3}, {4, 5, 6}, "foo"} |
in the following examples:
l[7] = 4 error--> E_RANGE l[1][8] = 35 error--> E_RANGE l[3][2] = 7 error--> E_TYPE l[1][1][1] = 3 error--> E_TYPE l[2][2] = -l[2][2] ⇒ -5 l ⇒ {{1, 2, 3}, {4, -5, 6}, "foo"} l[2] = "bar" ⇒ "bar" l ⇒ {{1, 2, 3}, "bar", "foo"} l[2][$] = "z" ⇒ "z" l ⇒ {{1, 2, 3}, "baz", "foo"} |
The first two examples raise E_RANGE
because 7 is out of the range of
l
and 8 is out of the range of l[1]
. The next two examples
raise E_TYPE
because l[3]
and l[1][1]
are not lists.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The range expression extracts a specified subsequence from a list or string:
expression-1[expression-2..expression-3] |
The three expressions are evaluated in order. Expression-1 must return a
list or string (the sequence) and the other two expressions must return
integers (the low and high indices, respectively); otherwise,
E_TYPE
is raised. The $
expression can be used in either or both
of expression-2 and expression-3 just as before, meaning the length
of the value of expression-1.
If the low index is greater than the high index, then the empty string or list
is returned, depending on whether the sequence is a string or a list.
Otherwise, both indices must be between 1 and the length of the sequence;
E_RANGE
is raised if they are not. A new list or string is returned
that contains just the elements of the sequence with indices between the low
and high bounds.
"foobar"[2..$] ⇒ "oobar" "foobar"[3..3] ⇒ "o" "foobar"[17..12] ⇒ "" {"one", "two", "three"}[$ - 1..$] ⇒ {"two", "three"} {"one", "two", "three"}[3..3] ⇒ {"three"} {"one", "two", "three"}[17..12] ⇒ {} |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The subrange assigment replaces a specified subsequence of a list or string with a supplied subsequence. The allowed forms are:
variable[start-index-expr..end-index-expr] = result-expr object-expr.name[start-index-expr..end-index-expr] = result-expr object-expr.(name-expr)[start-index-expr..end-index-expr] = result-expr $name[start-index-expr..end-index-expr] = result-expr |
As with indexed assigments, the first form writes into a variable, and the last
three forms write into a property. The same errors (E_TYPE
,
E_INVIND
, E_PROPNF
and E_PERM
for lack of read/write
permission on the property) may be raised. If variable does not yet have
a value (i.e., it has never been assigned to), E_VARNF
will be raised.
As before, the $
expression can be used in either start-index-expr
or end-index-expr, meaning the length of the original value of the
expression just before the […]
part.
If start-index-expr or end-index-expr is not an integer, if the value
of variable or the property is not a list or string, or result-expr
is not the same type as variable or the property, E_TYPE
is
raised. E_RANGE
is raised if end-index-expr is less than zero
or if start-index-expr is greater than the length of the list or string
plus one. Note: the length of result-expr does not need to be the same
as the length of the specified range.
In precise terms, the subrange assigment
v[start..end] = value |
is equivalent to
v = {@v[1..start - 1], @value, @v[end + 1..$]} |
if v is a list and to
v = v[1..start - 1] + value + v[end + 1..$] |
if v is a string.
The assigment expression itself returns the value of result-expr. For
the following examples, assume that l
initially contains the list
{1, 2, 3}
and that s
initially contains the string "foobar":
l[5..6] = {7, 8} error--> E_RANGE l[2..3] = 4 error--> E_TYPE l[#2..3] = {7} error--> E_TYPE s[2..3] = {6} error--> E_TYPE l[2..3] = {6, 7, 8, 9} ⇒ {6, 7, 8, 9} l ⇒ {1, 6, 7, 8, 9} l[2..1] = {10, "foo"} ⇒ {10, "foo"} l ⇒ {1, 10, "foo", 6, 7, 8, 9} l[3][2..$] = "u" ⇒ "u" l ⇒ {1, 10, "fu", 6, 7, 8, 9} s[7..12] = "baz" ⇒ "baz" s ⇒ "foobarbaz" s[1..3] = "fu" ⇒ "fu" s ⇒ "fubarbaz" s[1..0] = "test" ⇒ "test" s ⇒ "testfubarbaz" |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As was mentioned earlier, lists can be constructed by writing a comma-separated sequence of expressions inside curly braces:
{expression-1, expression-2, …, expression-N} |
The resulting list has the value of expression-1 as its first element, that of expression-2 as the second, etc.
{3 < 4, 3 <= 4, 3 >= 4, 3 > 4} ⇒ {1, 1, 0, 0} |
Additionally, one may precede any of these expressions by the splicing
operator, ‘@’. Such an expression must return a list; rather than the
old list itself becoming an element of the new list, all of the elements of
the old list are included in the new list. This concept is easy to
understand, but hard to explain in words, so here are some examples. For
these examples, assume that the variable a
has the value {2, 3,
4}
and that b
has the value {"Foo", "Bar"}
:
{1, a, 5} ⇒ {1, {2, 3, 4}, 5} {1, @a, 5} ⇒ {1, 2, 3, 4, 5} {a, @a} ⇒ {{2, 3, 4}, 2, 3, 4} {@a, @b} ⇒ {2, 3, 4, "Foo", "Bar"} |
If the splicing operator (‘@’) precedes an expression whose value
is not a list, then E_TYPE
is raised as the value of the list
construction as a whole.
The list membership expression tests whether or not a given MOO value is an element of a given list and, if so, with what index:
expression-1 in expression-2 |
Expression-2 must return a list; otherwise, E_TYPE
is raised.
If the value of expression-1 is in that list, then the index of its first
occurrence in the list is returned; otherwise, the ‘in’ expression returns
0.
2 in {5, 8, 2, 3} ⇒ 3 7 in {5, 8, 2, 3} ⇒ 0 "bar" in {"Foo", "Bar", "Baz"} ⇒ 2 |
Note that the list membership operator is case-insensitive in comparing strings, just like the comparison operators. To perform a case-sensitive list membership test, use the ‘is_member’ function described later. Note also that since it returns zero only if the given value is not in the given list, the ‘in’ expression can be used either as a membership test or as an element locator.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
It is often the case in MOO programming that you will want to access the elements of a list individually, with each element stored in a separate variables. This desire arises, for example, at the beginning of almost every MOO verb, since the arguments to all verbs are delivered all bunched together in a single list. In such circumstances, you could write statements like these:
first = args[1]; second = args[2]; if (length(args) > 2) third = args[3]; else third = 0; endif |
This approach gets pretty tedious, both to read and to write, and it's prone to errors if you mistype one of the indices. Also, you often want to check whether or not any extra list elements were present, adding to the tedium.
MOO provides a special kind of assignment expression, called scattering assignment made just for cases such as these. A scattering assignment expression looks like this:
{target, …} = expr |
where each target describes a place to store elements of the list that results from evaluating expr. A target has one of the following forms:
variable
This is the simplest target, just a simple variable; the list element in the corresponding position is assigned to the variable. This is called a required target, since the assignment is required to put one of the list elements into the variable.
?variable
This is called an optional target, since it doesn't always get assigned an element. If there are any list elements left over after all of the required targets have been accounted for (along with all of the other optionals to the left of this one), then this variable is treated like a required one and the list element in the corresponding position is assigned to the variable. If there aren't enough elements to assign one to this target, then no assignment is made to this variable, leaving it with whatever its previous value was.
?variable = default-expr
This is also an optional target, but if there aren't enough list elements available to assign one to this target, the result of evaluating default-expr is assigned to it instead. Thus, default-expr provides a default value for the variable. The default value expressions are evaluated and assigned working from left to right after all of the other assignments have been performed.
@variable
By analogy with the @
syntax in list construction, this variable is
assigned a list of all of the `leftover' list elements in this part of the list
after all of the other targets have been filled in. It is assigned the empty
list if there aren't any elements left over. This is called a rest
target, since it gets the rest of the elements. There may be at most one rest
target in each scattering assignment expression.
If there aren't enough list elements to fill all of the required targets, or if
there are more than enough to fill all of the required and optional targets but
there isn't a rest target to take the leftover ones, then E_ARGS
is
raised.
Here are some examples of how this works. Assume first that the verb
me:foo()
contains the following code:
b = c = e = 17; {a, ?b, ?c = 8, @d, ?e = 9, f} = args; return {a, b, c, d, e, f}; |
Then the following calls return the given values:
me:foo(1) error--> E_ARGS me:foo(1, 2) ⇒ {1, 17, 8, {}, 9, 2} me:foo(1, 2, 3) ⇒ {1, 2, 8, {}, 9, 3} me:foo(1, 2, 3, 4) ⇒ {1, 2, 3, {}, 9, 4} me:foo(1, 2, 3, 4, 5) ⇒ {1, 2, 3, {}, 4, 5} me:foo(1, 2, 3, 4, 5, 6) ⇒ {1, 2, 3, {4}, 5, 6} me:foo(1, 2, 3, 4, 5, 6, 7) ⇒ {1, 2, 3, {4, 5}, 6, 7} me:foo(1, 2, 3, 4, 5, 6, 7, 8) ⇒ {1, 2, 3, {4, 5, 6}, 7, 8} |
Using scattering assignment, the example at the begining of this section could be rewritten more simply, reliably, and readably:
{first, second, ?third = 0} = args; |
It is good MOO programming style to use a scattering assignment at the top of nearly every verb, since it shows so clearly just what kinds of arguments the verb expects.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Usually, one can read the value of a property on an object with a simple expression:
expression.name |
Expression must return an object number; if not, E_TYPE
is
raised. If the object with that number does not exist, E_INVIND
is
raised. Otherwise, if the object does not have a property with that name,
then E_PROPNF
is raised. Otherwise, if the named property is not
readable by the owner of the current verb, then E_PERM
is raised.
Finally, assuming that none of these terrible things happens, the value of the
named property on the given object is returned.
I said “usually” in the paragraph above because that simple expression only works if the name of the property obeys the same rules as for the names of variables (i.e., consists entirely of letters, digits, and underscores, and doesn't begin with a digit). Property names are not restricted to this set, though. Also, it is sometimes useful to be able to figure out what property to read by some computation. For these more general uses, the following syntax is also allowed:
expression-1.(expression-2) |
As before, expression-1 must return an object number. Expression-2
must return a string, the name of the property to be read; E_TYPE
is raised otherwise. Using this syntax, any property can be read,
regardless of its name.
Note that, as with almost everything in MOO, case is not significant in the names of properties. Thus, the following expressions are all equivalent:
foo.bar foo.Bar foo.("bAr") |
The LambdaCore database uses several properties on #0
, the system
object, for various special purposes. For example, the value of
#0.room
is the “generic room” object, #0.exit
is the “generic
exit” object, etc. This allows MOO programs to refer to these useful objects
more easily (and more readably) than using their object numbers directly. To
make this usage even easier and more readable, the expression
$name |
(where name obeys the rules for variable names) is an abbreviation for
#0.name |
Thus, for example, the value $nothing
mentioned earlier is really
#-1
, the value of #0.nothing
.
As with variables, one uses the assignment operator (‘=’) to change the value of a property. For example, the expression
14 + (#27.foo = 17) |
changes the value of the ‘foo’ property of the object numbered 27 to be
17 and then returns 31. Assignments to properties check that the owner of the
current verb has write permission on the given property, raising
E_PERM
otherwise. Read permission is not required.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
MOO provides a large number of useful functions for performing a wide variety of operations; a complete list, giving their names, arguments, and semantics, appears in a separate section later. As an example to give you the idea, there is a function named ‘length’ that returns the length of a given string or list.
The syntax of a call to a function is as follows:
name(expr-1, expr-2, …, expr-N) |
where name is the name of one of the built-in functions. The
expressions between the parentheses, called arguments, are each
evaluated in turn and then given to the named function to use in its
appropriate way. Most functions require that a specific number of arguments
be given; otherwise, E_ARGS
is raised. Most also require that
certain of the arguments have certain specified types (e.g., the
length()
function requires a list or a string as its argument);
E_TYPE
is raised if any argument has the wrong type.
As with list construction, the splicing operator ‘@’ can precede
any argument expression. The value of such an expression must be a
list; E_TYPE
is raised otherwise. The elements of this list
are passed as individual arguments, in place of the list as a whole.
Verbs can also call other verbs, usually using this syntax:
expr-0:name(expr-1, expr-2, …, expr-N) |
Expr-0 must return an object number; E_TYPE
is raised otherwise.
If the object with that number does not exist, E_INVIND
is raised. If
this task is too deeply nested in verbs calling verbs calling verbs, then
E_MAXREC
is raised; the default limit is 50 levels, but this can be
changed from within the database; see the chapter on server assumptions about
the database for details. If neither the object nor any of its ancestors
defines a verb matching the given name, E_VERBNF
is raised.
Otherwise, if none of these nasty things happens, the named verb on the given
object is called; the various built-in variables have the following initial
values in the called verb:
this
an object, the value of expr-0
verb
a string, the name used in calling this verb
args
a list, the values of expr-1, expr-2, etc.
caller
an object, the value of this
in the calling verb
player
an object, the same value as it had initially in the calling verb or, if the calling verb is running with wizard permissions, the same as the current value in the calling verb.
All other built-in variables (argstr
, dobj
, etc.) are initialized
with the same values they have in the calling verb.
As with the discussion of property references above, I said “usually” at the beginning of the previous paragraph because that syntax is only allowed when the name follows the rules for allowed variable names. Also as with property reference, there is a syntax allowing you to compute the name of the verb:
expr-0:(expr-00)(expr-1, expr-2, …, expr-N) |
The expression expr-00 must return a string; E_TYPE
is raised
otherwise.
The splicing operator (‘@’) can be used with verb-call arguments, too, just as with the arguments to built-in functions.
In many databases, a number of important verbs are defined on #0
, the
system object. As with the ‘$foo’ notation for properties on
#0
, the server defines a special syntax for calling verbs on #0
:
$name(expr-1, expr-2, …, expr-N) |
(where name obeys the rules for variable names) is an abbreviation for
#0:name(expr-1, expr-2, …, expr-N) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
It is often useful to be able to catch an error that an expression raises, to keep the error from aborting the whole task, and to keep on running as if the expression had returned some other value normally. The following expression accomplishes this:
` expr-1 ! codes => expr-2 ' |
Note: The open- and close-quotation marks in the previous line are really part of the syntax; you must actually type them as part of your MOO program for this kind of expression.
The 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. The
=> expr-2
part of the error-catching expression is optional.
First, the codes part is evaluated, yielding a list of error codes that
should be caught if they're raised; if codes is ANY
, then it is
equivalent to the list of all possible MOO values.
Next, expr-1 is evaluated. If it evaluates normally, without raising an error, then its value becomes the value of the entire error-catching expression. If evaluating expr-1 results in an error being raised, then call that error E. If E is in the list resulting from evaluating codes, then E is considered caught by this error-catching expression. In such a case, if expr-2 was given, it is evaluated to get the outcome of the entire error-catching expression; if expr-2 was omitted, then E becomes the value of the entire expression. If E is not in the list resulting from codes, then this expression does not catch the error at all and it continues to be raised, possibly to be caught by some piece of code either surrounding this expression or higher up on the verb-call stack.
Here are some examples of the use of this kind of expression:
`x + 1 ! E_TYPE => 0' |
Returns x + 1
if x
is an integer, returns 0
if x
is
not an integer, and raises E_VARNF
if x
doesn't have a value.
`x.y ! E_PROPNF, E_PERM => 17' |
Returns x.y
if that doesn't cause an error, 17
if x
doesn't have a y
property or that property isn't readable, and raises
some other kind of error (like E_INVIND
) if x.y
does.
`1 / 0 ! ANY' |
Returns E_DIV
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As shown in a few examples above, MOO allows you to use parentheses to make it clear how you intend for complex expressions to be grouped. For example, the expression
3 * (4 + 5) |
performs the addition of 4 and 5 before multiplying the result by 3.
If you leave out the parentheses, MOO will figure out how to group the expression according to certain rules. The first of these is that some operators have higher precedence than others; operators with higher precedence will more tightly bind to their operands than those with lower precedence. For example, multiplication has higher precedence than addition; thus, if the parentheses had been left out of the expression in the previous paragraph, MOO would have grouped it as follows:
(3 * 4) + 5 |
The table below gives the relative precedence of all of the MOO operators; operators on higher lines in the table have higher precedence and those on the same line have identical precedence:
! - (without a left operand) ^ * / % + - == != < <= > >= in && || … ? … | … (the conditional expression) = |
Thus, the horrendous expression
x = a < b && c > d + e * f ? w in y | - q - r |
would be grouped as follows:
x = (((a < b) && (c > (d + (e * f)))) ? (w in y) | ((- q) - r)) |
It is best to keep expressions simpler than this and to use parentheses liberally to make your meaning clear to other humans.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] |
This document was generated by Roger Crew on March, 27 2010 using texi2html 1.78.