In this chapter, I begin by describing in detail the various kinds of data that can appear in a LambdaMOO database and that, therefore, MOO programs can manipulate. In a few places, I refer to the LambdaCore database. This is one particular LambdaMOO database, created every so often by extracting the "core" of the current database for the original LambdaMOO.
Note: The original LambdaMOO resides on the host
lambda.parc.xerox.com
(the numeric address for which is192.216.54.2
), on port 8888. Feel free to drop by! A copy of the most recent release of the LambdaCore database can be obtained by anonymous FTP from hostftp.parc.xerox.com
in the directorypub/MOO
.
There are only a few kinds of values that MOO programs can manipulate:
MOO supports the integers from -2^31 (that is, negative two to the power of 31) up to 2^31 - 1 (one less than two to the power of 31); that's from -2147483648 to 2147483647, enough for most purposes. In MOO programs, integers are written just as you see them here, an optional minus sign followed by a non-empty sequence of decimal digits. In particular, you may not put commas, periods, or spaces in the middle of large integers, as we sometimes do in English and other natural languages (e.g., `2,147,483,647').
Real numbers in MOO are represented as they are in almost all other programming languages, using so-called floating-point numbers. These have certain (large) limits on size and precision that make them useful for a wide range of applications. Floating-point numbers are written with an optional minus sign followed by a non-empty sequence of digits punctuated at some point with a decimal point (`.') and/or followed by a scientific-notation marker (the letter `E' or `e' followed by an optional sign and one or more digits). Here are some examples of floating-point numbers:
325.0 325. 3.25e2 0.325E3 325.E1 .0325e+4 32500e-2
All of these examples mean the same number. The third of these, as an example of scientific notation, should be read "3.25 times 10 to the power of 2".
Fine points: The MOO represents floating-point numbers using the local meaning of the C-language
double
type, which is almost always equivalent to IEEE 754 double precision floating point. If so, then the smallest positive floating-point number is no larger than2.2250738585072014e-308
and the largest floating-point number is1.7976931348623157e+308
.IEEE infinities and NaN values are not allowed in MOO. The error
E_FLOAT
is raised whenever an infinity would otherwise be computed;E_INVARG
is raised whenever a NaN would otherwise arise. The value0.0
is always returned on underflow.
Character strings are arbitrarily-long sequences of normal, ASCII printing characters. When written as values in a program, strings are enclosed in double-quotes, like this:
"This is a character string."
To include a double-quote in the string, precede it with a backslash (`\'), like this:
"His name was \"Leroy\", but nobody ever called him that."
Finally, to include a backslash in a string, double it:
"Some people use backslash ('\\') to mean set difference."
MOO strings may not include special ASCII characters like carriage-return, line-feed, bell, etc. The only non-printing characters allowed are spaces and tabs.
Fine point: There is a special kind of string used for representing the arbitrary bytes used in general, binary input and output. In a binary string, any byte that isn't an ASCII printing character or the space character is represented as the three-character substring "~XX", where XX is the hexadecimal representation of the byte; the input character `~' is represented by the three-character substring "~7E". This special representation is used by the functions
encode_binary()
anddecode_binary()
and by the functionsnotify()
andread()
with network connections that are in binary mode. See the descriptions of theset_connection_option()
,encode_binary()
, anddecode_binary()
functions for more details.
Objects are the backbone of the MOO database and, as such, deserve a great deal of discussion; the entire next section is devoted to them. For now, let it suffice to say that every object has a number, unique to that object. In programs, we write a reference to a particular object by putting a hash mark (`#') followed by the number, like this:
#495
Object numbers are always integers.
There are three special object numbers used for a variety of purposes:
#-1
, #-2
, and #-3
, usually referred to in the
LambdaCore database as $nothing
, $ambiguous_match
, and
$failed_match
, respectively.
Errors are, by far, the least frequently used values in MOO. In the normal case, when a program attempts an operation that is erroneous for some reason (for example, trying to add a number to a character string), the server stops running the program and prints out an error message. However, it is possible for a program to stipulate that such errors should not stop execution; instead, the server should just let the value of the operation be an error value. The program can then test for such a result and take some appropriate kind of recovery action. In programs, error values are written as words beginning with `E_'. The complete list of error values, along with their associated messages, is as follows:
E_NONE No error E_TYPE Type mismatch E_DIV Division by zero E_PERM Permission denied E_PROPNF Property not found E_VERBNF Verb not found E_VARNF Variable not found E_INVIND Invalid indirection E_RECMOVE Recursive move E_MAXREC Too many verb calls E_RANGE Range error E_ARGS Incorrect number of arguments E_NACC Move refused by destination E_INVARG Invalid argument E_QUOTA Resource limit exceeded E_FLOAT Floating-point arithmetic error
The final kind of value in MOO programs is lists. A list is a sequence of arbitrary MOO values, possibly including other lists. In programs, lists are written in mathematical set notation with each of the elements written out in order, separated by commas, the whole enclosed in curly braces (`{' and `}'). For example, a list of the names of the days of the week is written like this:
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
Note that it doesn't matter that we put a line-break in the middle of the list. This is true in general in MOO: anywhere that a space can go, a line-break can go, with the same meaning. The only exception is inside character strings, where line-breaks are not allowed.
Objects are, in a sense, the whole point of the MOO programming language. They are used to represent objects in the virtual reality, like people, rooms, exits, and other concrete things. Because of this, MOO makes a bigger deal out of creating objects than it does for other kinds of value, like integers.
Numbers always exist, in a sense; you have only to write them down in order to operate on them. With objects, it is different. The object with number `#958' does not exist just because you write down its number. An explicit operation, the `create()' function described later, is required to bring an object into existence. Symmetrically, once created, objects continue to exist until they are explicitly destroyed by the `recycle()' function (also described later).
The identifying number associated with an object is unique to that object. It was assigned when the object was created and will never be reused, even if the object is destroyed. Thus, if we create an object and it is assigned the number `#1076', the next object to be created will be assigned `#1077', even if `#1076' is destroyed in the meantime.
Every object is made up of three kinds of pieces that together define its behavior: attributes, properties, and verbs.
There are three fundamental attributes to every object:
The act of creating a character sets the player attribute of an object and
only a wizard (using the function set_player_flag()
) can change that
setting. Only characters have the player bit set to 1.
The parent/child hierarchy is used for classifying objects into general classes
and then sharing behavior among all members of that class. For example, the
LambdaCore database contains an object representing a sort of "generic" room.
All other rooms are descendants (i.e., children or children's children,
or ...) of that one. The generic room defines those pieces of behavior
that are common to all rooms; other rooms specialize that behavior for their
own purposes. The notion of classes and specialization is the very essence of
what is meant by object-oriented programming. Only the functions
create()
, recycle()
, chparent()
, and renumber()
can
change the parent and children attributes.
A property is a named "slot" in an object that can hold an arbitrary MOO value. Every object has eight built-in properties whose values are constrained to be of particular types. In addition, an object can have any number of other properties, none of which have type constraints. The built-in properties are as follows:
name a string, the usual name for this object owner an object, the player who controls access to it location an object, where the object is in virtual reality contents a list of objects, the inverse of `location' programmer a bit, does the object have programmer rights? wizard a bit, does the object have wizard rights? r a bit, is the object publicly readable? w a bit, is the object publicly writable? f a bit, is the object fertile?
The `name' property is used to identify the object in various printed messages. It can only be set by a wizard or by the owner of the object. For player objects, the `name' property can only be set by a wizard; this allows the wizards, for example, to check that no two players have the same name.
The `owner' identifies the object that has owner rights to this object, allowing them, for example, to change the `name' property. Only a wizard can change the value of this property.
The `location' and `contents' properties describe a hierarchy of
object containment in the virtual reality. Most objects are located
"inside" some other object and that other object is the value of the
`location' property. The `contents' property is a list of those
objects for which this object is their location. In order to maintain the
consistency of these properties, only the move()
function is able to
change them.
The `wizard' and `programmer' bits are only applicable to characters, objects representing players. They control permission to use certain facilities in the server. They may only be set by a wizard.
The `r' bit controls whether or not players other than the owner of this object can obtain a list of the properties or verbs in the object. Symmetrically, the `w' bit controls whether or not non-owners can add or delete properties and/or verbs on this object. The `r' and `w' bits can only be set by a wizard or by the owner of the object.
The `f' bit specifies whether or not this object is fertile, whether
or not players other than the owner of this object can create new objects with
this one as the parent. It also controls whether or not non-owners can use the
chparent()
built-in function to make this object the parent of an
existing object. The `f' bit can only be set by a wizard or by the owner
of the object.
All of the built-in properties on any object can, by default, be read by any player. It is possible, however, to override this behavior from within the database, making any of these properties readable only by wizards. See the chapter on server assumptions about the database for details.
As mentioned above, it is possible, and very useful, for objects to have other properties aside from the built-in ones. These can come from two sources.
First, an object has a property corresponding to every property in its parent object. To use the jargon of object-oriented programming, this is a kind of inheritance. If some object has a property named `foo', then so will all of its children and thus its children's children, and so on.
Second, an object may have a new property defined only on itself and its descendants. For example, an object representing a rock might have properties indicating its weight, chemical composition, and/or pointiness, depending upon the uses to which the rock was to be put in the virtual reality.
Every defined property (as opposed to those that are built-in) has an owner and a set of permissions for non-owners. The owner of the property can get and set the property's value and can change the non-owner permissions. Only a wizard can change the owner of a property.
The initial owner of a property is the player who added it; this is usually, but not always, the player who owns the object to which the property was added. This is because properties can only be added by the object owner or a wizard, unless the object is publicly writable (i.e., its `w' property is 1), which is rare. Thus, the owner of an object may not necessarily be the owner of every (or even any) property on that object.
The permissions on properties are drawn from this set: `r' (read), `w' (write), and `c' (change ownership in descendants). Read permission lets non-owners get the value of the property and, of course, write permission lets them set that value. The `c' permission bit is a little more complicated.
Recall that every object has all of the properties that its parent does and perhaps some more. Ordinarily, when a child object inherits a property from its parent, the owner of the child becomes the owner of that property. This is because the `c' permission bit is "on" by default. If the `c' bit is not on, then the inherited property has the same owner in the child as it does in the parent.
As an example of where this can be useful, the LambdaCore database ensures
that every player has a `password' property containing the encrypted
version of the player's connection password. For security reasons, we don't
want other players to be able to see even the encrypted version of the
password, so we turn off the `r' permission bit. To ensure that the
password is only set in a consistent way (i.e., to the encrypted version of a
player's password), we don't want to let anyone but a wizard change the
property. Thus, in the parent object for all players, we made a wizard the
owner of the password property and set the permissions to the empty string,
""
. That is, non-owners cannot read or write the property and, because
the `c' bit is not set, the wizard who owns the property on the parent
class also owns it on all of the descendants of that class.
Another, perhaps more down-to-earth example arose when a character named Ford started building objects he called "radios" and another character, yduJ, wanted to own one. Ford kindly made the generic radio object fertile, allowing yduJ to create a child object of it, her own radio. Radios had a property called `channel' that identified something corresponding to the frequency to which the radio was tuned. Ford had written nice programs on radios (verbs, discussed below) for turning the channel selector on the front of the radio, which would make a corresponding change in the value of the `channel' property. However, whenever anyone tried to turn the channel selector on yduJ's radio, they got a permissions error. The problem concerned the ownership of the `channel' property.
As I explain later, programs run with the permissions of their author. So, in this case, Ford's nice verb for setting the channel ran with his permissions. But, since the `channel' property in the generic radio had the `c' permission bit set, the `channel' property on yduJ's radio was owned by her. Ford didn't have permission to change it! The fix was simple. Ford changed the permissions on the `channel' property of the generic radio to be just `r', without the `c' bit, and yduJ made a new radio. This time, when yduJ's radio inherited the `channel' property, yduJ did not inherit ownership of it; Ford remained the owner. Now the radio worked properly, because Ford's verb had permission to change the channel.
The final kind of piece making up an object is verbs. A verb is a named MOO program that is associated with a particular object. Most verbs implement commands that a player might type; for example, in the LambdaCore database, there is a verb on all objects representing containers that implements commands of the form `put object in container'. It is also possible for MOO programs to invoke the verbs defined on objects. Some verbs, in fact, are designed to be used only from inside MOO code; they do not correspond to any particular player command at all. Thus, verbs in MOO are like the `procedures' or `methods' found in some other programming languages.
As with properties, every verb has an owner and a set of permission bits. The owner of a verb can change its program, its permission bits, and its argument specifiers (discussed below). Only a wizard can change the owner of a verb. The owner of a verb also determines the permissions with which that verb runs; that is, the program in a verb can do whatever operations the owner of that verb is allowed to do and no others. Thus, for example, a verb owned by a wizard must be written very carefully, since wizards are allowed to do just about anything.
The permission bits on verbs are drawn from this set: `r' (read), `w' (write), `x' (execute), and `d' (debug). Read permission lets non-owners see the program for a verb and, symmetrically, write permission lets them change that program. The other two bits are not, properly speaking, permission bits at all; they have a universal effect, covering both the owner and non-owners.
The execute bit determines whether or not the verb can be invoked from within a MOO program (as opposed to from the command line, like the `put' verb on containers). If the `x' bit is not set, the verb cannot be called from inside a program. The `x' bit is usually set.
The setting of the debug bit determines what happens when the verb's program does something erroneous, like subtracting a number from a character string. If the `d' bit is set, then the server raises an error value; such raised errors can be caught by certain other pieces of MOO code. If the error is not caught, however, the server aborts execution of the command and, by default, prints an error message on the terminal of the player whose command is being executed. (See the chapter on server assumptions about the database for details on how uncaught errors are handled.) If the `d' bit is not set, then no error is raised, no message is printed, and the command is not aborted; instead the error value is returned as the result of the erroneous operation.
Note: the `d' bit exists only for historical reasons; it used to be the only way for MOO code to catch and handle errors. With the introduction of the
try
-except
statement and the error-catching expression, the `d' bit is no longer useful. All new verbs should have the `d' bit set, using the newer facilities for error handling if desired. Over time, old verbs written assuming the `d' bit would not be set should be changed to use the new facilities instead.
In addition to an owner and some permission bits, every verb has three `argument specifiers', one each for the direct object, the preposition, and the indirect object. The direct and indirect specifiers are each drawn from this set: `this', `any', or `none'. The preposition specifier is `none', `any', or one of the items in this list:
with/using at/to in front of in/inside/into on top of/on/onto/upon out of/from inside/from over through under/underneath/beneath behind beside for/about is as off/off of
The argument specifiers are used in the process of parsing commands, described in the next chapter.
Go to the first, previous, next, last section, table of contents.