shithub: mlisp

ref: e5cbdb2d92963fccf56980ea7a60ecc2b03204cf
dir: /subr.c/

View raw version
#include "lisp.h"

int
floeq(flonum x, flonum y)
{
	return fabs(x-y) < 0.000003;
}

typedef int (*Eql)(C *a, C *b);

int
eq(C *a, C *b)
{
	return a == b;
}
int
equal(C *a, C *b)
{
tail:
	if(atom(a) != atom(b))
		return 0;
	if(atom(a)){
		if(fixnump(a))
			return fixnump(b) &&
				a->fix == b->fix;
		if(flonump(a))
			return flonump(b) &&
				floeq(a->flo, b->flo);
		if(stringp(a))
			return stringp(b) &&
				strcmp(a->str, b->str) == 0;
		return a == b;
	}
	if(!equal(a->a, b->a))
		return 0;
	a = a->d;
	b = b->d;
	goto tail;
}

/* this is a bit ugly... */
int
getnumcase(C *lt, C *rt)
{
	int type;
	type = 0;
	if(fixnump(lt))
		{}
	else if(flonump(lt))
		type |= 1;
	else
		type |= ~0;
	if(fixnump(rt))
		{}
	else if(flonump(rt))
		type |= 2;
	else
		type |= ~0;
	return type;
}

/* Types */

C *atom_subr(void){
	return atom(alist[0]) ? t : nil;
}
C *fixp_subr(void){
	return fixnump(alist[0]) ? t : nil;
}
C *floatp_subr(void){
	return flonump(alist[0]) ? t : nil;
}
C *numberp_subr(void){
	return numberp(alist[0]) ? t : nil;
}
C *stringp_subr(void){
	return stringp(alist[0]) ? t : nil;
}

/* Basics */

C *eval_subr(void){
	nargs = 0;
	return eval(alist[0], alist[1]);
}
C *apply_subr(void){
	nargs = 0;
	return apply(alist[0], alist[1], alist[2]);
}
C *quote_fsubr(void){
	if(alist[0] == nil)
		err("error: arg count");
	return alist[0]->a;
}
C *function_fsubr(void){
	if(alist[0] == nil)
		err("error: arg count");
	return cons(funarg, cons(alist[0]->a, cons(alist[1], nil)));
}
C *comment_fsubr(void){
	return noval;
}
C *prog2_lsubr(void){
	if(largs.nargs < 2)
		err("error: arg count");
	return largs.alist[2];
}
C *progn_lsubr(void){
	if(largs.nargs < 1)
		err("error: arg count");
	return largs.alist[largs.nargs];
}
C *arg_subr(void){
	fixnum n;
	if(!fixnump(alist[0]))
		err("error: not a fixnum");
	n = alist[0]->fix;
	if(n < 1 || n > largs.nargs)
		err("error: arg out of bounds");
	return largs.alist[n];
}

/* List functions */

