lpmoo-1.2/etc/
lpmoo-1.2/mudlib/
lpmoo-1.2/mudlib/etc/
lpmoo-1.2/mudlib/include/
lpmoo-1.2/mudlib/include/moo/
lpmoo-1.2/mudlib/lpc/
lpmoo-1.2/mudlib/std/auto/
lpmoo-1.2/mudlib/std/bfuns/
How to write new "built-in" functions for LPMOO
===============================================

The addition of new built-in functions is discouraged. Instead, consider
writing an LPC module to be called with the existing MOO->LPC mechanism. See
the file ./calling-lpc for more details.

Adding new built-in functions (to be available to programmers) is fairly
simple. It requires programming the new functionality in LPC, which should not
be difficult for any seasoned C or MOO programmer.

To add a new function, edit /std/bfuns/extrafuns.c (note that the `mudlib'
directory in the LPMOO distribution is always treated as the root).

Begin writing the function like this:

# ifdef FUNCDEF
FUNCDEF(0, "function_name", minargs, maxargs)
# else

/*
 * NAME:	bfun->function_name()
 * DESCRIPTION:	what this function does
 */
varargs
MOOVAL b_function_name(mixed *info, MOOVAL arg1, MOOVAL etc...)
{
  ...
  return value;
}
# endif

Replace `minargs' and `maxargs' with the number of arguments this function
expects to receive. You may use -1 for `maxargs' to indicate only a
minimum.

If your function will accept a variable number of arguments, you should
declare the function exactly as above (with MOOVAL etc... to represent all
optional arguments). Otherwise, you should name all arguments explicitly.

If you declare optional arguments as above, the extra arguments actually
passed to your function will be accumulated into the array `etc', so to access
them you would reference etc[0], etc[1], and so forth. The number of extra
arguments can be found with sizeof(etc).

Before using any incoming arguments, you should assert their types:

  ASSERT(arg, type);

where `arg' is the argument to test, and `type' is one of NUM, STR, OBJ, ERR,
LST, FLT, or TBL. This will cause an error to be raised if the indicated
argument is not of the indicated type. If an argument can be of any type, you
can determine the type with TYPEOF(arg). This yields an integer value, which
you can match with the macros T_NUM, T_STR, T_OBJ, and so forth. If you just
want to know if a value is of a specific type, you can test instead with the
macros STRP(), NUMP(), OBJP(), and so forth. (The latter method is much more
efficient than using TYPEOF().)

To access the value of an argument, you must first already have determined
what type the argument is. You can then access the value as follows:

  Expression	Resulting LPC Type
  ==========	==================
  NUMVAL(x)	int
  STRVAL(x)	string
  OBJVAL(x)	int
  ERRVAL(x)	int
  LSTVAL(x)	MOOVAL *
  FLTVAL(x)	float
  TBLVAL(x)	mapping
  BUFVAL(x)	string

You can declare LPC variables in your function according to this table. You
can also declare a variable of type `MOOVAL' to hold any arbitrary MOO
value. (The * suffix above for lists denotes an LPC array of arbitrary MOO
values.)

There is an additional type used internally by LPMOO to indicate various
status conditions. The type is T_STW, and you can test for it with STWP(x).

MOO objects are represented in LPC by DGD objects. The LPC datatype is
`object', but to translate an object argument (which is represented by an
integer) into a DGD object, you must do the following:

  object ob;
  ...
  GET_VALID_OBJ(ob, OBJVAL(arg));

`ob' will now be a "pointer" to the appropriate DGD object, or else an error
will have been raised if the object was not valid.

MOO tables have a particular structure in LPC that must be adhered to; the
following macros are used to manipulate tables:

  TNEW()			returns a new, empty table
  TLOOKUP(table, key)		returns the value associated with `key' in
				  `table', or STW(0) if `key' is not bound
  TINSERT(table, key, value)	inserts {key ~ value} into `table', and
				  returns a pointer to a two-element array
				  in the table containing ({ key, value })
  TDELETE(table, key)		deletes `key' and its associated value from
				  `table' (returns nothing)
  TMERGE(table1, table2)	merges all key/value pairs from `table2'
				  into `table1'
  TCOMPARE(table1, table2)	returns 1 iff `table1' and `table2' contain
				  precisely the same key/value pairs
  TKEYS(table)			returns the keys in `table' as an array
  TVALUES(table)		returns the values in `table' as an array

These functions will mutate the given table argument; to preserve
immutability, you must make a copy of the table before passing them to the
macro. For example: TDELETE(table = table + TNEW(), STR("foo"))

Your function must return a value before completing. Values should be
packaged back from their LPC representation into the various MOO types:

  Expression and Arg	Example				MOO Result
  ==================	===========================	===========
  NUM(int)		NUM(-24)			-24
  STR(string)		STR("something")		"something"
  OBJ(int)		OBJ(99)				#99
  ERR(int)		ERR(E_RANGE)			E_RANGE
  LST(MOOVAL *)		LST( ({ NUM(3), OBJ(7) }) )	{3, #7}
  FLT(float)		FLT(12.34)			12.34
  TBL(mapping)		TBL(TNEW())			{~}
  BUF(string)		BUF("Abc\n")			[65, 98, 99, 10]

To return a value, use:

  return value;

If at any time you need to raise an error, use this:

  return RAISE(error);

where `error' is one of the standard MOO errors, E_PERM, E_TYPE, and so forth.
Note this is different from simply returning an error value!

You may wish to examine the file /std/bfuns/moofuns.c for examples of how all
of the LambdaMOO builtins were implemented.

Once you are finished making additions to /std/bfuns/extrafuns.c, you must
recompile DGD to incorporate those changes. The LPC code will be translated
into C and linked with the rest of the server. To bring up your database with
the new version of the server, you must first make a text dump with
dump_database(1) and bootstrap from the resulting db file. (Instructions
for this are in the `checkpoints' doc file.)

Note that introducing dependencies on special builtin functions in your
database means that you will be unable to use your database with a server that
does not implement these builtins! This is why adding new builtins is
discouraged, especially if the same functionality can be written in LPC and
made accessible through the MOO->LPC interface.