dgd/
dgd/doc/net/
dgd/src/host/unix/
dgd/src/host/win32/res/
dgd/src/lpc/
dgd/src/parser/
			DGD extension interface

1. Introduction

DGD presents an interface for extending the driver with new kfuns and special
object types.  To enable this interface, DGD should be compiled with the macro
DGD_EXTENSION defined.  The driver will then call an external function at
initialization time:

    void extension_init();

From this function, new kfuns may be added to the driver.  The interface
furthermore allows objects to be marked as `special'.

The extension code must be linked with DGD at compile time.  The include file
`dgd_ext.h' defines the entire interface with the driver, using macros that
all start with the prefix `DGD_'.  Compliant extensions should use these
macros, only.  Some of these macros will evaluate their arguments several
times; to be compatible with future releases, extensions should assume that
all DGD_* macros with arguments may do so.


2. Kernel functions

Kernel functions may be added during the initialization phase by calling the
following function:

    void DGD_EXT_KFUN(DGD_EXTKFUN_T *kf, int n);

where `kf' is an array of DGD_EXTFUN_T structs, and `n' the number of elements
in that array.  DGD_EXT_KFUN() should be called only once, and must not
redeclare any existing kfuns.

The DGD_EXTKFUN_T struct has the following fields:

    char *name;		/* kfun name */
    char *proto;	/* kfun prototype */
    func;		/* kfun function pointer */

Here `func' is a pointer to a C function with the following prototype:

    void func(DGD_FRAME_T f, int nargs, DGD_VALUE_T *retval);

Calls to the kfun with name `name' will be routed to that C function.  The
`f' argument contains the function context, `nargs' is the number of
arguments on the stack, and `retval' is a pointer to the return value.  The
function should not attempt to push or pop arguments; instead, arguments
should be accessed using the relevant DGD_FRAME_* macros listed below, and the
return value, if any, should be stored in the retval value using one of the
DGD_RETVAL_* macros.  The default return value is nil.

Kfuns can call DGD_ERROR() to indicate that an error occurred.  Errors will
interrupt the flow of execution, so DGD_ERROR() is best called before any
LPC values are constructed or changed, including the return value of the
kfun.

Errors can be caught in code enclosed between DGD_ECONTEXT_PUSH() and
DGD_ECONTEXT_POP().  DGD_ECONTEXT_PUSH() creates a new errorcontext, and
returns TRUE if DGD_ERROR() was called from within the enclosed code.
DGD_ECONTEXT_POP() clears the current errorcontext, and should only be called
when no error occurred since DGD_ERROR() also implicitly removes the current
errorcontext.  DGD_ERROR(f, NULL) can be used to pass on the last error to the
surrounding errorcontext.  DGD_ECONTEXT_PUSH() and DGD_ERROR() make use of
setjmp() and longjmp(), respectively.

Note that at present, the extension interface does not define a way to call an
LPC function from a kfun.

    DGD_OBJECT_T DGD_FRAME_OBJECT(DGD_FRAME_T f);	/* current object */
    DGD_DATA_T   DGD_FRAME_DATASPACE(DGD_FRAME_T f);	/* current dataspace */
    DGD_VALUE_T  DGD_FRAME_ARG(DGD_FRAME_T f, int nargs, int n);/* argument n */
    bool	 DGD_FRAME_ATOMIC(DGD_FRAME_T f);	/* atomic? */

    void	 DGD_RETVAL_INT(DGD_VALUE_T *retval, DGD_INT_T num);
    void	 DGD_RETVAL_FLT(DGD_VALUE_T *retval, DGD_FLOAT_T flt);
    void	 DGD_RETVAL_STR(DGD_VALUE_T *retval, DGD_STRING_T str);
    void	 DGD_RETVAL_OBJ(DGD_VALUE_T *retval, DGD_OBJECT_T obj);
    void	 DGD_RETVAL_ARR(DGD_VALUE_T *retval, DGD_ARRAY_T arr);
    void	 DGD_RETVAL_MAP(DGD_VALUE_T *retval, DGD_MAPPING_T map);

    void	 DGD_ERROR(DGD_FRAME_T f, char *format, ...);
    bool	 DGD_ECONTEXT_PUSH(DGD_FRAME_T f);
    void	 DGD_ECONTEXT_POP(DGD_FRAME_T f);

The `proto' field of the DGD_EXTKFUN_T struct is a prototype for the function,
represented as a 0-terminated array of chars.  For example, the LPC prototypes

    string lower_case(string str);
    string concat(string str...);
    int foo(int *a, varargs object **b);
    void bar(void);
    void gnu();