C *car(C *pair){
	if(pair == nil)
		return nil;
	if(numberp(pair))
		err("error: not a pair");
	return pair->a;
}
C *cdr(C *pair){
	if(pair == nil)
		return nil;
	if(numberp(pair))
		err("error: not a pair");
	return pair->d;
}
C *car_subr(void){ return car(alist[0]); }
C *cdr_subr(void){ return cdr(alist[0]); }
C *caar_subr(void){ return car(car(alist[0])); }
C *cadr_subr(void){ return car(cdr(alist[0])); }
C *cdar_subr(void){ return cdr(car(alist[0])); }
C *cddr_subr(void){ return cdr(cdr(alist[0])); }
C *caaar_subr(void){ return car(car(car(alist[0]))); }
C *caadr_subr(void){ return car(car(cdr(alist[0]))); }
C *cadar_subr(void){ return car(cdr(car(alist[0]))); }
C *caddr_subr(void){ return car(cdr(cdr(alist[0]))); }
C *cdaar_subr(void){ return cdr(car(car(alist[0]))); }
C *cdadr_subr(void){ return cdr(car(cdr(alist[0]))); }
C *cddar_subr(void){ return cdr(cdr(car(alist[0]))); }
C *cdddr_subr(void){ return cdr(cdr(cdr(alist[0]))); }
C *caaaar_subr(void){ return car(car(car(car(alist[0])))); }
C *caaadr_subr(void){ return car(car(car(cdr(alist[0])))); }
C *caadar_subr(void){ return car(car(cdr(car(alist[0])))); }
C *caaddr_subr(void){ return car(car(cdr(cdr(alist[0])))); }
C *cadaar_subr(void){ return car(cdr(car(car(alist[0])))); }
C *cadadr_subr(void){ return car(cdr(car(cdr(alist[0])))); }
C *caddar_subr(void){ return car(cdr(cdr(car(alist[0])))); }
C *cadddr_subr(void){ return car(cdr(cdr(cdr(alist[0])))); }
C *cdaaar_subr(void){ return cdr(car(car(car(alist[0])))); }
C *cdaadr_subr(void){ return cdr(car(car(cdr(alist[0])))); }
C *cdadar_subr(void){ return cdr(car(cdr(car(alist[0])))); }
C *cdaddr_subr(void){ return cdr(car(cdr(cdr(alist[0])))); }
C *cddaar_subr(void){ return cdr(cdr(car(car(alist[0])))); }
C *cddadr_subr(void){ return cdr(cdr(car(cdr(alist[0])))); }
C *cdddar_subr(void){ return cdr(cdr(cdr(car(alist[0])))); }
C *cddddr_subr(void){ return cdr(cdr(cdr(cdr(alist[0])))); }
C *eq_subr(void){
	return alist[0] == alist[1] ? t : nil;
}
C *equal_subr(void){
	return equal(alist[0], alist[1]) ? t : nil;
}
C *assoc_subr(void){
	C *l;
	if(!listp(alist[1]))
		err("error: no list");
	for(l = alist[1]; l != nil; l = l->d)
		if(equal(l->a->a, alist[0]))
			return l->a;
	return nil;
}
C *assq_subr(void){
	if(!listp(alist[1]))
		err("error: no list");
	return assq(alist[0], alist[1]);
}
C *sassoc_subr(void){
	C *l;
	l = assoc_subr();
	return l != nil ? l : apply(alist[2], nil, nil);
}
C *sassq_subr(void){
	C *l;
	l = assq_subr();
	return l != nil ? l : apply(alist[2], nil, nil);
}
C *last_subr(void){
	C *l;
	if(!listp(alist[0]))
		err("error: no list");
	for(l = alist[0]; l != nil; l = l->d)
		if(atom(l->d))
			return l;
	return nil;
}
C *length_subr(void){
	return mkfix(length(alist[0]));
}
C *member_aux(Eql cmp){
	C *l;
	for(l = alist[1]; l != nil; l = l->d){
		if(atom(l))
			err("error: no list");
		if(cmp(l->a, alist[0]))
			return t;
	}
	return nil;
}
C *member_subr(void){ return member_aux(equal); }
C *memq_subr(void){ return member_aux(eq); }
C *null_subr(void){
	return alist[0] == nil ? t : nil;
}

/* Creating list structure */

C *cons_subr(void){
	return cons(alist[0], alist[1]);
}
C *ncons_subr(void){
	return cons(alist[0], nil);
}
C *xcons_subr(void){
	return cons(alist[1], alist[0]);
}
C *list_fsubr(void){
	return evlis(alist[0], alist[1]);
}
C *append_subr(void){
	C *l, **p;
	assert(temlis.a == nil);
	p = (C**)&temlis.a;
	for(l = alist[0]; l != nil; l = l->d){
		if(atom(l))
			err("error: no list");
		*p = cons(l->a, nil);
		p = &(*p)->d;
	}
	*p = alist[1];
	l = temlis.a;
	temlis.a = nil;
	return l;
}
C *reverse_subr(void){
	C *l;
	assert(temlis.a == nil);
	for(l = alist[0]; l != nil; l = l->d){
		if(atom(l))
			err("error: no list");
		temlis.a = cons(l->a, temlis.a);
	}
	l = temlis.a;
	temlis.a = nil;
	return l;
}

