[net.sources] xlisp part 1 of 4

betz (01/06/83)

::::::::::::::
xlisp.mem
::::::::::::::
XLISP code and documentation

Copyright 1983, by David M. Betz
		   114 Davenport Ave.
		   Manchester, NH 03103
		   603-625-4691
		   UUCP:  decvax!betz
	All rights reserved
	Permission granted for unrestricted non-commercial use


XLISP is an experimental programming language combining some of the features
of LISP with an object oriented extension capability.  All of the builtin
functions of XLISP are LISP like functions.  The only builtin object class
is "class".  This is sufficient to allow the language to be extended in an
object oriented manner.  XLISP is very slow because of the way that it handles
dynamic memory allocation, hence it isn't a practical language for serious
applications.  It was implemented to allow experimentation with object
oriented programming before Smalltalk-80 becomes available from Xerox.


Utility functions:

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

(mem)		SHOW MEMORY ALLOCATION STATISTICS
    returns	nil

(gc)		FORCE GARBAGE COLLECTION
    returns	nil

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

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


Functions:

(eval <list>)	EVALUATE A LIST AS XLISP CODE
    <list>	the list to be evaluated as an xlisp expression (evaluated)
    returns	the result of evaluating the expression

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

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

(print <expr>...)	PRINT A LIST OF VALUES
    <expr>	the expressions to be printed (evaluated)
    returns	null

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

(if <expr> <expr1> [ <expr2> ])	EXECUTE EXPRESSIONS CONDITIONALLY
    <texpr>	test expression (evaluated)
    <expr1>	expression evaluated if texpr is non-null or non-zero
    <expr2>	expression evaluated if texpr is null or zero
    returns	the valued of the expression evaluated

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

(defun <qsym> <qfargs> <expr>...)	DEFINE A NEW FUNCTION
    <qsym>	symbol to be defined (quoted)
    <qfargs>	list of formal arguments (quoted)
		  this list is of the form:
		    ( <farg>... [ / <local>... ] )
		  where
		    <farg>	is a formal argument
		    <local>	is a local variable
    <expr>	expressions constituting the body of the function (quoted)
    returns	the function symbol


I/O Functions:

(readchr)	READ A CHARACTER WITHOUT ECHO
    returns	one character string with next input character

(getnum)	READ A SIGNED NUMBER WITHOUT ECHO
    returns	number as read from input (gobbles terminator)


String Functions:

(concat <expr>...) CONCATENATE STRINGS
    <expr>	string expressions (must be strings)
    returns	string with concatenation

(substr <expr> <sexpr> [<lexpr>]) RETURN SUBSTRING
    <expr>	string expressin
    <sexpr>	starting position (first char is 1)
    <lexpr>	optional length (default is rest of string)
    returns	substring starting at <sexpr> for <lexpr>

(makestr <expr> <sexpr>)	MAKE STRING OF CHARS
    <expr>	length of result string
    <sexpr>	string, fill new string with first char
    returns	string <expr> long all of <sexpr>

(ascii <expr>)	NUMERIC VALUE OF CHARACTER
    <expr>	string exprssion
    returns	numeric value of first character (according to ASCII)

(chr <expr>)	CHARACTER EQUIVALENT OF ASCII VALUE
    <expr>	numeric expression
    returns	one character string with ASCII equivalent of <expr>


List Functions:

(head <expr>)	RETURN THE HEAD ELEMENT OF A LIST (CAR sortof)
    <expr>	the list (evaluated)
    returns	the first element of the list

(tail <expr>)	RETURN THE TAIL ELEMENTS OF A LIST (CDR sortof)
    <expr>	the list (evaluated)
    returns	the list minus the first element

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

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

(append <list> <expr>...)	APPEND TO A LIST
    <list>	the initial list (evaluated)
    <expr>	expressions to be appended to the list (evaluated)
    returns	the new list

(prepend <list> <expr>...)	APPEND TO THE FRONT OF A LIST
    <list>	the initial list (evaluated)
    <expr>	expressions to be prepended to the list (evaluated)
    returns	the new list


Arithmetic Functions:

(+ <expr>...)	ADD A LIST OF VALUES
    <expr>	expressions to be added (evaluated)
    returns	the result of the addition