would be represented as

    char lower_case_proto[] = { DGD_TYPE_STRING, DGD_TYPE_STRING, 0 };
    char concat_proto[] = { DGD_TYPE_STRING, DGD_TYPE_STRING,
			    DGD_TYPE_ELLIPSIS, 0 };
    char foo_proto[] = { DGD_TYPE_INT, DGD_TYPE_ARRAY_OF(DGD_TYPE_INT),
			 DGD_TYPE_VARARGS,
			 DGD_TYPE_ARRAY_OF(DGD_TYPE_ARRAY_OF(DGD_TYPE_OBJECT)),
			 0 };
    char bar_proto[] = { DGD_TYPE_VOID, DGD_TYPE_VOID, 0 };
    char gnu_proto[] = { DGD_TYPE_VOID, 0 };

Note that the prototypes of bar() and gnu() are effectively identical, just
as they would be for LPC functions; also note that varargs must be specified
in the new way, among the arguments of the prototype rather than in front of
the return type.

The building blocks for prototypes are:

    DGD_TYPE_VOID
    DGD_TYPE_INT
    DGD_TYPE_FLOAT
    DGD_TYPE_STRING
    DGD_TYPE_OBJECT
    DGD_TYPE_ARRAY_OF(dgd_type)
    DGD_TYPE_MAPPING
    DGD_TYPE_VARARGS
    DGD_TYPE_ELLIPSIS


3. Special objects

DGD defines various types of special objects: user objects, editor objects
and parser objects.  Extensions may define their own additional special
object type.  Special objects have an additional LPC value.

Objects should only be marked as special if they are not special already.
If an object is to be unmarked, and a special LPC value has been set, it
should be reset to DGD_NIL_VALUE first.

    bool DGD_OBJECT_ISSPECIAL(DGD_OBJECT_T obj);	/* special object */
    bool DGD_OBJECT_ISMARKED(DGD_OBJECT_T obj);		/* user-def special */
    void DGD_OBJECT_MARK(DGD_OBJECT_T obj);		/* mark as special */
    void DGD_OBJECT_UNMARK(DGD_OBJECT_T obj);		/* unmark as special */

To retrieve or set the LPC value associated with a special object, the
following functions can be used.

    DGD_VALUE_T     DGD_DATA_GET_VAL(DGD_DATASPACE_T data);
    void	    DGD_DATA_SET_VAL(DGD_DATASPACE_T data, DGD_VALUE_T val);


4. Operations on LPC values

The type of an LPC value can be determined with

    int DGD_TYPEOF(DGD_VALUE_T val);

The types can be:

    DGD_TYPE_NIL
    DGD_TYPE_INT
    DGD_TYPE_FLOAT
    DGD_TYPE_STRING
    DGD_TYPE_OBJECT
    DGD_TYPE_ARRAY
    DGD_TYPE_MAPPING
    DGD_TYPE_LWOBJ

Each type has its own set of macros.

    DGD_NIL_VALUE		/* the nil value */

    DGD_INT_T DGD_INT_GETVAL(DGD_VALUE_T val);
    void      DGD_INT_PUTVAL(DGD_VALUE_T val, DGD_INT_T num);

Floats consist of a 1-bit sign, a 11-bit exponent, and a 36-bit mantissa.
To make a C double out of a sign, exponent and mantissa, use this expression:

	((sign) ? -1 : 1) * ldexp(mantissa, exponent - 36)