/* Modifying list structure */

C *rplaca_subr(void){
	if(atom(alist[0]))
		err("error: atom");
	alist[0]->a = alist[1];
	return alist[0];
}
C *rplacd_subr(void){
	if(atom(alist[0]))		/* this could work on a symbolic atom */
		err("error: atom");
	alist[0]->d = alist[1];
	return alist[0];
}
C *nconc_subr(void){
	C *l;
	for(l = alist[0]; l != nil; l = l->d){
		if(atom(l))
			err("error: no list");
		if(l->d == nil){
			l->d = alist[1];
			break;
		}
	}
	return alist[0];
}
C *nreverse_subr(void){

	C *l, *n, *last;
	last = nil;
	for(l = alist[0]; l != nil; l = n){
		if(atom(l))
			err("error: no list");
		n = l->d;
		l->d = last;
		last = l;
	}
	return last;
}
C *delete_aux(Eql cmp){
	C **p;
	fixnum n;
	if(largs.nargs < 2)
		err("error: arg count");
	n = -1;
	if(largs.nargs > 2)
		n = largs.alist[3]->fix;
	for(p = &largs.alist[2]; *p != nil; p = &(*p)->d){
		if(atom(*p))
			err("error: no list");
		if(cmp((*p)->a, largs.alist[1])){
			if(n-- == 0)
				break;
			*p = (*p)->d;
		}
	}
	return largs.alist[2];
}
C *delete_lsubr(void){ return delete_aux(equal); }
C *delq_lsubr(void){ return delete_aux(eq); }


/* Boolean logic */

C *and_fsubr(void){
	C *l;
	int ret;
	ret = 1;
	for(l = alist[0]; l != nil; l = l->d)
		if(eval(l->a, alist[1]) == nil){
			ret = 0;
			break;
		}
	return ret ? t : nil;
}
C *or_fsubr(void){
	C *l;
	int ret;
	ret = 0;
	for(l = alist[0]; l != nil; l = l->d)
		if(eval(l->a, alist[1]) != nil){
			ret = 1;
			break;
		}
	return ret ? t : nil;
}

/* Symbols, values */

C *setq_fsubr(void){
	C *tt, *a, *l, *last;
	last = nil;
	for(l = alist[0]; l != nil; l = l->d->d){
		a = l->a;
		if(!atom(a))
			err("error: need atom");
		last = eval(l->d->a, alist[1]);
		tt = assq(a, alist[1]);
		if(tt == nil)
			putprop(a, last, value);
		else
			tt->d = last;
	}
	return last;
}
/* Has to be FSUBR here, also extended syntax */
C *set_fsubr(void){
	C *tt, *a, *l, *last;
	last = nil;
	for(l = alist[0]; l != nil; l = l->d->d){
		a = eval(l->a, alist[1]);
		if(!atom(a))
			err("error: need atom");
		last = eval(l->d->a, alist[1]);
		tt = assq(a, alist[1]);
		if(tt == nil)
			putprop(a, last, value);
		else
			tt->d = last;
	}
	return last;
}

/* Property list */

C *get_subr(void){
	return get(alist[0], alist[1]);
}
C *putprop_subr(void){
	return putprop(alist[0], alist[1], alist[2]);
}
C *defprop_fsubr(void){
	if(length(alist[0]) != 3)
		err("error: arg count");
	return putprop(alist[0]->a, alist[0]->d->a, alist[0]->d->d->a);
}
C *remprop_subr(void){
	C *l, **p;
	p = &alist[0]->d;
	for(l = *p; l != nil; l = l->d){
		if(l->a == alist[1]){
			*p = l->d->d;
			break;
		}
		p = &(*p)->d;
	}
	return nil;
}