(- <expr>...)	SUBTRACT A LIST OF VALUES
    <expr>	expressions to be subtracted (evaluated)
    returns	the result of the subtraction

(* <expr>...)	MULTIPLY A LIST OF VALUES
    <expr>	expressions to be multiplied (evaluated)
    returns	the result of the multiplication

(/ <expr>...)	DIVIDE A LIST OF VALUES
    <expr>	expressions to be divided (evaluated)
    returns	the result of the division

(% <expr>...)	MODify A LIST OF VALUES? (the mod function)
    <expr>	expressions to be MODified? (evaluated)
    returns	the result of mod

(&& <expr>...)	THE LOGICAL AND OF A LIST OF VALUES
    <expr>	expressions to be ANDed (evaluated)
    returns	the result of anding the expressions
		(evaluation of expressions stops after the first expression
		 that evaluates to false)

(|| <expr>...)	THE LOGICAL OR OF A LIST OF VALUES
    <expr>	expressions to be ORed (evaluated)
    returns	the result of oring the expressions
		(evaluation of expressions stops after the first expression
		 that evaluates to true)

(!  <expr>)	THE LOGICAL NOT OF A VALUE
    <expr>	expression to be NOTed (evaluated)
    return	logical not of <expr>

(< <e1> <e2>)	TEST WHETHER AN EXPRESSION IS LESS THAN ANOTHER
    <e1>	the left operand of the comparison (evaluated)
    <e2>	the right operand of the comparison (evaluated)
    returns	the result of comparing <e1> with <e2>

(<= <e1> <e2>)	TEST WHETHER AN EXPRESSION IS LESS THAN OR EQUAL TO ANOTHER
    <e1>	the left operand of the comparison (evaluated)
    <e2>	the right operand of the comparison (evaluated)
    returns	the result of comparing <e1> with <e2>

(== <e1> <e2>)	TEST WHETHER AN EXPRESSION IS EQUAL TO ANOTHER
    <e1>	the left operand of the comparison (evaluated)
    <e2>	the right operand of the comparison (evaluated)
    returns	the result of comparing <e1> with <e2>

(!= <e1> <e2>)	TEST WHETHER AN EXPRESSION IS NOT EQUAL TO ANOTHER
    <e1>	the left operand of the comparison (evaluated)
    <e2>	the right operand of the comparison (evaluated)
    returns	the result of comparing <e1> with <e2>

(>= <e1> <e2>)	TEST WHETHER AN EXPRESSION IS GREATER THAN OR EQUAL TO ANOTHER
    <e1>	the left operand of the comparison (evaluated)
    <e2>	the right operand of the comparison (evaluated)
    returns	the result of comparing <e1> with <e2>

(> <e1> <e2>)	TEST WHETHER AN EXPRESSION IS GREATER THAN ANOTHER
    <e1>	the left operand of the comparison (evaluated)
    <e2>	the right operand of the comparison (evaluated)
    returns	the result of comparing <e1> with <e2>

(& <expr>...)	THE BITWISE AND OF A LIST OF VALUES
    <expr>	expressions to be ANDed (evaluated)
    returns	the bit by bit ANDing of expressions

(| <expr...)	THE BITWISE OR OF A LIST OF VALUES
    <expr>	expressions to be ORed (evaluated)
    returns	the bit by bit ORing of expressions

(~  <expr>)	THE BITWISE NOT OF A VALUE
    <expr>	expression to be NOTed (evaluated)
    returns	the bit by bit inversion of expression

(min <expr>...)	THE SMALLEST OF A LIST OF VALUES
    <expr>	expressions to be checked (evaluated)
    returns	the smallest value of the list

(max <expr>...)	THE LARGEST OF A LIST OF VALUES
    <expr>	expressions to be checked (evaluated)
    returns	the largest value of the list


Keymap Functions:

(keymap)	CREATE A NEW KEYMAP
    returns	a new keymap

(key <km> <kstr> <ksym>)	ADD A KEY DEFINITION TO A KEYMAP
    <km>	the keymap (evaluated)
    <kstr>	the string defining the key (evaluated)
    <ksym>	the symbol for the message (evaluated)
    returns	the keymap

(kmprocess <km> <envlist>)	PROCESS INPUT USING A KEYMAP
    <km>	the keymap (evaluated)
    <envlist>	list of active objects (evaluated)
    returns	(never returns)


