ref: a88cd71d79e142d686b01ff33624a4cc8febb268
dir: /lisp.c/
#include "lisp.h" #ifdef PLAN9 void exit(int n) { if(n == 0) exits(nil); exits("error"); } #endif C *fclist; F *fflist; C *pdl[PDLSZ]; int pdp; Temlis temlis; C **alist; int nargs; C *oblist; Arglist largs; int gcen; int gcdbg = 0; void *Atom = (void*)CAR_ATOM; void *Fixnum = (void*)(CAR_ATOM|CAR_FIX); void *Flonum = (void*)(CAR_ATOM|CAR_FLO); void *String = (void*)(CAR_ATOM|CAR_STR); /* absence of a value */ C *noval = (C*)~0; /* some important atoms */ C *pname; C *value; C *unbound; // not interned C *expr; C *subr; C *lsubr; C *fexpr; C *fsubr; C *macro; C *t; C *quote; C *label; C *function; C *funarg; C *lambda; C *cond; C *set; C *setq; C *go; C *retrn; C *star; C *digits[10]; jmp_buf errlabel[10]; int errsp; /* print error and jmp back into toplevel */ void err(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); longjmp(errlabel[errsp], 1); } void panic(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); #ifdef PLAN9 exits("panic"); #else exit(1); #endif } void* emalloc(ulong size) { char *p; p = malloc(size); if(p == nil) panic("out of memory"); return p; } void* erealloc(void *p, ulong size) { p = realloc(p, size); if(p == nil) panic("out of memory"); return p; } char* estrdup(char *s) { char *t; t = emalloc(strlen(s)+1); strcpy(t, s); return t; } C** push(C *c) { C **p; assert(pdp >= 0 && pdp < PDLSZ); p = &pdl[pdp++]; *p = c; return p; } C* pop(void) { assert(pdp > 0 && pdp <= PDLSZ); return pdl[--pdp]; } /* * Type constructors */ C* cons(void *a, C *d) { C *c; if(((P)a & CAR_ATOM) == 0) temlis.ca = a; temlis.cd = d; if(gcen && (fclist == nil || gcdbg)) gc(); c = fclist; assert(c != nil); fclist = fclist->d; temlis.ca = nil; temlis.cd = nil; c->a = a; c->d = d; return c; } F* consw(word fw) { F *f; if(gcen && (fflist == nil || gcdbg)) gc(); f = fflist; assert(f != nil); fflist = fflist->p; f->fw = fw; return f; } C* mkfix(fixnum fix) { C *c; if(fix >= 0 && fix < 10) return digits[fix]; c = cons(Fixnum, nil); c->fix = fix; return c; } C* mkflo(flonum flo) { C *c; c = cons(Flonum, nil); c->flo = flo; return c; } C* mkstr(char *s) { C *c; c = cons(String, nil); c->str = estrdup(s); return c; } C* mksubr(C *(*subr)(void), int n) { F nf, sf; nf.n = n; sf.subr = subr; temlis.ca = consw(nf.fw); temlis.cd = consw(sf.fw); return cons(temlis.ca, temlis.cd); } C* mksym(char *name) { return cons(Atom, cons(pname, cons(mkstr(name), nil))); } /* * Type predicates */ int atom(C *c) { return c == nil || c->ap & CAR_ATOM; } int symbolp(C *c) { return c == nil || (c->ap&~CAR_MARK) == CAR_ATOM; } int fixnump(C *c) { return c != nil && c->ap & CAR_ATOM && c->ap & CAR_FIX; } int flonump(C *c) { return c != nil && c->ap & CAR_ATOM && c->ap & CAR_FLO; } int numberp(C *c) { return c != nil && c->ap & CAR_ATOM && c->ap & CAR_NUM; } int listp(C *c) { return c == nil || !(c->ap & CAR_ATOM); } int stringp(C *c) { return c != nil && c->ap & CAR_ATOM && c->ap & CAR_STR; } /* * Elementary functions */ fixnum length(C *c) { fixnum n; if(!listp(c)) err("error: not a list"); for(n = 0; c != nil; c = c->d){ if(atom(c)) err("error: not a proper list"); n++; } return n; } C* get(C *l, C *p) { if(l == nil || !(listp(l) || symbolp(l))) return nil; for(; l->d != nil; l = l->d->d){ assert(listp(l->d)); if(l->d->a == p){ assert(listp(l->d->d)); return l->d->d->a; } } return nil; } C* getpname(C *a) { return get(a, pname); } C* symeval(C *s) { for(s = s->d; s != nil; s = s->d->d) if(s->a == value) return s->d->a; return unbound; } C* assq(C *x, C *y) { for(; y != nil; y = y->d) if(y->a->a == x) return y->a; return nil; } C* putprop(C *a, C *val, C *ind) { C *tt; if(a == nil || !symbolp(a)) err("error: no p-list"); for(tt = a->d; tt != nil; tt = tt->d->d) if(tt->a == ind){ tt->d->a = val; return val; } temlis.a = a; temlis.b = ind; a->d = cons(ind, cons(val, a->d)); temlis.a = nil; temlis.b = nil; return val; } C* nconc(C *x, C *e) { C *m; if(x == nil) return e; m = x; for(; x->d != nil; x = x->d); x->d = e; return m; } C* pair(C *x, C *y) { C *m, **p; // args are GC-safe, only called by apply assert(temlis.a == nil); p = (C**)&temlis.a; while(x != nil && y != nil){ *p = cons(cons(x->a, y->a), nil); p = &(*p)->d; x = x->d; y = y->d; } if(x != nil || y != nil) err("error: pair not same length"); m = temlis.a; temlis.a = nil; return m; } C* findsym(char *name) { C *c; C *pn; for(c = oblist; c != nil; c = c->d){ if(!symbolp(c->a)) continue; pn = getpname(c->a); if(pn == nil) continue; assert(stringp(pn)); if(strcmp(pn->str, name) == 0) return c->a; } return nil; } C* intern(char *name) { C *c; c = findsym(name); if(c == nil){ c = mksym(name); oblist = cons(c, oblist); } return c; } /* * Eval Apply */ Arglist spread(C *l) { Arglist al; al.nargs = nargs; al.alist = alist; al.pdp = pdp; nargs = 0; alist = &pdl[pdp]; for(; l != nil; l = l->d){ push(l->a); nargs++; } return al; } void restore(Arglist al) { pdp = al.pdp; alist = al.alist; nargs = al.nargs; } C* evbody(C *c, C *a) { C *tt; int spdp; spdp = pdp; push(c); push(a); for(tt = nil; c != nil; c = c->d) tt = eval(c->a, a); pdp = spdp; return tt; } C* evcon(C *c, C *a) { int spdp; spdp = pdp; push(c); push(a); for(; c != nil; c = c->d) if(eval(c->a->a, a) != nil){ pdp = spdp; return evbody(c->a->d, a); } pdp = spdp; return nil; } C* applysubr(C *subr, C *args) { C *tt; Arglist al; al = spread(args); if(subr->af->n != nargs) err("error: arg count (expected %d, got %d)", subr->af->n, nargs); tt = subr->df->subr(); restore(al); return tt; } C* applylsubr(C *subr, C *args) { C *tt; Arglist al, ll; al = spread(args); ll = largs; largs.nargs = nargs; largs.alist = alist-1; tt = subr->df->subr(); largs = ll; restore(al); return tt; } C* eval(C *form, C *a) { C *tt, *arg; int spdp; Arglist al; tail: if(form == nil) return nil; if(numberp(form) || stringp(form)) return form; if(atom(form)){ if(tt = assq(form, a), tt != nil) return tt->d; if(tt = symeval(form), tt != unbound) return tt; err("error: no value"); } if(form->a == cond) return evcon(form->d, a); spdp = pdp; push(form); push(a); if(atom(form->a)){ if(form->a == nil || !symbolp(form->a)) lprint(form), err("error: no function"); for(tt = form->a->d; tt != nil; tt = tt->d->d){ if(tt->a == expr){ arg = evlis(form->d, a); pdp = spdp; return apply(tt->d->a, arg, a); }else if(tt->a == fexpr){ arg = cons(form->d, cons(a, nil)); pdp = spdp; return apply(tt->d->a, arg, a); }else if(tt->a == subr){ arg = evlis(form->d, a); pdp = spdp; return applysubr(tt->d->a, arg); }else if(tt->a == lsubr){ arg = evlis(form->d, a); pdp = spdp; return applylsubr(tt->d->a, arg); }else if(tt->a == fsubr){ pdp = spdp; al = spread(nil); push(form->d); push(a); nargs = 2; tt = tt->d->af->subr(); restore(al); return tt; }else if(tt->a == macro){ arg = cons(form, nil); pdp = spdp; form = apply(tt->d->a, arg, a); goto tail; } } if(tt = assq(form->a, a), tt != nil){ form = cons(tt->d, form->d); pdp = spdp; goto tail; } lprint(form), err("error: no function"); } arg = evlis(form->d, a); pdp = spdp; return apply(form->a, arg, a); } C* evlis(C *m, C *a) { C **p; int spdp; p = push(nil); spdp = pdp; push(m); push(a); for(; m != nil; m = m->d){ *p = cons(eval(m->a, a), nil); p = &(*p)->d; } pdp = spdp; return pop(); } C* apply(C *fn, C *args, C *a) { C *tt, *n; int spdp; Arglist al, ll; if(atom(fn)){ if(fn == nil || !symbolp(fn)) lprint(fn), err("error: no function"); for(tt = fn->d; tt != nil; tt = tt->d->d){ if(tt->a == expr) return apply(tt->d->a, args, a); else if(tt->a == subr) return applysubr(tt->d->a, args); else if(tt->a == lsubr) return applylsubr(tt->d->a, args); } if(tt = assq(fn, a), tt != nil) return apply(tt->d, args, a); lprint(fn), err("error: no function"); } spdp = pdp; push(fn); push(args); push(a); if(fn->a == label){ a = cons(cons(fn->d->a, fn->d->d->a), a); pdp = spdp; return apply(fn->d->d->a, args, a); } if(fn->a == funarg){ pdp = spdp; return apply(fn->d->a, args, fn->d->d->a); } if(fn->a == lambda){ if(fn->d->a != nil && symbolp(fn->d->a)){ a = cons(cons(fn->d->a, n = mkfix(0)), a); pdp = spdp; /* almost same code as applylsubr... */ al = spread(args); ll = largs; largs.nargs = nargs; largs.alist = alist-1; n->fix = nargs; tt = evbody(fn->d->d, a); largs = ll; restore(al); return tt; }else{ args = pair(fn->d->a, args); pdp = spdp; return evbody(fn->d->d, nconc(args, a)); } } fn = eval(fn, a); pdp = spdp; return apply(fn, args, a); } /* * top level */ void init(void) { int i; initio(); gc(); /* init oblist so we can use intern */ pname = cons(Atom, nil); pname->d = cons(pname, cons(mkstr("PNAME"), nil)); oblist = cons(pname, nil); unbound = cons(Atom, cons(pname, cons(mkstr("UNBOUND"), nil))); temlis.unbound = unbound; /* Now enable GC */ gcen = 1; t = intern("T"); value = intern("VALUE"); subr = intern("SUBR"); lsubr = intern("LSUBR"); fsubr = intern("FSUBR"); expr = intern("EXPR"); fexpr = intern("FEXPR"); macro = intern("MACRO"); quote = intern("QUOTE"); label = intern("LABEL"); funarg = intern("FUNARG"); function = intern("FUNCTION"); lambda = intern("LAMBDA"); cond = intern("COND"); set = intern("SET"); setq = intern("SETQ"); go = intern("GO"); retrn = intern("RETURN"); for(i = 0; i < 10; i++){ digits[i] = cons(Fixnum, nil); digits[i]->fix = i; oblist = cons(digits[i], oblist); } initsubr(); star = intern("*"); } void eval_repl(void) { C *e; putprop(star, star, value); for(;;){ tyo('\n'); lprint(eval(star, nil)); tyo('\n'); e = readsxp(1); if(e == noval) return; e = eval(e, nil); if(e == noval) putprop(star, star, value); else putprop(star, e, value); } } void eval_file(void) { C *e; for(;;){ e = readsxp(1); if(e == noval) return; eval(e, nil); } } void load(char *filename) { FILE *f; Stream strsv; f = fopen(filename, "r"); if(f == nil) return; strsv = sysin; sysin.type = IO_FILE; sysin.file = f; sysin.nextc = 0; if(setjmp(errlabel[errsp])) exit(1); eval_file(); sysin = strsv; fclose(f); } #ifdef PLAN9 void main(int, char**) #else int main() #endif { #ifdef LISP32 /* only works on 32 bits */ assert(sizeof(void*) == 4); #else /* only works on 64 bits */ assert(sizeof(void*) == 8); #endif errsp = 0; init(); load("lib.l"); if(setjmp(errlabel[errsp])) fprintf(stdout, "→\n"); pdp = 0; alist = nil; memset(&prog, 0, sizeof(prog)); memset(&temlis, 0, sizeof(temlis)); temlis.unbound = unbound; eval_repl(); #ifdef PLAN9 exits(nil); #else return 0; #endif }