/* Number predicates */

C *zerop_subr(void){
	int res;
	res = 0;
	if(fixnump(alist[0]))
		res = alist[0]->fix == 0;
	else if(flonump(alist[0]))
		res = floeq(alist[0]->flo, 0.0);
	else
		err("error: not a number");
	return res ? t : nil;
}
C *plusp_subr(void){
	int res;
	res = 0;
	if(fixnump(alist[0]))
		res = alist[0]->fix > 0;
	else if(flonump(alist[0]))
		res = alist[0]->flo > 0.0;
	else
		err("error: not a number");
	return res ? t : nil;
}
C *minusp_subr(void){
	int res;
	res = 0;
	if(fixnump(alist[0]))
		res = alist[0]->fix < 0;
	else if(flonump(alist[0]))
		res = alist[0]->flo < 0.0;
	else
		err("error: not a number");
	return res ? t : nil;
}
C *greaterp_lsubr(void){
	C *lt, *rt;
	int i;

	if(largs.nargs < 2)
		err("error: arg count");
	for(i = 1; i < largs.nargs; i++){
		lt = largs.alist[i];
		rt = largs.alist[i+1];
		switch(getnumcase(lt, rt)){
		case 0:
			if(lt->fix <= rt->fix)
				return nil;
			break;
		case 1:
			if(lt->flo <= rt->fix)
				return nil;
			break;
		case 2:
			if(lt->fix <= rt->flo)
				return nil;
			break;
		case 3:
			if(lt->flo <= rt->flo)
				return nil;
			break;
		default:
			err("error: not a number");
			return nil;
		}
	}
	return t;
}
C *lessp_lsubr(void){
	C *lt, *rt;
	int i;

	if(largs.nargs < 2)
		err("error: arg count");
	for(i = 1; i < largs.nargs; i++){
		lt = largs.alist[i];
		rt = largs.alist[i+1];
		switch(getnumcase(lt, rt)){
		case 0:
			if(lt->fix >= rt->fix)
				return nil;
			break;
		case 1:
			if(lt->flo >= rt->fix)
				return nil;
			break;
		case 2:
			if(lt->fix >= rt->flo)
				return nil;
			break;
		case 3:
			if(lt->flo >= rt->flo)
				return nil;
			break;
		default:
			err("error: not a number");
			return nil;
		}
	}
	return t;
}
C *max_lsubr(void){
	int i;
	C *tt;
	fixnum fix;
	flonum flo;
	int type;

	fix = FIXMIN;
	flo = -FLOMAX;
	type = 0;	// fix;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix = tt->fix > fix ? tt->fix : fix;
		else if(flonump(tt)){
			flo = tt->flo > flo ? tt->flo : flo;
			type = 1;
		}else
			err("error: not a number");
	}
	return type == 0 ? mkfix(fix) : mkflo(fix > flo ? fix : flo);
}
C *min_lsubr(void){
	int i;
	C *tt;
	fixnum fix;
	flonum flo;
	int type;

	fix = FIXMAX;
	flo = FLOMAX;
	type = 0;	// fix;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix = tt->fix < fix ? tt->fix : fix;
		else if(flonump(tt)){
			flo = tt->flo < flo ? tt->flo : flo;
			type = 1;
		}else
			err("error: not a number");
	}
	return type == 0 ? mkfix(fix) : mkflo(fix < flo ? fix : flo);
}

/* Arithmetic */

