lisp(3) Geometry Center (Oct 22 1992) lisp(3)
NAME
geomview lisp interpreter
NOTE
This document describes the geomview 1.3 lisp interpreter.
This version is incompatible with previous versions in
several ways. Since the previous one was used mostly just
by Geometry Center staff, I am not going to write a document
detailing the changes. The geomview lisp interpreter is not
very well documented in general because I am strongly
considering phasing it out and replacing it with a real lisp
interpreter in a future version of geomview. If you have
any questions about the current version or how to convert
programs from an older version please contact me directly [
mbp@geom.umn.edu ].
SYNOPSIS
#include "lisp.h"
void LInit();
Lake * LakeDefine(FILE *streamin, FILE *streamout, void *river);
void LakeFree(Lake *lake);
LObject * LNew(LType *type, LCell *cell);
LObject * LRefIncr(LObject *obj);
void LRefDecr(LObject *obj);
void LWrite(FILE *fp, LObject *obj);
void LFree(LObject *obj);
LObject * LCopy(LObject *obj);
LObject * LSexpr(Lake *lake);
LObject * LEval(LObject *obj);
LObject * LEvalSexpr(Lake *lake);
LList * LListNew();
LList * LListAppend(LList *list, LObject *obj);
void LListFree(LList *list);
LList * LListCopy(LList *list);
LObject * LListEntry(LList *list, int n);
int LListLength(LList *list);
int LParseArgs(char *name, Lake *lake, LList *args, ...);
int LDefun(char *name, LObjectFunc func, char *help);
void LListWrite(FILE *fp, LList *list);
LInterest * LInterestList(char *funcname);
LObject * LEvalFunc(char *name, ...);
int LArgClassValid(LType *type);
void LHelpDef(char *key, char *message);
LDEFINE(name, ltype, doc)
LDECLARE((name, LBEGIN,
...,
LEND));
Page 1 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
DESCRIPTION
Geomview contains a minimal lisp interpreter for parsing and
evaluating commands. This lisp interpreter is part of the
"-loogutil" library and thus any program which links with
this library may use the interpreter. This provides a
simple but powerful way to build up a command language.
This manual page assumes that you are familiar with the
syntax of lisp. The first part describes the basics of
using the interpreter. Some gory details that don't concern
most users then follow.
The main steps in using the lisp interpreter are
1. call Linit() to initialize the interpreter
2. make calls to LDefun(), one for each lisp function you want the
interpreter to know about
3. define the "i/o lake"
4. parse input with calls to LSexpr() and evaluate the resulting
lisp objects with LEval() (or use LEvalSexpr() to combine both steps).
For example the following code defines a single function "f"
and executes commands from standard input:
#include "lisp.h"
Lake *lake;
LObject *obj, *val;
LInit();
LDefun("f", f, NULL);
lake = LakeDefine(stdin, stdout, NULL);
while (!feof(stdin)) {
obj = LSexpr(lake);
val = LEval(obj);
LFree(obj);
LFree(val);
}
The second argument to LDefun() is a function pointer;
LDefun() sets up a correspondence between the string "f" and
the function f, which is assumed to have been previously
declared. The section FUNCTION DEFINITIONS below gives the
expected call syntax and behavior of such functions. (The
third argument to LDefun() is a pointer to a string which
documents the function and may be NULL if you don't care
about documentation.) LakeDefine() defines an i/o lake;
this is a generalization of the notion of an i/o stream.
Most programs don't need to use the generalization, though,
and can simply pass FILE pointers as LakeDefine()'s first
two arguments and NULL as the third one. The section LAKES
below gives the details for those who are interested.
LSexpr() [which stands for Lisp Symbolic EXPRession] parses
Page 2 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
a single lisp expression from the lake, returning a lisp
object which represents that expression. The lisp object is
returned as an LObject pointer, which points to an opaque
structure containing a representation of the expression.
LEval() then evaluates the object; it is during the call to
LEval() that the action of the expression takes place. Note
that the last two lines of code in this example could have
been replaced by the single line LEval(LSexpr(lake)) or,
more efficiently, by LEvalSexpr(lake).
FUNCTION DEFINITIONS
The functions defined by calls to LDefun() are expected to
have a certain call syntax; LEval() calls them when it
encounters a call to the lisp function named with the
corresponding string. The macro LDEFINE is provided for
declaring them. For example:
LDEFINE(f, LSTRING, "(f a b) returns a string representing the0um of the integer a with the floating point number b.")
{
int a;
float b;
char buf[20], *s;
LDECLARE(("f", LBEGIN,
LINT, &a,
LFLOAT, &b,
LEND));
sprintf(buf,"%f",a+b);
s = strdup(buf);
return LNew(LSTRING, &s);
}
The important things about this function are:
1. It is declared with the LDEFINE macro, the general syntax
of which is LDEFINE(name, type, helpstr). name should be
a valid C identifer and will be used to construct the actual
name of the C function by prepending an 'L' and the name of
the help string by prepending an 'H'. type should be a lisp
object type identifier (see below) and determines the type
of object that the function returns. helpstr is a documentation
string.
2. The use of the LDECLARE macro. More about this below.
3. It returns an LObject *. All lisp functions must actually
return a value. If you don't care what value they return
you can return one of the pre-defined values Lnil or Lt
(and specify LVOID as the type in the LDEFINE header).
This particular example is a function which takes two
Page 3 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
arguments, an int and a float, and returns a string object
representing their sum. A lisp call to this function might
look like "(f 1 3.4)".
The LDECLARE macro, defined in lisp.h, sets up the
correspondence between variables in the C code and arguments
in the lisp call to the function. Note that the arguments
to LDECLARE are delimited by *two* pairs of parentheses
(this is because C does not allow macros with a variable
number of arguments; LDECLARE thus actually takes one
argument which is a parenthesized list of an arbitrary
number of items). The general usage of LDECLARE is
LDECLARE(( name, LBEGIN,
<argspec>,
...,
LEND ));
where name is the name of the function (as specified to
LDefun()). <argspec> is an argument specification, which in
general consists of a lisp type identifier followed by an
address. The identifier indicates the data type of the
argument. The builtin type identifiers are LINT (integer),
LFLOAT (float), LSTRING (string), LLOBJECT (lisp object),
and LLIST (lisp list). Applications may define additional
types whose identifiers may also be used here; see the
section CUSTOM LISP TYPES below for details. There may be
any number of <argspec>'s; the last must be followed by the
special keyword LEND.
STOP HERE
Most users of the lisp interpreter can stop reading this man
page here. What follows is only used in advanced
situations.
EVALUATION OF FUNCTION ARGUMENTS
Normally the lisp interpreter evaluates function arguments
before passing them to the function; to prevent this
evaluation from happening you can insert the special token
LHOLD in an LDECLARE argument specification before the type
keyword. For example
LHOLD, LLIST, &list,
specifies an unevalutated list argument. This feature is
really useful only for LLIST, LLOBJECT, and array types (see
below) since the other types evalutate to themselves.
ARRAYS
Page 4 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
In general an <argspec> in the LDECLARE call consists of a
keyword followed by the address of a scalar data type.
Since it is relatively common to use a lisp list to
represent an array of values, however, the special <argspec>
keyword LARRAY is provided for dealing with them. It has a
different syntax: it should be followed by a lisp type
identifier which specifies the type of the elements of the
array and then by two addresses --- the address of an array
and the address of an integer count. Upon entry to LDECLARE
the count specifies how many elements may be written into
the array. LDECLARE then modifies this number to indicate
the number of entries actually parsed. For example:
LDEFINE(myfunc, ...)
{
float f[2];
int fn = 2;
LDECLARE(("myfunc", LEBGIN
LHOLD, LARRAY, f, &fn,
LEND));
/* at this point the value of fn has been modified to
be the number of entries actually appearing in the
list argument; and this number of values have been
written into the array f. */
...
}
defines a function "myfunc" which takes a list of up to 2
floats as its only argument. Valid calls to "myfunc" would
be "(myfunc ())", "(myfunc (7))", and "(myfunc (7 8))".
Note the use of LHOLD; this is necessary because otherwise
the lisp system would attempt to evaluate the list as a
function call before passing it off to myfunc.
OPTIONAL ARGUMENTS
Normally the lisp interpreter will generate (to stderr) a
reasonable error message if a function is called with fewer
arguments than were specified in LDECLARE. Some functions,
however, may have some arguments that are optional. You can
define a function which takes optional arguments by putting
the keyword LOPTIONAL after the last required argument in
the LDECLARE call. Any arguments specified in the list
after that are considered optional; the interpreter doesn't
complain if they are not supplied. Note that all optional
arguments must come after all required arguments.
Normally excess arguments also elicit an error message. The
LREST keyword allows control over this situation. If LREST
is followed by a pointer to an LList * variable, then
trailing arguments are parsed, evaluated (unless LHOLD was
Page 5 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
used), and the list of them is stored in the given variable.
(Note that the value is an LList, not an LObject of type
LLIST -- if there are no excess arguments, the value is
NULL, not an empty LLIST.) If LREST is followed by a NULL
pointer, excess arguments are silently ignored. LREST might
be useful when a function's argument types are not known.
It's not necessary to specify LEND after LREST.
LISP OBJECTS
The basic data type of the lisp interpreter is the lisp
object; it is represented by an LObject pointer, which
points to an opaque data structure. The functions for
manipulating lisp objects (i.e. the object's methods) are:
LNew(): creates a new lisp object of the given type with
the given value. The "type" argument is one of the
values LSTRING or LLIST, or a type pointer defining
a custom object type (see CUSTOM OBJECT TYPES
below).
LRefIncr(): increments the reference count of a lisp
object. The lisp interpreter uses the convention
that when a procedure returns a lisp object, the
caller owns the object and thus has responsibility
for freeing it. LRefIncr() can be used to increment
the reference count of an existing object about to
be returned. New objects created by LNew() have
their reference count initialized to 1 and hence do
not need to be LRefIncr()'ed.
LRefDecr(): decrements the reference count of a lisp
object. This should probably not be called by
application programs; it is used internally.
LWrite(): writes a formatted string representation of a
lisp object to a stream.
LFree(): free the space assoicated with a lisp object
LCopy(): construct a copy of a lisp object
CUSTOM OBJECT TYPES
In addition to the predefined lisp object types you may
define your own custom types. This is done by constructing
a structure containing various function pointers for
manipulating objects of your new type. The address of this
structure is then the type identifier for this type and may
be used in LDECLARE <argspec>'s and in LNew() calls. (The
type names LINT, LSTRING and the other builtin types are
actually pointers to predefined structures.) The structure
is of type LType as defined in lisp.h:
struct LType {
/* name of type */
Page 6 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
char *name;
/* size of corresponding C type */
int size;
/* extract cell value from obj */
int (*fromobj)(/* LObject *obj, void *x */);
/* create a new LObject of this type */
LObject *(*toobj)(/* void *x */);
/* free a cell of this type */
void (*free)(/* void *x */);
/* write a cell value to a stream */
void (*write)(/* FILE *fp, void *x */);
/* test equality of two cells of this type */
int (*match)(/* void *a, void *b */);
/* pull a cell value from a va_list */
void (*pull)(/* va_list *a_list, void *x */);
/* parse an object of this type */
LObject *(*parse)(/* Lake *lake */);
/* magic number; always set to LTypeMagic */
int magic;
};
The void * pointers in the above point to objects of the
type you are defining. For examples of how to define new
types see the code in lisp.c that defines the string and
list types. See also the file TYPES.DOC in the lisp source
code directory for further details.
LISTS
The LList pointer is used to refer to objects of type LLIST,
which implement a linked list. The operations on these
objects are LListNew(), LListLength(), LListEntry(),
LListAppend(), LListCopy(), and LListFree(). These are
mostly used internally by the lisp system but are available
for outside use. Maybe I'll write more documentation for
them later if it seems necessary.
LAKES The Lake structure is a
It contains three members: an input FILE pointer
("streamin"), an output FILE pointer ("streamout"), and an
arbitrary pointer ("river"). The input FILE pointer is
Page 7 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
required; the lisp interpreter assumes that every lake has a
valid input file pointer. The output FILE pointer is
required if you do any operations that result in the
intepreter producing any output. The third pointer may
point to whatever you want. The lisp interpreter itself
does not directly refer to this pointer. It may be used by
the parser that you supply when defining a new lisp object
type.
The term "Lake" is supposed to connote something more
general than a stream; it also seemed particularly
appropriate since this interpreter was written in the City
of Lakes.
HIDDEN LAKE ARGUMENTS AND OTHER WET
This section is X rated. Don't read it unless you are
really serious.
The lisp interpreter works by first parsing (LSexpr()) an
expression then evaluating it (LEval()). The LDECLARE macro
is a mechanism which allows both the syntax (for parsing)
and the semantics (for evaluation) of an expression to be
specified in the same convenient place --- at the top of the
C function which implements the function. The call syntax
of all such C functions is
LObject *func(Lake *lake, LList *args)
When parsing a call to the corresponding lisp function,
LSexpr() calls func with that lake pointer, and with args
pointing to the head of the list in the parse tree
corresponding to this function call. LDECLARE parses the
arguments in the call (by reading them from the lake) and
appends them to this list. (Note: the head of this list is
the function itself, so the first argument becomes entry #2
in the list.)
When evaluating the function call, LEval() calls func with
lake=NULL and with args pointing to the call's argument
list. (In this case the first entry of the list is the
first argument.) LDECLARE then converts the arguments in
the list into the appropriate C data types, writing their
values into the addresses in the <argspec>s.
One side-effect of using lake=NULL as the signal to evaluate
rather than to parse is that the value of the lake pointer
is not available at evaluation time. Some functions,
however, may want to do something with the lake they were
parsed from. For example, the "write" function in geomview
writes data to the output stream associated with its input
Page 8 (printed 12/22/98)
lisp(3) Geometry Center (Oct 22 1992) lisp(3)
stream. (In geomview these streams are all stored in a
general "Pool" structure which is retained as the "river"
member of the lake.) The special token LLAKE may be used to
cause the lake pointer to be saved in the args list at parse
time and written into a variable at evaluation time. It is
used exactly like the other (scalar) argument keywords:
LObject *func(Lake *lake, LList *args)
Lake *mylake;
LDECLARE(("myfunc", LBEGIN
LARG_LAKE, &mylake,
...
LARG_END));
At evaluation time LDECLARE will set mylake to have the
value that lake had at parse time. This looks just like a
specification for an argument to the lisp function but it is
not --- it is just a way to tell LDECLARE to remember the
lake pointer between parse- and evaluation-time.
BUGS
The documentation is incomplete.
AUTHOR
The lisp interpreter was written mostly by Mark Phillips
with lots of input and moral support from Stuart Levy and
Tamara Munzner.
Page 9 (printed 12/22/98)