[net.sources] Dave Betz' XLISP 1.2

jfw@mit-eddie.UUCP (John Woods) (02/03/85)

[ Replace this line with your bug ]

I am FINALLY getting around to posting Dave Betz' XLISP 1.2 (his newest version,
which has little to do with my 1.2 version).  I haven't done anything with it
at all yet (including compiling it...), and he's asked me to send changes to
him rather than posting them, to lessen the general confusion.

There are 5 shar files, of which this is the first.  Forgive the file names,
but the only interchange medium we shared was CP/M floppies...

echo extract with sh, not csh
echo x PT.LSP
cat > PT.LSP << '!Funky!Stuff!'
; This is a sample XLISP program.
; It implements a simple form of programmable turtle for VT100 compatible
; terminals.

; To run it:

;	A>xlisp pt

; This should cause the screen to be cleared and two turtles to appear.
; They should each execute their simple programs and then the prompt
; should return.  Look at the code to see how all of this works.

; Clear the screen
(defun clear ()
    (princ "\e[H\e[J"))

; Move the cursor
(defun setpos (x y)
    (princ "\e[") (princ y) (princ ";") (princ x) (princ "H"))

; Kill the remainder of the line
(defun kill ()
    (princ "\e[K"))

; Move the cursor to the currently set bottom position and clear the line
;  under it
(defun bottom ()
    (setpos bx (+ by 1))
    (kill)
    (setpos bx by)
    (kill))

; Clear the screen and go to the bottom
(defun cb ()
    (clear)
    (bottom))


; ::::::::::::
; :: Turtle ::
; ::::::::::::