C *plus_lsubr(void){
	int i;
	C *tt;
	fixnum fix;
	flonum flo;
	int type;

	fix = 0;
	flo = 0.0;
	type = 0;	// fix;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix += tt->fix;
		else if(flonump(tt)){
			flo += tt->flo;
			type = 1;
		}else
			err("error: not a number");
	}
	return type == 0 ? mkfix(fix) : mkflo(fix+flo);
}
C *difference_lsubr(void){
	int i;
	C *tt;
	fixnum fix;
	flonum flo;
	int type;
	int first;

	first = 1;
	fix = 0;
	flo = 0.0;
	type = 0;	// fix;
	if(largs.nargs == 0)
		err("error: not enough args");
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix += first ? tt->fix : -tt->fix;
		else if(flonump(tt)){
			flo += first ? tt->flo : -tt->flo;
			type = 1;
		}else
			err("error: not a number");
		first = 0;
	}
	if(largs.nargs == 1)
		return type == 0 ? mkfix(-fix) : mkflo(-fix-flo);
	return type == 0 ? mkfix(fix) : mkflo(fix+flo);
}
C *times_lsubr(void){
	int i;
	C *tt;
	fixnum fix;
	flonum flo;
	int type;

	fix = 1;
	flo = 1.0;
	type = 0;	// fix;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix *= tt->fix;
		else if(flonump(tt)){
			flo *= tt->flo;
			type = 1;
		}else
			err("error: not a number");
	}
	return type == 0 ? mkfix(fix) : mkflo(fix*flo);
}
C *quotient_lsubr(void){
	int i;
	C *tt;
	fixnum fix;
	flonum flo;
	int type;

	fix = 1;
	flo = 1.0;
	type = 0;	// fix;
	if(largs.nargs == 0)
		return mkfix(1);
	for(i = 2; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix *= tt->fix;
		else if(flonump(tt)){
			flo *= tt->flo;
			type = 1;
		}else
			err("error: not a number");
	}

	tt = largs.alist[1];
	if(largs.nargs == 1){
		if(fixnump(tt))
			return mkfix(1/tt->fix);
		else if(flonump(tt))
			return mkflo(1.0/tt->flo);
		else
			err("error: not a number");
	}

	if(fixnump(tt))
		return type == 0 ? mkfix(tt->fix/fix) : mkflo(tt->fix/(fix*flo));
	else if(flonump(tt))
		return type == 0 ? mkflo(tt->flo/fix) : mkflo(tt->flo/(fix*flo));
	else
		err("error: not a number");
	/* can't happen */
	return nil;
}
C *add1_subr(void){
	if(fixnump(alist[0]))
		return mkfix(alist[0]->fix+1);
	if(flonump(alist[0]))
		return mkflo(alist[0]->flo+1.0);
	err("error: not a number");
	return nil;
}
C *sub1_subr(void){
	if(fixnump(alist[0]))
		return mkfix(alist[0]->fix-1);
	if(flonump(alist[0]))
		return mkflo(alist[0]->flo-1.0);
	err("error: not a number");
	return nil;
}
C *remainder_subr(void){
	switch(getnumcase(alist[0], alist[1])){
	case 0:
		if(alist[1]->fix == 0)
			err("error: division by zero");
		return mkfix(alist[0]->fix % alist[1]->fix);
		break;
	case 1:
		return mkflo(fmod(alist[0]->flo, alist[1]->fix));
		break;
	case 2:
		return mkflo(fmod(alist[0]->fix, alist[1]->flo));
		break;
	case 3:
		return mkflo(fmod(alist[0]->flo, alist[1]->flo));
		break;
	default:
		err("error: not a number");
		return nil;
	}
}
C *expt_subr(void){
	switch(getnumcase(alist[0], alist[1])){
	case 0:
		if(alist[1]->fix == 0)
			err("error: division by zero");
		return mkfix(pow(alist[0]->fix, alist[1]->fix));
		break;
	case 1:
		return mkflo(exp(log(alist[0]->flo) * alist[1]->fix));
		break;
	case 2:
		return mkflo(exp(log(alist[0]->fix) * alist[1]->flo));
		break;
	case 3:
		return mkflo(exp(log(alist[0]->flo) * alist[1]->flo));
		break;
	default:
		err("error: not a number");
		return nil;
	}
}