To convert in the other direction, use the following code:

	sign = (Cfloatval < 0);
	mantissa = ldexp(frexp(fabs(Cfloatval), &exponent), 37);
	--exponent;

    void	DGD_FLOAT_GETVAL(DGD_VALUE_T val, DGD_FLOAT_T flt);
    void	DGD_FLOAT_PUTVAL(DGD_VALUE_T val, DGD_FLOAT_T flt);
    void	DGD_FLOAT_GET(DGD_FLOAT_T flt, int sign, int exp,
			      int64 mantissa); /* assign to sign/exp/mantissa */
    void	DGD_FLOAT_PUT(DGD_FLOAT_T flt, int sign, int exp,
			      int64 mantissa);

    DGD_STRING_T DGD_STRING_GETVAL(DGD_VALUE_T val);
    void	 DGD_STRING_PUTVAL(DGD_VALUE_T val, DGD_STRING_T str);
    DGD_STRING_T DGD_STRING_NEW(DGD_FRAME_T f, char *text, int len);
    char	*DGD_STRING_TEXT(DGD_STRING_T str);
    unsigned int DGD_STRING_LENGTH(DGD_STRING_T str);

The current object may be stored as an array or mapping element.

    void	 DGD_OBJECT_PUTVAL(DGD_VALUE_T val, DGD_OBJECT_T obj);
    void	 DGD_OBJECT_NAME(DGD_FRAME_T f, char *buffer, DGD_OBJECT_T obj);

Don't modify array elements directly, but use DGD_ARRAY_ASSIGN() to change
their value.

    DGD_ARRAY_T	 DGD_ARRAY_GETVAL(DGD_VALUE_T val);
    void	 DGD_ARRAY_PUTVAL(DGD_VALUE_T val, DGD_ARRAY_T arr);
    DGD_ARRAY_T	 DGD_ARRAY_NEW(DGD_DATASPACE_T data, int size);
    DGD_VALUE_T *DGD_ARRAY_ELTS(DGD_ARRAY_T arr);
    DGD_VALUE_T	 DGD_ARRAY_INDEX(DGD_ARRAY_T arr, int i);
    void	 DGD_ARRAY_ASSIGN(DGD_DATASPACE_T data, DGD_ARRAY_T arr,
				  int index, DGD_VALUE_T val);

The elements of a mapping may be retrieved as a sorted array of alternate
index-value pairs with DGD_MAPPING_ELTS().  Don't modify mapping elements
directly, but ise DGD_MAPPING_ASSIGN() to change their value.

    DGD_MAPPING_T DGD_MAPPING_GETVAL(DGD_VALUE_T val);
    void	  DGD_MAPPING_PUTVAL(DGD_VALUE_T val, DGD_MAPPING_T map);
    DGD_MAPPING_T DGD_MAPPING_NEW(DGD_DATASPACE_T data);
    DGD_VALUE_T  *DGD_MAPPING_ELTS(DGD_MAPPING_T map);
    DGD_VALUE_T   DGD_MAPPING_INDEX(DGD_MAPPING_T map, DGD_VALUE_T index);
    void	  DGD_MAPPING_ASSIGN(DGD_DATASPACE_T data, DGD_MAPPING_T map,
				     DGD_VALUE_T index, DGD_VALUE_T val);


5. Example

The following code implements a lower_case() kfun.

# include "dgd_ext.h"

static void lower_case(DGD_FRAME_T f, int nargs, DGD_VALUE_T *retval)
{
    DGD_VALUE_T val;
    DGD_STRING_T str;
    char *p;
    unsigned int i;

    /* fetch the argument string */
    val = DGD_FRAME_ARG(f, nargs, 0);
    str = DGD_STRING_GETVAL(val);

    /* make a copy */
    str = DGD_STRING_NEW(f, DGD_STRING_TEXT(str), DGD_STRING_LENGTH(str));

    /* turn to lowercase */
    p = DGD_STRING_TEXT(str);
    for (i = DGD_STRING_LENGTH(str); i != 0; --i) {
	if (*p >= 'A' && *p <= 'Z') {
	    *p += 'a' - 'A';
	}
	p++;
    }

    /* put result in return value */
    DGD_RETVAL_STR(retval, str);
}

static char lower_case_proto[] = { DGD_TYPE_STRING, DGD_TYPE_STRING, 0 };
static DGD_EXTKFUN_T kf[1] = {
    "lower_case",
    lower_case_proto,
    &lower_case
};

void extension_init(void)
{
    DGD_EXT_KFUN(kf, 1);
}