SDB Database Functions:

(select <sstr>)	SELECT RECORDS FROM AN SDB DATABASE
    <sstr>	SDB selection expression string (evaluated)
    returns	a database pointer

(fetch <dbptr)	FETCH THE NEXT RECORD IN A SELECTION
    <dbptr>	the database pointer (evaluated)
    returns	the database pointer if a record was fetched
    		null otherwise

(update <dbptr>)	UPDATE THE CURRENT RECORD
    <dbptr>	the database pointer (evaluated)
    returns	the database pointer

(store <dbptr>)	STORE A NEW RECORD
    <dbptr>	the database pointer (evaluated)
    returns	the database pointer

(done <dbptr>)	CLOSE A DATABASE SELECTION
    <dbptr>	the database pointer (evaluated)
    returns	null

(get <dbptr> <fname>)	GET THE VALUE OF A FIELD IN THE CURRENT RECORD
    <dbptr>	the database pointer (evaluated)
    <fname>	the field name string (evaluated)
    returns	the database pointer

(put <dbptr> <fname> <vstr>)	STORE A FIELD VALUE INTO THE CURRENT RECORD
    <dbptr>	the database pointer (evaluated)
    <fname>	the field name string (evaluated)
    <vstr>	the new value string (evaluated)
    returns	the database pointer


Symbols:

newline		the newline character
tab		the tab character
bell		the bell character
self		the current object (within a message context)


Classes:

class	THE CLASS OF ALL OBJECT CLASSES (including itself)

    Messages:

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

	isnew	INITIALIZE A NEW CLASS
	    returns	the new class object

    	answer <msg> <fargs> <code>	ADD A MESSAGE TO A CLASS
	    <msg>	the message symbol (evaluated)
	    <fargs>	the formal argument list (evaluated)
			  this list is of the form:
			    ( <farg>... [ / <local>... ] )
			  where
			    <fargs>	is a list of formal arguments
			    <locals>	is a list of local variables
	    <code>	a list of executable expressions (evaluated)
	    returns	the object

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

(Note: 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 send to the newly created object)


::::::::::::::
keymap.mem
::::::::::::::
KEYMAPS

A keymap is data structure that translates a sequence of keystrokes into
a message.

In order to create a keymap:

	(setq km (keymap))