/* Bitwise operations */

C *logior_lsubr(void){
	int i;
	C *tt;
	fixnum fix;

	fix = 0;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix |= tt->fix;
		else
			err("error: not a fixnum");
	}
	return mkfix(fix);
}
C *logand_lsubr(void){
	int i;
	C *tt;
	fixnum fix;

	fix = ~0;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix &= tt->fix;
		else
			err("error: not a fixnum");
	}
	return mkfix(fix);
}
C *logxor_lsubr(void){
	int i;
	C *tt;
	fixnum fix;

	fix = 0;
	for(i = 1; i <= largs.nargs; i++){
		tt = largs.alist[i];
		if(fixnump(tt))
			fix ^= tt->fix;
		else
			err("error: not a fixnum");
	}
	return mkfix(fix);
}
C *lsh_subr(void){
	if(!fixnump(alist[0]) || !fixnump(alist[1]))
		err("error: not a fixnum");
	if(alist[1]->fix < 0)
		return mkfix((word)alist[0]->fix >> -alist[1]->fix);
	else
		return mkfix((word)alist[0]->fix << alist[1]->fix);
}

/* Mapping */

/* zip is for internal use.
 * It returns successively zipped lists for mapping
 * leaving the list on the stack. */
static int
zip(C *(*f)(C*))
{
	int i;
	C **ap;
	ap = push(nil);
	for(i = 2; i <= largs.nargs; i++){
		if(largs.alist[i] == nil){
			pop();
			return 1;
		}
		*ap = cons(f(largs.alist[i]), nil);
		ap = &(*ap)->d;
		largs.alist[i] = largs.alist[i]->d;
	}
	return 0;
}
C *id(C *c) { return c; }
static int ziplist(void){ return zip(id); }
static int zipcar(void){ return zip(car); }

C *maplist_aux(int (*zip)(void)){
	C **p;
	if(largs.nargs < 2)
		err("error: arg count");
	p = push(nil);
	while(!zip()){
		*p = cons(apply(largs.alist[1], pop(), nil), nil);
		p = &(*p)->d;
	}
	return pop();
}
C *maplist_lsubr(void){ return maplist_aux(ziplist); }
C *mapcar_lsubr(void){ return maplist_aux(zipcar); }
C *map_aux(int (*zip)(void)){
	C *ret;
	if(largs.nargs < 2)
		err("error: arg count");
	ret = largs.alist[2];
	while(!zip())
		apply(largs.alist[1], pop(), nil);
	return ret;
}
C *map_lsubr(void){ return map_aux(ziplist); }
C *mapc_lsubr(void){ return map_aux(zipcar); }
C *mapcon_aux(int (*zip)(void)){
	C **p;
	if(largs.nargs < 2)
		err("error: arg count");
	p = push(nil);
	while(!zip()){
		*p = apply(largs.alist[1], pop(), nil);
		for(; *p != nil; p = &(*p)->d)
			if(atom(*p))
				err("error: no list");
	}
	return pop();
}
C *mapcon_lsubr(void){ return mapcon_aux(ziplist); }
C *mapcan_lsubr(void){ return mapcon_aux(zipcar); }

/* IO */

C *read_subr(void){
	return readsxp();
}
C *prin1_subr(void){
	lprint(alist[0]);
	return t;
}
C *print_subr(void){
	fprintf(sysout, "\n");
	lprint(alist[0]);
	return t;
}
C *princ_subr(void){
	princ(alist[0]);
	return t;
}
C *terpri_subr(void){
	fprintf(sysout, "\n");
	return nil;
}


/* Prog feature */
Prog prog;

