shithub: mlisp

ref: a88cd71d79e142d686b01ff33624a4cc8febb268
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(!listp(pair))
		err("error: not a pair");
	return pair->a;
}
C *cdr(C *pair){
	if(pair == nil)
		return nil;
	if(!listp(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(a == nil || !symbolp(a))
			err("error: need symbol");
		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(a == nil || !symbolp(a))
			err("error: need symbol");
		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;
}
C *boundp_subr(void){
	if(alist[0] == nil || !symbolp(alist[0]))
		err("error: need symbol");
	return symeval(alist[0]) == unbound ? nil : t;
}
C *makunbound_subr(void){
	if(alist[0] == nil || !symbolp(alist[0]))
		err("error: need symbol");
	putprop(alist[0], unbound, value);
	return alist[0];
}

/* Property list */

C *get_subr(void){
	return get(alist[0], alist[1]);
}
C *getl_subr(void){
	C *pl, *l;
	pl = alist[0];
	if(pl == nil || !(listp(pl) || symbolp(pl)))
		return nil;
	for(pl = pl->d; pl != nil; pl = pl->d->d){
		assert(listp(pl));
		for(l = alist[1]; l != nil; l = l->d){
			if(atom(l))
				err("error: no list");
			if(pl->a == l->a)
				return pl;
		}
	}
	return nil;
}
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;
}

C*
mkchar(char c)
{
	char str[2];
	str[0] = c;
	str[1] = '\0';
	return intern(str);
}

#define NEEDNAME(x) if(symbolp(x)) x = getpname(x); if(!stringp(x)) err("error: not a string")

/* pname/string functions */
C *samepnamep_subr(void){
	NEEDNAME(alist[0]);
	NEEDNAME(alist[1]);
	return strcmp(alist[0]->str, alist[1]->str) == 0 ? t : nil;
}
C *alphalessp_subr(void){
	NEEDNAME(alist[0]);
	NEEDNAME(alist[1]);
	return strcmp(alist[0]->str, alist[1]->str) < 0 ? t : nil;
}
C *getchar_subr(void){
	NEEDNAME(alist[0]);
	if(!fixnump(alist[1])) err("error: not a number");
	if(alist[1]->fix < 1 || alist[1]->fix > strlen(alist[0]->str))
		return nil;
	return mkchar(alist[0]->str[alist[1]->fix-1]);
}
C *intern_subr(void){
	C *c, *name;
	name = alist[0];
	NEEDNAME(name);
	c = findsym(name->str);
	if(c == nil){
		if(symbolp(alist[0]))
			c = alist[0];
		else
			c = mksym(name->str);
		oblist = cons(c, oblist);
	}
	return c;
}
C *remob_subr(void){
	C **c;
	if(!symbolp(alist[0])) err("error: not a symbol");
	for(c = &oblist; *c != nil; c = &(*c)->d){
		if((*c)->a == alist[0]){
			*c = (*c)->d;
			break;
		}
	}
	return nil;
}
C *gensym_lsubr(void){
	static int num = 1;
	static char chr = 'G';
	char str[6];

	if(largs.nargs == 1){
		if(symbolp(largs.alist[1])) largs.alist[1] = getpname(largs.alist[1]);
		if(stringp(largs.alist[1]))
			chr = largs.alist[1]->str[0];
		else if(fixnump(largs.alist[1]))
			num = largs.alist[1]->fix;
		else
			err("error: not string or number");
	}

	str[0] = chr;
	str[1] = '0' + ((num/1000)%10);
	str[2] = '0' + ((num/100)%10);
	str[3] = '0' + ((num/10)%10);
	str[4] = '0' + (num%10);
	num++;
	return mksym(str);
}

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

/* Character manipulation */

static C *mkfixchar(char c) { return mkfix(c); }
static C *str2list(char *str, C *(*f)(char)){
	C **lp;
	char *s;
	lp = push(nil);
	for(s = str; *s != '\0'; s++){
		*lp = cons(f(*s), nil);
		lp = &(*lp)->d;
	}
	return pop();
}
static Strbuf list2str(C *l){
	Strbuf buf;
	if(!listp(l)) err("error: not a list");
	initbuf(&buf);
	for(; l != nil; l = l->d){
		if(atom(l)){
			freebuf(&buf);
			err("error: no list");
		}
		if(symbolp(l->a))
			pushchar(&buf, getpname(l->a)->str[0]);
		else if(fixnump(l->a))
			pushchar(&buf, l->a->fix);
		else{
			freebuf(&buf);
			err("error: not an ascii character");
		}
	}
	pushchar(&buf, '\0');
	return buf;
}

C *ascii_subr(void){
	if(!fixnump(alist[0])) err("error: not a fixnum");
	return mkchar(alist[0]->fix);
}
C *maknam_subr(void){
	C *l;
	Strbuf buf;
	buf = list2str(alist[0]);
	l = mksym(buf.buf);
	freebuf(&buf);
	return l;
}
C *implode_subr(void){
	alist[0] = maknam_subr();
	return intern_subr();
}
C *explode_aux(void (*prnt)(C*), C *(*f)(char)){
	C *s;
	Stream strsv;

	strsv = sysout;
	sysout.type = IO_BUF;
	initbuf(&sysout.strbuf);
	prnt(alist[0]);
	tyo('\0');
	s = str2list(sysout.strbuf.buf, f);
	freebuf(&sysout.strbuf);
	sysout = strsv;
	return s;
}
C *explode_subr(void){ return explode_aux(lprint, mkchar); }
C *explodec_subr(void){ return explode_aux(princ, mkchar); }
C *exploden_subr(void){ return explode_aux(princ, mkfixchar); }
C *flat_aux(void (*prnt)(C*)){
	C *s;
	Stream strsv;

	strsv = sysout;
	sysout.type = IO_BUF;
	initbuf(&sysout.strbuf);
	prnt(alist[0]);
	tyo('\0');
	s = mkfix(strlen(sysout.strbuf.buf));
	freebuf(&sysout.strbuf);
	sysout = strsv;
	return s;
}
C *flatc_subr(void){ return flat_aux(princ); }
C *flatsize_subr(void){ return flat_aux(lprint); }
C *readlist_subr(void){
	C *l;
	Strbuf buf;
	Stream strsv;

	buf = list2str(alist[0]);
	buf.len = buf.pos;
	buf.pos = 0;

	strsv = sysin;
	sysin.type = IO_BUF;
	sysin.strbuf = buf;
	sysin.nextc = 0;

	// Be careful to clean up after errors here
	errsp++;
	if(setjmp(errlabel[errsp])){
		errsp--;
		sysin = strsv;
		freebuf(&buf);
		longjmp(errlabel[errsp], 1);
	}
	l = readsxp(1);
	errsp--;
	sysin = strsv;
	freebuf(&buf);
	return l;
}

/* 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(1);
}
C *prin1_subr(void){
	lprint(alist[0]);
	return t;
}
C *print_subr(void){
	tyo('\n');
	lprint(alist[0]);
	tyo(' ');
	return t;
}
C *princ_subr(void){
	princ(alist[0]);
	return t;
}
C *terpri_subr(void){
	tyo('\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("BOUNDP", boundp_subr, 1);
	SUBR("MAKUNBOUND", makunbound_subr, 1);

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

	SUBR("SAMEPNAMEP", samepnamep_subr, 2)
	SUBR("ALPHALESSP", alphalessp_subr, 2)
	SUBR("GETCHAR", getchar_subr, 2)
	SUBR("INTERN", intern_subr, 1)
	SUBR("REMOB", remob_subr, 1)
	LSUBR("GENSYM", gensym_lsubr)

	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)

	SUBR("ASCII", ascii_subr, 1)
	SUBR("MAKNAM", maknam_subr, 1)
	SUBR("IMPLODE", implode_subr, 1)
	SUBR("EXPLODE", explode_subr, 1)
	SUBR("EXPLODEC", explodec_subr, 1)
	SUBR("EXPLODEN", exploden_subr, 1)
	SUBR("FLATC", flatc_subr, 1)
	SUBR("FLATSIZE", flatsize_subr, 1)
	SUBR("READLIST", readlist_subr, 1)

	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)
}