In order to add a key definition to a keymap (km):

	(key km "\eA" 'up)
	(key km "\eB" 'down)
	(key km "\eC" 'right)
	(key km "\eD" 'left)

In order to invoke a keymap:

	(setq env (list ob1 ob2 ob3 ob4))
	(kmprocess km env)

When kmprocess is called, it enters a character input loop calling kbin to
get single unechoed characters from the keyboard (note that you'll have to
figure out how to do single character input on your system and write a new
version of xlkbin.c to implement it).  When a sequence of characters is found
that matches one of the sequences defined in a key function call, the
corresponding message is sent.  Kmprocess tries to send the message to each
of the objects in the environment list.  It stops when it finds an object
that knows how to answer the message.  Along with the message selector given
in the key definition, kmprocess also sends the sequence of characters that
matched as a single string parameter.

I got this idea from emacs, but thought that it might be interesting to
implement it in a more general way to allow for experimenting with uses
other than text editors.  I have used it to implement a form processing
system at DEC, but that code is proprietary.


::::::::::::::
xlisp.h
::::::::::::::
/* xlisp - a small subset of lisp */

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

/* program limits */
#define STRMAX	100	/* maximum length of a string constant */
#define NNODES	2000	/* number of nodes to allocate in each request */

/* node types */
#define FREE	0
#define SUBR	1
#define LIST	2
#define MSG	2
#define BND	2
#define SYM	3
#define INT	4
#define STR	5
#define DBPTR	6
#define KMAP	7
#define FUN	8
#define OBJ	9

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

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

/* symbol structure */
struct xsym {
    char *xsy_name;		/* symbol name */
    struct node *xsy_value;	/* the current value */
};

/* subr node structure */
struct xsubr {
    int (*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 */
};

/* database pointer structure */
struct xdbptr {
    char *xdb_sptr;		/* selection pointer */
    int xdb_flags;		/* flag bits */
};

/* keymap structure */
struct xkmap {
    struct node *(*xkm_map)[];	/* selection pointer */
};

/* function node structure */
struct xfun {
    struct node *xf_funargs;	/* list of formal arguments */
    struct node *xf_funcode;	/* function code */
};

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

/* shorthand macros for accessing node substructures */

/* symbol node */
#define n_symname	n_info.n_xsym.xsy_name
#define n_symvalue	n_info.n_xsym.xsy_value

/* subr 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

/* database pointer node */
#define n_dbsptr	n_info.n_xdbptr.xdb_sptr
#define n_dbflags	n_info.n_xdbptr.xdb_flags

/* key map node */
#define n_kmap		n_info.n_xkmap.xkm_map

/* function node */
#define n_funargs	n_info.n_xfun.xf_funargs
#define n_funcode	n_info.n_xfun.xf_funcode

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

/* 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 node */
	struct xlist n_xlist;	/*     list node */
	struct xint n_xint;	/*     integer node */
	struct xstr n_xstr;	/*     string node */
	struct xdbptr n_xdbptr;	/*     database pointer node */
	struct xkmap n_xkmap;	/*     key map node */
	struct xfun n_xfun;	/*     function node */
	struct xobj n_xobj;	/*     object node */
    } n_info;
};


::::::::::::::
xlisp.c
::::::::::::::
/* xlisp - a small subset of lisp */

#include <setjmp.h>
#include "xlisp.h"

/* global variables */
jmp_buf xljmpbuf;

extern struct node *xlread();
extern struct node *xleval();

/* external variables */
extern struct node *xlenv;
extern struct node *xlstack;
extern int xlpvals;

/* inhibit the argv prompt */
int $$narg = 1;

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

    /* initialize the dynamic memory module (must be first) */
    xldmeminit();

    /* initialize xlisp */
    xlinit();
    xleinit(); xllinit(); xlminit();
    xlkinit(); xloinit(); xlsinit();

    /* initialize terminal input */
    xltin();

    /* read the input file if specified */
    if (argc > 1)
	xlfin(argv[1]);

    /* 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 */
	expr.n_ptr = xlread();

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

	/* print it if necessary */
	if (xlpvals) {
	    xlprint(expr.n_ptr);
	    putchar('\n');
	}
    }
}


::::::::::::::
xlread.c
::::::::::::::
/* xlread - xlisp expression input routine */

#include <stdio.h>
#include <ctype.h>
#include "xlisp.h"

/* global variables */
struct node *oblist;

/* external variables */
extern struct node *xlstack;
extern int (*xlgetc)();

/* local variables */
static int savech;

/* forward declarations (the extern hack is for decusc) */
extern struct node *parse();
extern struct node *plist();
extern struct node *pstring();
extern struct node *pnumber();
extern struct node *pquote();
extern struct node *pname();

/* xlread - read an xlisp expression */
struct node *xlread()
{
    struct node *val;
    int ch;

    /* initialize */
    savech = -1;

    /* parse an expression */
    val = parse();

    /* skip to end of line */
    while ((ch = thisch()) > 0 && ch != '\n') {
	if (!isspace(ch))
	    xlfail("extra characters after expression");
	savech = -1;
    }

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

/* parse - parse an xlisp expression */
static struct node *parse()
{
    int ch;

    /* keep looking for a node skipping comments */
    while (TRUE)

	/* check next character for type of node */
	switch (ch = nextch()) {
	case '\'':			/* a quoted expression */
		return (pquote());
	case '(':			/* a sublist */
		return (plist());
	case ')':			/* closing paren - shouldn't happen */
		xlfail("extra right paren");
	case ';':			/* a comment */
		pcomment();
		break;
	case '"':			/* a string */
		return (pstring());
	default:
		if (isdigit(ch))	/* a number */
		    return (pnumber());
		else			/* a name */
		    return (pname());
	}
}

/* pcomment - parse a comment */
static pcomment()
{
    int ch;

    /* skip to end of line */
    while ((ch = getch()) > 0)
	if (ch == '\n')
	    break;
}

/* plist - parse a list */
static struct node *plist()
{
    struct node *oldstk,val,*lastnptr,*nptr;
    int ch;

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

    /* skip the opening paren */
    savech = -1;

    /* keep appending nodes until a closing paren is found */
    for (lastnptr = NULL; (ch = nextch()) > 0 && ch != ')'; lastnptr = nptr) {

	/* 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 */
	nptr->n_listvalue = parse();
    }

    /* skip the closing paren */
    savech = -1;

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

    /* return successfully */
    return (val.n_ptr);
}

/* pstring - parse a string */
static struct node *pstring()
{
    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 */
    savech = -1;

    /* loop looking for a closing quote */
    for (i = 0; i < STRMAX && (ch = getch()) > 0 && ch != '"'; i++) {
	switch (ch) {
	case '\\':
		switch (ch = getch()) {
		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 = getch() - '0';
			    d3 = getch() - '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);

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

    /* return the new string */
    return (val.n_ptr);
}

/* pnumber - parse a number */
static struct node *pnumber()
{
    struct node *val;
    int ch,ival;

    /* loop looking for a closing quote */
    for (ival = 0; (ch = thisch()) > 0 && isdigit(ch); savech = -1)
	ival = ival * 10 + ch - '0';

    /* initialize the node */
    val = newnode(INT);
    val->n_int = ival;

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

/* xlenter - enter a symbol into the symbol table */
struct node *xlenter(sname)
  char *sname;
{
    struct node *sptr;

    /* check for nil */
    if (strcmp(sname,"nil") == 0)
	return (NULL);

    /* check for symbol already in table */
    for (sptr = oblist; sptr != NULL; sptr = sptr->n_listnext) 
	if (sptr->n_listvalue == NULL)
	    printf("bad oblist\n");
	else if (sptr->n_listvalue->n_symname == NULL)
	    printf("bad oblist symbol\n");
	else
	if (strcmp(sptr->n_listvalue->n_symname,sname) == 0)
	    return (sptr->n_listvalue);
    
    /* enter a new symbol and link it into the symbol list */
    sptr = newnode(LIST);
    sptr->n_listnext = oblist;
    oblist = sptr;
    sptr->n_listvalue = newnode(SYM);
    sptr->n_listvalue->n_symname = strsave(sname);

    /* return the new symbol */
    return (sptr->n_listvalue);
}

/* pquote - parse a quoted expression */
static struct node *pquote()
{
    struct node *oldstk,val;

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

    /* skip the quote character */
    savech = -1;

    /* allocate two nodes */
    val.n_ptr = newnode(LIST);
    val.n_ptr->n_listvalue = xlenter("quote");
    val.n_ptr->n_listnext = newnode(LIST);

    /* initialize the second to point to the quoted expression */
    val.n_ptr->n_listnext->n_listvalue = parse();

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

    /* return the quoted expression */
    return (val.n_ptr);
}

/* pname - parse a symbol name */
static struct node *pname()
{
    char sname[STRMAX+1];
    int ch,i;

    /* get symbol name */
    for (i = 0; i < STRMAX && (ch = thisch()) > 0 && issym(ch); i++)
	sname[i] = getch();
    sname[i] = 0;

    /* initialize value */
    return (xlenter(sname));
}

/* nextch - look at the next non-blank character */
static int nextch()
{
    int ch;

    /* look for a non-blank character */
    while ((ch = thisch()) > 0)
	if (isspace(ch))
	    savech = -1;
	else
	    break;

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

/* thisch - look at the current character */
static int thisch()
{
    /* return and save the current character */
    return (savech = getch());
}

/* getch - get the next character */
static int getch()
{
    int ch;

    /* check for a saved character */
    if ((ch = savech) >= 0)
	savech = -1;
    else
	ch = (*xlgetc)();

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

/* issym - check whether a character if valid in a symbol name */
static int issym(ch)
  int ch;
{
    if (isspace(ch) || ch == '(' || ch == ')' || ch == ';' || ch == '\'')
	return (FALSE);
    else
	return (TRUE);
}


::::::::::::::
xleval.c
::::::::::::::
/* xleval - xlisp evaluator */

#include <setjmp.h>
#include "xlisp.h"

/* global variables */
struct node *xlstack;

/* debugging stuff */
#define TRACE_DEPTH	1024

static struct node *trace_stack[TRACE_DEPTH];
static int trace_pointer;

/* external variables */
extern jmp_buf xljmpbuf;
extern struct node *xlenv;
extern struct node *self;

/* local variables */
static struct node *slash;

/* forward declarations (the extern hack is for decusc) */
extern struct node *evlist();
extern struct node *evsym();
extern struct node *evfun();

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

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

/* 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);
}

/* evlist - evaluate a list */
static struct node *evlist(nptr)
  struct node *nptr;
{
    struct node *oldstk,fun,args,*val;

    /* 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;

    /* add trace entry */
    xltpush(nptr);

    /* 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:
	    val = (*fun.n_ptr->n_subr)(args.n_ptr);
	    break;
    case FUN:
    case LIST:
	    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;

    /* remove trace entry */
    xltpop();

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

/* evsym - evaluate a symbol */
static struct node *evsym(sym)
  struct node *sym;
{
    struct node *optr,*lptr,*bptr;

    /* check for a current object */
    if ((optr = self->n_symvalue) != NULL && optr->n_type == OBJ)
	for (lptr = optr->n_obdata; lptr != NULL; lptr = lptr->n_listnext)
	    if ((bptr = lptr->n_listvalue) != NULL && bptr->n_type == BND)
		if (bptr->n_bndsym == sym)
		    return (bptr->n_bndvalue);

    /* return the current symbol value */
    return (sym->n_symvalue);
}

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

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

    /* bind the formal parameters */
    oldenv = xlenv;
    xlabind(fun->n_funargs,args);
    xlfixbindings(oldenv);

    /* execute the code */
    for (cptr.n_ptr = fun->n_funcode; 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 */
int xlabind(fargs,aargs)
  struct node *fargs,*aargs;
{
    struct node *oldstk,farg,aarg,val;

    /* create a stack frame */
    oldstk = xlsave(&farg,&aarg,&val,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 local variable separator */
	if (farg.n_ptr->n_listvalue == slash)
	    break;

	/* evaluate the argument */
	val.n_ptr = xlevarg(&aarg.n_ptr);

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

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

    /* check for local variables */
    if (farg.n_ptr != NULL && farg.n_ptr->n_listvalue == slash)
	while ((farg.n_ptr = farg.n_ptr->n_listnext) != NULL)
	    xlbind(farg.n_ptr->n_listvalue,NULL);

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

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

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

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

    /* restore input to the terminal */
    xltin();

    /* do the back trace */
    xltrace();
    trace_pointer = -1;

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

/* xltpush - add an entry to the trace stack */
xltpush(nptr)
    struct node *nptr;
{
    if (trace_pointer >= TRACE_DEPTH)
	xlfail("trace stack overflow");

    trace_stack[++trace_pointer] = nptr;
}

/* xltpop - pop an entry from the trace stack */
xltpop()
{
    if (trace_pointer >= 0) --trace_pointer;
}

/* xltrace - do a back trace */
xltrace()
{
    int tptr;

    for (tptr=trace_pointer;
    	 tptr>=0;
	 tptr-- ) {
	xlprint(trace_stack[tptr]);
    	puts("\n");
    }
}

/* xleinit - initialize the evaluator */
xleinit()
{
    /* enter the local variable separator symbol */
    slash = xlenter("/");

    /* initialize debugging stuff */
    trace_pointer = -1;
}


::::::::::::::
xlprin.c
::::::::::::::
/* xlprint - xlisp print routine */

#include "xlisp.h"

/* xlprint - print an xlisp value */
xlprint(vptr)
  struct node *vptr;
{
    struct node *nptr,*next;

    /* print null as the empty list */
    if (vptr == NULL) {
	printf("()");
	return;
    }

    /* check value type */
    switch (vptr->n_type) {
    case SUBR:
	    printf("#%o",vptr->n_subr);
	    break;
    case FUN:
    case LIST:
	    putchar('(');
	    for (nptr = vptr; nptr != NULL; nptr = next) {
	        xlprint(nptr->n_listvalue);
		if ((next = nptr->n_listnext) != NULL)
		    if (next->n_type == LIST)
			putchar(' ');
		    else {
			putchar('.');
			xlprint(next);
			break;
		    }
	    }
	    putchar(')');
	    break;
    case SYM:
	    printf("%s",vptr->n_symname);
	    break;
    case INT:
	    printf("%d",vptr->n_int);
	    break;
    case STR:
	    printf("%s",vptr->n_str);
	    break;
    }
}