C *go_fsubr(void){
	C *tt, *p;
	if(prog.prog == nil)
		err("error: not in prog");
	if(alist[0] == nil)
		err("error: arg count");
	tt = alist[0]->a;
	while(!atom(tt))
		tt = eval(tt, alist[1]);
	for(p = prog.prog; p != nil; p = p->d)
		if(p->a == tt){
			prog.pc = p->d;
			return nil;
		}
	err("undefined label");
	return nil;	// hm...
}
C *return_fsubr(void){
	if(prog.prog == nil)
		err("error: not in prog");
	if(alist[0] == nil)
		prog.ret = nil;
	else
		prog.ret = eval(alist[0]->a, alist[1]);
	prog.pc = nil;
	return nil;	// hm...
}

C *prog_fsubr(void){
	Prog progsv;

	C *p, *a;
	C **ap;

	progsv = prog;
	prog.prog = alist[0]->d;
	prog.pc = alist[0]->d;

	/* build a-list */
	assert(temlis.a == nil);
	ap = (C**)&temlis.a;
	for(p = alist[0]->a; p != nil; p = p->d){
		*ap = cons(cons(p->a, nil), nil);
		ap = &(*ap)->d;
	}
	*ap = alist[1];		/* nconc */
	alist[1] = a = temlis.a;
	temlis.a = nil;

	/* execute */
	prog.ret = nil;
	while(prog.pc != nil){
		p = prog.pc->a;
		prog.pc = prog.pc->d;
		if(!atom(p))
			eval(p, a);
	}

	p = prog.ret;
	prog = progsv;
	return p;
}

