home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
RiscOS
/
APP
/
DEVS
/
LISP
/
FOOLS.ZIP
/
Fools
/
doc
/
primitives
< prev
Wrap
Text File
|
1991-10-31
|
4KB
|
127 lines
writing primitives
A primitive in fools' lisp is essentially a wrapper for a C function.
The arguments applied to the primitive are passed to the function in
an array along with the argument count.
The functions must be declared properly. A macro, DEFINE(func) in prim.h,
expands into the proper declaration. The function's formal parameters are
argv, the argument array, and argc, the argument count. You may not modify
the elements of argv. When argc is zero, argv is garbage.
The function newPrim will create a lisp object to representing the primitive:
Obj newPrim(alloc, name, cfunc, env, opts, args)
F_OBJ alloc;
char *name;
Obj (*cfunc)(), env;
int opts, args;
alloc is either gcNew or gcTemp (see below)
name is the name to which the primitive will be bound
cfunc is the pointer to the C function of the primitive
env is the environment where the primitive will be defined (usually
GlobalEnv or UserEnv)
opts is either 0 or OPTARG (for a variable number of arguments)
args is the number of arguments expected (or if opts is OPTARG, the
minimum number)
There are two object allocators, gcNew and gcTemp.
gcNew returns an object with a reference count of zero, so the
object's reference count must be maintained manually (or it might never
get freed). When it is possible to add an actual reference to a newly
created object, gcNew is appropriate. Primitives, for example, use
gcNew to allocate objects that they return. By convention, the caller
of a primitive will add a real reference to whatever the primitive
returns.
gcTemp allocates objects that are disposed of automatically. Because
of the way errors are handled and for ease of use, most objects are
allocated by this routine. gcTemp is not used everywhere because of
the extra overhead for for automatically freeing them. gcBegin and
gcEnd is a block structured-like mechanism for managing objects.
Objects allocated after a gcBegin call are freed by the matching gcEnd
if they did not gain an additional reference count in the meantime.
The begin/end pairs can nest but not overlap.
Before the C function is called, the number of arguments passed to the
primitive is checked. The function will never be called with the wrong
number of arguments unless it accepts a variable number (opt = OPTARG).
In this case, the interpreter only checks for a minimum number of
arguments. The primitive ought to check for excess arguments itself.
The argc parameter is unnecessary except for the variable argument
primitives.
Examples:
/* (list ...) Return a list of the args. */
DEFINE(primList)
{
Obj list;
list = NilSymb;
argv += argc;
while (--argc >= 0)
list = newPair(gcNew, *(--argv), list);
return list;
}
/* (set-car! pair val) Replace the car of pair with val and return val. */
DEFINE(primSetCar)
{
Obj res = argv[1];
if (!objIsClass(argv[0], Pair))
errorPrint(BadClass, "%O is not a pair", argv[0]);
objSetCdr(argv[0], res);
return res;
}
/* (square x) Return the square of x */
DEFINE(primSquare)
{
double num;
if (!objIsClass(argv[0], Number));
errorPrint(BadClass, "%O is not a number", argv[0]);
num = objNum(argv[0]);
return newNumber(gcNew, num * num);
}
/* Create the primitives. */
void primInit()
{
...
newPrim(gcNew, "list", primList, GlobalEnv, OPTARG, 0);
newPrim(gcNew, "set-car!", primSetCdr, GlobalEnv, 0, 2);
newPrim(gcNew, "square", primSquare, GlobalEnv, 0, 2);
...
}
primList is called with argc equal to 0 and garbage for argv when
(list) is evaluated. (list a b c) results in argc set to 3 and argv[0]
through argv[2] filled with the symbol objects a, b, and c.
Although set-car! does not have to return anything in particular, many
implementations return the previous value of the pair. This requires
a direct manipulation of the reference counts:
DEFINE(primSetCar)
{
Obj prev;
if (!objIsClass(argv[0], Pair))
errorPrint(BadClass, "%O is not a pair", argv[0]);
prev = objCar(argv[0]);
objLink(argv[1]);
DATA(argv[0], car, pairInst) = argv[1];
--DATA(prev, rc, basicInst);
return prev;
}
The reference to the original car of pair, due to the pair itself, must
be removed without triggering the car's automatic destruction if that
was the only reference.