; Define "Turtle" class
(setq Turtle (Class 'new))

; Define instance variables
(Turtle 'ivars '(xpos ypos char))

; Answer "isnew" by initing a position and char and displaying.
(Turtle 'answer 'isnew '() '(
    (setq xpos (setq newx (+ newx 1)))
    (setq ypos 12)
    (setq char "*")
    (self 'display)
    self))

; Message "display" prints its char at its current position
(Turtle 'answer 'display '() '(
    (setpos xpos ypos)
    (princ char)
    (bottom)
    self))

; Message "char" sets char to its arg and displays it
(Turtle 'answer 'char '(c) '(
    (setq char c)
    (self 'display)))

; Message "goto" goes to a new place after clearing old one
(Turtle 'answer 'goto '(x y) '(
    (setpos xpos ypos) (princ " ")
    (setq xpos x)
    (setq ypos y)
    (self 'display)))

; Message "up" moves up if not at top
(Turtle 'answer 'up '() '(
    (if (> ypos 0)
	(self 'goto xpos (- ypos 1))
	(bottom))))

; Message "down" moves down if not at bottom
(Turtle 'answer 'down '() '(
    (if (< ypos by)
	(self 'goto xpos (+ ypos 1))
	(bottom))))

; Message "right" moves right if not at right
(Turtle 'answer 'right '() '(
    (if (< xpos 80)
	(self 'goto (+ xpos 1) ypos)
	(bottom))))

; Message "left" moves left if not at left
(Turtle 'answer 'left '() '(
    (if (> xpos 0)
	(self 'goto (- xpos 1) ypos)
	(bottom))))


; :::::::::::::
; :: PTurtle ::
; :::::::::::::

; Define "DPurtle" programable turtle class
(setq PTurtle (Class 'new Turtle))

; Define instance variables
(PTurtle 'ivars '(prog pc))

; Message "program" stores a program
(PTurtle 'answer 'program '(p) '(
    (setq prog p)
    (setq pc prog)
    self))

; Message "step" executes a single program step
(PTurtle 'answer 'step '() '(
    (if (null pc)
	(setq pc prog))
    (if pc
	(progn (self (car pc))
	       (setq pc (cdr pc))))
    self))

; Message "step:" steps each turtle program n times
(PTurtle 'answer 'step: '(n) '(
    (repeat n (self 'step))
    self))


; ::::::::::::::
; :: PTurtles ::
; ::::::::::::::

; Define "PTurtles" class
(setq PTurtles (Class 'new))

; Define instance variables
(PTurtles 'ivars '(turtles))

; Message "make" makes a programable turtle and adds it to the collection
(PTurtles 'answer 'make '(x y &aux newturtle) '(
    (setq newturtle (PTurtle 'new))
    (newturtle 'goto x y)
    (setq turtles (cons newturtle turtles))
    newturtle))

; Message "step" steps each turtle program once
(PTurtles 'answer 'step '() '(
    (mapcar '(lambda (turtle) (turtle 'step)) turtles)
    self))

; Message "step:" steps each turtle program n times
(PTurtles 'answer 'step: '(n) '(
    (repeat n (self 'step))
    self))


; Initialize things and start up
(setq bx 0)
(setq by 20)
(setq newx 0)

; Create some programmable turtles
(cb)
(setq turtles (PTurtles 'new))
(setq t1 (turtles 'make 40 10))
(setq t2 (turtles 'make 41 10))
(t1 'program '(left right up down))
(t2 'program '(right left down up))
!Funky!Stuff!
echo x XLBFUN.C
cat > XLBFUN.C << '!Funky!Stuff!'
/* xlbfun.c - xlisp basic builtin functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;
extern struct node *s_lambda,*s_nlambda,*s_unbound;

/* local variables */
static char gsprefix[STRMAX+1] = { 'G',0 };
static char gsnumber = 1;

/* forward declarations */
FORWARD struct node *defun();

/* xeval - the builtin function 'eval' */
struct node *xeval(args)
  struct node *args;
{
    struct node *oldstk,expr,*val;

    /* create a new stack frame */
    oldstk = xlsave(&expr,NULL);

    /* get the expression to evaluate */
    expr.n_ptr = xlarg(&args);
    xllastarg(args);

    /* evaluate the expression */
    val = xleval(expr.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the expression evaluated */
    return (val);
}

/* xapply - the builtin function 'apply' */
struct node *xapply(args)
  struct node *args;
{
    struct node *oldstk,fun,arglist,*val;

    /* create a new stack frame */
    oldstk = xlsave(&fun,&arglist,NULL);

    /* get the function and argument list */
    fun.n_ptr = xlarg(&args);
    arglist.n_ptr = xlarg(&args);
    xllastarg(args);

    /* if the function is a symbol, get its value */
    if (fun.n_ptr && fun.n_ptr->n_type == SYM)
	fun.n_ptr = xleval(fun.n_ptr);

    /* apply the function to the arguments */
    val = xlapply(fun.n_ptr,arglist.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the expression evaluated */
    return (val);
}

/* xfuncall - the builtin function 'funcall' */
struct node *xfuncall(args)
  struct node *args;
{
    struct node *oldstk,fun,arglist,*val;

    /* create a new stack frame */
    oldstk = xlsave(&fun,&arglist,NULL);

    /* get the function and argument list */
    fun.n_ptr = xlarg(&args);
    arglist.n_ptr = args;

    /* if the function is a symbol, get its value */
    if (fun.n_ptr && fun.n_ptr->n_type == SYM)
	fun.n_ptr = xleval(fun.n_ptr);

    /* apply the function to the arguments */
    val = xlapply(fun.n_ptr,arglist.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the expression evaluated */
    return (val);
}

/* xquote - builtin function to quote an expression */
struct node *xquote(args)
  struct node *args;
{
    /* make sure there is exactly one argument */
    if (args == NULL || args->n_listnext != NULL)
	xlfail("incorrect number of arguments");

    /* return the quoted expression */
    return (args->n_listvalue);
}

/* xset - builtin function set */
struct node *xset(args)
  struct node *args;
{
    struct node *sym,*val;

    /* get the symbol and new value */
    sym = xlmatch(SYM,&args);
    val = xlarg(&args);
    xllastarg(args);

    /* assign the symbol the value of argument 2 and the return value */
    assign(sym,val);

    /* return the result value */
    return (val);
}

/* xsetq - builtin function setq */
struct node *xsetq(args)
  struct node *args;
{
    struct node *oldstk,arg,sym,val;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&sym,&val,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* get the symbol and new value */
    sym.n_ptr = xlmatch(SYM,&arg.n_ptr);
    val.n_ptr = xlevarg(&arg.n_ptr);
    xllastarg(arg.n_ptr);

    /* assign the symbol the value of argument 2 and the return value */
    assign(sym.n_ptr,val.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result value */
    return (val.n_ptr);
}

/* xdefun - builtin function 'defun' */
struct node *xdefun(args)
  struct node *args;
{
    return (defun(args,s_lambda));
}

/* xndefun - builtin function 'ndefun' */
struct node *xndefun(args)
  struct node *args;
{
    return (defun(args,s_nlambda));
}

/* defun - internal function definition routine */
LOCAL struct node *defun(args,type)
  struct node *args,*type;
{
    struct node *oldstk,sym,fargs,fun;

    /* create a new stack frame */
    oldstk = xlsave(&sym,&fargs,&fun,NULL);

    /* get the function symbol and formal argument list */
    sym.n_ptr = xlmatch(SYM,&args);
    fargs.n_ptr = xlmatch(LIST,&args);

    /* create a new function definition */
    fun.n_ptr = newnode(LIST);
    fun.n_ptr->n_listvalue = type;
    fun.n_ptr->n_listnext = newnode(LIST);
    fun.n_ptr->n_listnext->n_listvalue = fargs.n_ptr;
    fun.n_ptr->n_listnext->n_listnext = args;

    /* make the symbol point to a new function definition */
    assign(sym.n_ptr,fun.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the function symbol */
    return (sym.n_ptr);
}

/* xgensym - generate a symbol */
struct node *xgensym(args)
  struct node *args;
{
    char sym[STRMAX+1];
    struct node *x;

    /* get the prefix or number */
    if (args) {
	x = xlarg(&args);
	switch (x->n_type) {
	case SYM:
		strcpy(gsprefix,xlsymname(x));
		break;
	case STR:
		strcpy(gsprefix,x->n_str);
		break;
	case INT:
		gsnumber = x->n_int;
		break;
	default:
		xlfail("bad argument type");
	}
    }
    xllastarg(args);

    /* create the pname of the new symbol */
    sprintf(sym,"%s%d",gsprefix,gsnumber++);

    /* make a symbol with this print name */
    return (xlmakesym(sym,DYNAMIC));
}

/* xintern - intern a symbol */
struct node *xintern(args)
  struct node *args;
{
    struct node *oldstk,sym;

    /* create a new stack frame */
    oldstk = xlsave(&sym,NULL);

    /* get the symbol to intern */
    sym.n_ptr = xlmatch(SYM,&args);
    xllastarg(args);

    /* intern the symbol */
    sym.n_ptr = xlintern(sym.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the symbol */
    return (sym.n_ptr);
}

/* xsymname - get the print name of a symbol */
struct node *xsymname(args)
  struct node *args;
{
    struct node *sym;

    /* get the symbol */
    sym = xlmatch(SYM,&args);
    xllastarg(args);

    /* return the print name */
    return (sym->n_symplist->n_listvalue);
}

/* xsymplist - get the property list of a symbol */
struct node *xsymplist(args)
  struct node *args;
{
    struct node *sym;

    /* get the symbol */
    sym = xlmatch(SYM,&args);
    xllastarg(args);

    /* return the property list */
    return (sym->n_symplist->n_listnext);
}

/* xget - get the value of a property */
struct node *xget(args)
  struct node *args;
{
    struct node *sym,*prp;

    /* get the symbol and property */
    sym = xlmatch(SYM,&args);
    prp = xlmatch(SYM,&args);
    xllastarg(args);

    /* retrieve the property value */
    return (xlgetprop(sym,prp));
}

/* xputprop - put a property value onto a property list */
struct node *xputprop(args)
  struct node *args;
{
    struct node *oldstk,sym,val,prp;

    /* create a new stack frame */
    oldstk = xlsave(&sym,&val,&prp,NULL);

    /* get the symbol, value and property */
    sym.n_ptr = xlmatch(SYM,&args);
    val.n_ptr = xlarg(&args);
    prp.n_ptr = xlmatch(SYM,&args);
    xllastarg(args);

    /* put the property onto the property list */
    xlputprop(sym.n_ptr,val.n_ptr,prp.n_ptr);

    /* restore the previouse stack frame */
    xlstack = oldstk;

    /* return the value */
    return (val.n_ptr);
}

/* xremprop - remove a property value from a property list */
struct node *xremprop(args)
  struct node *args;
{
    struct node *sym,*prp;

    /* get the symbol and property */
    sym = xlmatch(SYM,&args);
    prp = xlmatch(SYM,&args);
    xllastarg(args);

    /* remove the property */
    xlremprop(sym,prp);

    /* return nil */
    return (NULL);
}
!Funky!Stuff!
echo x XLBIND.C
cat > XLBIND.C << '!Funky!Stuff!'
/* xlbind - xlisp symbol binding routines */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* global variables */
struct node *xlenv;

/* xlunbind - unbind symbols bound in this environment */
xlunbind(env)
  struct node *env;
{
    struct node *bnd;

    /* unbind each symbol in the environment chain */
    for (; xlenv != env; xlenv = xlenv->n_listnext) {
	bnd = xlenv->n_listvalue;
	bnd->n_bndsym->n_symvalue = bnd->n_bndvalue;
    }
}

/* xlbind - bind a symbol to a value */
xlbind(sym,val)
  struct node *sym,*val;
{
    struct node *lptr,*bptr;

    /* create a new environment list entry */
    lptr = newnode(LIST);
    lptr->n_listnext = xlenv;
    xlenv = lptr;

    /* create a new variable binding */
    lptr->n_listvalue = bptr = newnode(LIST);
    bptr->n_bndsym = sym;
    bptr->n_bndvalue = val;
}

/* xlfixbindings - make a new set of bindings visible */
xlfixbindings(env)
  struct node *env;
{
    struct node *eptr,*bnd,*sym,*oldvalue;

    /* fix the bound value of each symbol in the environment chain */
    for (eptr = xlenv; eptr != env; eptr = eptr->n_listnext) {
	bnd = eptr->n_listvalue;
	sym = bnd->n_bndsym;
	oldvalue = sym->n_symvalue;
	sym->n_symvalue = bnd->n_bndvalue;
	bnd->n_bndvalue = oldvalue;
    }
}
!Funky!Stuff!
echo x XLCONT.C
cat > XLCONT.C << '!Funky!Stuff!'
/* xlcont - xlisp control builtin functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack,*xlenv;
extern struct node *true;

/* xcond - builtin function cond */
struct node *xcond(args)
  struct node *args;
{
    struct node *oldstk,arg,list,*val;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&list,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* initialize the return value */
    val = NULL;

    /* find a predicate that is true */
    while (arg.n_ptr != NULL) {

	/* get the next conditional */
	list.n_ptr = xlmatch(LIST,&arg.n_ptr);

	/* evaluate the predicate part */
	if (xlevarg(&list.n_ptr) != NULL) {

	    /* evaluate each expression */
	    while (list.n_ptr != NULL)
		val = xlevarg(&list.n_ptr);

	    /* exit the loop */
	    break;
	}
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the value */
    return (val);
}

/* xand - builtin function 'and; */
struct node *xand(args)
  struct node *args;
{
    struct node *oldstk,arg,*val;

    /* create a new stack frame */
    oldstk = xlsave(&arg,NULL);

    /* initialize */
    arg.n_ptr = args;
    val = true;

    /* evaluate each argument */
    while (arg.n_ptr != NULL)

	/* get the next argument */
	if ((val = xlevarg(&arg.n_ptr)) == NULL)
	    break;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result value */
    return (val);
}

/* xor - builtin function 'or' */
struct node *xor(args)
  struct node *args;
{
    struct node *oldstk,arg,*val;

    /* create a new stack frame */
    oldstk = xlsave(&arg,NULL);

    /* initialize */
    arg.n_ptr = args;
    val = NULL;

    /* evaluate each argument */
    while (arg.n_ptr != NULL)
	if ((val = xlevarg(&arg.n_ptr)) != NULL)
	    break;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result value */
    return (val);
}

/* xlet - establish some local bindings and execute some code */
struct node *xlet(args)
  struct node *args;
{
    struct node *oldstk,*oldenv,arg,bnd,sym,val,*p;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&bnd,&sym,&val,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* get the list of bindings */
    bnd.n_ptr = xlmatch(LIST,&arg.n_ptr);

    /* initialize the local environment */
    oldenv = xlenv;

    /* bind each symbol in the list of bindings */
    while (bnd.n_ptr && bnd.n_ptr->n_type == LIST) {

	/* get the next binding */
	p = bnd.n_ptr->n_listvalue;

	/* check its type */
	switch (p->n_type) {
	case SYM:
		sym.n_ptr = p;
		val.n_ptr = NULL;
		break;
	case LIST:
		sym.n_ptr = p->n_listvalue;
		val.n_ptr = p->n_listnext->n_listvalue;
		val.n_ptr = xleval(val.n_ptr);
		break;
	default:
		xlfail("bad binding");
	}

	/* bind the value to the symbol */
	xlbind(sym.n_ptr,val.n_ptr);

	/* get next binding */
	bnd.n_ptr = bnd.n_ptr->n_listnext;
    }

    /* fix the bindings */
    xlfixbindings(oldenv);

    /* execute the code */
    for (val.n_ptr = NULL; arg.n_ptr; )
	val.n_ptr = xlevarg(&arg.n_ptr);

    /* unbind the arguments */
    xlunbind(oldenv);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result */
    return (val.n_ptr);
}

/* xwhile - builtin function while */
struct node *xwhile(args)
  struct node *args;
{
    struct node *oldstk,farg,arg,*val;

    /* create a new stack frame */
    oldstk = xlsave(&farg,&arg,NULL);

    /* initialize */
    farg.n_ptr = arg.n_ptr = args;

    /* loop until test fails */
    val = NULL;
    for (; TRUE; arg.n_ptr = farg.n_ptr) {

	/* evaluate the test expression */
	if (xlevarg(&arg.n_ptr) == NULL)
	    break;

	/* evaluate each remaining argument */
	while (arg.n_ptr != NULL)
	    val = xlevarg(&arg.n_ptr);
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the last test expression value */
    return (val);
}

/* xrepeat - builtin function repeat */
struct node *xrepeat(args)
  struct node *args;
{
    struct node *oldstk,farg,arg,*val;
    int cnt;

    /* create a new stack frame */
    oldstk = xlsave(&farg,&arg,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* evaluate the repeat count */
    cnt = xlevmatch(INT,&arg.n_ptr)->n_int;

    /* save the first expression to repeat */
    farg.n_ptr = arg.n_ptr;

    /* loop until test fails */
    val = NULL;
    for (; cnt > 0; cnt--) {

	/* evaluate each remaining argument */
	while (arg.n_ptr != NULL)
	    val = xlevarg(&arg.n_ptr);

	/* restore pointer to first expression */
	arg.n_ptr = farg.n_ptr;
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the last test expression value */
    return (val);
}

/* xif - builtin function 'if' */
struct node *xif(args)
  struct node *args;
{
    struct node *oldstk,testexpr,thenexpr,elseexpr,*val;

    /* create a new stack frame */
    oldstk = xlsave(&testexpr,&thenexpr,&elseexpr,NULL);

    /* get the test expression, then clause and else clause */
    testexpr.n_ptr = xlarg(&args);
    thenexpr.n_ptr = xlarg(&args);
    elseexpr.n_ptr = (args ? xlarg(&args) : NULL);
    xllastarg(args);

    /* evaluate the appropriate clause */
    val = xleval(xleval(testexpr.n_ptr) ? thenexpr.n_ptr : elseexpr.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the last value */
    return (val);
}

/* xprogn - builtin function 'progn' */
struct node *xprogn(args)
  struct node *args;
{
    struct node *oldstk,arg,*val;
    int cnt;

    /* create a new stack frame */
    oldstk = xlsave(&arg,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* evaluate each remaining argument */
    for (val = NULL; arg.n_ptr != NULL; )
	val = xlevarg(&arg.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the last test expression value */
    return (val);
}
!Funky!Stuff!
echo x XLDMEM.C
cat > XLDMEM.C << '!Funky!Stuff!'
/* xldmem - xlisp dynamic memory management routines */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* useful definitions */
#define ALLOCSIZE (sizeof(struct segment) + (anodes-1) * sizeof(struct node))

/* memory segment structure definition */
struct segment {
    int sg_size;
    struct segment *sg_next;
    struct node sg_nodes[1];
};

/* external variables */
extern struct node *oblist;
extern struct node *xlstack;
extern struct node *xlenv;

/* external procedures */
extern char *malloc();
extern char *calloc();

/* local variables */
int anodes,nnodes,nsegs,nfree,gccalls;
static struct segment *segs;
static struct node *fnodes;

/* newnode - allocate a new node */
struct node *newnode(type)
  int type;
{
    struct node *nnode;

    /* get a free node */
    if ((nnode = fnodes) == NULL) {
	gc();
	if ((nnode = fnodes) == NULL)
	    xlfail("insufficient node space");
    }

    /* unlink the node from the free list */
    fnodes = nnode->n_right;
    nfree -= 1;

    /* initialize the new node */
    nnode->n_type = type;
    nnode->n_right = NULL;

    /* return the new node */
    return (nnode);
}

/* stralloc - allocate memory for a string adding a byte for the terminator */
char *stralloc(size)
  int size;
{
    char *sptr;

    /* allocate memory for the string copy */
    if ((sptr = malloc(size+1)) == NULL) {
	gc();
	if ((sptr = malloc(size+1)) == NULL)
	    xlfail("insufficient string space");
    }

    /* return the new string memory */
    return (sptr);
}

/* strsave - generate a dynamic copy of a string */
char *strsave(str)
  char *str;
{
    char *sptr;

    /* create a new string */
    sptr = stralloc(strlen(str));
    strcpy(sptr,str);

    /* return the new string */
    return (sptr);
}

/* strfree - free string memory */
strfree(str)
  char *str;
{
    free(str);
}

/* gc - garbage collect */
gc()
{
    struct node *p;

    /* mark all accessible nodes */
    mark(oblist);
    mark(xlenv);

    /* mark the evaluation stack */
    for (p = xlstack; p; p = p->n_listnext)
	mark(p->n_listvalue);

    /* sweep memory collecting all unmarked nodes */
    sweep();

    /* if there's still nothing available, allocate more memory */
    if (fnodes == NULL)
	addseg();

    /* count the gc call */
    gccalls += 1;
}

/* mark - mark all accessible nodes */
LOCAL mark(ptr)
  struct node *ptr;
{
    struct node *this,*prev,*tmp;

    /* just return on null */
    if (ptr == NULL)
	return;

    /* initialize */
    prev = NULL;
    this = ptr;

    /* mark this list */
    while (TRUE) {

	/* descend as far as we can */
	while (TRUE) {

	    /* check for this node being marked */
	    if (this->n_flags & MARK)
		break;

	    /* mark it and its descendants */
	    else {

		/* mark the node */
		this->n_flags |= MARK;

		/* follow the left sublist if there is one */
		if (left(this)) {
		    this->n_flags |= LEFT;
		    tmp = prev;
		    prev = this;
		    this = prev->n_left;
		    prev->n_left = tmp;
		}
		else if (right(this)) {
		    this->n_flags &= ~LEFT;
		    tmp = prev;
		    prev = this;
		    this = prev->n_right;
		    prev->n_right = tmp;
		}
		else
		    break;
	    }
	}

	/* backup to a point where we can continue descending */
	while (TRUE) {

	    /* check for termination condition */
	    if (prev == NULL)
		return;

	    /* check for coming from the left side */
	    if (prev->n_flags & LEFT)
		if (right(prev)) {
		    prev->n_flags &= ~LEFT;
		    tmp = prev->n_left;
		    prev->n_left = this;
		    this = prev->n_right;
		    prev->n_right = tmp;
		    break;
		}
		else {
		    tmp = prev;
		    prev = tmp->n_left;
		    tmp->n_left = this;
		    this = tmp;
		}

	    /* came from the right side */
	    else {
		tmp = prev;
		prev = tmp->n_right;
		tmp->n_right = this;
		this = tmp;
	    }
	}
    }
}

/* sweep - sweep all unmarked nodes and add them to the free list */
LOCAL sweep()
{
    struct segment *seg;
    struct node *p;
    int n;

    /* empty the free list */
    fnodes = NULL;
    nfree = 0;

    /* add all unmarked nodes */
    for (seg = segs; seg != NULL; seg = seg->sg_next) {
	p = &seg->sg_nodes[0];
	for (n = seg->sg_size; n--; p++)
	    if (!(p->n_flags & MARK)) {
		switch (p->n_type) {
		case STR:
			if (p->n_strtype == DYNAMIC && p->n_str != NULL)
			    strfree(p->n_str);
			break;
		}
		p->n_type = FREE;
		p->n_flags = 0;
		p->n_left = NULL;
		p->n_right = fnodes;
		fnodes = p;
		nfree += 1;
	    }
	    else
		p->n_flags &= ~(MARK | LEFT);
    }
}

/* addseg - add a segment to the available memory */
int addseg()
{
    struct segment *newseg;
    struct node *p;
    int n;

    /* check for zero allocation */
    if (anodes == 0)
	return (FALSE);

    /* allocate a new segment */
    if ((newseg = (struct segment *) calloc(1,ALLOCSIZE)) != NULL) {

	/* initialize the new segment */
	newseg->sg_size = anodes;
	newseg->sg_next = segs;
	segs = newseg;

	/* add each new node to the free list */
	p = &newseg->sg_nodes[0];
	for (n = anodes; n--; ) {
	    p->n_right = fnodes;
	    fnodes = p++;
	}

	/* update the statistics */
	nnodes += anodes;
	nfree += anodes;
	nsegs += 1;

	/* return successfully */
	return (TRUE);
    }
    else
	return (FALSE);
}
 
/* left - check for a left sublist */
LOCAL int left(n)
  struct node *n;
{
    switch (n->n_type) {
    case SUBR:
    case FSUBR:
    case INT:
    case STR:
    case FPTR:
	    return (FALSE);
    case SYM:
    case LIST:
    case OBJ:
	    return (n->n_left != NULL);
    default:
	    printf("bad node type (%d) found during left scan\n",n->n_type);
	    exit();
    }
}

/* right - check for a right sublist */
LOCAL int right(n)
  struct node *n;
{
    switch (n->n_type) {
    case SUBR:
    case FSUBR:
    case INT:
    case STR:
    case FPTR:
	    return (FALSE);
    case SYM:
    case LIST:
    case OBJ:
	    return (n->n_right != NULL);
    default:
	    printf("bad node type (%d) found during right scan\n",n->n_type);
	    exit();
    }
}

/* stats - print memory statistics */
stats()
{
    printf("Nodes:       %d\n",nnodes);
    printf("Free nodes:  %d\n",nfree);
    printf("Segments:    %d\n",nsegs);
    printf("Allocate:    %d\n",anodes);
    printf("Collections: %d\n",gccalls);
}

/* xlminit - initialize the dynamic memory module */
xlminit()
{
    /* initialize our internal variables */
    anodes = NNODES;
    nnodes = nsegs = nfree = gccalls = 0;
    segs = fnodes = NULL;

    /* initialize structures that are marked by the collector */
    xlstack = xlenv = oblist = NULL;
}
!Funky!Stuff!
echo x XLEVAL.C
cat > XLEVAL.C << '!Funky!Stuff!'
/* xleval - xlisp evaluator */

#ifdef AZTEC
#include "stdio.h"
#include "setjmp.h"
#else
#include <stdio.h>
#include <setjmp.h>
#endif

#include "xlisp.h"

/* global variables */
struct node *xlstack;

/* trace stack */
static struct node *trace_stack[TDEPTH];
static int trace_pointer;

/* external variables */
extern jmp_buf *xljmpbuf;
extern struct node *xlenv;
extern struct node *s_lambda,*s_nlambda;
extern struct node *s_unbound;
extern struct node *s_stdout;
extern struct node *s_tracenable;
extern struct node *k_rest;
extern struct node *k_aux;

/* forward declarations */
FORWARD struct node *evform();
FORWARD struct node *evsym();
FORWARD struct node *evfun();

/* xleval - evaluate an xlisp expression */
struct node *xleval(expr)
  struct node *expr;
{
    /* evaluate null to itself */
    if (expr == NULL)
	return (NULL);

    /* add trace entry */
    tpush(expr);

    /* check type of value */
    switch (expr->n_type) {
    case LIST:
	    expr = evform(expr);
	    break;
    case SYM:
	    expr = evsym(expr);
	    break;
    case INT:
    case STR:
    case SUBR:
    case FSUBR:
	    break;
    default:
	    xlfail("can't evaluate expression");
    }

    /* remove trace entry */
    tpop();

    /* return the value */
    return (expr);
}

/* xlapply - apply a function to a list of arguments */
struct node *xlapply(fun,args)
  struct node *fun,*args;
{
    struct node *val;

    /* check for a null function */
    if (fun == NULL)
	xlfail("null function");

    /* evaluate the function */
    switch (fun->n_type) {
    case SUBR:
	    val = (*fun->n_subr)(args);
	    break;
    case LIST:
	    if (fun->n_listvalue != s_lambda)
		xlfail("bad function type");
	    val = evfun(fun,args);
	    break;
    default:
	    xlfail("bad function");
    }

    /* return the result value */
    return (val);
}

/* evform - evaluate a form */
LOCAL struct node *evform(nptr)
  struct node *nptr;
{
    struct node *oldstk,fun,args,*val,*type;

    /* create a stack frame */
    oldstk = xlsave(&fun,&args,NULL);

    /* get the function and the argument list */
    fun.n_ptr = nptr->n_listvalue;
    args.n_ptr = nptr->n_listnext;

    /* evaluate the first expression */
    if ((fun.n_ptr = xleval(fun.n_ptr)) == NULL)
	xlfail("null function");

    /* evaluate the function */
    switch (fun.n_ptr->n_type) {
    case SUBR:
	    args.n_ptr = xlevlist(args.n_ptr);
    case FSUBR:
	    val = (*fun.n_ptr->n_subr)(args.n_ptr);
	    break;
    case LIST:
	    if ((type = fun.n_ptr->n_listvalue) == s_lambda)
		args.n_ptr = xlevlist(args.n_ptr);
	    else if (type != s_nlambda)
		xlfail("bad function type");
	    val = evfun(fun.n_ptr,args.n_ptr);
	    break;
    case OBJ:
	    val = xlsend(fun.n_ptr,args.n_ptr);
	    break;
    default:
	    xlfail("bad function");
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result value */
    return (val);
}

/* xlevlist - evaluate a list of arguments */
struct node *xlevlist(args)
  struct node *args;
{
    struct node *oldstk,src,dst,*new,*last,*val;

    /* create a stack frame */
    oldstk = xlsave(&src,&dst,NULL);

    /* initialize */
    src.n_ptr = args;

    /* evaluate each argument */
    for (val = NULL; src.n_ptr; src.n_ptr = src.n_ptr->n_listnext) {

	/* check this entry */
	if (src.n_ptr->n_type != LIST)
	    xlfail("bad argument list");

	/* allocate a new list entry */
	new = newnode(LIST);
	if (val)
	    last->n_listnext = new;
	else
	    val = dst.n_ptr = new;
	new->n_listvalue = xleval(src.n_ptr->n_listvalue);
	last = new;
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new list */
    return (val);
}

/* evsym - evaluate a symbol */
LOCAL struct node *evsym(sym)
  struct node *sym;
{
    struct node *p;

    /* check for a current object */
    if ((p = xlobsym(sym)) != NULL)
	return (p->n_listvalue);
    else if ((p = sym->n_symvalue) == s_unbound)
	xlfail("unbound variable");
    else
	return (p);
}

/* evfun - evaluate a function */
LOCAL struct node *evfun(fun,args)
  struct node *fun,*args;
{
    struct node *oldenv,*oldstk,cptr,*fargs,*val;

    /* create a stack frame */
    oldstk = xlsave(&cptr,NULL);

    /* skip the function type */
    if ((fun = fun->n_listnext) == NULL)
	xlfail("bad function definition");

    /* get the formal argument list */
    if ((fargs = fun->n_listvalue) != NULL && fargs->n_type != LIST)
	xlfail("bad formal argument list");

    /* bind the formal parameters */
    oldenv = xlenv;
    xlabind(fargs,args);
    xlfixbindings(oldenv);

    /* execute the code */
    for (cptr.n_ptr = fun->n_listnext; cptr.n_ptr != NULL; )
	val = xlevarg(&cptr.n_ptr);

    /* restore the environment */
    xlunbind(oldenv);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result value */
    return (val);
}

/* xlabind - bind the arguments for a function */
xlabind(fargs,aargs)
  struct node *fargs,*aargs;
{
    struct node *oldstk,farg,aarg,*arg;

    /* create a stack frame */
    oldstk = xlsave(&farg,&aarg,NULL);

    /* initialize the pointers */
    farg.n_ptr = fargs;
    aarg.n_ptr = aargs;

    /* evaluate and bind each argument */
    while (farg.n_ptr != NULL && aarg.n_ptr != NULL) {

	/* check for a keyword */
	if (iskeyword(arg = farg.n_ptr->n_listvalue))
	    break;

	/* bind the formal variable to the argument value */
	xlbind(arg,aarg.n_ptr->n_listvalue);

	/* move the argument list pointers ahead */
	farg.n_ptr = farg.n_ptr->n_listnext;
	aarg.n_ptr = aarg.n_ptr->n_listnext;
    }

    /* check for the '&rest' keyword */
    if (farg.n_ptr && farg.n_ptr->n_listvalue == k_rest) {
	farg.n_ptr = farg.n_ptr->n_listnext;
	if (farg.n_ptr && (arg = farg.n_ptr->n_listvalue) && !iskeyword(arg))
	    xlbind(arg,aarg.n_ptr);
	else
	    xlfail("symbol missing after &rest");
	farg.n_ptr = farg.n_ptr->n_listnext;
	aarg.n_ptr = NULL;
    }

    /* check for the '&aux' keyword */
    if (farg.n_ptr && farg.n_ptr->n_listvalue == k_aux)
	while ((farg.n_ptr = farg.n_ptr->n_listnext) != NULL)
	    xlbind(farg.n_ptr->n_listvalue,NULL);

    /* make sure the correct number of arguments were supplied */
    if (farg.n_ptr != aarg.n_ptr)
	xlfail("incorrect number of arguments to a function");

    /* restore the previous stack frame */
    xlstack = oldstk;
}

/* iskeyword - check to see if a symbol is a keyword */
LOCAL int iskeyword(sym)
  struct node *sym;
{
    return (sym == k_rest || sym == k_aux);
}

/* xlsave - save nodes on the stack */
struct node *xlsave(n)
  struct node *n;
{
    struct node **nptr,*oldstk;

    /* save the old stack pointer */
    oldstk = xlstack;

    /* save each node */
    for (nptr = &n; *nptr != NULL; nptr++) {
	(*nptr)->n_type = LIST;
	(*nptr)->n_listvalue = NULL;
	(*nptr)->n_listnext = xlstack;
	xlstack = *nptr;
    }

    /* return the old stack pointer */
    return (oldstk);
}

/* xlfail - error handling routine */
xlfail(err)
  char *err;
{
    /* print the error message */
    printf("error: %s\n",err);

    /* flush the terminal input buffer */
    xlflush();

    /* unbind bound symbols */
    xlunbind(NULL);

    /* do the back trace */
    if (s_tracenable->n_symvalue)
	baktrace();
    trace_pointer = -1;

    /* restart */
    longjmp(xljmpbuf,1);
}

/* tpush - add an entry to the trace stack */
LOCAL tpush(nptr)
    struct node *nptr;
{
    if (++trace_pointer < TDEPTH)
	trace_stack[trace_pointer] = nptr;
}

/* tpop - pop an entry from the trace stack */
LOCAL tpop()
{
    trace_pointer--;
}

/* baktrace - do a back trace */
LOCAL baktrace()
{
    for (; trace_pointer >= 0; trace_pointer--)
	if (trace_pointer < TDEPTH)
	    stdprint(trace_stack[trace_pointer]);
}

/* stdprint - print to standard output */
stdprint(expr)
  struct node *expr;
{
    xlprint(s_stdout->n_symvalue,expr,TRUE);
    xlterpri(s_stdout->n_symvalue);
}

/* xleinit - initialize the evaluator */
xleinit()
{
    /* initialize debugging stuff */
    trace_pointer = -1;
}
!Funky!Stuff!
exit 0
-- 
John Woods, Charles River Data Systems
decvax!frog!john, mit-eddie!jfw, JFW%mit-ccc@MIT-XX

When your puppy goes off in another room,
is it because of the explosive charge?

jfw@mit-eddie.UUCP (John Woods) (02/03/85)

[ Replace this line with your bug ]

Here is part two of the Newest XLISP 1.2 posting.

echo extract with sh, not csh
echo x XLFIO.C
cat > XLFIO.C << '!Funky!Stuff!'
/* xlfio.c - xlisp file i/o */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#include <ctype.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *s_stdin,*s_stdout;
extern struct node *xlstack;
extern int xlfsize;

/* external routines */
extern FILE *fopen();

/* local variables */
static char buf[STRMAX+1];

/* forward declarations */
FORWARD struct node *printit();
FORWARD struct node *flatsize();
FORWARD struct node *explode();
FORWARD struct node *makesym();
FORWARD struct node *openit();
FORWARD struct node *getfile();

/* xread - read an expression */
struct node *xread(args)
  struct node *args;
{
    struct node *oldstk,fptr,eof,*val;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&eof,NULL);

    /* get file pointer and eof value */
    fptr.n_ptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    eof.n_ptr = (args ? xlarg(&args) : NULL);
    xllastarg(args);

    /* read an expression */
    if (!xlread(fptr.n_ptr,&val))
	val = eof.n_ptr;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the expression */
    return (val);
}

/* xprint - builtin function 'print' */
struct node *xprint(args)
  struct node *args;
{
    return (printit(args,TRUE,TRUE));
}

/* xprin1 - builtin function 'prin1' */
struct node *xprin1(args)
  struct node *args;
{
    return (printit(args,TRUE,FALSE));
}

/* xprinc - builtin function princ */
struct node *xprinc(args)
  struct node *args;
{
    return (printit(args,FALSE,FALSE));
}

/* xterpri - terminate the current print line */
struct node *xterpri(args)
  struct node *args;
{
    struct node *fptr;

    /* get file pointer */
    fptr = (args ? getfile(&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* terminate the print line and return nil */
    xlterpri(fptr);
    return (NULL);
}

/* printit - common print function */
LOCAL struct node *printit(args,pflag,tflag)
  struct node *args; int pflag,tflag;
{
    struct node *oldstk,fptr,val;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&val,NULL);

    /* get expression to print and file pointer */
    val.n_ptr = xlarg(&args);
    fptr.n_ptr = (args ? getfile(&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* print the value */
    xlprint(fptr.n_ptr,val.n_ptr,pflag);

    /* terminate the print line if necessary */
    if (tflag)
	xlterpri(fptr.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result */
    return (val.n_ptr);
}

/* xflatsize - compute the size of a printed representation using prin1 */
struct node *xflatsize(args)
  struct node *args;
{
    return (flatsize(args,TRUE));
}

/* xflatc - compute the size of a printed representation using princ */
struct node *xflatc(args)
  struct node *args;
{
    return (flatsize(args,FALSE));
}

/* flatsize - compute the size of a printed expression */
LOCAL struct node *flatsize(args,pflag)
  struct node *args; int pflag;
{
    struct node *oldstk,val;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* get the expression */
    val.n_ptr = xlarg(&args);
    xllastarg(args);

    /* print the value to compute its size */
    xlfsize = 0;
    xlprint(NULL,val.n_ptr,pflag);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the length of the expression */
    val.n_ptr = newnode(INT);
    val.n_ptr->n_int = xlfsize;
    return (val.n_ptr);
}

/* xexplode - explode an expression */
struct node *xexplode(args)
  struct node *args;
{
    return (explode(args,TRUE));
}

/* xexplc - explode an expression using princ */
struct node *xexplc(args)
  struct node *args;
{
    return (explode(args,FALSE));
}

/* explode - internal explode routine */
LOCAL struct node *explode(args,pflag)
  struct node *args; int pflag;
{
    struct node *oldstk,val,strm;

    /* create a new stack frame */
    oldstk = xlsave(&val,&strm,NULL);

    /* get the expression */
    val.n_ptr = xlarg(&args);
    xllastarg(args);

    /* create a stream */
    strm.n_ptr = newnode(LIST);

    /* print the value into the stream */
    xlprint(strm.n_ptr,val.n_ptr,pflag);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the list of characters */
    return (strm.n_ptr->n_listvalue);
}

/* ximplode - implode a list of characters into an expression */
struct node *ximplode(args)
  struct node *args;
{
    return (makesym(args,TRUE));
}

/* xmaknam - implode a list of characters into an uninterned symbol */
struct node *xmaknam(args)
  struct node *args;
{
    return (makesym(args,FALSE));
}

/* makesym - internal implode routine */
LOCAL struct node *makesym(args,intflag)
  struct node *args; int intflag;
{
    struct node *list,*val;
    char *p;

    /* get the list */
    list = xlarg(&args);
    xllastarg(args);

    /* assemble the symbol's pname */
    for (p = buf; list && list->n_type == LIST; list = list->n_listnext) {
	if ((val = list->n_listvalue) == NULL || val->n_type != INT)
	    xlfail("bad character list");
	if ((int)(p - buf) < STRMAX)
	    *p++ = val->n_int;
    }
    *p = 0;

    /* create a symbol */
    val = (intflag ? xlenter(buf,DYNAMIC) : xlmakesym(buf,DYNAMIC));

    /* return the symbol */
    return (val);
}

/* xopeni - open an input file */
struct node *xopeni(args)
  struct node *args;
{
    return (openit(args,"r"));
}

/* xopeno - open an output file */
struct node *xopeno(args)
  struct node *args;
{
    return (openit(args,"w"));
}

/* openit - common file open routine */
LOCAL struct node *openit(args,mode)
  struct node *args; char *mode;
{
    struct node *fname,*val;
    FILE *fp;

    /* get the file name */
    fname = xlmatch(STR,&args);
    xllastarg(args);

    /* try to open the file */
    if ((fp = fopen(fname->n_str,mode)) != NULL) {
	val = newnode(FPTR);
	val->n_fp = fp;
	val->n_savech = 0;
    }
    else
	val = NULL;

    /* return the file pointer */
    return (val);
}

/* xclose - close a file */
struct node *xclose(args)
  struct node *args;
{
    struct node *fptr;

    /* get file pointer */
    fptr = xlmatch(FPTR,&args);
    xllastarg(args);

    /* make sure the file exists */
    if (fptr->n_fp == NULL)
	xlfail("file not open");

    /* close the file */
    fclose(fptr->n_fp);
    fptr->n_fp = NULL;

    /* return nil */
    return (NULL);
}

/* xrdchar - read a character from a file */
struct node *xrdchar(args)
  struct node *args;
{
    struct node *fptr,*val;
    int ch;

    /* get file pointer */
    fptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    xllastarg(args);

    /* get character and check for eof */
    if ((ch = xlgetc(fptr)) == EOF)
	val = NULL;
    else {
	val = newnode(INT);
	val->n_int = ch;
    }

    /* return the character */
    return (val);
}

/* xpkchar - peek at a character from a file */
struct node *xpkchar(args)
  struct node *args;
{
    struct node *flag,*fptr,*val;
    int ch;

    /* peek flag and get file pointer */
    flag = (args ? xlarg(&args) : NULL);
    fptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    xllastarg(args);

    /* skip leading white space and get a character */
    if (flag)
	while ((ch = xlpeek(fptr)) != EOF && isspace(ch))
	    xlgetc(fptr);
    else
	ch = xlpeek(fptr);

    /* check for eof */
    if (ch == EOF)
	val = NULL;
    else {
	val = newnode(INT);
	val->n_int = ch;
    }

    /* return the character */
    return (val);
}

/* xwrchar - write a character to a file */
struct node *xwrchar(args)
  struct node *args;
{
    struct node *fptr,*chr;

    /* get the character and file pointer */
    chr = xlmatch(INT,&args);
    fptr = (args ? getfile(&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* put character to the file */
    xlputc(fptr,chr->n_int);

    /* return the character */
    return (chr);
}

/* xreadline - read a line from a file */
struct node *xreadline(args)
  struct node *args;
{
    struct node *oldstk,fptr,str;
    char *p,*sptr;
    int len,ch;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&str,NULL);

    /* get file pointer */
    fptr.n_ptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    xllastarg(args);

    /* make a string node */
    str.n_ptr = newnode(STR);
    str.n_ptr->n_strtype = DYNAMIC;

    /* get character and check for eof */
    len = 0; p = buf;
    while ((ch = xlgetc(fptr.n_ptr)) != EOF && ch != '\n') {

	/* check for buffer overflow */
	if ((int)(p - buf) == STRMAX) {
	    *p = 0;
 	    sptr = stralloc(len + STRMAX); *sptr = 0;
	    if (len) {
		strcpy(sptr,str.n_ptr->n_str);
		strfree(str.n_ptr->n_str);
	    }
	    str.n_ptr->n_str = sptr;
	    strcat(sptr,buf);
	    len += STRMAX;
	    p = buf;
	}

	/* store the character */
	*p++ = ch;
    }

    /* check for end of file */
    if (len == 0 && p == buf && ch == EOF) {
	xlstack = oldstk;
	return (NULL);
    }

    /* append the last substring */
    *p = 0;
    sptr = stralloc(len + (int)(p - buf)); *sptr = 0;
    if (len) {
	strcpy(sptr,str.n_ptr->n_str);
	strfree(str.n_ptr->n_str);
    }
    str.n_ptr->n_str = sptr;
    strcat(sptr,buf);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the string */
    return (str.n_ptr);
}

/* getfile - get a file or stream */
LOCAL struct node *getfile(pargs)
  struct node **pargs;
{
    struct node *arg;

    /* get a file or stream (cons) or nil */
    if (arg = xlarg(pargs)) {
	if (arg->n_type == FPTR) {
	    if (arg->n_fp == NULL)
		xlfail("file closed");
	}
	else if (arg->n_type != LIST)
	    xlfail("bad file or stream");
    }
    return (arg);
}
!Funky!Stuff!
echo x XLFIO.C
cat > XLFIO.C << '!Funky!Stuff!'
/* xlfio.c - xlisp file i/o */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#include <ctype.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *s_stdin,*s_stdout;
extern struct node *xlstack;
extern int xlfsize;

/* external routines */
extern FILE *fopen();

/* local variables */
static char buf[STRMAX+1];

/* forward declarations */
FORWARD struct node *printit();
FORWARD struct node *flatsize();
FORWARD struct node *explode();
FORWARD struct node *makesym();
FORWARD struct node *openit();
FORWARD struct node *getfile();

/* xread - read an expression */
struct node *xread(args)
  struct node *args;
{
    struct node *oldstk,fptr,eof,*val;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&eof,NULL);

    /* get file pointer and eof value */
    fptr.n_ptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    eof.n_ptr = (args ? xlarg(&args) : NULL);
    xllastarg(args);

    /* read an expression */
    if (!xlread(fptr.n_ptr,&val))
	val = eof.n_ptr;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the expression */
    return (val);
}

/* xprint - builtin function 'print' */
struct node *xprint(args)
  struct node *args;
{
    return (printit(args,TRUE,TRUE));
}

/* xprin1 - builtin function 'prin1' */
struct node *xprin1(args)
  struct node *args;
{
    return (printit(args,TRUE,FALSE));
}

/* xprinc - builtin function princ */
struct node *xprinc(args)
  struct node *args;
{
    return (printit(args,FALSE,FALSE));
}

/* xterpri - terminate the current print line */
struct node *xterpri(args)
  struct node *args;
{
    struct node *fptr;

    /* get file pointer */
    fptr = (args ? getfile(&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* terminate the print line and return nil */
    xlterpri(fptr);
    return (NULL);
}

/* printit - common print function */
LOCAL struct node *printit(args,pflag,tflag)
  struct node *args; int pflag,tflag;
{
    struct node *oldstk,fptr,val;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&val,NULL);

    /* get expression to print and file pointer */
    val.n_ptr = xlarg(&args);
    fptr.n_ptr = (args ? getfile(&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* print the value */
    xlprint(fptr.n_ptr,val.n_ptr,pflag);

    /* terminate the print line if necessary */
    if (tflag)
	xlterpri(fptr.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result */
    return (val.n_ptr);
}

/* xflatsize - compute the size of a printed representation using prin1 */
struct node *xflatsize(args)
  struct node *args;
{
    return (flatsize(args,TRUE));
}

/* xflatc - compute the size of a printed representation using princ */
struct node *xflatc(args)
  struct node *args;
{
    return (flatsize(args,FALSE));
}

/* flatsize - compute the size of a printed expression */
LOCAL struct node *flatsize(args,pflag)
  struct node *args; int pflag;
{
    struct node *oldstk,val;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* get the expression */
    val.n_ptr = xlarg(&args);
    xllastarg(args);

    /* print the value to compute its size */
    xlfsize = 0;
    xlprint(NULL,val.n_ptr,pflag);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the length of the expression */
    val.n_ptr = newnode(INT);
    val.n_ptr->n_int = xlfsize;
    return (val.n_ptr);
}

/* xexplode - explode an expression */
struct node *xexplode(args)
  struct node *args;
{
    return (explode(args,TRUE));
}

/* xexplc - explode an expression using princ */
struct node *xexplc(args)
  struct node *args;
{
    return (explode(args,FALSE));
}

/* explode - internal explode routine */
LOCAL struct node *explode(args,pflag)
  struct node *args; int pflag;
{
    struct node *oldstk,val,strm;

    /* create a new stack frame */
    oldstk = xlsave(&val,&strm,NULL);

    /* get the expression */
    val.n_ptr = xlarg(&args);
    xllastarg(args);

    /* create a stream */
    strm.n_ptr = newnode(LIST);

    /* print the value into the stream */
    xlprint(strm.n_ptr,val.n_ptr,pflag);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the list of characters */
    return (strm.n_ptr->n_listvalue);
}

/* ximplode - implode a list of characters into an expression */
struct node *ximplode(args)
  struct node *args;
{
    return (makesym(args,TRUE));
}

/* xmaknam - implode a list of characters into an uninterned symbol */
struct node *xmaknam(args)
  struct node *args;
{
    return (makesym(args,FALSE));
}

/* makesym - internal implode routine */
LOCAL struct node *makesym(args,intflag)
  struct node *args; int intflag;
{
    struct node *list,*val;
    char *p;

    /* get the list */
    list = xlarg(&args);
    xllastarg(args);

    /* assemble the symbol's pname */
    for (p = buf; list && list->n_type == LIST; list = list->n_listnext) {
	if ((val = list->n_listvalue) == NULL || val->n_type != INT)
	    xlfail("bad character list");
	if ((int)(p - buf) < STRMAX)
	    *p++ = val->n_int;
    }
    *p = 0;

    /* create a symbol */
    val = (intflag ? xlenter(buf,DYNAMIC) : xlmakesym(buf,DYNAMIC));

    /* return the symbol */
    return (val);
}

/* xopeni - open an input file */
struct node *xopeni(args)
  struct node *args;
{
    return (openit(args,"r"));
}

/* xopeno - open an output file */
struct node *xopeno(args)
  struct node *args;
{
    return (openit(args,"w"));
}

/* openit - common file open routine */
LOCAL struct node *openit(args,mode)
  struct node *args; char *mode;
{
    struct node *fname,*val;
    FILE *fp;

    /* get the file name */
    fname = xlmatch(STR,&args);
    xllastarg(args);

    /* try to open the file */
    if ((fp = fopen(fname->n_str,mode)) != NULL) {
	val = newnode(FPTR);
	val->n_fp = fp;
	val->n_savech = 0;
    }
    else
	val = NULL;

    /* return the file pointer */
    return (val);
}

/* xclose - close a file */
struct node *xclose(args)
  struct node *args;
{
    struct node *fptr;

    /* get file pointer */
    fptr = xlmatch(FPTR,&args);
    xllastarg(args);

    /* make sure the file exists */
    if (fptr->n_fp == NULL)
	xlfail("file not open");

    /* close the file */
    fclose(fptr->n_fp);
    fptr->n_fp = NULL;

    /* return nil */
    return (NULL);
}

/* xrdchar - read a character from a file */
struct node *xrdchar(args)
  struct node *args;
{
    struct node *fptr,*val;
    int ch;

    /* get file pointer */
    fptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    xllastarg(args);

    /* get character and check for eof */
    if ((ch = xlgetc(fptr)) == EOF)
	val = NULL;
    else {
	val = newnode(INT);
	val->n_int = ch;
    }

    /* return the character */
    return (val);
}

/* xpkchar - peek at a character from a file */
struct node *xpkchar(args)
  struct node *args;
{
    struct node *flag,*fptr,*val;
    int ch;

    /* peek flag and get file pointer */
    flag = (args ? xlarg(&args) : NULL);
    fptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    xllastarg(args);

    /* skip leading white space and get a character */
    if (flag)
	while ((ch = xlpeek(fptr)) != EOF && isspace(ch))
	    xlgetc(fptr);
    else
	ch = xlpeek(fptr);

    /* check for eof */
    if (ch == EOF)
	val = NULL;
    else {
	val = newnode(INT);
	val->n_int = ch;
    }

    /* return the character */
    return (val);
}

/* xwrchar - write a character to a file */
struct node *xwrchar(args)
  struct node *args;
{
    struct node *fptr,*chr;

    /* get the character and file pointer */
    chr = xlmatch(INT,&args);
    fptr = (args ? getfile(&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* put character to the file */
    xlputc(fptr,chr->n_int);

    /* return the character */
    return (chr);
}

/* xreadline - read a line from a file */
struct node *xreadline(args)
  struct node *args;
{
    struct node *oldstk,fptr,str;
    char *p,*sptr;
    int len,ch;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&str,NULL);

    /* get file pointer */
    fptr.n_ptr = (args ? getfile(&args) : s_stdin->n_symvalue);
    xllastarg(args);

    /* make a string node */
    str.n_ptr = newnode(STR);
    str.n_ptr->n_strtype = DYNAMIC;

    /* get character and check for eof */
    len = 0; p = buf;
    while ((ch = xlgetc(fptr.n_ptr)) != EOF && ch != '\n') {

	/* check for buffer overflow */
	if ((int)(p - buf) == STRMAX) {
	    *p = 0;
 	    sptr = stralloc(len + STRMAX); *sptr = 0;
	    if (len) {
		strcpy(sptr,str.n_ptr->n_str);
		strfree(str.n_ptr->n_str);
	    }
	    str.n_ptr->n_str = sptr;
	    strcat(sptr,buf);
	    len += STRMAX;
	    p = buf;
	}

	/* store the character */
	*p++ = ch;
    }

    /* check for end of file */
    if (len == 0 && p == buf && ch == EOF) {
	xlstack = oldstk;
	return (NULL);
    }

    /* append the last substring */
    *p = 0;
    sptr = stralloc(len + (int)(p - buf)); *sptr = 0;
    if (len) {
	strcpy(sptr,str.n_ptr->n_str);
	strfree(str.n_ptr->n_str);
    }
    str.n_ptr->n_str = sptr;
    strcat(sptr,buf);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the string */
    return (str.n_ptr);
}

/* getfile - get a file or stream */
LOCAL struct node *getfile(pargs)
  struct node **pargs;
{
    struct node *arg;

    /* get a file or stream (cons) or nil */
    if (arg = xlarg(pargs)) {
	if (arg->n_type == FPTR) {
	    if (arg->n_fp == NULL)
		xlfail("file closed");
	}
	else if (arg->n_type != LIST)
	    xlfail("bad file or stream");
    }
    return (arg);
}
!Funky!Stuff!
echo x XLFTAB.C
cat > XLFTAB.C << '!Funky!Stuff!'
/* xlftab.c - xlisp function table */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external functions */
extern struct node
    *xeval(),*xapply(),*xfuncall(),*xquote(),
    *xset(),*xsetq(),*xdefun(),*xndefun(),
    *xgensym(),*xintern(),*xsymname(),*xsymplist(),
    *xget(),*xputprop(),*xremprop(),
    *xcar(),*xcaar(),*xcadr(),*xcdr(),*xcdar(),*xcddr(),
    *xcons(),*xlist(),*xappend(),*xreverse(),*xlast(),*xnth(),*xnthcdr(),
    *xmember(),*xmemq(),*xassoc(),*xassq(),*xsubst(),*xsublis(),*xlength(),
    *xmapcar(),*xmaplist(),
    *xrplca(),*xrplcd(),*xnconc(),*xdelete(),*xdelq(),
    *xatom(),*xsymbolp(),*xnumberp(),*xboundp(),*xnull(),*xlistp(),*xconsp(),
    *xeq(),*xequal(),
    *xcond(),*xand(),*xor(),*xlet(),*xif(),*xprogn(),
    *xwhile(),*xrepeat(),
    *xadd(),*xsub(),*xmul(),*xdiv(),*xrem(),*xminus(),*xmin(),*xmax(),*xabs(),
    *xadd1(),*xsub1(),*xbitand(),*xbitior(),*xbitxor(),*xbitnot(),
    *xlss(),*xleq(),*xeql(),*xneq(),*xgeq(),*xgtr(),
    *xstrlen(),*xstrcat(),*xsubstr(),*xascii(),*xchr(),*xatoi(),*xitoa(),
    *xread(),*xprint(),*xprin1(),*xprinc(),*xterpri(),
    *xflatsize(),*xflatc(),*xexplode(),*xexplc(),*ximplode(),*xmaknam(),
    *xopeni(),*xopeno(),*xclose(),*xrdchar(),*xpkchar(),*xwrchar(),*xreadline(),
    *xload(),*xgc(),*xexpand(),*xalloc(),*xmem(),*xtype(),*xexit();

struct fdef ftab[] = {

	/* evaluator functions */
	"eval",		SUBR,	xeval,
	"apply",	SUBR,	xapply,
	"funcall",	SUBR,	xfuncall,
	"quote",	FSUBR,	xquote,

	/* symbol functions */
	"set",		SUBR,	xset,
	"setq",		FSUBR,	xsetq,
	"defun",	FSUBR,	xdefun,
	"ndefun",	FSUBR,	xndefun,
	"gensym",	SUBR,	xgensym,
	"intern",	SUBR,	xintern,
	"symbol-name",	SUBR,	xsymname,
	"symbol-plist",	SUBR,	xsymplist,
	"get",		SUBR,	xget,
	"putprop",	SUBR,	xputprop,
	"remprop",	SUBR,	xremprop,

	/* list functions */
	"car",		SUBR,	xcar,
	"caar",		SUBR,	xcaar,
	"cadr",		SUBR,	xcadr,
	"cdr",		SUBR,	xcdr,
	"cdar",		SUBR,	xcdar,
	"cddr",		SUBR,	xcddr,
	"cons",		SUBR,	xcons,
	"list",		SUBR,	xlist,
	"append",	SUBR,	xappend,
	"reverse",	SUBR,	xreverse,
	"last",		SUBR,	xlast,
	"nth",		SUBR,	xnth,
	"nthcdr",	SUBR,	xnthcdr,
	"member",	SUBR,	xmember,
	"memq",		SUBR,	xmemq,
	"assoc",	SUBR,	xassoc,
	"assq",		SUBR,	xassq,
	"subst",	SUBR,	xsubst,
	"sublis",	SUBR,	xsublis,
	"length",	SUBR,	xlength,
	"mapcar",	SUBR,	xmapcar,
	"maplist",	SUBR,	xmaplist,

	/* destructive list functions */
	"rplaca",	SUBR,	xrplca,
	"rplacd",	SUBR,	xrplcd,
	"nconc",	SUBR,	xnconc,
	"delete",	SUBR,	xdelete,
	"delq",		SUBR,	xdelq,

	/* predicate functions */
	"atom",		SUBR,	xatom,
	"symbolp",	SUBR,	xsymbolp,
	"numberp",	SUBR,	xnumberp,
	"boundp",	SUBR,	xboundp,
	"null",		SUBR,	xnull,
	"not",		SUBR,	xnull,
	"listp",	SUBR,	xlistp,
	"consp",	SUBR,	xconsp,
	"eq",		SUBR,	xeq,
	"equal",	SUBR,	xequal,

	/* control functions */
	"cond",		FSUBR,	xcond,
	"and",		FSUBR,	xand,
	"or",		FSUBR,	xor,
	"let",		FSUBR,	xlet,
	"if",		FSUBR,	xif,
	"progn",	FSUBR,	xprogn,
	"while",	FSUBR,	xwhile,
	"repeat",	FSUBR,	xrepeat,

	/* arithmetic functions */
	"+",		SUBR,	xadd,
	"-",		SUBR,	xsub,
	"*",		SUBR,	xmul,
	"/",		SUBR,	xdiv,
	"1+",		SUBR,	xadd1,
	"1-",		SUBR,	xsub1,
	"rem",		SUBR,	xrem,
	"minus",	SUBR,	xminus,
	"min",		SUBR,	xmin,
	"max",		SUBR,	xmax,
	"abs",		SUBR,	xabs,

	/* bitwise logical functions */
	"bit-and",	SUBR,	xbitand,
	"bit-ior",	SUBR,	xbitior,
	"bit-xor",	SUBR,	xbitxor,
	"bit-not",	SUBR,	xbitnot,

	/* numeric comparison functions */
	"<",		SUBR,	xlss,
	"<=",		SUBR,	xleq,
	"=",		SUBR,	xeql,
	"/=",		SUBR,	xneq,
	">=",		SUBR,	xgeq,
	">",		SUBR,	xgtr,

	/* string functions */
	"strlen",	SUBR,	xstrlen,
	"strcat",	SUBR,	xstrcat,
	"substr",	SUBR,	xsubstr,
	"ascii",	SUBR,	xascii,
	"chr",		SUBR,	xchr,
	"atoi",		SUBR,	xatoi,
	"itoa",		SUBR,	xitoa,

	/* I/O functions */
	"read",		SUBR,	xread,
	"print",	SUBR,	xprint,
	"prin1",	SUBR,	xprin1,
	"princ",	SUBR,	xprinc,
	"terpri",	SUBR,	xterpri,
	"flatsize",	SUBR,	xflatsize,
	"flatc",	SUBR,	xflatc,
	"explode",	SUBR,	xexplode,
	"explodec",	SUBR,	xexplc,
	"implode",	SUBR,	ximplode,
	"maknam",	SUBR,	xmaknam,

	/* file I/O functions */
	"openi",	SUBR,	xopeni,
	"openo",	SUBR,	xopeno,
	"close",	SUBR,	xclose,
	"read-char",	SUBR,	xrdchar,
	"peek-char",	SUBR,	xpkchar,
	"write-char",	SUBR,	xwrchar,
	"readline",	SUBR,	xreadline,

	/* system functions */
	"load",		SUBR,	xload,
	"gc",		SUBR,	xgc,
	"expand",	SUBR,	xexpand,
	"alloc",	SUBR,	xalloc,
	"mem",		SUBR,	xmem,
	"type",		SUBR,	xtype,
	"exit",		SUBR,	xexit,

	0
};
!Funky!Stuff!
echo x XLINIT.C
cat > XLINIT.C << '!Funky!Stuff!'
/* xlinit.c - xlisp initialization module */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* global variables */
struct node *true;
struct node *s_quote;
struct node *s_lambda,*s_nlambda;
struct node *s_stdin,*s_stdout;
struct node *s_tracenable;
struct node *k_rest,*k_aux;
struct node *a_subr;
struct node *a_fsubr;
struct node *a_list;
struct node *a_sym;
struct node *a_int;
struct node *a_str;
struct node *a_obj;
struct node *a_fptr;

/* external variables */
extern struct fdef ftab[];

/* xlinit - xlisp initialization routine */
xlinit()
{
    struct fdef *fptr;
    struct node *sym;

    /* initialize xlisp (must be in this order) */
    xlminit();	/* initialize xldmem.c */
    xlsinit();	/* initialize xlsym.c */
    xleinit();	/* initialize xleval.c */
    xloinit();	/* initialize xlobj.c */

    /* enter the builtin functions */
    for (fptr = ftab; fptr->f_name; fptr++)
	xlsubr(fptr->f_name,fptr->f_type,fptr->f_fcn);

    /* enter the 't' symbol */
    true = xlsenter("t");
    true->n_symvalue = true;

    /* enter some important symbols */
    s_quote	= xlsenter("quote");
    s_lambda	= xlsenter("lambda");
    s_nlambda	= xlsenter("nlambda");
    k_rest	= xlsenter("&rest");
    k_aux	= xlsenter("&aux");

    /* enter *standard-input* and *standard-output* */
    s_stdin = xlsenter("*standard-input*");
    s_stdin->n_symvalue = newnode(FPTR);
    s_stdin->n_symvalue->n_fp = stdin;
    s_stdin->n_symvalue->n_savech = 0;
    s_stdout = xlsenter("*standard-output*");
    s_stdout->n_symvalue = newnode(FPTR);
    s_stdout->n_symvalue->n_fp = stdout;
    s_stdout->n_symvalue->n_savech = 0;

    /* enter the error traceback enable flag */
    s_tracenable = xlsenter("*tracenable*");
    s_tracenable->n_symvalue = true;

    /* enter a copyright notice into the oblist */
    sym = xlsenter("**Copyright-1984-by-David-Betz**");
    sym->n_symvalue = true;

    /* enter type names */
    a_subr	= xlsenter("SUBR");
    a_fsubr	= xlsenter("FSUBR");
    a_list	= xlsenter("LIST");
    a_sym	= xlsenter("SYM");
    a_int	= xlsenter("INT");
    a_str	= xlsenter("STR");
    a_obj	= xlsenter("OBJ");
    a_fptr	= xlsenter("FPTR");
}
!Funky!Stuff!
echo x XLIO.C
cat > XLIO.C << '!Funky!Stuff!'
/* xlio - xlisp i/o routines */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* global variables */
int xlplevel=0;
int xlfsize=0;

/* external variables */
extern struct node *xlstack;
extern struct node *s_stdin;

/* local variables */
static int prompt=TRUE;

/* xlgetc - get a character from a file or stream */
int xlgetc(fptr)
  struct node *fptr;
{
    struct node *lptr,*cptr;
    FILE *fp;
    int ch;

    /* check for input from nil */
    if (fptr == NULL)
	ch = EOF;

    /* otherwise, check for input from a stream */
    else if (fptr->n_type == LIST) {
	if ((lptr = fptr->n_listvalue) == NULL)
	    ch = EOF;
	else {
	    if (lptr->n_type != LIST ||
		(cptr = lptr->n_listvalue) == NULL || cptr->n_type != INT)
		xlfail("bad stream");
	    if ((fptr->n_listvalue = lptr->n_listnext) == NULL)
		fptr->n_listnext = NULL;
	    ch = cptr->n_int;
	}
    }

    /* otherwise, check for a buffered file character */
    else if (ch = fptr->n_savech)
	fptr->n_savech = 0;

    /* otherwise, get a new character */
    else {

	/* get the file pointer */
	fp = fptr->n_fp;

	/* prompt if necessary */
	if (prompt && fp == stdin) {
	    if (xlplevel > 0)
		printf("%d> ",xlplevel);
	    else
		printf("> ");
	    prompt = FALSE;
	}

	/* get the character */
	if ((ch = getc(fp)) == '\n' && fp == stdin)
	    prompt = TRUE;

	/* check for input abort */
	if (fp == stdin && ch == '\007') {
	    putchar('\n');
	    xlfail("input aborted");
	}
    }

    /* return the character */
    return (ch);
}

/* xlpeek - peek at a character from a file or stream */
int xlpeek(fptr)
  struct node *fptr;
{
    struct node *lptr,*cptr;
    int ch;

    /* check for input from nil */
    if (fptr == NULL)
	ch = EOF;

    /* otherwise, check for input from a stream */
    else if (fptr->n_type == LIST) {
	if ((lptr = fptr->n_listvalue) == NULL)
	    ch = EOF;
	else {
	    if (lptr->n_type != LIST ||
		(cptr = lptr->n_listvalue) == NULL || cptr->n_type != INT)
		xlfail("bad stream");
	    ch = cptr->n_int;
	}
    }

    /* otherwise, get the next file character and save it */
    else
	ch = fptr->n_savech = xlgetc(fptr);

    /* return the character */
    return (ch);
}

/* xlputc - put a character to a file or stream */
xlputc(fptr,ch)
  struct node *fptr; int ch;
{
    struct node *oldstk,lptr;

    /* count the character */
    xlfsize++;

    /* check for output to nil */
    if (fptr == NULL)
	;

    /* otherwise, check for output to a stream */
    else if (fptr->n_type == LIST) {
	oldstk = xlsave(&lptr,NULL);
	lptr.n_ptr = newnode(LIST);
	lptr.n_ptr->n_listvalue = newnode(INT);
	lptr.n_ptr->n_listvalue->n_int = ch;
	if (fptr->n_listnext)
	    fptr->n_listnext->n_listnext = lptr.n_ptr;
	else
	    fptr->n_listvalue = lptr.n_ptr;
	fptr->n_listnext = lptr.n_ptr;
	xlstack = oldstk;
    }

    /* otherwise, output the character to a file */
    else
	putc(ch,fptr->n_fp);
}

/* xlflush - flush the input buffer */
int xlflush()
{
    if (!prompt)
	while (xlgetc(s_stdin->n_symvalue) != '\n')
	    ;
}
!Funky!Stuff!
echo x XLISP.C
cat > XLISP.C << '!Funky!Stuff!'
/* xlisp - a small subset of lisp */

#ifdef AZTEC
#include "stdio.h"
#include "setjmp.h"
#else
#include <stdio.h>
#include <setjmp.h>
#endif

#include "xlisp.h"

/* global variables */
jmp_buf *xljmpbuf;
jmp_buf topjmpbuf;

/* external variables */
extern struct node *xlenv;
extern struct node *xlstack;
extern struct node *s_stdin,*s_stdout;

/* main - the main routine */
main(argc,argv)
  int argc; char *argv[];
{
    struct node expr;
    int i;

    /* print the banner line */
    printf("XLISP version 1.2\n");

    /* setup the error handler context buffer */
    xljmpbuf = topjmpbuf;

    /* setup initialization error handler */
    if (setjmp(xljmpbuf)) {
	printf("fatal initialization error\n");
	exit();
    }

    /* initialize xlisp */
    xlinit();

    /* load "init.lsp" */
    if (setjmp(xljmpbuf) == 0)
	xlload("init");

    /* load any files mentioned on the command line */
    if (setjmp(xljmpbuf) == 0)
	for (i = 1; i < argc; i++) {
	    printf("[ loading \"%s\" ]\n",argv[i]);
	    if (!xlload(argv[i]))
		xlfail("can't load file");
	}

    /* main command processing loop */
    while (TRUE) {

	/* setup the error return */
	setjmp(xljmpbuf);

	/* free any previous expression and leftover context */
	xlstack = xlenv = NULL;

	/* create a new stack frame */
	xlsave(&expr,NULL);

	/* read an expression */
	if (!xlread(s_stdin->n_symvalue,&expr.n_ptr))
	    break;

	/* evaluate the expression */
	expr.n_ptr = xleval(expr.n_ptr);

	/* print it */
	xlprint(s_stdout->n_symvalue,expr.n_ptr,TRUE);
	xlterpri(s_stdout->n_symvalue);
    }
}
!Funky!Stuff!
echo x XLISP.H
cat > XLISP.H << '!Funky!Stuff!'
/* xlisp - a small subset of lisp */

/* system specific definitions */

/* NNODES	number of nodes to allocate in each request */
/* TDEPTH	trace stack depth */
/* FORWARD	type of a forward declaration (usually "") */
/* LOCAL	type of a local function (usually "static") */

/* for the Computer Innovations compiler */
#ifdef CI
#define NNODES		1000
#define TDEPTH		500
#endif

/* for the CPM68K compiler */
#ifdef CPM68K
#define NNODES		1000
#define TDEPTH		500
#define LOCAL
#undef NULL
#define NULL		(char *)0
#endif

/* for the DeSmet compiler */
#ifdef DESMET
#define NNODES		1000
#define TDEPTH		500
#define LOCAL
#define getc(fp)	getcx(fp)
#define EOF		-1
#endif

/* for the VAX-11 C compiler */
#ifdef vms
#define NNODES		2000
#define TDEPTH		1000
#endif

/* for the DECUS C compiler */
#ifdef decus
#define NNODES		200
#define TDEPTH		100
#define FORWARD		extern
#endif

/* for unix compilers */
#ifdef unix
#define NNODES		200
#define TDEPTH		100
#endif

/* for the AZTEC C compiler */
#ifdef AZTEC
#define NNODES		200
#define TDEPTH		100
#define getc(fp)	getcx(fp)
#define putc(ch,fp)	aputc(ch,fp)
#define malloc		alloc
#define strchr		index
#endif

/* default important definitions */
#ifndef NNODES
#define NNODES	200
#endif
#ifndef TDEPTH
#define TDEPTH	100
#endif
#ifndef FORWARD
#define FORWARD
#endif
#ifndef LOCAL
#define LOCAL	static
#endif

/* useful definitions */
#define TRUE	1
#define FALSE	0

/* program limits */
#define STRMAX		100		/* maximum length of a string constant */
	
/* node types */
#define FREE	0
#define SUBR	1
#define FSUBR	2
#define LIST	3
#define SYM	4
#define INT	5
#define STR	6
#define OBJ	7
#define FPTR	8

/* node flags */
#define MARK	1
#define LEFT	2

/* string types */
#define DYNAMIC	0
#define STATIC	1

/* symbol structure */
struct xsym {
    struct node *xsy_plist;	/* symbol plist - points to (name.plist) */
    struct node *xsy_value;	/* the current value */
};

/* subr/fsubr node structure */
struct xsubr {
    struct node *(*xsu_subr)();	/* pointer to an internal routine */
};

/* list node structure */
struct xlist {
    struct node *xl_value;	/* value at this node */
    struct node *xl_next;	/* next node */
};

/* integer node structure */
struct xint {
    int xi_int;			/* integer value */
};

/* string node structure */
struct xstr {
    int xst_type;		/* string type */
    char *xst_str;		/* string pointer */
};

/* object node structure */
struct xobj {
    struct node *xo_obclass;	/* class of object */
    struct node *xo_obdata;	/* instance data */
};

/* file pointer node structure */
struct xfptr {
    FILE *xf_fp;		/* the file pointer */
    int xf_savech;		/* lookahead character for input files */
};


/* shorthand macros for accessing node substructures */

/* symbol node */
#define n_symplist	n_info.n_xsym.xsy_plist
#define n_symvalue	n_info.n_xsym.xsy_value

/* subr/fsubr node */
#define n_subr		n_info.n_xsubr.xsu_subr

/* list node (and message node and binding node) */
#define n_listvalue	n_info.n_xlist.xl_value
#define n_listnext	n_info.n_xlist.xl_next
#define n_msg		n_info.n_xlist.xl_value
#define n_msgcode	n_info.n_xlist.xl_next
#define n_bndsym	n_info.n_xlist.xl_value
#define n_bndvalue	n_info.n_xlist.xl_next
#define n_left		n_info.n_xlist.xl_value
#define n_right		n_info.n_xlist.xl_next
#define n_ptr		n_info.n_xlist.xl_value

/* integer node */
#define n_int		n_info.n_xint.xi_int

/* string node */
#define n_str		n_info.n_xstr.xst_str
#define n_strtype	n_info.n_xstr.xst_type

/* object node */
#define n_obclass	n_info.n_xobj.xo_obclass
#define n_obdata	n_info.n_xobj.xo_obdata

/* file pointer node */
#define n_fp		n_info.n_xfptr.xf_fp
#define n_savech	n_info.n_xfptr.xf_savech

/* node structure */
struct node {
    char n_type;		/* type of node */
    char n_flags;		/* flag bits */
    union {			/* value */
	struct xsym n_xsym;	/*     symbol node */
	struct xsubr n_xsubr;	/*     subr/fsubr node */
	struct xlist n_xlist;	/*     list node */
	struct xint n_xint;	/*     integer node */
	struct xstr n_xstr;	/*     string node */
	struct xobj n_xobj;	/*     object node */
	struct xfptr n_xfptr;	/*     file pointer node */
    } n_info;
};

/* function table entry structure */
struct fdef {
    char *f_name;
    int f_type;
    struct node *(*f_fcn)();
};

/* external procedure declarations */
extern struct node *xleval();		/* evaluate an expression */
extern struct node *xlapply();		/* apply a function to arguments */
extern struct node *xlevlist();		/* evaluate a list of arguments */
extern struct node *xlarg();		/* fetch an argument */
extern struct node *xlevarg();		/* fetch and evaluate an argument */
extern struct node *xlmatch();		/* fetch an typed argument */
extern struct node *xlevmatch();	/* fetch and evaluate a typed arg */
extern struct node *xlsend();		/* send a message to an object */
extern struct node *xlenter();		/* enter a symbol */
extern struct node *xlsenter();		/* enter a symbol with a static pname */
extern struct node *xlintern();		/* intern a symbol */
extern struct node *xlmakesym();	/* make an uninterned symbol */
extern struct node *xlsave();		/* generate a stack frame */
extern struct node *xlobsym();		/* find an object's class or instance
					   variable */
extern struct node *xlgetprop();	/* get the value of a property */
extern char *xlsymname();		/* get the print name of a symbol */

extern struct node *newnode();		/* allocate a new node */
extern char *stralloc();		/* allocate string space */
extern char *strsave();			/* make a safe copy of a string */
!Funky!Stuff!
exit 0
-- 
John Woods, Charles River Data Systems
decvax!frog!john, mit-eddie!jfw, JFW%mit-ccc@MIT-XX

When your puppy goes off in another room,
is it because of the explosive charge?

jfw@mit-eddie.UUCP (John Woods) (02/03/85)

Replace this line with your message
Ok, I will:

This is the third of five parts in my posting of Dave Betz' XLISP 1.2.

echo extract with sh, not csh
echo x XLISP.MEM
cat > XLISP.MEM << '!Funky!Stuff!'





          XLISP: An Experimental Object Oriented Language

                            Version 1.2

                          October 11, 1984


                                 by
                             David Betz
                         114 Davenport Ave.
                       Manchester, NH  03103

                       (603) 625-4691 (home)


    XLISP: An Experimental Object Oriented Language                 Page 2
    TABLE OF CONTENTS


            1.0     INTRODUCTION . . . . . . . . . . . . . . . . . . . . 3
            2.0     A NOTE FROM THE AUTHOR . . . . . . . . . . . . . . . 4
            3.0     XLISP COMMAND LOOP . . . . . . . . . . . . . . . . . 5
            4.0     DATA TYPES . . . . . . . . . . . . . . . . . . . . . 6
            5.0     THE EVALUATOR  . . . . . . . . . . . . . . . . . . . 7
            6.0     LEXICAL CONVENTIONS  . . . . . . . . . . . . . . . . 8
            7.0     OBJECTS  . . . . . . . . . . . . . . . . . . . . . . 9
            8.0     SYMBOLS  . . . . . . . . . . . . . . . . . . . . .  12
            9.0     EVALUATION FUNCTIONS . . . . . . . . . . . . . . .  13
            10.0    SYMBOL FUNCTIONS . . . . . . . . . . . . . . . . .  14
            11.0    PROPERTY LIST FUNCTIONS  . . . . . . . . . . . . .  15
            12.0    LIST FUNCTIONS . . . . . . . . . . . . . . . . . .  16
            13.0    DESTRUCTIVE LIST FUNCTIONS . . . . . . . . . . . .  18
            14.0    PREDICATE FUNCTIONS  . . . . . . . . . . . . . . .  19
            15.0    CONTROL FUNCTIONS  . . . . . . . . . . . . . . . .  20
            16.0    ARITHMETIC FUNCTIONS . . . . . . . . . . . . . . .  22
            17.0    BITWISE LOGICAL FUNCTIONS  . . . . . . . . . . . .  23
            18.0    RELATIONAL FUNCTIONS . . . . . . . . . . . . . . .  24
            19.0    STRING FUNCTIONS . . . . . . . . . . . . . . . . .  25
            20.0    INPUT/OUTPUT FUNCTIONS . . . . . . . . . . . . . .  26
            21.0    FILE I/O FUNCTIONS . . . . . . . . . . . . . . . .  27
            22.0    SYSTEM FUNCTIONS . . . . . . . . . . . . . . . . .  28


    XLISP: An Experimental Object Oriented Language                 Page 3
    INTRODUCTION


    1.0  INTRODUCTION

    XLISP is an experimental programming language combining some
    of  the  features  of LISP with an object oriented extension
    capability.  It was  implemented  to  allow  experimentation
    with  object oriented programming on small computers.  There
    are currently implementations running on  the  PDP-11  under
    RSX-11,  RT-11, and UNIX V7, on the VAX-11 under VAX/VMS and
    Berkeley VAX/UNIX, on the Z-80 under CP/M-80, on  the  Z8000
    under UNIX V7, and on the 8088/8086 under CP/M-86 or MS-DOS.
    A version is currently being developed for the  68000  under
    CP/M-68K.   It  is  completely  written  in  the programming
    language 'C'  and  is  easily  extended  with  user  written
    built-in  functions  and classes.  It is available in source
    form free of charge to  non-commercial  users.   Prospective
    commercial users should contact the author for permission to
    use XLISP.

    Many traditional LISP functions are built  into  XLISP.   In
    addition,  XLISP defines the objects 'Object' and 'Class' as
    primitives.   'Object'  is  the  only  class  that  has   no
    superclass  and  hence  is  the  root of the class heirarchy
    tree.  'Class'  is  the  class  of  which  all  classes  are
    instances  (it  is  the  only  object that is an instance of
    itself).

    This document is intended  to  be  a  brief  description  of
    XLISP.    It   assumes  some  knowledge  of  LISP  and  some
    understanding   of   the   concepts   of   object   oriented
    programming.

    Version 1.2 of XLISP differs from  version  1.1  in  several
    ways.   It  supports  many  more Lisp functions.  Also, many
    version 1.1  functions  have  been  renamed  and/or  changed
    slightly  to follow traditional Lisp usage.  One of the most
    frequently reported problems in version  1.1  resulted  from
    many  functions being named after their equivilent functions
    in the C language.  This turned  out  to  be  confusing  for
    people who were trying to learn XLISP using traditional LISP
    texts as references.  Version 1.2 renames these functions to
    be compatible with more traditional dialects of LISP.

    A recommended text for learning LISP programming is the book
    "LISP"  by Winston and Horn and published by Addison Wesley.
    The first edition of this book is based on MacLisp  and  the
    second  edition  is based on Common Lisp.  Future version of
    XLISP will migrate towards compatiblility with Common Lisp.


    XLISP: An Experimental Object Oriented Language                 Page 4
    A NOTE FROM THE AUTHOR


    2.0  A NOTE FROM THE AUTHOR

    If you have any problems with XLISP, feel free to contact me
    for  help  or  advice.   Please remember that since XLISP is
    available in source form in  a  high  level  language,  many
    users  have  been  making versions available on a variety of
    machines.  If you call to report a problem with  a  specific
    version,  I may not be able to help you if that version runs
    on a machine to which I don't have access.

    If you find a bug  in  XLISP,  first  try  to  fix  the  bug
    yourself  using  the  source  code  provided.   If  you  are
    successful in fixing the bug, send the bug report along with
    the  fix to me.  If you don't have access to a C compiler or
    are unable to fix a bug, please send the bug  report  to  me
    and I'll try to fix it.

    Any suggestions for improvements  will  be  welcomed.   Feel
    free  to  extend  the  language  in  whatever way suits your
    needs.  However, PLEASE DO  NOT  RELEASE  ENHANCED  VERSIONS
    WITHOUT  CHECKING  WITH  ME  FIRST!!  I would like to be the
    clearing house for new features added to XLISP.  If you want
    to  add  features for your own personal use, go ahead.  But,
    if you want to distribute your enhanced version, contact  me
    first.  Please remember that the goal of XLISP is to provide
    a language to learn and  experiment  with  LISP  and  object
    oriented programming on small computers.  Version 1.2 barely
    fits on a 64K Z-80 running CP/M-80.


    XLISP: An Experimental Object Oriented Language                 Page 5
    XLISP COMMAND LOOP


    3.0  XLISP COMMAND LOOP

    When XLISP is started, it first  tries  to  load  "init.lsp"
    from  the  default directory.  It then loads any files named
    as parameters on the command line (after appending ".lsp" to
    their names).  It then issues the following prompt:

    >

    This indicates that XLISP is waiting for an expression to be
    typed.   When  an  incomplete expression has been typed (one
    where the left and right parens don't match)  XLISP  changes
    its prompt to:

    n>

    where n is an integer indicating how  many  levels  of  left
    parens remain unclosed.

    When a complete expression has been entered, XLISP  attempts
    to  evaluate  that  expression.  If the expression evaluates
    successfully, XLISP prints the result of the evaluation  and
    then  returns  to  the  initial  prompt  waiting for another
    expression to be typed.

    Input can be aborted at any time by typing the CONTROL-G key
    (it may be necessary to follow CONTROL-G by return).


    XLISP: An Experimental Object Oriented Language                 Page 6
    DATA TYPES


    4.0  DATA TYPES

    There are several different data types  available  to  XLISP
    programmers.


          o  lists

          o  symbols

          o  strings

          o  integers

          o  objects

          o  file pointers

          o  subrs/fsubrs (built-in functions)

    Another data type is the stream.  A stream is  a  list  node
    whose car points to the head of a list of integers and whose
    cdr points to the last list node  of  the  list.   An  empty
    stream  is  a  list node whose car and cdr are nil.  Each of
    the integers in the list represent characters in the stream.
    When  a  character  is read from a stream, the first integer
    from the head of the list is removed and returned.   When  a
    character  is  written to a stream, the integer representing
    the character code of the character is appended to  the  end
    of  the  list.   When  a function indicates that it takes an
    input source as a parameter, this parameter can either be an
    input  file pointer or a stream.  Similarly, when a function
    indicates that it takes an output sink as a parameter,  this
    parameter can either be an output file pointer or a stream.


    XLISP: An Experimental Object Oriented Language                 Page 7
    THE EVALUATOR


    5.0  THE EVALUATOR

    The process of evaluation in XLISP:

          o  Integers,  strings,  objects,  file  pointers,  and
             subrs evaluate to themselves

          o  Symbols evaluate to the value associated with their
             current binding

          o  Lists are evaluated by evaluating the first element
             of the list

              o  If it evaluates to a subr, the  remaining  list
                 elements  are  evaluated and the subr is called
                 with these evaluated expressions as arguments.

              o  If it evaluates  to  an  fsubr,  the  fsubr  is
                 called  using  the  remaining  list elements as
                 arguments  (they  are  evaluated  by  the  subr
                 itself if necessary)

              o  If it evaluates to a list and the  car  of  the
                 list  is  'lambda', the remaining list elements
                 are evaluated and the resulting expressions are
                 bound  to  the  formal  arguments of the lambda
                 expression.   The  body  of  the  function   is
                 executed within this new binding environment.

              o  If it evaluates to a list and the  car  of  the
                 list  is 'nlambda', the remaining list elements
                 are  bound  to  the  formal  arguments  of  the
                 nlambda  expression.   The body of the function
                 is   executed   within   this    new    binding
                 environment.

              o  If it evaluates to an object, the  second  list
                 element  is  evaluated  and  used  as a message
                 selector.  The message formed by combining  the
                 selector  with the values of the remaining list
                 elements is sent to the object.




    XLISP: An Experimental Object Oriented Language                 Page 8
    LEXICAL CONVENTIONS


    6.0  LEXICAL CONVENTIONS

    The following conventions are followed when  entering  XLISP
    programs:

    Comments in XLISP code begin with a semi-colon character and
    continue to the end of the line.

    Symbol names  in  XLISP  can  consist  of  any  sequence  of
    non-blank printable characters except the following:

            ( ) . ' " ;

    Upper and lower case characters are distinct.   The  symbols
    'CAR' and 'car' are not the same.  The names of all built-in
    functions are in lower case.   The  names  of  all  built-in
    objects  are  lower  case  with  an initial capital.  Symbol
    names must not begin with a digit.

    Integer literals consist of a sequence of digits  optionally
    beginning with a '+' or '-'.  The range of values an integer
    can represent is limited by the size of a  C  'int'  on  the
    machine that XLISP is running on.

    Literal strings are sequences of  characters  surrounded  by
    double  quotes.   Within quoted strings the '\' character is
    used to allow non-printable characters to be included.   The
    codes recognized are:

            \\      means the character '\'
            \n      means newline
            \t      means tab
            \r      means return
            \e      means escape
            \nnn    means the character whose octal code is nnn

    The single quote character can be used as a shorthand for  a
    call on the function 'quote':

                            'foo
    is equivalent to:
                            (quote foo)


    XLISP: An Experimental Object Oriented Language                 Page 9
    OBJECTS


    7.0  OBJECTS

    Definitions:

          o  selector - a symbol used to select  an  appropriate
             method

          o  message - a selector and a list of actual arguments

          o  method - the code that implements a message

    Since XLISP was  created  to  provide  a  simple  basis  for
    experimenting  with  object oriented programming, one of the
    primitive data types included was 'object'.   In  XLISP,  an
    object  consists of a data structure containing a pointer to
    the object's class as well as a list containing  the  values
    of the object's instance variables.

    Officially, there is no way to see inside an object (look at
    the  values  of  its  instance  variables).  The only way to
    communicate with an object is by sending it a message.  When
    the  XLISP  evaluator  evaluates  a  list the value of whose
    first element is an object, it interprets the value  of  the
    second  element  of the list (which must be a symbol) as the
    message selector.  The evaluator determines the class of the
    receiving object and attempts to find a method corresponding
    to the message selector in the set of messages  defined  for
    that  class.   If  the  message is not found in the object's
    class and the class has a super-class, the search  continues
    by  looking  at  the  messages  defined for the super-class.
    This process continues from  one  super-class  to  the  next
    until  a  method  for the message is found.  If no method is
    found, an error occurs.

    When a method is found, the evaluator  binds  the  receiving
    object  to  the  symbol 'self', binds the class in which the
    method was found to the symbol 'msgclass', and evaluates the
    method  using the remaining elements of the original list as
    arguments  to  the  method.   These  arguments  are   always
    evaluated prior to being bound to their corresponding formal
    arguments.  The result of evaluating the method becomes  the
    result of the expression.


    XLISP: An Experimental Object Oriented Language                Page 10
    OBJECTS


    Classes:

    Object  THE TOP OF THE CLASS HEIRARCHY

        Messages:

            show    SHOW AN OBJECT'S INSTANCE VARIABLES
                returns     the object

            class   RETURN THE CLASS OF AN OBJECT
                returns     the class of the object

            isnew   THE DEFAULT OBJECT INITIALIZATION ROUTINE
                returns     the object

            sendsuper <sel> [<args>...] SEND SUPERCLASS A MESSAGE
                <sel>       the message selector
                <args>      the message arguments
                returns     the result of sending the message


    XLISP: An Experimental Object Oriented Language                Page 11
    OBJECTS


    Class   THE CLASS OF ALL OBJECT CLASSES (including itself)

        Messages:

            new     CREATE A NEW INSTANCE OF A CLASS
                returns     the new class object

            isnew [<scls>]  INITIALIZE A NEW CLASS
                <scls>      the superclass
                returns     the new class object

            answer <msg> <fargs> <code>     ADD A MESSAGE TO A CLASS
                <msg>       the message symbol
                <fargs>     the formal argument list
                              this list is of the form:
                                (<farg>... [&rest <rarg>] [&aux <aux>...])
                              where
                                <farg>      a formal argument
                                <rarg>      bound to the rest of the arguments
                                <aux>       a auxiliary variable
                <code>      a list of executable expressions
                returns     the object

            ivars <vars>    DEFINE THE LIST OF INSTANCE VARIABLES
                <vars>      the list of instance variable symbols
                returns     the object

            cvars <vars>    DEFINE THE LIST OF CLASS VARIABLES
                <vars>      the list of class variable symbols
                returns     the object


    When a new instance of a class is  created  by  sending  the
    message  'new'  to  an  existing  class, the message 'isnew'
    followed by whatever parameters were  passed  to  the  'new'
    message is sent to the newly created object.

    When a new class is created by sending the 'new' message  to
    the  object  'Class', an optional parameter may be specified
    indicating  the  superclass  of  the  new  class.   If  this
    parameter  is  omitted,  the new class will be a subclass of
    'Object'.  A class inherits all  instance  variables,  class
    variables, and methods from its super-class.


    XLISP: An Experimental Object Oriented Language                Page 12
    SYMBOLS


    8.0  SYMBOLS


          o  self  -  the  current  object  (within  a   message
             context)

          o  msgclass - the class in which  the  current  method
             was found

          o  *oblist* - the object list

          o  *standard-input* - the standard input file

          o  *standard-output* - the standard output file

          o  *tracenable* - flag controlling trace back printout
             on errors

          o  *unbound* - indicator for unbound symbols



    XLISP: An Experimental Object Oriented Language                Page 13
    EVALUATION FUNCTIONS


    9.0  EVALUATION FUNCTIONS

    (eval <expr>)  EVALUATE AN XLISP EXPRESSION
        <expr>      the expression to be evaluated
        returns     the result of evaluating the expression

    (apply <fun> <args>)  APPLY A FUNCTION TO A LIST OF ARGUMENTS
        <fun>       the function to apply (or function symbol)
        <args>      the argument list
        returns     the result of applying the function to the argument list

    (funcall <fun> <arg>...)  CALL A FUNCTION WITH ARGUMENTS
        <fun>       the function to call (or function symbol)
        <arg>       arguments to pass to the function
        returns     the result of calling the function with the arguments

    (quote <expr>)  RETURN AN EXPRESSION UNEVALUATED
        <expr>      the expression to be quoted (quoted)
        returns     <expr> unevaluated


    XLISP: An Experimental Object Oriented Language                Page 14
    SYMBOL FUNCTIONS


    10.0  SYMBOL FUNCTIONS

    (set <sym> <expr>)  SET THE VALUE OF A SYMBOL
        <sym>       the symbol being set
        <expr>      the new value
        returns     the new value

    (setq <sym> <expr>)  SET THE VALUE OF A SYMBOL
        <sym>       the symbol being set (quoted)
        <expr>      the new value
        returns     the new value

    (defun <sym> <fargs> <expr>...)  DEFINE A FUNCTION WITH EVALUATED ARGS
    (ndefun <sym> <fargs> <expr>...)  DEFINE A FUNCTION WITH UNEVALUATED ARGS
        <sym>       symbol being defined (quoted)
        <fargs>     list of formal arguments (quoted)
                      this list is of the form:
                        (<farg>... [&rest <rarg>] [&aux <aux>...])
                      where
                        <farg>      is a formal argument
                        <rarg>      bound to the rest of the arguments
                        <aux>       is an auxiliary variable
        <expr>      expressions constituting the body of the
                    function (quoted)
        returns     the function symbol

    (gensym <tag>)  GENERATE A SYMBOL
        <tag>       symbol/string/number
        returns     the new symbol

    (intern <sym>)  INTERN A SYMBOL ON THE OBLIST
        <sym>       the symbol
        returns     the interned symbol

    (symbol-name <sym>)  GET THE PRINT NAME OF A SYMBOL
        <sym>       the symbol
        returns     the symbol's print name

    (symbol-plist <sym>)  GET THE PROPERTY LIST OF A SYMBOL
        <sym>       the symbol
        returns     the symbol's property list


    XLISP: An Experimental Object Oriented Language                Page 15
    PROPERTY LIST FUNCTIONS


    11.0  PROPERTY LIST FUNCTIONS

    (get <sym> <prop>)  GET THE VALUE OF A PROPERTY
        <sym>       the symbol
        <prop>      the property symbol
        returns     the property value or nil

    (putprop <sym> <value> <prop>)  PUT A PROPERTY ONTO A PROPERTY LIST
        <sym>       the symbol
        <value>     the property value
        <prop>      the property symbol
        returns     the value

    (remprop <prop> <sym>)  REMOVE A PROPERTY
        <sym>       the symbol
        <prop>      the property symbol
        returns     nil


    XLISP: An Experimental Object Oriented Language                Page 16
    LIST FUNCTIONS


    12.0  LIST FUNCTIONS

    (car <expr>)  RETURN THE CAR OF A LIST NODE
        <expr>      the list node
        returns     the car of the list node

    (cdr <expr>)  RETURN THE CDR OF A LIST NODE
        <expr>      the list node
        returns     the cdr of the list node

    (caar <expr>) == (car (car <expr>))
    (cadr <expr>) == (car (cdr <expr>))
    (cdar <expr>) == (cdr (car <expr>))
    (cddr <expr>) == (cdr (cdr <expr>))

    (cons <expr1> <expr2>)  CONSTRUCT A NEW LIST NODE
        <expr1>     the car of the new list node
        <expr2>     the cdr of the new list node
        returns     the new list node

    (list <expr>...)  CREATE A LIST OF VALUES
        <expr>      expressions to be combined into a list
        returns     the new list

    (append <expr>...)  APPEND LISTS
        <expr>      lists whose elements are to be appended
        returns     the new list

    (reverse <expr>)  REVERSE A LIST
        <expr>      the list to reverse
        returns     a new list in the reverse order

    (last <list>)  RETURN THE LAST LIST NODE OF A LIST
        <list>      the list
        returns     the last list node in the list

    (member <expr> <list>)  FIND AN EXPRESSION IN A LIST
        <expr>      the expression to find (equal test)
        <list>      the list to search
        returns     the remainder of the list starting with the expression

    (memq <expr> <list>)  FIND AN EXPRESSION IN A LIST
        <expr>      the expression to find (eq test)
        <list>      the list to find it in
        returns     the remainder of the list starting with the expression


    XLISP: An Experimental Object Oriented Language                Page 17
    LIST FUNCTIONS


    (assoc <expr> <alist>)  FIND AN EXPRESSION IN AN ASSOCIATION LIST
        <expr>      the expression to find (equal test)
        <alist>     the association list
        returns     the alist entry or nil

    (assq <expr> <alist>)  FIND AN EXPRESSION IN AN ASSOCIATION LIST
        <expr>      the expression to find (eq test)
        <alist>     the association list
        returns     the alist entry or nil

    (length <expr>)  FIND THE LENGTH OF A LIST
        <expr>      the list
        returns     the length of the list

    (nth <n> <list>)  RETURN THE NTH ELEMENT OF A LIST
        <n>         the number of the element to return (zero origin)
        <list>      the list
        returns     the nth element or nil if the list isn't that long

    (nthcdr <n> <list>)  RETURN THE NTH CDR OF A LIST
        <n>         the number of the element to return (zero origin)
        <list>      the list
        returns     the nth cdr or nil if the list isn't that long

    (mapcar <fcn> <list1>...<listn>)  APPLY FUNCTION TO SUCCESSIVE CARS
        <fcn>       the function or function name
        <list1..n>  a list for each argument of the function
        returns     the list of values returned by each function invocation

    (maplist <fcn> <list1>...<listn>)  APPLY FUNCTION TO SUCCESSIVE CDRS
        <fcn>       the function or function name
        <list1..n>  a list for each argument of the function
        returns     the list of values returned by each function invocation

    (subst <to> <from> <expr>)  SUBSTITUTE ONE EXPRESSION FOR ANOTHER
        <to>        the new expression
        <from>      the old expression
        <expr>      the expression in which to do the substitutions
        returns     the expression with substitutions

    (sublis <alist> <expr>)  SUBSTITUTE USING AN ASSOCIATION LIST
        <alist>     the association list
        <expr>      the expression in which to do the substitutions
        returns     the expression with substitutions


    XLISP: An Experimental Object Oriented Language                Page 18
    DESTRUCTIVE LIST FUNCTIONS


    13.0  DESTRUCTIVE LIST FUNCTIONS

    (rplaca <list> <expr>)  REPLACE THE CAR OF A LIST NODE
        <list>      the list node
        <expr>      the new value for the car of the list node
        returns     the list node after updating the car

    (rplacd <list> <expr>)  REPLACE THE CDR OF A LIST NODE
        <list>      the list node
        <expr>      the new value for the cdr of the list node
        returns     the list node after updating the cdr

    (nconc <list>...)  DESTRUCTIVELY CONCATENATE LISTS
        <list>      lists to concatenate
        returns     the result of concatenating the lists

    (delete <expr> <list>)  DELETE OCCURANCES OF AN EXPRESSION FROM A LIST
        <expr>      the expression to delete (equal test)
        <list>      the list
        returns     the list with the matching expressions deleted

    (delq <expr> <list>)  DELETE OCCURANCES OF AN EXPRESSION FROM A LIST
        <expr>      the expression to delete (eq test)
        <list>      the list
        returns     the list with the matching expressions deleted


    XLISP: An Experimental Object Oriented Language                Page 19
    PREDICATE FUNCTIONS


    14.0  PREDICATE FUNCTIONS

    (atom <expr>)  IS THIS AN ATOM?
        <expr>      the expression to check
        returns     t if the value is an atom, nil otherwise

    (symbolp <expr>)  IS THIS A SYMBOL?
        <expr>      the expression to check
        returns     t if the expression is a symbol, nil otherwise

    (numberp <expr>)  IS THIS A NUMBER?
        <expr>      the expression to check
        returns     t if the expression is a symbol, nil otherwise

    (null <expr>)  IS THIS AN EMPTY LIST?
        <expr>      the list to check
        returns     t if the list is empty, nil otherwise

    (not <expr>)  IS THIS FALSE?
        <expr>      the expression to check
        return      t if the expression is nil, nil otherwise

    (listp <expr>)  IS THIS A LIST?
        <expr>      the expression to check
        returns     t if the value is a list node or nil, nil otherwise

    (consp <expr>)  IS THIS A NON-EMPTY LIST?
        <expr>      the expression to check
        returns     t if the value is a list node, nil otherwise

    (boundp <sym>)  IS THIS A BOUND SYMBOL?
        <sym>       the symbol
        returns     t if a value is bound to the symbol, nil otherwise

    (eq <expr1> <expr2>)  ARE THE EXPRESSIONS IDENTICAL?
        <expr1>     the first expression
        <expr2>     the second expression
        returns     t if they are equal, nil otherwise

    (equal <expr1> <expr2>)  ARE THE EXPRESSIONS EQUAL?
        <expr1>     the first expression
        <expr2>     the second expression
        returns     t if they are equal, nil otherwise


    XLISP: An Experimental Object Oriented Language                Page 20
    CONTROL FUNCTIONS


    15.0  CONTROL FUNCTIONS

    (cond <pair>...)  EVALUATE CONDITIONALLY
        <pair>      pair consisting of:
                        (<pred> <expr>...)
                      where
                        <pred>      is a predicate expression
                        <expr>      evaluated if the predicate
                                    is not nil
        returns     the value of the first expression whose predicate
                    is not nil

    (let (<binding>...) <expr>...)  BIND SYMBOLS AND EVALUATE EXPRESSIONS
        <binding>   the variable bindings each of which is either:
                    1)  a symbol (which is initialized to nil)
                    2)  a list whose car is a symbol and whose cadr
                            is an initialization expression
        <expr>...   the expressions to be evaluated with the specified bindings
        returns     the value of the last expression

    (and <expr>...)  THE LOGICAL AND OF A LIST OF EXPRESSIONS
        <expr>...   the expressions to be ANDed
        returns     nil if any expression evaluates to nil,
                    otherwise the value of the last expression
                    (evaluation of expressions stops after the first
                     expression that evaluates to nil)

    (or <expr>...)  THE LOGICAL OR OF A LIST OF EXPRESSIONS
        <expr>...   the expressions to be ORed
        returns     nil if all expressions evaluate to nil,
                    otherwise the value of the first non-nil expression
                    (evaluation of expressions stops after the first
                     expression that does not evaluate to nil)

    (if <texpr> <expr1> [<expr2>])  EXECUTE EXPRESSIONS CONDITIONALLY
        <texpr>     the test expression
        <expr1>     the expression to be evaluated if texpr is non-nil
        <expr2>     the expression to be evaluated if texpr is nil
        returns     the value of the selected expression

    (progn <expr>...)  EXECUTE EXPRESSIONS SEQUENTIALLY
        <expr>...   the expressions to evaluate
        returns     the value of the last expression

    (while <texpr> <expr>...)  ITERATE WHILE AN EXPRESSION IS TRUE
        <texpr>     the test expression evaluated at start of each iteration
        <expr>...   the expressions evaluated as long as <texpr> evaluates to
                    non-nil
        returns     the value of the last expression


    XLISP: An Experimental Object Oriented Language                Page 21
    CONTROL FUNCTIONS


    (repeat <iexpr> <expr>...)  ITERATE USING A REPEAT COUNT
        <iexpr>     the integer expression indicating the repeat count
        <expr>...   the expressions evaluated <iexpr> times
        returns     the value of the last expression


    XLISP: An Experimental Object Oriented Language                Page 22
    ARITHMETIC FUNCTIONS


    16.0  ARITHMETIC FUNCTIONS

    (+ <expr>...)  ADD A LIST OF NUMBERS
        <expr>...   the numbers
        returns     the result of the addition

    (- <expr>...)  SUBTRACT A LIST OF NUMBERS
        <expr>...   the numbers
        returns     the result of the subtraction

    (* <expr>...)  MULTIPLY A LIST OF NUMBERS
        <expr>...   the numbers
        returns     the result of the multiplication

    (/ <expr>...)  DIVIDE A LIST OF NUMBERS
        <expr>...   the numbers
        returns     the result of the division

    (1+ <expr>)  ADD ONE TO A NUMBER
        <expr>      the number
        returns     the number plus one

    (1- <expr>)  SUBTRACT ONE FROM A NUMBER
        <expr>      the number
        returns     the number minus one

    (rem <expr>...)  REMAINDER OF A LIST OF NUMBERS
        <expr>...   the numbers
        returns     the result of the remainder operation

    (minus <expr>)  NEGATE A NUMBER
        <expr>      the number
        returns     the number negated

    (min <expr>...)  THE SMALLEST OF A LIST OF NUMBERS
        <expr>...   the expressions to be checked
        returns     the smallest number in the list

    (max <expr>...)  THE LARGEST OF A LIST OF NUMBERS
        <expr>...   the expressions to be checked
        returns     the largest number in the list

    (abs <expr>)  THE ABSOLUTE VALUE OF A NUMBER
        <expr>      the number
        returns     the absolute value of the number


    XLISP: An Experimental Object Oriented Language                Page 23
    BITWISE LOGICAL FUNCTIONS


    17.0  BITWISE LOGICAL FUNCTIONS

    (bit-and <expr>...)  THE BITWISE AND OF A LIST OF NUMBERS
        <expr>      the numbers
        returns     the result of the and operation

    (bit-ior <expr...)  THE BITWISE INCLUSIVE OR OF A LIST OF NUMBERS
        <expr>      the numbers
        returns     the result of the inclusive or operation

    (bit-xor <expr...)  THE BITWISE EXCLUSIVE OR OF A LIST OF NUMBERS
        <expr>      the numbers
        returns     the result of the exclusive or operation

    (bit-not <expr>)  THE BITWISE NOT OF A NUMBER
        <expr>      the number
        returns     the bitwise inversion of number


    XLISP: An Experimental Object Oriented Language                Page 24
    RELATIONAL FUNCTIONS


    18.0  RELATIONAL FUNCTIONS

    The relational functions can be used to compare integers  or
    strings.   The  functions  '='  and '/=' can also be used to
    compare other types.  The result  of  these  comparisons  is
    computed the same way as for 'eq'.

    (< <e1> <e2>)  TEST FOR LESS THAN
        <e1>        the left operand of the comparison
        <e2>        the right operand of the comparison
        returns     the result of comparing <e1> with <e2>

    (<= <e1> <e2>)  TEST FOR LESS THAN OR EQUAL TO
        <e1>        the left operand of the comparison
        <e2>        the right operand of the comparison
        returns     the result of comparing <e1> with <e2>

    (= <e1> <e2>)  TEST FOR EQUAL TO
        <e1>        the left operand of the comparison
        <e2>        the right operand of the comparison
        returns     the result of comparing <e1> with <e2>

    (/= <e1> <e2>)  TEST FOR NOT EQUAL TO
        <e1>        the left operand of the comparison
        <e2>        the right operand of the comparison
        returns     the result of comparing <e1> with <e2>

    (>= <e1> <e2>)  TEST FOR GREATER THAN OR EQUAL TO
        <e1>        the left operand of the comparison
        <e2>        the right operand of the comparison
        returns     the result of comparing <e1> with <e2>

    (> <e1> <e2>)  TEST FOR GREATER THAN
        <e1>        the left operand of the comparison
        <e2>        the right operand of the comparison
        returns     the result of comparing <e1> with <e2>


    XLISP: An Experimental Object Oriented Language                Page 25
    STRING FUNCTIONS


    19.0  STRING FUNCTIONS

    (strcat <expr>...)  CONCATENATE STRINGS
        <expr>...   the strings to concatenate
        returns     the result of concatenating the strings

    (strlen <expr>)  COMPUTE THE LENGTH OF A STRING
        <expr>      the string
        returns     the length of the string

    (substr <expr> <sexpr> [<lexpr>]) EXTRACT A SUBSTRING
        <expr>      the string
        <sexpr>     the starting position
        <lexpr>     the length (default is rest of string)
        returns     substring starting at <sexpr> for <lexpr>

    (ascii <expr>)  NUMERIC VALUE OF CHARACTER
        <expr>      the string
        returns     the ascii code of the first character

    (chr <expr>)  CHARACTER EQUIVALENT OF ASCII VALUE
        <expr>      the numeric expression
        returns     a one character string whose first character is <expr>

    (atoi <expr>)  CONVERT AN ASCII STRING TO AN INTEGER
        <expr>      the string
        returns     the integer value of the string expression

    (itoa <expr>)  CONVERT AN INTEGER TO AN ASCII STRING
        <expr>      the integer
        returns     the string representation of the integer value


    XLISP: An Experimental Object Oriented Language                Page 26
    INPUT/OUTPUT FUNCTIONS


    20.0  INPUT/OUTPUT FUNCTIONS

    (read [<source>[<eof>]])  READ AN XLISP EXPRESSION
        <source>    the input source (default is standard input)
        <eof>       the value to return on end of file (default is nil)
        returns     the expression read

    (print <expr> [<sink>])  PRINT A LIST OF VALUES ON A NEW LINE
        <expr>      the expressions to be printed
        <sink>      the output sink (default is standard output)
        returns     nil

    (prin1 <expr> [<sink>])  PRINT A LIST OF VALUES
        <expr>      the expressions to be printed
        <sink>      the output sink (default is standard output)
        returns     nil

    (princ <expr> [<sink>])  PRINT A LIST OF VALUES WITHOUT QUOTING
        <expr>      the expressions to be printed
        <sink>      the output sink (default is standard output)
        returns     nil

    (terpri [<sink>])  TERMINATE THE CURRENT PRINT LINE
        <sink>      the output sink (default is standard output)
        returns     nil

    (flatsize <expr>)  LENGTH OF PRINTED REPRESENTATION USING PRIN1
        <expr>      the expression
        returns     the length

    (flatc <expr>)  LENGTH OF PRINTED REPRESENTATION USING PRINC
        <expr>      the expression
        returns     the length

    (explode <expr>)  CHARACTERS IN PRINTED REPRESENTATION USING PRIN1
        <expr>      the expression
        returns     the list of characters

    (explodec <expr>)  CHARACTERS IN PRINTED REPRESENTATION USING PRINC
        <expr>      the expression
        returns     the list of characters

    (maknam <list>)  BUILD AN UNINTERNED SYMBOL FROM A LIST OF CHARACTERS
        <list>      list of characters in symbol name
        returns     the symbol

    (implode <list>)  BUILD AN INTERNED SYMBOL FROM A LIST OF CHARACTERS
        <list>      list of characters in symbol name
        returns     the symbol


    XLISP: An Experimental Object Oriented Language                Page 27
    FILE I/O FUNCTIONS


    21.0  FILE I/O FUNCTIONS

    (openi <fname>)  OPEN AN INPUT FILE
        <fname>     the file name string
        returns     a file pointer

    (openo <fname>)  OPEN AN OUTPUT FILE
        <fname>     the file name string
        returns     a file pointer

    (close <fp>)  CLOSE A FILE
        <fp>        the file pointer
        returns     nil

    (read-char [<source>])  READ A CHARACTER FROM A FILE OR STREAM
        <source>    the input source (default is standard input)
        returns     the character (integer)

    (peek-char [<flag> [<source>]])  PEEK AT THE NEXT CHARACTER
        <flag>      flag for skipping white space (default is nil)
        <source>    the input source (default is standard input)
        returns     the character (integer)

    (write-char <ch> [<sink>])  WRITE A CHARACTER TO A FILE OR STREAM
        <ch>        the character to put (integer)
        <sink>      the output sink (default is standard output)
        returns     the character (integer)

    (readline [<source>])  READ A LINE FROM A FILE OR STREAM
        <source>    the input source (default is standard input)
        returns     the input string


    XLISP: An Experimental Object Oriented Language                Page 28
    SYSTEM FUNCTIONS


    22.0  SYSTEM FUNCTIONS

    (load <fname>)  LOAD AN XLISP SOURCE FILE
        <fname>     the filename string (".lsp" is appended)
        returns     the filename

    (gc)  FORCE GARBAGE COLLECTION
        returns     nil

    (expand <num>)  EXPAND MEMORY BY ADDING SEGMENTS
        <num>       the number of segments to add
        returns     the number of segments added

    (alloc <num>)  CHANGE NUMBER OF NODES TO ALLOCATE IN EACH SEGMENT
        <num>       the number of nodes to allocate
        returns     the old number of nodes to allocate

    (mem)  SHOW MEMORY ALLOCATION STATISTICS
        returns     nil

    (type <expr>)  RETURNS THE TYPE OF THE EXPRESSION
        <expr>      the expression to return the type of
        returns     nil if the value is nil otherwise one of the symbols:
                        SYM   for symbols
                        OBJ   for objects
                        LIST  for list nodes
                        SUBR  for subroutine nodes with evaluated arguments
                        FSUBR for subroutine nodes with unevaluated arguments
                        STR   for string nodes
                        INT   for integer nodes
                        FPTR  for file pointer nodes

    (exit)  EXIT XLISP
        returns     never returns
!Funky!Stuff!
exit 0
-- 
John Woods, Charles River Data Systems
decvax!frog!john, mit-eddie!jfw, JFW%mit-ccc@MIT-XX

When your puppy goes off in another room,
is it because of the explosive charge?

jfw@mit-eddie.UUCP (John Woods) (02/03/85)

Replace this line with your cute comment

This is part 4 of 5 in a posting of Dave Betz' newest XLISP (mentioned on
net.sources some time back).  It is, as the other four parts, in shar format.

==================================
echo extract with sh, not csh
echo x XLLIST.C
cat > XLLIST.C << '!Funky!Stuff!'
/* xllist - xlisp list builtin functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;
extern struct node *s_unbound;
extern struct node *true;

/* forward declarations */
FORWARD struct node *nth(),*member(),*assoc(),*afind();
FORWARD struct node *delete(),*subst(),*sublis(),*map();
FORWARD int eq(),equal();

/* xcar - return the car of a list */
struct node *xcar(args)
  struct node *args;
{
    struct node *list;

    /* get the list and return its car */
    list = xlmatch(LIST,&args);
    xllastarg(args);
    return (list ? list->n_listvalue : NULL);
}

/* xcaar - return the caar of a list */
struct node *xcaar(args)
  struct node *args;
{
    struct node *list;

    /* get the list and return its caar */
    list = xlmatch(LIST,&args);
    xllastarg(args);
    if (list) list = list->n_listvalue;
    return (list ? list->n_listvalue : NULL);
}

/* xcadr - return the cadr of a list */
struct node *xcadr(args)
  struct node *args;
{
    struct node *list;

    /* get the list and return its cadr */
    list = xlmatch(LIST,&args);
    xllastarg(args);
    if (list) list = list->n_listnext;
    return (list ? list->n_listvalue : NULL);
}

/* xcdr - return the cdr of a list */
struct node *xcdr(args)
  struct node *args;
{
    struct node *list;

    /* get the list and return its cdr */
    list = xlmatch(LIST,&args);
    xllastarg(args);
    return (list ? list->n_listnext : NULL);
}

/* xcdar - return the cdar of a list */
struct node *xcdar(args)
  struct node *args;
{
    struct node *list;

    /* get the list and return its cdar */
    list = xlmatch(LIST,&args);
    xllastarg(args);
    if (list) list = list->n_listvalue;
    return (list ? list->n_listnext : NULL);
}

/* xcddr - return the cddr of a list */
struct node *xcddr(args)
  struct node *args;
{
    struct node *list;

    /* get the list and return its cddr */
    list = xlmatch(LIST,&args);
    xllastarg(args);
    if (list) list = list->n_listnext;
    return (list ? list->n_listnext : NULL);
}

/* xcons - construct a new list cell */
struct node *xcons(args)
  struct node *args;
{
    struct node *arg1,*arg2,*val;

    /* get the two arguments */
    arg1 = xlarg(&args);
    arg2 = xlarg(&args);
    xllastarg(args);

    /* construct a new list element */
    val = newnode(LIST);
    val->n_listvalue = arg1;
    val->n_listnext  = arg2;

    /* return the list */
    return (val);
}

/* xlist - built a list of the arguments */
struct node *xlist(args)
  struct node *args;
{
    struct node *oldstk,arg,list,val,*last,*lptr;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&list,&val,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* evaluate and append each argument */
    for (last = NULL; arg.n_ptr != NULL; last = lptr) {

	/* evaluate the next argument */
	val.n_ptr = xlarg(&arg.n_ptr);

	/* append this argument to the end of the list */
	lptr = newnode(LIST);
	if (last == NULL)
	    list.n_ptr = lptr;
	else
	    last->n_listnext = lptr;
	lptr->n_listvalue = val.n_ptr;
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the list */
    return (list.n_ptr);
}

/* xappend - builtin function append */
struct node *xappend(args)
  struct node *args;
{
    struct node *oldstk,arg,list,last,val,*lptr;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&list,&last,&val,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* evaluate and append each argument */
    while (arg.n_ptr != NULL) {

	/* evaluate the next argument */
	list.n_ptr = xlmatch(LIST,&arg.n_ptr);

	/* append each element of this list to the result list */
	while (list.n_ptr != NULL && list.n_ptr->n_type == LIST) {

	    /* append this element */
	    lptr = newnode(LIST);
	    if (last.n_ptr == NULL)
		val.n_ptr = lptr;
	    else
		last.n_ptr->n_listnext = lptr;
	    lptr->n_listvalue = list.n_ptr->n_listvalue;

	    /* save the new last element */
	    last.n_ptr = lptr;

	    /* move to the next element */
	    list.n_ptr = list.n_ptr->n_listnext;
	}

	/* make sure the list ended in a nil */
	if (list.n_ptr != NULL)
	    xlfail("bad list");
    }

    /* restore previous stack frame */
    xlstack = oldstk;

    /* return the list */
    return (val.n_ptr);
}

/* xreverse - builtin function reverse */
struct node *xreverse(args)
  struct node *args;
{
    struct node *oldstk,list,val,*lptr;

    /* create a new stack frame */
    oldstk = xlsave(&list,&val,NULL);

    /* get the list to reverse */
    list.n_ptr = xlmatch(LIST,&args);
    xllastarg(args);

    /* append each element of this list to the result list */
    while (list.n_ptr != NULL && list.n_ptr->n_type == LIST) {

	/* append this element */
	lptr = newnode(LIST);
	lptr->n_listvalue = list.n_ptr->n_listvalue;
	lptr->n_listnext = val.n_ptr;
	val.n_ptr = lptr;

	/* move to the next element */
	list.n_ptr = list.n_ptr->n_listnext;
    }

    /* make sure the list ended in a nil */
    if (list.n_ptr != NULL)
	xlfail("bad list");

    /* restore previous stack frame */
    xlstack = oldstk;

    /* return the list */
    return (val.n_ptr);
}

/* xlast - return the last cons of a list */
struct node *xlast(args)
  struct node *args;
{
    struct node *list;

    /* get the list */
    list = xlmatch(LIST,&args);
    xllastarg(args);

    /* find the last cons */
    while (list && list->n_type == LIST && list->n_listnext)
	list = list->n_listnext;

    /* make sure the list ended correctly */
    if (list == NULL && list->n_type != LIST)
	xlfail("bad list");

    /* return the last element */
    return (list);
}

/* xmember - builtin function 'member' */
struct node *xmember(args)
  struct node *args;
{
    return (member(args,equal));
}

/* xmemq - builtin function 'memq' */
struct node *xmemq(args)
  struct node *args;
{
    return (member(args,eq));
}

/* member - internal member function */
LOCAL struct node *member(args,fcn)
  struct node *args; int (*fcn)();
{
    struct node *x,*list;

    /* get the expression to look for and the list */
    x = xlarg(&args);
    list = xlmatch(LIST,&args);
    xllastarg(args);

    /* look for the expression */
    for (; list && list->n_type == LIST; list = list->n_listnext)
	if ((*fcn)(x,list->n_listvalue))
	    return (list);

    /* return failure indication */
    return (NULL);
}

/* xassoc - builtin function 'assoc' */
struct node *xassoc(args)
  struct node *args;
{
    return (assoc(args,equal));
}

/* xassq - builtin function 'assq' */
struct node *xassq(args)
  struct node *args;
{
    return (assoc(args,eq));
}

/* assoc - internal assoc function */
LOCAL struct node *assoc(args,fcn)
  struct node *args; int (*fcn)();
{
    struct node *expr,*alist,*pair;

    /* get the expression to look for and the association list */
    expr = xlarg(&args);
    alist = xlmatch(LIST,&args);
    xllastarg(args);

    /* look for the expression */
    return (afind(expr,alist,fcn));
}

/* afind - find a pair in an association list */
LOCAL struct node *afind(expr,alist,fcn)
  struct node *expr,*alist; int (*fcn)();
{
    struct node *pair;

    for (; alist && alist->n_type == LIST; alist = alist->n_listnext)
	if ((pair = alist->n_listvalue) && pair->n_type == LIST)
	    if ((*fcn)(expr,pair->n_listvalue))
		return (pair);
    return (NULL);
}

/* xsubst - substitute one expression for another */
struct node *xsubst(args)
  struct node *args;
{
    struct node *oldstk,to,from,expr,*val;

    /* create a new stack frame */
    oldstk = xlsave(&to,&from,&expr,NULL);

    /* get the to value, the from value and the expression */
    to.n_ptr = xlarg(&args);
    from.n_ptr = xlarg(&args);
    expr.n_ptr = xlarg(&args);
    xllastarg(args);

    /* do the substitution */
    val = subst(to.n_ptr,from.n_ptr,expr.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result */
    return (val);
}

/* subst - substitute one expression for another */
LOCAL struct node *subst(to,from,expr)
  struct node *to,*from,*expr;
{
    struct node *oldstk,car,cdr,*val;

    if (eq(expr,from))
	val = to;
    else if (expr == NULL || expr->n_type != LIST)
	val = expr;
    else {
	oldstk = xlsave(&car,&cdr,NULL);
	car.n_ptr = subst(to,from,expr->n_listvalue);
	cdr.n_ptr = subst(to,from,expr->n_listnext);
	val = newnode(LIST);
	val->n_listvalue = car.n_ptr;
	val->n_listnext = cdr.n_ptr;
	xlstack = oldstk;
    }
    return (val);
}

/* xsublis - substitute using an association list */
struct node *xsublis(args)
  struct node *args;
{
    struct node *oldstk,alist,expr,*val;

    /* create a new stack frame */
    oldstk = xlsave(&alist,&expr,NULL);

    /* get the assocation list and the expression */
    alist.n_ptr = xlmatch(LIST,&args);
    expr.n_ptr = xlarg(&args);
    xllastarg(args);

    /* do the substitution */
    val = sublis(alist.n_ptr,expr.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result */
    return (val);
}

/* sublis - substitute using an association list */
LOCAL struct node *sublis(alist,expr)
  struct node *alist,*expr;
{
    struct node *oldstk,car,cdr,*val;

    if (val = afind(expr,alist,eq))
	val = val->n_listnext;
    else if (expr == NULL || expr->n_type != LIST)
	val = expr;
    else {
	oldstk = xlsave(&car,&cdr,NULL);
	car.n_ptr = sublis(alist,expr->n_listvalue);
	cdr.n_ptr = sublis(alist,expr->n_listnext);
	val = newnode(LIST);
	val->n_listvalue = car.n_ptr;
	val->n_listnext = cdr.n_ptr;
	xlstack = oldstk;
    }
    return (val);
}

/* xnth - return the nth element of a list */
struct node *xnth(args)
  struct node *args;
{
    return (nth(args,FALSE));
}

/* xnthcdr - return the nth cdr of a list */
struct node *xnthcdr(args)
  struct node *args;
{
    return (nth(args,TRUE));
}

/* nth - internal nth function */
LOCAL struct node *nth(args,cdrflag)
  struct node *args; int cdrflag;
{
    struct node *list;
    int n;

    /* get n and the list */
    if ((n = xlmatch(INT,&args)->n_int) < 0)
	xlfail("invalid argument");
    if ((list = xlmatch(LIST,&args)) == NULL)
	xlfail("invalid argument");
    xllastarg(args);

    /* find the nth element */
    for (; n > 0; n--) {
	list = list->n_listnext;
	if (list == NULL || list->n_type != LIST)
	    xlfail("invalid argument");
    }

    /* return the list beginning at the nth element */
    return (cdrflag ? list : list->n_listvalue);
}

/* xlength - return the length of a list */
struct node *xlength(args)
  struct node *args;
{
    struct node *list,*val;
    int n;

    /* get the list */
    list = xlmatch(LIST,&args);
    xllastarg(args);

    /* find the length */
    for (n = 0; list != NULL; n++)
	list = list->n_listnext;

    /* create the value node */
    val = newnode(INT);
    val->n_int = n;

    /* return the length */
    return (val);
}

/* xmapcar - builtin function 'mapcar' */
struct node *xmapcar(args)
  struct node *args;
{
    return (map(args,TRUE));
}

/* xmaplist - builtin function 'maplist' */
struct node *xmaplist(args)
  struct node *args;
{
    return (map(args,FALSE));
}

/* map - internal mapping function */
LOCAL struct node *map(args,carflag)
  struct node *args; int carflag;
{
    struct node *oldstk,fcn,lists,arglist,val,*last,*p,*x,*y;

    /* create a new stack frame */
    oldstk = xlsave(&fcn,&lists,&arglist,&val,NULL);

    /* get the function to apply */
    fcn.n_ptr = xlarg(&args);

    /* make sure there is at least one argument list */
    if (args == NULL)
	xlfail("too few arguments");

    /* get the argument lists */
    while (args) {
	p = newnode(LIST);
	p->n_listnext = lists.n_ptr;
	lists.n_ptr = p;
	p->n_listvalue = xlmatch(LIST,&args);
    }

    /* if the function is a symbol, get its value */
    if (fcn.n_ptr && fcn.n_ptr->n_type == SYM)
	fcn.n_ptr = xleval(fcn.n_ptr);

    /* loop through each of the argument lists */
    for (;;) {

	/* build an argument list from the sublists */
	arglist.n_ptr = NULL;
	for (x = lists.n_ptr; x && (y = x->n_listvalue); x = x->n_listnext) {
	    p = newnode(LIST);
	    p->n_listnext = arglist.n_ptr;
	    arglist.n_ptr = p;
	    p->n_listvalue = (carflag ? y->n_listvalue : y);
	    x->n_listvalue = y->n_listnext;
	}

	/* quit if any of the lists were empty */
	if (x) break;

	/* apply the function to the arguments */
	p = newnode(LIST);
	if (val.n_ptr)
	    last->n_listnext = p;
	else
	    val.n_ptr = p;
	last = p;
	p->n_listvalue = xlapply(fcn.n_ptr,arglist.n_ptr);
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the last test expression value */
    return (val.n_ptr);
}
/* xrplca - replace the car of a list node */
struct node *xrplca(args)
  struct node *args;
{
    struct node *list,*newcar;

    /* get the list and the new car */
    if ((list = xlmatch(LIST,&args)) == NULL)
	xlfail("null list");
    newcar = xlarg(&args);
    xllastarg(args);

    /* replace the car */
    list->n_listvalue = newcar;

    /* return the list node that was modified */
    return (list);
}

/* xrplcd - replace the cdr of a list node */
struct node *xrplcd(args)
  struct node *args;
{
    struct node *list,*newcdr;

    /* get the list and the new cdr */
    if ((list = xlmatch(LIST,&args)) == NULL)
	xlfail("null list");
    newcdr = xlarg(&args);
    xllastarg(args);

    /* replace the cdr */
    list->n_listnext = newcdr;

    /* return the list node that was modified */
    return (list);
}

/* xnconc - destructively append lists */
struct node *xnconc(args)
  struct node *args;
{
    struct node *list,*last,*val;

    /* concatenate each argument */
    for (val = NULL; args; ) {

	/* concatenate this list */
	if (list = xlmatch(LIST,&args)) {

	    /* check for this being the first non-empty list */
	    if (val)
		last->n_listnext = list;
	    else
		val = list;

	    /* find the end of the list */
	    while (list && list->n_type == LIST && list->n_listnext)
		list = list->n_listnext;

	    /* make sure the list ended correctly */
	    if (list == NULL || list->n_type != LIST)
		xlfail("bad list");

	    /* save the new last element */
	    last = list;
	}
    }

    /* return the list */
    return (val);
}

/* xdelete - builtin function 'delete' */
struct node *xdelete(args)
  struct node *args;
{
    return (delete(args,equal));
}

/* xdelq - builtin function 'delq' */
struct node *xdelq(args)
  struct node *args;
{
    return (delete(args,eq));
}

/* delete - internal delete function */
LOCAL struct node *delete(args,fcn)
  struct node *args; int (*fcn)();
{
    struct node *x,*list,*last,*val;

    /* get the expression to delete and the list */
    x = xlarg(&args);
    list = xlmatch(LIST,&args);
    xllastarg(args);

    /* delete leading matches */
    while (list && list->n_type == LIST) {
	if (!(*fcn)(x,list->n_listvalue))
	    break;
	list = list->n_listnext;
    }
    val = last = list;

    /* delete embedded matches */
    if (list && list->n_type == LIST) {

	/* skip the first non-matching element */
	list = list->n_listnext;

	/* look for embedded matches */
	while (list && list->n_type == LIST) {

	    /* check to see if this element should be deleted */
	    if ((*fcn)(x,list->n_listvalue))
		last->n_listnext = list->n_listnext;
	    else
		last = list;

	    /* move to the next element */
	    list = list->n_listnext;
 	}
    }

    /* make sure the list ended in a nil */
    if (list != NULL)
	xlfail("bad list");

    /* return the updated list */
    return (val);
}

/* xatom - is this an atom? */
struct node *xatom(args)
  struct node *args;
{
    struct node *arg;
    return ((arg = xlarg(&args)) == NULL || arg->n_type != LIST ? true : NULL);
}

/* xsymbolp - is this an symbol? */
struct node *xsymbolp(args)
  struct node *args;
{
    struct node *arg;
    return ((arg = xlarg(&args)) && arg->n_type == SYM ? true : NULL);
}

/* xnumberp - is this an number? */
struct node *xnumberp(args)
  struct node *args;
{
    struct node *arg;
    return ((arg = xlarg(&args)) && arg->n_type == INT ? true : NULL);
}

/* xboundp - is this a value bound to this symbol? */
struct node *xboundp(args)
  struct node *args;
{
    struct node *sym;
    sym = xlmatch(SYM,&args);
    return (sym->n_symvalue == s_unbound ? NULL : true);
}

/* xnull - is this null? */
struct node *xnull(args)
  struct node *args;
{
    return (xlarg(&args) == NULL ? true : NULL);
}

/* xlistp - is this a list? */
struct node *xlistp(args)
  struct node *args;
{
    struct node *arg;
    return ((arg = xlarg(&args)) == NULL || arg->n_type == LIST ? true : NULL);
}

/* xconsp - is this a cons? */
struct node *xconsp(args)
  struct node *args;
{
    struct node *arg;
    return ((arg = xlarg(&args)) != NULL && arg->n_type == LIST ? true : NULL);
}

/* xeq - are these equal? */
struct node *xeq(args)
  struct node *args;
{
    struct node *arg1,*arg2;

    /* get the two arguments */
    arg1 = xlarg(&args);
    arg2 = xlarg(&args);
    xllastarg(args);

    /* compare the arguments */
    return (eq(arg1,arg2) ? true : NULL);
}

/* eq - internal eq function */
LOCAL int eq(arg1,arg2)
  struct node *arg1,*arg2;
{
    /* compare the arguments */
    if (arg1 != NULL && arg1->n_type == INT &&
    	arg2 != NULL && arg2->n_type == INT)
	return (arg1->n_int == arg2->n_int);
    else
	return (arg1 == arg2);
}

/* xequal - are these equal? */
struct node *xequal(args)
  struct node *args;
{
    struct node *arg1,*arg2;

    /* get the two arguments */
    arg1 = xlarg(&args);
    arg2 = xlarg(&args);
    xllastarg(args);

    /* compare the arguments */
    return (equal(arg1,arg2) ? true : NULL);
}

/* equal - internal equal function */
LOCAL int equal(arg1,arg2)
  struct node *arg1,*arg2;
{
    /* compare the arguments */
    if (eq(arg1,arg2))
	return (TRUE);
    else if (arg1 && arg1->n_type == LIST &&
	     arg2 && arg2->n_type == LIST)
	return (equal(arg1->n_listvalue,arg2->n_listvalue) &&
		equal(arg1->n_listnext, arg2->n_listnext));
    else
	return (FALSE);
}
!Funky!Stuff!
echo x XLMATH.C
cat > XLMATH.C << '!Funky!Stuff!'
/* xlmath - xlisp builtin arithmetic functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;
extern struct node *true;

/* forward declarations */
FORWARD struct node *unary();
FORWARD struct node *binary();
FORWARD struct node *compare();

/* xadd - builtin function for addition */
LOCAL int add(val,arg)
  int val,arg;
{
    return (val + arg);
}
struct node *xadd(args)
  struct node *args;
{
    return (binary(args,add));
}

/* xsub - builtin function for subtraction */
LOCAL int sub(val,arg)
  int val,arg;
{
    return (val - arg);
}
struct node *xsub(args)
  struct node *args;
{
    return (binary(args,sub));
}

/* xmul - builtin function for multiplication */
LOCAL int mul(val,arg)
  int val,arg;
{
    return (val * arg);
}
struct node *xmul(args)
  struct node *args;
{
    return (binary(args,mul));
}

/* xdiv - builtin function for division */
LOCAL int div(val,arg)
  int val,arg;
{
    return (val / arg);
}
struct node *xdiv(args)
  struct node *args;
{
    return (binary(args,div));
}

/* xrem - builtin function for remainder */
LOCAL int rem(val,arg)
  int val,arg;
{
    return (val % arg);
}
struct node *xrem(args)
  struct node *args;
{
    return (binary(args,rem));
}

/* xmin - builtin function for minimum */
LOCAL int min(val,arg)
  int val,arg;
{
    return (val < arg ? val : arg);
}
struct node *xmin(args)
  struct node *args;
{
    return (binary(args,min));
}

/* xmax - builtin function for maximum */
LOCAL int max(val,arg)
  int val,arg;
{
    return (val > arg ? val : arg);
}
struct node *xmax(args)
  struct node *args;
{
    return (binary(args,max));
}

/* xbitand - builtin function for bitwise and */
LOCAL int bitand(val,arg)
  int val,arg;
{
    return (val & arg);
}
struct node *xbitand(args)
  struct node *args;
{
    return (binary(args,bitand));
}

/* xbitior - builtin function for bitwise inclusive or */
LOCAL int bitior(val,arg)
  int val,arg;
{
    return (val | arg);
}
struct node *xbitior(args)
  struct node *args;
{
    return (binary(args,bitior));
}

/* xbitxor - builtin function for bitwise exclusive or */
LOCAL int bitxor(val,arg)
  int val,arg;
{
    return (val ^ arg);
}
struct node *xbitxor(args)
  struct node *args;
{
    return (binary(args,bitxor));
}

/* xbitnot - bitwise not */
LOCAL int bitnot(arg)
  int arg;
{
    return (~arg);
}
struct node *xbitnot(args)
  struct node *args;
{
    return (unary(args,bitnot));
}

/* xabs - builtin function for absolute value */
LOCAL int abs(arg)
  int arg;
{
    return (arg >= 0 ? arg : -arg);
}
struct node *xabs(args)
  struct node *args;
{
    return (unary(args,abs));
}

/* xadd1 - builtin function for adding one */
LOCAL int add1(arg)
  int arg;
{
    return (arg + 1);
}
struct node *xadd1(args)
  struct node *args;
{
    return (unary(args,add1));
}

/* xsub1 - builtin function for subtracting one */
LOCAL int sub1(arg)
  int arg;
{
    return (arg - 1);
}
struct node *xsub1(args)
  struct node *args;
{
    return (unary(args,sub1));
}

/* xminus - negate a value */
LOCAL int minus(arg)
  int arg;
{
    return (-arg);
}
struct node *xminus(args)
  struct node *args;
{
    return (unary(args,minus));
}

/* unary - handle unary operations */
LOCAL struct node *unary(args,fcn)
  struct node *args; int (*fcn)();
{
    struct node *rval;
    int val;

    /* evaluate the argument */
    val = xlmatch(INT,&args)->n_int;

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* convert and check the value  */
    rval = newnode(INT);
    rval->n_int = (*fcn)(val);

    /* return the result value */
    return (rval);
}

/* binary - handle binary operations */
LOCAL struct node *binary(args,funct)
  struct node *args; int (*funct)();
{
    int first,ival,iarg;
    struct node *val;

    /* initialize */
    first = TRUE;
    ival = 0;

    /* evaluate and sum each argument */
    while (args != NULL) {

	/* get the next argument */
	iarg = xlmatch(INT,&args)->n_int;

	/* accumulate the result value */
	if (first) {
	    ival = iarg;
	    first = FALSE;
	}
	else
	    ival = (*funct)(ival,iarg);
    }

    /* initialize value */
    val = newnode(INT);
    val->n_int = ival;

    /* return the result value */
    return (val);
}

/* xlss - builtin function for < */
LOCAL int lss(cmp)
  int cmp;
{
    return (cmp < 0);
}
struct node *xlss(args)
  struct node *args;
{
    return (compare(args,lss));
}

/* xleq - builtin function for <= */
LOCAL int leq(cmp)
  int cmp;
{
    return (cmp <= 0);
}
struct node *xleq(args)
  struct node *args;
{
    return (compare(args,leq));
}

/* eql - builtin function for = */
LOCAL int eql(cmp)
  int cmp;
{
    return (cmp == 0);
}
struct node *xeql(args)
  struct node *args;
{
    return (compare(args,eql));
}

/* xneq - builtin function for /= */
LOCAL int neq(cmp)
  int cmp;
{
    return (cmp != 0);
}
struct node *xneq(args)
  struct node *args;
{
    return (compare(args,neq));
}

/* xgeq - builtin function for >= */
LOCAL int geq(cmp)
  int cmp;
{
    return (cmp >= 0);
}
struct node *xgeq(args)
  struct node *args;
{
    return (compare(args,geq));
}

/* xgtr - builtin function for > */
LOCAL int gtr(cmp)
  int cmp;
{
    return (cmp > 0);
}
struct node *xgtr(args)
  struct node *args;
{
    return (compare(args,gtr));
}

/* compare - common compare function */
LOCAL struct node *compare(args,funct)
  struct node *args; int (*funct)();
{
    struct node *arg1,*arg2;
    int type1,type2,cmp;

    /* get argument 1 */
    arg1 = xlarg(&args);
    type1 = gettype(arg1);

    /* get argument 2 */
    arg2 = xlarg(&args);
    type2 = gettype(arg2);

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* do the compare */
    if (type1 == STR && type2 == STR)
	cmp = strcmp(arg1->n_str,arg2->n_str);
    else if (type1 == INT && type2 == INT)
	cmp = arg1->n_int - arg2->n_int;
    else
	cmp = arg1 - arg2;

    /* return result of the compare */
    if ((*funct)(cmp))
	return (true);
    else
	return (NULL);
}

/* gettype - return the type of an argument */
LOCAL int gettype(arg)
  struct node *arg;
{
    if (arg == NULL)
	return (LIST);
    else
	return (arg->n_type);
}
!Funky!Stuff!
echo x XLOBJ.C
cat > XLOBJ.C << '!Funky!Stuff!'
/* xlobj - xlisp object functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* global variables */
struct node *self;

/* external variables */
extern struct node *xlstack;
extern struct node *xlenv;
extern struct node *s_stdout;

/* local variables */
static struct node *class;
static struct node *object;
static struct node *new;
static struct node *isnew;
static struct node *msgcls;
static struct node *msgclass;
static int varcnt;

/* instance variable numbers for the class 'Class' */
#define MESSAGES	0	/* list of messages */
#define IVARS		1	/* list of instance variable names */
#define CVARS		2	/* list of class variable names */
#define CVALS		3	/* list of class variable values */
#define SUPERCLASS	4	/* pointer to the superclass */
#define IVARCNT		5	/* number of class instance variables */
#define IVARTOTAL	6	/* total number of instance variables */

/* number of instance variables for the class 'Class' */
#define CLASSSIZE	7

/* forward declarations */
FORWARD struct node *xlivar();
FORWARD struct node *xlcvar();
FORWARD struct node *findmsg();
FORWARD struct node *findvar();
FORWARD struct node *defvars();
FORWARD struct node *makelist();

/* xlclass - define a class */
struct node *xlclass(name,vcnt)
  char *name; int vcnt;
{
    struct node *sym,*cls;

    /* create the class */
    sym = xlsenter(name);
    cls = sym->n_symvalue = newnode(OBJ);
    cls->n_obclass = class;
    cls->n_obdata = makelist(CLASSSIZE);

    /* set the instance variable counts */
    if (vcnt > 0) {
	(xlivar(cls,IVARCNT)->n_listvalue = newnode(INT))->n_int = vcnt;
	(xlivar(cls,IVARTOTAL)->n_listvalue = newnode(INT))->n_int = vcnt;
    }

    /* set the superclass to 'Object' */
    xlivar(cls,SUPERCLASS)->n_listvalue = object;

    /* return the new class */
    return (cls);
}

/* xlmfind - find the message binding for a message to an object */
struct node *xlmfind(obj,msym)
  struct node *obj,*msym;
{
    return (findmsg(obj->n_obclass,msym));
}

/* xlxsend - send a message to an object */
struct node *xlxsend(obj,msg,args)
  struct node *obj,*msg,*args;
{
    struct node *oldstk,method,cptr,eargs,val,*isnewmsg,*oldenv;

    /* save the old environment */
    oldenv = xlenv;

    /* create a new stack frame */
    oldstk = xlsave(&method,&cptr,&eargs,&val,NULL);

    /* get the method for this message */
    method.n_ptr = msg->n_msgcode;

    /* make sure its a function or a subr */
    if (method.n_ptr->n_type != SUBR && method.n_ptr->n_type != LIST)
	xlfail("bad method");

    /* bind the symbols 'self' and 'msgclass' */
    xlbind(self,obj);
    xlbind(msgclass,msgcls);

    /* evaluate the function call */
    eargs.n_ptr = xlevlist(args);
    if (method.n_ptr->n_type == SUBR) {
	xlfixbindings(oldenv);
	val.n_ptr = (*method.n_ptr->n_subr)(eargs.n_ptr);
    }
    else {

	/* bind the formal arguments */
	xlabind(method.n_ptr->n_listvalue,eargs.n_ptr);
	xlfixbindings(oldenv);

	/* execute the code */
	cptr.n_ptr = method.n_ptr->n_listnext;
	while (cptr.n_ptr != NULL)
	    val.n_ptr = xlevarg(&cptr.n_ptr);
    }

    /* restore the environment */
    xlunbind(oldenv);

    /* after creating an object, send it the "isnew" message */
    if (msg->n_msg == new && val.n_ptr != NULL) {
	if ((isnewmsg = xlmfind(val.n_ptr,isnew)) == NULL)
	    xlfail("no method for the isnew message");
	val.n_ptr = xlxsend(val.n_ptr,isnewmsg,args);
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the result value */
    return (val.n_ptr);
}

/* xlsend - send a message to an object (message in arg list) */
struct node *xlsend(obj,args)
  struct node *obj,*args;
{
    struct node *msg;

    /* find the message binding for this message */
    if ((msg = xlmfind(obj,xlevmatch(SYM,&args))) == NULL)
	xlfail("no method for this message");

    /* send the message */
    return (xlxsend(obj,msg,args));
}

/* xlobsym - find a class or instance variable for the current object */
struct node *xlobsym(sym)
  struct node *sym;
{
    struct node *obj;

    if ((obj = self->n_symvalue) != NULL && obj->n_type == OBJ)
	return (findvar(obj,sym));
    else
	return (NULL);
}

/* mnew - create a new object instance */
LOCAL struct node *mnew()
{
    struct node *oldstk,obj,*cls;

    /* create a new stack frame */
    oldstk = xlsave(&obj,NULL);

    /* get the class */
    cls = self->n_symvalue;

    /* generate a new object */
    obj.n_ptr = newnode(OBJ);
    obj.n_ptr->n_obclass = cls;
    obj.n_ptr->n_obdata = makelist(getivcnt(cls,IVARTOTAL));

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new object */
    return (obj.n_ptr);
}

/* misnew - initialize a new class */
LOCAL struct node *misnew(args)
  struct node *args;
{
    struct node *oldstk,super,*obj;

    /* create a new stack frame */
    oldstk = xlsave(&super,NULL);

    /* get the superclass if there is one */
    if (args != NULL)
	super.n_ptr = xlmatch(OBJ,&args);
    else
	super.n_ptr = object;
    xllastarg(args);

    /* get the object */
    obj = self->n_symvalue;

    /* store the superclass */
    xlivar(obj,SUPERCLASS)->n_listvalue = super.n_ptr;
    (xlivar(obj,IVARTOTAL)->n_listvalue = newnode(INT))->n_int =
    	 getivcnt(super.n_ptr,IVARTOTAL);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new object */
    return (obj);
}

/* xladdivar - enter an instance variable */
xladdivar(cls,var)
  struct node *cls; char *var;
{
    struct node *ivar,*lptr;

    /* find the 'ivars' instance variable */
    ivar = xlivar(cls,IVARS);

    /* add the instance variable */
    lptr = newnode(LIST);
    lptr->n_listnext = ivar->n_listvalue;
    ivar->n_listvalue = lptr;
    lptr->n_listvalue = xlsenter(var);
}

/* entermsg - add a message to a class */
LOCAL struct node *entermsg(cls,msg)
  struct node *cls,*msg;
{
    struct node *ivar,*lptr,*mptr;

    /* find the 'messages' instance variable */
    ivar = xlivar(cls,MESSAGES);

    /* lookup the message */
    for (lptr = ivar->n_listvalue; lptr != NULL; lptr = lptr->n_listnext)
	if ((mptr = lptr->n_listvalue)->n_msg == msg)
	    return (mptr);

    /* allocate a new message entry if one wasn't found */
    lptr = newnode(LIST);
    lptr->n_listnext = ivar->n_listvalue;
    ivar->n_listvalue = lptr;
    lptr->n_listvalue = mptr = newnode(LIST);
    mptr->n_msg = msg;

    /* return the symbol node */
    return (mptr);
}

/* answer - define a method for answering a message */
LOCAL struct node *answer(args)
  struct node *args;
{
    struct node *oldstk,arg,msg,fargs,code;
    struct node *obj,*mptr,*fptr;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&msg,&fargs,&code,NULL);

    /* initialize */
    arg.n_ptr = args;

    /* message symbol, formal argument list and code */
    msg.n_ptr = xlmatch(SYM,&arg.n_ptr);
    fargs.n_ptr = xlmatch(LIST,&arg.n_ptr);
    code.n_ptr = xlmatch(LIST,&arg.n_ptr);
    xllastarg(arg.n_ptr);

    /* get the object node */
    obj = self->n_symvalue;

    /* make a new message list entry */
    mptr = entermsg(obj,msg.n_ptr);

    /* setup the message node */
    mptr->n_msgcode = fptr = newnode(LIST);
    fptr->n_listvalue = fargs.n_ptr;
    fptr->n_listnext = code.n_ptr;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the object */
    return (obj);
}

/* mivars - define the list of instance variables */
LOCAL struct node *mivars(args)
  struct node *args;
{
    struct node *cls,*super;
    int scnt;

    /* define the list of instance variables */
    cls = defvars(args,IVARS);

    /* get the superclass instance variable count */
    if ((super = xlivar(cls,SUPERCLASS)->n_listvalue) != NULL)
	scnt = getivcnt(super,IVARTOTAL);
    else
	scnt = 0;

    /* save the number of instance variables */
    (xlivar(cls,IVARCNT)->n_listvalue = newnode(INT))->n_int = varcnt;
    (xlivar(cls,IVARTOTAL)->n_listvalue = newnode(INT))->n_int = scnt+varcnt;

    /* return the class */
    return (cls);
}

/* getivcnt - get the number of instance variables for a class */
LOCAL int getivcnt(cls,ivar)
  struct node *cls; int ivar;
{
    struct node *cnt;

    if ((cnt = xlivar(cls,ivar)->n_listvalue) != NULL)
	if (cnt->n_type == INT)
	    return (cnt->n_int);
	else
	    xlfail("bad value for instance variable count");
    else
	return (0);
}

/* mcvars - define the list of class variables */
LOCAL struct node *mcvars(args)
  struct node *args;
{
    struct node *cls;

    /* define the list of class variables */
    cls = defvars(args,CVARS);

    /* make a new list of values */
    xlivar(cls,CVALS)->n_listvalue = makelist(varcnt);

    /* return the class */
    return (cls);
}

/* defvars - define a class or instance variable list */
LOCAL struct node *defvars(args,varnum)
  struct node *args; int varnum;
{
    struct node *oldstk,vars,*vptr,*cls,*sym;

    /* create a new stack frame */
    oldstk = xlsave(&vars,NULL);

    /* get ivar list */
    vars.n_ptr = xlmatch(LIST,&args);
    xllastarg(args);

    /* get the class node */
    cls = self->n_symvalue;

    /* check each variable in the list */
    varcnt = 0;
    for (vptr = vars.n_ptr;
	 vptr != NULL && vptr->n_type == LIST;
	 vptr = vptr->n_listnext) {

	/* make sure this is a valid symbol in the list */
	if ((sym = vptr->n_listvalue) == NULL || sym->n_type != SYM)
	    xlfail("bad variable list");

	/* make sure its not already defined */
	if (checkvar(cls,sym))
	    xlfail("multiply defined variable");

	/* count the variable */
	varcnt++;
    }

    /* make sure the list ended properly */
    if (vptr != NULL)
	xlfail("bad variable list");

    /* define the new variable list */
    xlivar(cls,varnum)->n_listvalue = vars.n_ptr;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the class */
    return (cls);
}

/* xladdmsg - add a message to a class */
xladdmsg(cls,msg,code)
  struct node *cls; char *msg; struct node *(*code)();
{
    struct node *mptr;

    /* enter the message selector */
    mptr = entermsg(cls,xlsenter(msg));

    /* store the method for this message */
    mptr->n_msgcode = newnode(SUBR);
    mptr->n_msgcode->n_subr = code;
}

/* getclass - get the class of an object */
LOCAL struct node *getclass(args)
  struct node *args;
{
    /* make sure there aren't any arguments */
    xllastarg(args);

    /* return the object's class */
    return (self->n_symvalue->n_obclass);
}

/* obshow - show the instance variables of an object */
LOCAL struct node *obshow(args)
  struct node *args;
{
    struct node *fptr;

    /* get the file pointer */
    fptr = (args ? xlmatch(FPTR,&args) : s_stdout->n_symvalue);
    xllastarg(args);

    /* print the object's instance variables */
    xlprint(fptr,self->n_symvalue->n_obdata,TRUE);
    xlterpri(fptr);

    /* return the object */
    return (self->n_symvalue);
}

/* defisnew - default 'isnew' method */
LOCAL struct node *defisnew(args)
  struct node *args;
{
    /* make sure there aren't any arguments */
    xllastarg(args);

    /* return the object */
    return (self->n_symvalue);
}

/* sendsuper - send a message to an object's superclass */
LOCAL struct node *sendsuper(args)
  struct node *args;
{
    struct node *obj,*super,*msg;

    /* get the object */
    obj = self->n_symvalue;

    /* get the object's superclass */
    super = xlivar(obj->n_obclass,SUPERCLASS)->n_listvalue;

    /* find the message binding for this message */
    if ((msg = findmsg(super,xlmatch(SYM,&args))) == NULL)
	xlfail("no method for this message");

    /* send the message */
    return (xlxsend(obj,msg,args));
}

/* findmsg - find the message binding given an object and a class */
LOCAL struct node *findmsg(cls,sym)
  struct node *cls,*sym;
{
    struct node *lptr,*msg;

    /* start at the specified class */
    msgcls = cls;

    /* look for the message in the class or superclasses */
    while (msgcls != NULL) {

	/* lookup the message in this class */
	for (lptr = xlivar(msgcls,MESSAGES)->n_listvalue;
	     lptr != NULL;
	     lptr = lptr->n_listnext)
	    if ((msg = lptr->n_listvalue) != NULL && msg->n_msg == sym)
		return (msg);

	/* look in class's superclass */
	msgcls = xlivar(msgcls,SUPERCLASS)->n_listvalue;
    }

    /* message not found */
    return (NULL);
}

/* findvar - find a class or instance variable */
LOCAL struct node *findvar(obj,sym)
  struct node *obj,*sym;
{
    struct node *cls,*lptr;
    int base,varnum;
    int found;

    /* get the class of the object */
    cls = obj->n_obclass;

    /* get the total number of instance variables */
    base = getivcnt(cls,IVARTOTAL);

    /* find the variable */
    found = FALSE;
    for (; cls != NULL; cls = xlivar(cls,SUPERCLASS)->n_listvalue) {

	/* get the number of instance variables for this class */
	if ((base -= getivcnt(cls,IVARCNT)) < 0)
	    xlfail("error finding instance variable");

	/* check for finding the class of the current message */
	if (!found && cls == msgclass->n_symvalue)
	    found = TRUE;

	/* lookup the instance variable */
	varnum = 0;
	for (lptr = xlivar(cls,IVARS)->n_listvalue;
    	     lptr != NULL;
    	     lptr = lptr->n_listnext)
	    if (found && lptr->n_listvalue == sym)
		return (xlivar(obj,base + varnum));
	    else
		varnum++;

	/* skip the class variables if the message class hasn't been found */
	if (!found)
	    continue;

	/* lookup the class variable */
	varnum = 0;
	for (lptr = xlivar(cls,CVARS)->n_listvalue;
    	     lptr != NULL;
    	     lptr = lptr->n_listnext)
	    if (lptr->n_listvalue == sym)
		return (xlcvar(cls,varnum));
	    else
		varnum++;
    }

    /* variable not found */
    return (NULL);
}

/* checkvar - check for an existing class or instance variable */
LOCAL int checkvar(cls,sym)
  struct node *cls,*sym;
{
    struct node *lptr;

    /* find the variable */
    for (; cls != NULL; cls = xlivar(cls,SUPERCLASS)->n_listvalue) {

	/* lookup the instance variable */
	for (lptr = xlivar(cls,IVARS)->n_listvalue;
    	     lptr != NULL;
    	     lptr = lptr->n_listnext)
	    if (lptr->n_listvalue == sym)
		return (TRUE);

	/* lookup the class variable */
	for (lptr = xlivar(cls,CVARS)->n_listvalue;
    	     lptr != NULL;
    	     lptr = lptr->n_listnext)
	    if (lptr->n_listvalue == sym)
		return (TRUE);
    }

    /* variable not found */
    return (FALSE);
}

/* xlivar - get an instance variable */
struct node *xlivar(obj,num)
  struct node *obj; int num;
{
    struct node *ivar;

    /* get the instance variable */
    for (ivar = obj->n_obdata; num > 0; num--)
	if (ivar != NULL)
	    ivar = ivar->n_listnext;
	else
	    xlfail("bad instance variable list");

    /* return the instance variable */
    return (ivar);
}

/* xlcvar - get a class variable */
struct node *xlcvar(cls,num)
  struct node *cls; int num;
{
    struct node *cvar;

    /* get the class variable */
    for (cvar = xlivar(cls,CVALS)->n_listvalue; num > 0; num--)
	if (cvar != NULL)
	    cvar = cvar->n_listnext;
	else
	    xlfail("bad class variable list");

    /* return the class variable */
    return (cvar);
}

/* makelist - make a list of nodes */
LOCAL struct node *makelist(cnt)
  int cnt;
{
    struct node *oldstk,list,*lnew;

    /* create a new stack frame */
    oldstk = xlsave(&list,NULL);

    /* make the list */
    for (; cnt > 0; cnt--) {
	lnew = newnode(LIST);
	lnew->n_listnext = list.n_ptr;
	list.n_ptr = lnew;
    }

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the list */
    return (list.n_ptr);
}

/* xloinit - object function initialization routine */
xloinit()
{
    /* don't confuse the garbage collector */
    class = NULL;
    object = NULL;

    /* enter the object related symbols */
    new		= xlsenter("new");
    isnew	= xlsenter("isnew");
    self	= xlsenter("self");
    msgclass	= xlsenter("msgclass");

    /* create the 'Class' object */
    class = xlclass("Class",CLASSSIZE);
    class->n_obclass = class;

    /* create the 'Object' object */
    object = xlclass("Object",0);

    /* finish initializing 'class' */
    xlivar(class,SUPERCLASS)->n_listvalue = object;
    xladdivar(class,"ivartotal");	/* ivar number 6 */
    xladdivar(class,"ivarcnt");		/* ivar number 5 */
    xladdivar(class,"superclass");	/* ivar number 4 */
    xladdivar(class,"cvals");		/* ivar number 3 */
    xladdivar(class,"cvars");		/* ivar number 2 */
    xladdivar(class,"ivars");		/* ivar number 1 */
    xladdivar(class,"messages");	/* ivar number 0 */
    xladdmsg(class,"new",mnew);
    xladdmsg(class,"answer",answer);
    xladdmsg(class,"ivars",mivars);
    xladdmsg(class,"cvars",mcvars);
    xladdmsg(class,"isnew",misnew);

    /* finish initializing 'object' */
    xladdmsg(object,"class",getclass);
    xladdmsg(object,"show",obshow);
    xladdmsg(object,"isnew",defisnew);
    xladdmsg(object,"sendsuper",sendsuper);
}
!Funky!Stuff!
exit 0
-- 
John Woods, Charles River Data Systems
decvax!frog!john, mit-eddie!jfw, JFW%mit-ccc@MIT-XX

When your puppy goes off in another room,
is it because of the explosive charge?

jfw@mit-eddie.UUCP (John Woods) (02/03/85)

Replace this line with your message, but I'm out of cute things to say here.

This is part five of five in my posting of Dave Betz' newest XLISP 1.2.  If you
don't seem to have all five parts, send me mail at ...!mit-eddie!jfw and I'll
figure out how to get you the missing parts.

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
echo extract with sh, not csh
echo x XLPRIN.C
cat > XLPRIN.C << '!Funky!Stuff!'
/* xlprint - xlisp print routine */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;

/* local variables */
static char buf[STRMAX+1];

/* xlprint - print an xlisp value */
xlprint(fptr,vptr,flag)
  struct node *fptr,*vptr; int flag;
{
    struct node *nptr,*next,*msg;

    /* print null as the empty list */
    if (vptr == NULL) {
	putstr(fptr,"nil");
	return;
    }

    /* check value type */
    switch (vptr->n_type) {
    case SUBR:
	    putatm(fptr,"Subr",vptr);
	    break;
    case FSUBR:
	    putatm(fptr,"FSubr",vptr);
	    break;
    case LIST:
	    xlputc(fptr,'(');
	    for (nptr = vptr; nptr != NULL; nptr = next) {
	        xlprint(fptr,nptr->n_listvalue,flag);
		if ((next = nptr->n_listnext) != NULL)
		    if (next->n_type == LIST)
			xlputc(fptr,' ');
		    else {
			putstr(fptr," . ");
			xlprint(fptr,next,flag);
			break;
		    }
	    }
	    xlputc(fptr,')');
	    break;
    case SYM:
	    putstr(fptr,xlsymname(vptr));
	    break;
    case INT:
	    putdec(fptr,vptr->n_int);
	    break;
    case STR:
	    if (flag)
		putstring(fptr,vptr->n_str);
	    else
		putstr(fptr,vptr->n_str);
	    break;
    case FPTR:
	    putatm(fptr,"File",vptr);
	    break;
    case OBJ:
	    putatm(fptr,"Object",vptr);
	    break;
    default:
	    putatm(fptr,"Foo",vptr);
	    break;
    }
}

/* xlterpri - terminate the current print line */
xlterpri(fptr)
  struct node *fptr;
{
    xlputc(fptr,'\n');
}

/* putstring - output a string */
LOCAL putstring(fptr,str)
  struct node *fptr; char *str;
{
    int ch;

    /* output the initial quote */
    xlputc(fptr,'"');

    /* output each character in the string */
    while (ch = *str++)

	/* check for a control character */
	if (ch < 040 || ch == '\\') {
	    xlputc(fptr,'\\');
	    switch (ch) {
	    case '\033':
		    xlputc(fptr,'e');
		    break;
	    case '\n':
		    xlputc(fptr,'n');
		    break;
	    case '\r':
		    xlputc(fptr,'r');
		    break;
	    case '\t':
		    xlputc(fptr,'t');
		    break;
	    case '\\':
		    xlputc(fptr,'\\');
		    break;
	    default:
		    putoct(fptr,ch);
		    break;
	    }
	}

	/* output a normal character */
	else
	    xlputc(fptr,ch);

    /* output the terminating quote */
    xlputc(fptr,'"');
}

/* putatm - output an atom */
LOCAL putatm(fptr,tag,val)
  struct node *fptr; char *tag; int val;
{
    sprintf(buf,"<%s: #%x>",tag,val);
    putstr(fptr,buf);
}

/* putdec - output a decimal number */
LOCAL putdec(fptr,n)
  struct node *fptr; int n;
{
    sprintf(buf,"%d",n);
    putstr(fptr,buf);
}

/* puthex - output a hexadecimal number */
LOCAL puthex(fptr,n)
  struct node *fptr; unsigned int n;
{
    sprintf(buf,"%x",n);
    putstr(fptr,buf);
}

/* putoct - output an octal byte value */
LOCAL putoct(fptr,n)
  struct node *fptr; int n;
{
    sprintf(buf,"%03o",n);
    putstr(fptr,buf);
}

/* putstr - output a string */
LOCAL putstr(fptr,str)
  struct node *fptr; char *str;
{
    while (*str)
	xlputc(fptr,*str++);
}
!Funky!Stuff!
echo x XLREAD.C
cat > XLREAD.C << '!Funky!Stuff!'
/* xlread - xlisp expression input routine */

#ifdef AZTEC
#include "stdio.h"
#include "setjmp.h"
#else
#include <stdio.h>
#include <setjmp.h>
#include <ctype.h>
#endif

#include "xlisp.h"

/* external variables */
extern jmp_buf *xljmpbuf;
extern struct node *s_quote;
extern struct node *xlstack;
extern int xlplevel;

/* external routines */
extern FILE *fopen();

/* forward declarations */
FORWARD struct node *plist();
FORWARD struct node *pstring();
FORWARD struct node *pquote();
FORWARD struct node *pname();

/* xlload - load a file of xlisp expressions */
int xlload(name)
  char *name;
{
    jmp_buf loadjmpbuf,*oldjmpbuf;
    struct node *oldstk,fptr,val;
    char fname[50];
    FILE *fp;

    /* create a new stack frame */
    oldstk = xlsave(&fptr,&val,NULL);

    /* add the default extension */
    strcpy(fname,name); strcat(fname,".lsp");

    /* open the file */
    if ((fp = fopen(fname,"r")) == NULL)
	return (FALSE);

    /* allocate a file node */
    fptr.n_ptr = newnode(FPTR);
    fptr.n_ptr->n_fp = fp;
    fptr.n_ptr->n_savech = 0;

    /* setup to trap errors */
    oldjmpbuf = xljmpbuf;
    if (setjmp(xljmpbuf = loadjmpbuf)) {
	fclose(fp);
	longjmp(xljmpbuf = oldjmpbuf,1);
    }

    /* read and evaluate each expression in the file */
    while (xlread(fptr.n_ptr,&val.n_ptr))
	xleval(val.n_ptr);

    /* restore error trapping context and previous stack frame */
    xljmpbuf = oldjmpbuf;
    xlstack = oldstk;

    /* close the file */
    fclose(fp);

    /* return successfully */
    return (TRUE);
}

/* xlread - read an xlisp expression */
int xlread(fptr,pval)
  struct node *fptr,**pval;
{
    /* initialize */
    xlplevel = 0;

    /* parse an expression */
    return (parse(fptr,pval));
}

/* parse - parse an xlisp expression */
LOCAL int parse(fptr,pval)
  struct node *fptr,**pval;
{
    int ch;

    /* keep looking for a node skipping comments */
    while (TRUE)

	/* check next character for type of node */
	switch (ch = nextch(fptr)) {
	case EOF:
		return (FALSE);
	case '\'':			/* a quoted expression */
		*pval = pquote(fptr);
		return (TRUE);
	case '(':			/* a sublist */
		*pval = plist(fptr);
		return (TRUE);
	case ')':			/* closing paren - shouldn't happen */
		xlfail("extra right paren");
	case '.':			/* dot - shouldn't happen */
		xlfail("misplaced dot");
	case ';':			/* a comment */
		pcomment(fptr);
		break;
	case '"':			/* a string */
		*pval = pstring(fptr);
		return (TRUE);
	default:
		if (issym(ch))		/* a name */
		    *pval = pname(fptr);
		else
		    xlfail("invalid character");
		return (TRUE);
	}
}

/* pcomment - parse a comment */
LOCAL pcomment(fptr)
  struct node *fptr;
{
    int ch;

    /* skip to end of line */
    while ((ch = checkeof(fptr)) != EOF && ch != '\n')
	;
}

/* plist - parse a list */
LOCAL struct node *plist(fptr)
  struct node *fptr;
{
    struct node *oldstk,val,*lastnptr,*nptr;
    int ch;

    /* increment the nesting level */
    xlplevel += 1;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* skip the opening paren */
    xlgetc(fptr);

    /* keep appending nodes until a closing paren is found */
    lastnptr = NULL;
    for (lastnptr = NULL; (ch = nextch(fptr)) != ')'; lastnptr = nptr) {

	/* check for end of file */
	if (ch == EOF)
	    badeof();

	/* check for a dotted pair */
	if (ch == '.') {

	    /* skip the dot */
	    xlgetc(fptr);

	    /* make sure there's a node */
	    if (lastnptr == NULL)
		xlfail("invalid dotted pair");

	    /* parse the expression after the dot */
	    if (!parse(fptr,&lastnptr->n_listnext))
		badeof();

	    /* make sure its followed by a close paren */
	    if (nextch(fptr) != ')')
		xlfail("invalid dotted pair");

	    /* done with this list */
	    break;
	}

	/* allocate a new node and link it into the list */
	nptr = newnode(LIST);
	if (lastnptr == NULL)
	    val.n_ptr = nptr;
	else
	    lastnptr->n_listnext = nptr;

	/* initialize the new node */
	if (!parse(fptr,&nptr->n_listvalue))
	    badeof();
    }

    /* skip the closing paren */
    xlgetc(fptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* decrement the nesting level */
    xlplevel -= 1;

    /* return successfully */
    return (val.n_ptr);
}

/* pstring - parse a string */
LOCAL struct node *pstring(fptr)
  struct node *fptr;
{
    struct node *oldstk,val;
    char sbuf[STRMAX+1];
    int ch,i,d1,d2,d3;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* skip the opening quote */
    xlgetc(fptr);

    /* loop looking for a closing quote */
    for (i = 0; i < STRMAX && (ch = checkeof(fptr)) != '"'; i++) {
	switch (ch) {
	case EOF:
		badeof();
	case '\\':
		switch (ch = checkeof(fptr)) {
		case 'e':
			ch = '\033';
			break;
		case 'n':
			ch = '\n';
			break;
		case 'r':
			ch = '\r';
			break;
		case 't':
			ch = '\t';
			break;
		default:
			if (ch >= '0' && ch <= '7') {
			    d1 = ch - '0';
			    d2 = checkeof(fptr) - '0';
			    d3 = checkeof(fptr) - '0';
			    ch = (d1 << 6) + (d2 << 3) + d3;
			}
			break;
		}
	}
	sbuf[i] = ch;
    }
    sbuf[i] = 0;

    /* initialize the node */
    val.n_ptr = newnode(STR);
    val.n_ptr->n_str = strsave(sbuf);
    val.n_ptr->n_strtype = DYNAMIC;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new string */
    return (val.n_ptr);
}

/* pquote - parse a quoted expression */
LOCAL struct node *pquote(fptr)
  struct node *fptr;
{
    struct node *oldstk,val;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* skip the quote character */
    xlgetc(fptr);

    /* allocate two nodes */
    val.n_ptr = newnode(LIST);
    val.n_ptr->n_listvalue = s_quote;
    val.n_ptr->n_listnext = newnode(LIST);

    /* initialize the second to point to the quoted expression */
    if (!parse(fptr,&val.n_ptr->n_listnext->n_listvalue))
	badeof();

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the quoted expression */
    return (val.n_ptr);
}

/* pname - parse a symbol name */
LOCAL struct node *pname(fptr)
  struct node *fptr;
{
    char sname[STRMAX+1];
    struct node *val;
    int ch,i;

    /* get symbol name */
    for (i = 0; i < STRMAX && issym(xlpeek(fptr)); )
	sname[i++] = xlgetc(fptr);
    sname[i] = 0;

    /* check for a number or enter the symbol into the oblist */
    return (isnumber(sname,&val) ? val : xlenter(sname,DYNAMIC));
}

/* nextch - look at the next non-blank character */
LOCAL int nextch(fptr)
  struct node *fptr;
{
    int ch;

    /* return and save the next non-blank character */
    while ((ch = xlpeek(fptr)) != EOF && isspace(ch))
	xlgetc(fptr);
    return (ch);
}

/* checkeof - get a character and check for end of file */
LOCAL int checkeof(fptr)
  struct node *fptr;
{
    int ch;

    if ((ch = xlgetc(fptr)) == EOF)
	badeof();
    return (ch);
}

/* badeof - unexpected eof */
LOCAL badeof()
{
    xlfail("unexpected EOF");
}

/* isnumber - check if this string is a number */
int isnumber(str,pval)
  char *str; struct node **pval;
{
    char *p;
    int d;

    /* initialize */
    p = str; d = 0;

    /* check for a sign */
    if (*p == '+' || *p == '-')
	p++;

    /* check for a string of digits */
    while (isdigit(*p))
	p++, d++;

    /* make sure there was at least one digit and this is the end */
    if (d == 0 || *p)
	return (FALSE);

    /* convert the string to an integer and return successfully */
    *pval = newnode(INT);
    (*pval)->n_int = atoi(*str == '+' ? ++str : str);
    return (TRUE);
}

/* issym - check whether a character if valid in a symbol name */
LOCAL int issym(ch)
  int ch;
{
    if (ch <= ' ' ||
    	ch == '(' ||
    	ch == ')' ||
    	ch == ';' || 
    	ch == '.' ||
    	ch == '"' ||
    	ch == '\'')
	return (FALSE);
    else
	return (TRUE);
}
!Funky!Stuff!
echo x XLSTR.C
cat > XLSTR.C << '!Funky!Stuff!'
/* xlstr - xlisp string builtin functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;

/* external procedures */
extern char *strcat();

/* xstrlen - length of a string */
struct node *xstrlen(args)
  struct node *args;
{
    struct node *val;
    int total;

    /* initialize */
    total = 0;

    /* loop over args and total */
    while (args != NULL)
	total += strlen(xlmatch(STR,&args)->n_str);

    /* create the value node */
    val = newnode(INT);
    val->n_int = total;

    /* return the total */
    return (val);
}

/* xstrcat - concatenate a bunch of strings */
struct node *xstrcat(args)
  struct node *args;
{
    struct node *oldstk,val,*p;
    char *str;
    int len;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* find the length of the new string */
    for (p = args, len = 0; p; )
	len += strlen(xlmatch(STR,&p)->n_str);

    /* create the result string */
    val.n_ptr = newnode(STR);
    val.n_ptr->n_str = str = stralloc(len);
    *str = 0;

    /* combine the strings */
    while (args)
	strcat(str,xlmatch(STR,&args)->n_str);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new string */
    return (val.n_ptr);
}

/* xsubstr - return a substring */
struct node *xsubstr(args)
  struct node *args;
{
    struct node *oldstk,arg,src,val;
    int start,forlen,srclen;
    char *srcptr,*dstptr;

    /* create a new stack frame */
    oldstk = xlsave(&arg,&src,&val,NULL);

    /* initialize */
    arg.n_ptr = args;
    
    /* get string and its length */
    src.n_ptr = xlmatch(STR,&arg.n_ptr);
    srcptr = src.n_ptr->n_str;
    srclen = strlen(srcptr);

    /* get starting pos -- must be present */
    start = xlmatch(INT,&arg.n_ptr)->n_int;

    /* get length -- if not present use remainder of string */
    if (arg.n_ptr != NULL)
	forlen = xlmatch(INT,&arg.n_ptr)->n_int;
    else
	forlen = srclen;		/* use len and fix below */

    /* make sure there aren't any more arguments */
    xllastarg(arg.n_ptr);

    /* don't take more than exists */
    if (start + forlen > srclen)
	forlen = srclen - start + 1;

    /* if start beyond string -- return null string */
    if (start > srclen) {
	start = 1;
	forlen = 0; }
	
    /* create return node */
    val.n_ptr = newnode(STR);
    val.n_ptr->n_str = dstptr = stralloc(forlen);

    /* move string */
    for (srcptr += start-1; forlen--; *dstptr++ = *srcptr++)
	;
    *dstptr = 0;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the substring */
    return (val.n_ptr);
}

/* xascii - return ascii value */
struct node *xascii(args)
  struct node *args;
{
    struct node *val;

    /* build return node */
    val = newnode(INT);
    val->n_int = *(xlmatch(STR,&args)->n_str);

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* return the character */
    return (val);
}

/* xchr - convert an INT into a one character ascii string */
struct node *xchr(args)
  struct node *args;
{
    struct node *oldstk,val;
    char *sptr;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* build return node */
    val.n_ptr = newnode(STR);
    val.n_ptr->n_str = sptr = stralloc(1);
    *sptr++ = xlmatch(INT,&args)->n_int;
    *sptr = 0;

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new string */
    return (val.n_ptr);
}

/* xatoi - convert an ascii string to an integer */
struct node *xatoi(args)
  struct node *args;
{
    struct node *val;
    int n;

    /* get the string and convert it */
    n = atoi(xlmatch(STR,&args)->n_str);

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* create the value node */
    val = newnode(INT);
    val->n_int = n;

    /* return the number */
    return (val);
}

/* xitoa - convert an integer to an ascii string */
struct node *xitoa(args)
  struct node *args;
{
    struct node *val;
    char buf[20];
    int n;

    /* get the integer */
    n = xlmatch(INT,&args)->n_int;
    xllastarg(args);

    /* convert it to ascii */
    sprintf(buf,"%d",n);

    /* create the value node */
    val = newnode(STR);
    val->n_str = strsave(buf);

    /* return the string */
    return (val);
}
!Funky!Stuff!
echo x XLSTUB.C
cat > XLSTUB.C << '!Funky!Stuff!'
/* xlstub.c - stubs for replacing the 'xlobj' module */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

struct node *xloinit() {}
struct node *xlsend()  { return (NULL); }
struct node *xlobsym() { return (NULL); }
!Funky!Stuff!
echo x XLSUBR.C
cat > XLSUBR.C << '!Funky!Stuff!'
/* xlsubr - xlisp builtin function support routines */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;

/* xlsubr - define a builtin function */
xlsubr(sname,type,subr)
  char *sname; int type; struct node *(*subr)();
{
    struct node *sym;

    /* enter the symbol */
    sym = xlsenter(sname);

    /* initialize the value */
    sym->n_symvalue = newnode(type);
    sym->n_symvalue->n_subr = subr;
}

/* xlarg - get the next argument */
struct node *xlarg(pargs)
  struct node **pargs;
{
    struct node *arg;

    /* make sure the argument exists */
    if (*pargs == NULL)
	xlfail("too few arguments");

    /* get the argument value */
    arg = (*pargs)->n_listvalue;

    /* move the argument pointer ahead */
    *pargs = (*pargs)->n_listnext;

    /* return the argument */
    return (arg);
}

/* xlmatch - get an argument and match its type */
struct node *xlmatch(type,pargs)
  int type; struct node **pargs;
{
    struct node *arg;

    /* get the argument */
    arg = xlarg(pargs);

    /* check its type */
    if (type == LIST) {
	if (arg != NULL && arg->n_type != LIST)
	    xlfail("bad argument type");
    }
    else {
	if (arg == NULL || arg->n_type != type)
	    xlfail("bad argument type");
    }

    /* return the argument */
    return (arg);
}

/* xlevarg - get the next argument and evaluate it */
struct node *xlevarg(pargs)
  struct node **pargs;
{
    struct node *oldstk,val;

    /* create a new stack frame */
    oldstk = xlsave(&val,NULL);

    /* get the argument */
    val.n_ptr = xlarg(pargs);

    /* evaluate the argument */
    val.n_ptr = xleval(val.n_ptr);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the argument */
    return (val.n_ptr);
}

/* xlevmatch - get an evaluated argument and match its type */
struct node *xlevmatch(type,pargs)
  int type; struct node **pargs;
{
    struct node *arg;

    /* get the argument */
    arg = xlevarg(pargs);

    /* check its type */
    if (type == LIST) {
	if (arg != NULL && arg->n_type != LIST)
	    xlfail("bad argument type");
    }
    else {
	if (arg == NULL || arg->n_type != type)
	    xlfail("bad argument type");
    }

    /* return the argument */
    return (arg);
}

/* xllastarg - make sure the remainder of the argument list is empty */
xllastarg(args)
  struct node *args;
{
    if (args != NULL)
	xlfail("too many arguments");
}

/* assign - assign a value to a symbol */
assign(sym,val)
  struct node *sym,*val;
{
    struct node *lptr;

    /* check for a current object */
    if ((lptr = xlobsym(sym)) != NULL)
	lptr->n_listvalue = val;
    else
	sym->n_symvalue = val;
}
!Funky!Stuff!
echo x XLSYM.C
cat > XLSYM.C << '!Funky!Stuff!'
/* xlsym - symbol handling routines */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* global variables */
struct node *oblist;
struct node *s_unbound;

/* external variables */
extern struct node *xlstack;

/* forward declarations */
FORWARD struct node *xlmakesym();
FORWARD struct node *findprop();

/* xlenter - enter a symbol into the oblist */
struct node *xlenter(name,type)
  char *name;
{
    struct node *oldstk,*lsym,*nsym,newsym;
    int cmp;

    /* check for nil */
    if (strcmp(name,"nil") == 0)
	return (NULL);

    /* check for symbol already in table */
    lsym = NULL;
    nsym = oblist->n_symvalue;
    while (nsym) {
	if ((cmp = strcmp(name,xlsymname(nsym->n_listvalue))) <= 0)
	    break;
	lsym = nsym;
	nsym = nsym->n_listnext;
    }

    /* check to see if we found it */
    if (nsym && cmp == 0)
	return (nsym->n_listvalue);

    /* make a new symbol node and link it into the oblist */
    oldstk = xlsave(&newsym,NULL);
    newsym.n_ptr = newnode(LIST);
    newsym.n_ptr->n_listvalue = xlmakesym(name,type);
    newsym.n_ptr->n_listnext = nsym;
    if (lsym)
	lsym->n_listnext = newsym.n_ptr;
    else
	oblist->n_symvalue = newsym.n_ptr;
    xlstack = oldstk;

    /* return the new symbol */
    return (newsym.n_ptr->n_listvalue);
}

/* xlsenter - enter a symbol with a static print name */
struct node *xlsenter(name)
  char *name;
{
    return (xlenter(name,STATIC));
}

/* xlintern - intern a symbol onto the oblist */
struct node *xlintern(sym)
  struct node *sym;
{
    struct node *oldstk,*lsym,*nsym,newsym;
    char *name;
    int cmp;

    /* get the symbol's print name */
    name = xlsymname(sym);

    /* check for nil */
    if (strcmp(name,"nil") == 0)
	return (NULL);

    /* check for symbol already in table */
    lsym = NULL;
    nsym = oblist->n_symvalue;
    while (nsym) {
	if ((cmp = strcmp(name,xlsymname(nsym->n_listvalue))) <= 0)
	    break;
	lsym = nsym;
	nsym = nsym->n_listnext;
    }

    /* check to see if we found it */
    if (nsym && cmp == 0)
	return (nsym->n_listvalue);

    /* link the symbol into the oblist */
    oldstk = xlsave(&newsym,NULL);
    newsym.n_ptr = newnode(LIST);
    newsym.n_ptr->n_listvalue = sym;
    newsym.n_ptr->n_listnext = nsym;
    if (lsym)
	lsym->n_listnext = newsym.n_ptr;
    else
	oblist->n_symvalue = newsym.n_ptr;
    xlstack = oldstk;

    /* return the symbol */
    return (sym);
}

/* xlmakesym - make a new symbol node */
struct node *xlmakesym(name,type)
  char *name;
{
    struct node *oldstk,sym,*str;

    /* create a new stack frame */
    oldstk = xlsave(&sym,NULL);

    /* make a new symbol node */
    sym.n_ptr = newnode(SYM);
    sym.n_ptr->n_symvalue = s_unbound;
    sym.n_ptr->n_symplist = newnode(LIST);
    sym.n_ptr->n_symplist->n_listvalue = str = newnode(STR);
    str->n_str = (type == DYNAMIC ? strsave(name) : name);
    str->n_strtype = type;

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the new symbol node */
    return (sym.n_ptr);
}

/* xlsymname - return the print name of a symbol */
char *xlsymname(sym)
  struct node *sym;
{
    return (sym->n_symplist->n_listvalue->n_str);
}

/* xlgetprop - get the value of a property */
struct node *xlgetprop(sym,prp)
  struct node *sym,*prp;
{
    struct node *p;

    if ((p = findprop(sym,prp)) == NULL)
	return (NULL);
    return (p->n_listnext);
}

/* xlputprop - put a property value onto the property list */
xlputprop(sym,val,prp)
  struct node *sym,*val,*prp;
{
    struct node *oldstk,p,*pair;

    if ((pair = findprop(sym,prp)) == NULL) {
	oldstk = xlsave(&p,NULL);
	p.n_ptr = newnode(LIST);
	p.n_ptr->n_listvalue = pair = newnode(LIST);
	p.n_ptr->n_listnext = sym->n_symplist->n_listnext;
	sym->n_symplist->n_listnext = p.n_ptr;
	pair->n_listvalue = prp;
	xlstack = oldstk;
    }
    pair->n_listnext = val;
}

/* xlremprop - remove a property from a property list */
xlremprop(sym,prp)
  struct node *sym,*prp;
{
    struct node *last,*p;

    last = NULL;
    for (p = sym->n_symplist->n_listnext; p; p = p->n_listnext) {
	if (p->n_listvalue->n_listvalue == prp)
	    if (last)
		last->n_listnext = p->n_listnext;
	    else
		sym->n_symplist->n_listnext = p->n_listnext;
	last = p;
    }
}

/* findprop - find a property pair */
LOCAL struct node *findprop(sym,prp)
  struct node *sym,*prp;
{
    struct node *p;

    for (p = sym->n_symplist->n_listnext; p; p = p->n_listnext)
	if (p->n_listvalue->n_listvalue == prp)
	    return (p->n_listvalue);
    return (NULL);
}

/* xlsinit - symbol initialization routine */
xlsinit()
{
    /* initialize the oblist */
    oblist = xlmakesym("*oblist*",STATIC);
    oblist->n_symvalue = newnode(LIST);
    oblist->n_symvalue->n_listvalue = oblist;

    /* enter the unbound symbol indicator */
    s_unbound = xlsenter("*unbound*");
    s_unbound->n_symvalue = s_unbound;
}
!Funky!Stuff!
echo x XLSYS.C
cat > XLSYS.C << '!Funky!Stuff!'
/* xlsys.c - xlisp builtin system functions */

#ifdef AZTEC
#include "stdio.h"
#else
#include <stdio.h>
#endif

#include "xlisp.h"

/* external variables */
extern struct node *xlstack;
extern int anodes;

/* external symbols */
extern struct node *a_subr;
extern struct node *a_fsubr;
extern struct node *a_list;
extern struct node *a_sym;
extern struct node *a_int;
extern struct node *a_str;
extern struct node *a_obj;
extern struct node *a_fptr;

/* xload - direct input from a file */
struct node *xload(args)
  struct node *args;
{
    struct node *oldstk,fname,*val;

    /* create a new stack frame */
    oldstk = xlsave(&fname,NULL);

    /* get the file name */
    fname.n_ptr = xlmatch(STR,&args);
    xllastarg(args);

    /* load the file */
    val = (xlload(fname.n_ptr->n_str) ? fname.n_ptr : NULL);

    /* restore the previous stack frame */
    xlstack = oldstk;

    /* return the status */
    return (val);
}

/* xgc - xlisp function to force garbage collection */
struct node *xgc(args)
  struct node *args;
{
    /* make sure there aren't any arguments */
    xllastarg(args);

    /* garbage collect */
    gc();

    /* return null */
    return (NULL);
}

/* xexpand - xlisp function to force memory expansion */
struct node *xexpand(args)
  struct node *args;
{
    struct node *val;
    int n,i;

    /* get the new number to allocate */
    if (args == NULL)
	n = 1;
    else
	n = xlmatch(INT,&args)->n_int;

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* allocate more segments */
    for (i = 0; i < n; i++)
	if (!addseg())
	    break;

    /* return the number of segments added */
    val = newnode(INT);
    val->n_int = i;
    return (val);
}

/* xalloc - xlisp function to set the number of nodes to allocate */
struct node *xalloc(args)
  struct node *args;
{
    struct node *val;
    int n,oldn;

    /* get the new number to allocate */
    n = xlmatch(INT,&args)->n_int;

    /* make sure there aren't any more arguments */
    xllastarg(args);

    /* set the new number of nodes to allocate */
    oldn = anodes;
    anodes = n;

    /* return the old number */
    val = newnode(INT);
    val->n_int = oldn;
    return (val);
}

/* xmem - xlisp function to print memory statistics */
struct node *xmem(args)
  struct node *args;
{
    /* make sure there aren't any arguments */
    xllastarg(args);

    /* print the statistics */
    stats();

    /* return null */
    return (NULL);
}

/* xtype - return type of a thing */
struct node *xtype(args)
    struct node *args;
{
    struct node *arg;

    if (!(arg = xlarg(&args)))
	return (NULL);

    switch (arg->n_type) {
	case SUBR:	return (a_subr);
	case FSUBR:	return (a_fsubr);
	case LIST:	return (a_list);
	case SYM:	return (a_sym);
	case INT:	return (a_int);
	case STR:	return (a_str);
	case OBJ:	return (a_obj);
	case FPTR:	return (a_fptr);
	default:	xlfail("bad node type");
    }
}

/* xexit - get out of xlisp */
xexit()
{
    exit();
}
!Funky!Stuff!
exit 0
-- 
John Woods, Charles River Data Systems
decvax!frog!john, mit-eddie!jfw, JFW%mit-ccc@MIT-XX

When your puppy goes off in another room,
is it because of the explosive charge?