void
initsubr(void)
{
	C *a;

	putprop(t, t, value);

#define SUBR(str, func, narg) \
	a = intern(str); \
	putprop(a, mksubr(func, narg), subr);
#define LSUBR(str, func) \
	a = intern(str); \
	putprop(a, mksubr(func, -1), lsubr);
#define FSUBR(str, func) \
	a = intern(str); \
	putprop(a, (C*)consw((word)func), fsubr);

	SUBR("ATOM", atom_subr, 1)
	SUBR("FIXP", fixp_subr, 1)
	SUBR("FLOATP", floatp_subr, 1)
	SUBR("NUMBERP", numberp_subr, 1)
	SUBR("STRINGP", stringp_subr, 1)

	SUBR("APPLY", apply_subr, 3)
	SUBR("EVAL", eval_subr, 2)
	FSUBR("QUOTE", quote_fsubr)
	FSUBR("FUNCTION", function_fsubr)
	FSUBR("COMMENT", comment_fsubr)
	LSUBR("PROG2", prog2_lsubr)
	LSUBR("PROGN", progn_lsubr)
	SUBR("ARG", arg_subr, 1)

	SUBR("CAR", car_subr, 1)
	SUBR("CDR", cdr_subr, 1)
	SUBR("CAAR", caar_subr, 1)
	SUBR("CADR", cadr_subr, 1)
	SUBR("CDAR", cdar_subr, 1)
	SUBR("CDDR", cddr_subr, 1)
	SUBR("CAAAR", caaar_subr, 1)
	SUBR("CAADR", caadr_subr, 1)
	SUBR("CADAR", cadar_subr, 1)
	SUBR("CADDR", caddr_subr, 1)
	SUBR("CDAAR", cdaar_subr, 1)
	SUBR("CDADR", cdadr_subr, 1)
	SUBR("CDDAR", cddar_subr, 1)
	SUBR("CDDDR", cdddr_subr, 1)
	SUBR("CAAAAR", caaaar_subr, 1)
	SUBR("CAAADR", caaadr_subr, 1)
	SUBR("CAADAR", caadar_subr, 1)
	SUBR("CAADDR", caaddr_subr, 1)
	SUBR("CADAAR", cadaar_subr, 1)
	SUBR("CADADR", cadadr_subr, 1)
	SUBR("CADDAR", caddar_subr, 1)
	SUBR("CADDDR", cadddr_subr, 1)
	SUBR("CDAAAR", cdaaar_subr, 1)
	SUBR("CDAADR", cdaadr_subr, 1)
	SUBR("CDADAR", cdadar_subr, 1)
	SUBR("CDADDR", cdaddr_subr, 1)
	SUBR("CDDAAR", cddaar_subr, 1)
	SUBR("CDDADR", cddadr_subr, 1)
	SUBR("CDDDAR", cdddar_subr, 1)
	SUBR("CDDDDR", cddddr_subr, 1)
	SUBR("EQ", eq_subr, 2)
	SUBR("EQUAL", equal_subr, 2)
	SUBR("ASSOC", assoc_subr, 2)
	SUBR("ASSQ", assq_subr, 2)
	SUBR("SASSOC", sassoc_subr, 3)
	SUBR("SASSQ", sassq_subr, 3)
	SUBR("LAST", last_subr, 1)
	SUBR("LENGTH", length_subr, 1)
	SUBR("MEMBER", member_subr, 2)
	SUBR("MEMQ", memq_subr, 2)
	SUBR("NOT", null_subr, 1)
	SUBR("NULL", null_subr, 1)

	SUBR("CONS", cons_subr, 2)
	SUBR("NCONS", ncons_subr, 1)
	SUBR("XCONS", xcons_subr, 2)
	FSUBR("LIST", list_fsubr)
	SUBR("APPEND", append_subr, 2)
	SUBR("REVERSE", reverse_subr, 1)

	SUBR("RPLACA", rplaca_subr, 2)
	SUBR("RPLACD", rplacd_subr, 2)
	SUBR("NCONC", nconc_subr, 2)
	SUBR("NREVERSE", nreverse_subr, 1)
	LSUBR("DELETE", delete_lsubr)
	LSUBR("DELQ", delq_lsubr)

	FSUBR("AND", and_fsubr)
	FSUBR("OR", or_fsubr)
	FSUBR("PROG", prog_fsubr)
	FSUBR("RETURN", return_fsubr)
	FSUBR("GO", go_fsubr)

	FSUBR("SETQ", setq_fsubr)
	FSUBR("SET", set_fsubr)

	SUBR("GET", get_subr, 2)
	SUBR("PUTPROP", putprop_subr, 3)
	FSUBR("DEFPROP", defprop_fsubr)
	SUBR("REMPROP", remprop_subr, 2)

	SUBR("ZEROP", zerop_subr, 1)
	SUBR("PLUSP", plusp_subr, 1)
	SUBR("MINUSP", minusp_subr, 1)
	LSUBR("<", lessp_lsubr)
	LSUBR(">", greaterp_lsubr)
	LSUBR("MAX", max_lsubr)
	LSUBR("MIN", min_lsubr)

	LSUBR("+", plus_lsubr)
	LSUBR("-", difference_lsubr)
	LSUBR("*", times_lsubr)
	LSUBR("/", quotient_lsubr)
	SUBR("1+", add1_subr, 1)
	SUBR("1-", sub1_subr, 1)
	SUBR("\\", remainder_subr, 2)
	SUBR("EXPT", expt_subr, 2)

	LSUBR("LOGIOR", logior_lsubr)
	LSUBR("LOGAND", logand_lsubr)
	LSUBR("LOGXOR", logxor_lsubr)
	SUBR("LSH", lsh_subr, 2)

	LSUBR("MAPLIST", maplist_lsubr)
	LSUBR("MAPCAR", mapcar_lsubr)
	LSUBR("MAP", map_lsubr)
	LSUBR("MAPC", mapc_lsubr)
	LSUBR("MAPCON", mapcon_lsubr)
	LSUBR("MAPCAN", mapcan_lsubr)

	SUBR("READ", read_subr, 0)
	SUBR("PRIN1", prin1_subr, 1)
	SUBR("PRINT", print_subr, 1)
	SUBR("PRINC", princ_subr, 1)
	SUBR("TERPRI", terpri_subr, 0)
}