ref: 3e8336c0ea2e6f70eb0b277f3d0927b114d0709c
author: aap <[email protected]>
date: Fri Aug 19 06:30:52 EDT 2022
first commit
--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+++ b/Makefile
@@ -1,0 +1,6 @@
+CFLAGS=-g -Wall -Wextra -DLISP$(bits)
+LDFLAGS=-lm
+lisp: lisp.o subr.o mem.o
+lisp.o: lisp.h
+subr.o: lisp.h
+mem.o: lisp.h
--- /dev/null
+++ b/README.md
@@ -1,0 +1,34 @@
+# LISP
+
+This is an implementation of LISP inspired by MacLISP.
+It's still rather incomplete and whether I want to reach
+full MacLISP compatibility is also still uncertain.
+
+
+## Build
+
+On a 64 bit system just type `make` on UNIX, `mk` on Plan 9.
+On a 32 bit system: `bits=32 make/mk`.
+
+## How to use
+
+You're talking to a REPL. e.g.
+
+```
+% ./lisp
+*
+(car '(a b))
+
+A
+```
+
+## To-Do
+
+* Figure out what to do about lexical vs dynamic binding
+* Implement most of actual MacLISP functions
+* Arrays
+* Strings
+* maybe Bignums?
+* Better storage management
+* Assembler (and compiler?)
+* some code examples
--- /dev/null
+++ b/lib.l
@@ -1,0 +1,101 @@
+;;; taken from MACLISP
+(defprop defun
+ (lambda (l)
+ (cond ((and (caddr l)
+ (atom (caddr l)))
+ (list 'defprop (cadr l)
+ (cons 'lambda (cons (cadddr l) (cddddr l)))
+ (caddr l)))
+ (t (list 'defprop (cadr l)
+ (cons 'lambda (cons (caddr l) (cdddr l)))
+ 'expr))))
+ macro)
+
+;; LET
+(defun let-vars (l) (maplist #'(lambda (x) (caar x)) (cadr l)))
+(defun let-vals (l) (maplist #'(lambda (x) (cadar x)) (cadr l)))
+(defun let macro (l)
+ (cons (cons 'lambda (cons (let-vars l) (cddr l)))
+ (let-vals l)))
+
+
+
+
+;;;
+;;; examples
+;;;
+
+
+;;; compute greatest common divisor
+(defun gcd (a b)
+ (cond ((lessp a b) (gcd b a))
+ ((eq b 0) a)
+ (t (gcd b (difference a b)))))
+
+
+;;; differentiate expression exp w.r.t. x
+(defun diff (exp x)
+ (cond ((eq exp x) 1)
+ ((atom exp) 0)
+ ((eq (car exp) 'plus)
+ (cons 'plus (mapcar #'(lambda (j) (diff j x)) (cdr exp))))
+ ((eq (car exp) 'times)
+ (cons 'plus
+ (maplist
+ #'(lambda (J)
+ (cons 'times
+ (maplist
+ #'(lambda (K)
+ (cond ((equal J K) (diff (car K) x))
+ (t (car K))))
+ (cdr exp))))
+ (cdr exp))))
+ (t 'invalid)))
+
+
+
+;;; simplify mathematical expression
+(defun simplify (exp)
+ (cond ((atom exp) exp)
+ ((eq (car exp) 'plus) (simpsum (simplis (cdr exp))))
+ ((eq (car exp) 'times) (simpprod (simplis (cdr exp))))
+ (t exp)))
+
+;;; simplify a list of expressions
+(defun simplis (lst)
+ (mapcar #'(lambda (l) (simplify l)) lst))
+
+;;; simplify the terms of a sum
+(defun simpsum (terms)
+ (prog (sep const var)
+ (setq sep (separate terms nil nil))
+ (setq const (car sep))
+ (setq var (cadr sep))
+ (setq const (eval (cons 'plus const) nil))
+ (return (cond ((null var) const)
+ ((eq const 0)
+ (cond ((null (cdr var)) (car var))
+ (t (cons 'plus var))))
+ (t (cons 'plus (cons const var)))))))
+
+;;; simplify the terms of a product
+(defun simpprod (terms)
+ (prog (sep const var)
+ (setq sep (separate terms nil nil))
+ (setq const (car sep))
+ (setq var (cadr sep))
+ (setq const (eval (cons 'times const) nil))
+ (return (cond ((null var) const)
+ ((eq const 0) 0)
+ ((eq const 1)
+ (cond ((null (cdr var)) (car var))
+ (t (cons 'times var))))
+ (t (cons 'times (cons const var)))))))
+
+;;; separate constants from variables in a list
+(defun separate (lst const var)
+ (cond ((null lst)
+ (list const var))
+ ((numberp (car lst))
+ (separate (cdr lst) (cons (car lst) const) var))
+ (t (separate (cdr lst) const (cons (car lst) var)))))
--- /dev/null
+++ b/lisp.c
@@ -1,0 +1,1033 @@
+#include "lisp.h"
+
+#ifdef PLAN9
+void exit(int n)
+{
+ if(n == 0)
+ exits(nil);
+ exits("error");
+}
+#endif
+
+FILE *sysin, *sysout, *syserr;
+
+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);
+
+/* absence of a value */
+C *noval = (C*)~0;
+
+/* some important atoms */
+C *pname;
+C *value;
+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];
+C *plus, *minus;
+
+jmp_buf tljmp;
+
+/* print error and jmp back into toplevel */
+void
+err(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(syserr, fmt, ap);
+ fprintf(syserr, "\n");
+ va_end(ap);
+ longjmp(tljmp, 1);
+}
+
+void
+panic(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(syserr, fmt, ap);
+ fprintf(syserr, "\n");
+ va_end(ap);
+#ifdef PLAN9
+ exits("panic");
+#else
+ exit(1);
+#endif
+}
+
+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];
+}
+
+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*
+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);
+}
+
+int
+atom(C *c)
+{
+ return c == nil || c->ap & 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);
+}
+
+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;
+}
+
+/* functions for handling pnames */
+int
+matchpname(C *c, char *name)
+{
+ int i;
+ char *s;
+ char c1, c2;
+
+ s = name;
+ i = 0;
+ for(;;){
+ c1 = *s++;
+ c2 = c ? c->af->c[i++] : '\0';
+ if(i == C2W){
+ i = 0;
+ c = c->d;
+ }
+ if(c1 != c2)
+ return 0;
+ if(c1 == '\0')
+ return 1;
+ }
+}
+
+C*
+makepname(char *name)
+{
+ int i;
+ F w;
+ char *s;
+ C *ret, **next;
+
+ /* TODO: maybe do this elsewhere? */
+ ret = cons(nil, nil);
+ temlis.pn = ret;
+ next = &ret->a;
+
+ /* split up name into full words
+ * and build list structure */
+ s = name;
+ while(*s != '\0'){
+ w.fw = 0;
+ for(i = 0; i < C2W; i++){
+ if(*s == '\0')
+ break;
+ w.c[i] = *s++;
+ }
+ *next = cons(consw(w.fw), nil);
+ next = &(*next)->d;
+ }
+ temlis.pn = nil;
+ return ret;
+}
+
+C*
+get(C *l, C *p)
+{
+ assert(l != 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*
+getx(C *l, C *p)
+{
+ for(l = l->d; l != nil; l = l->d->d)
+ if(l->a == p)
+ return l->d;
+ return nil;
+}
+
+/* returns noval instead of evaluating a function */
+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 || numberp(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;
+// TODO: must save here?
+ temlis.b = x;
+ temlis.c = y;
+ 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;
+ temlis.b = nil;
+ temlis.c = nil;
+ return m;
+}
+
+C*
+intern(char *name)
+{
+ C *c;
+ C *pn;
+ for(c = oblist; c; c = c->d){
+ if(numberp(c->a))
+ continue;
+ pn = get(c->a, pname);
+ if(pn == nil)
+ continue;
+ if(matchpname(pn, name))
+ return c->a;
+ }
+ c = cons(Atom,
+ cons(pname,
+ makepname(name)));
+ oblist = cons(c, oblist);
+ return c;
+}
+
+/*
+ * output
+ */
+
+void
+princpname(C *c)
+{
+ char chr;
+ word fw;
+ int i;
+ for(c = c->a; c != nil; c = c->d){
+ fw = ((F*)c->a)->fw;
+ for(i = 0; i < C2W; i++){
+ chr = fw&0xFF;
+ if(chr == 0) return;
+ putc(chr, sysout);
+ fw >>= 8;
+ }
+ }
+}
+
+void
+printpname(C *c)
+{
+ char chr;
+ C *cc;
+ word fw;
+ int i;
+ int spec;
+
+ cc = c;
+ spec = 0;
+ for(c = c->a; c != nil; c = c->d){
+ fw = ((F*)c->a)->fw;
+ for(i = 0; i < C2W; i++){
+ chr = fw&0xFF;
+ if(chr == 0) goto pr;
+ if(!isupper(fw&0x7F)){
+ spec = 1;
+ goto pr;
+ }
+ fw >>= 8;
+ }
+ }
+pr:
+ if(spec) putc('|', sysout);
+ princpname(cc);
+ if(spec) putc('|', sysout);
+}
+
+void
+printatom(C *c, void (*pnm)(C *c))
+{
+ if(c == nil)
+ fprintf(sysout, "NIL");
+ else if(fixnump(c))
+ fprintf(sysout, "%lld", (long long int)c->fix);
+ else if(flonump(c))
+ fprintf(sysout, "%f", c->flo);
+ else{
+ assert(atom(c));
+ for(; c != nil; c = c->d)
+ if(c->a == pname){
+ pnm(c->d);
+ return;
+ }
+ fprintf(sysout, "%%ATOM%%");
+ }
+}
+
+void
+printsxp(C *c, void (*pnm)(C *c))
+{
+ int fst;
+ if(atom(c))
+ printatom(c, pnm);
+ else{
+ putc('(', sysout);
+ fst = 1;
+ for(; c != nil; c = c->d){
+ if(atom(c)){
+ fprintf(sysout, " . ");
+ printatom(c, pnm);
+ break;
+ }
+ if(!fst)
+ putc(' ', sysout);
+ lprint(c->a);
+ fst = 0;
+ }
+ putc(')', sysout);
+ }
+}
+
+void
+lprint(C *c)
+{
+ printsxp(c, printpname);
+}
+
+void
+princ(C *c)
+{
+ printsxp(c, princpname);
+}
+
+/*
+ * input
+ */
+
+int nextc;
+
+static int
+chsp(void)
+{
+ int c;
+ if(nextc){
+ c = nextc;
+ nextc = 0;
+ return c;
+ }
+ c = getc(sysin);
+ // remove comments
+ if(c == ';')
+ while(c != '\n')
+ c = getc(sysin);
+ if(isspace(c))
+ c = ' ';
+ return c;
+}
+
+static int
+ch(void)
+{
+ int c;
+ while(c = chsp(), c == ' ');
+ return c;
+}
+
+C*
+readnum(char *buf)
+{
+ int c;
+ int type;
+ fixnum oct;
+ fixnum dec;
+ flonum flo, fract, div;
+ int sign;
+ int ndigits;
+
+ sign = 1;
+ type = 0; /* octal */
+ oct = 0;
+ dec = 0;
+ flo = 0.0;
+ fract = 0.0;
+ div = 10.0;
+ ndigits = 0;
+
+
+ c = *buf;
+ if(c == '-' || c == '+'){
+ sign = c == '-' ? -1 : 1;
+ buf++;
+ }
+
+ while(c = *buf++, c != '\0'){
+ if(c >= '0' && c <= '9'){
+ if(type == 0){
+ oct = oct*8 + c-'0';
+ dec = dec*10 + c-'0';
+ flo = flo*10.0 + c-'0';
+ }else{
+ type = 2; /* float */
+ fract += (c-'0')/div;
+ div *= 10.0;
+ }
+ ndigits++;
+ }else if(c == '.' && type == 0){
+ type = 1; /* decimal */
+ }else
+ return nil;
+ }
+ if(ndigits == 0)
+ return nil;
+// use decimal default for now
+// if(type == 0)
+// return mkfix(sign*oct);
+// if(type == 1)
+// return mkfix(sign*dec);
+ if(type == 0 || type == 1)
+ return mkfix(sign*dec);
+ return mkflo(sign*(flo+fract));
+}
+
+C*
+readatom(void)
+{
+ C *num;
+ int c;
+ char buf[128], *p;
+ int spec, lc;
+
+ p = buf;
+ spec = 0;
+ lc = 1;
+ while(c = chsp(), c != EOF){
+ if(!spec && strchr(" ()", c)){
+ nextc = c;
+ break;
+ }
+ if(c == '|'){
+ lc = 0;
+ spec = !spec;
+ continue;
+ }
+ *p++ = c;
+ }
+ *p = '\0';
+ if(lc)
+ for(p = buf; *p; p++)
+ *p = toupper(*p);
+ if(strcmp(buf, "NIL") == 0)
+ return nil;
+ num = readnum(buf);
+ return num ? num : intern(buf);
+}
+
+C *readsxp(void);
+
+C*
+readlist(void)
+{
+ int first;
+ int c;
+ C **p;
+
+ first = 1;
+ p = push(nil);
+ while(c = ch(), c != ')'){
+ /* TODO: only valid when next letter is space */
+ if(c == '.'){
+ if(first)
+ err("error: unexpected '.'");
+ *p = readsxp();
+ if(c = ch(), c != ')')
+ err("error: expected ')' (got %c)", c);
+ break;
+ }
+ nextc = c;
+ *p = cons(readsxp(), nil);
+ p = &(*p)->d;
+ first = 0;
+ }
+ return pop();
+}
+
+C*
+readsxp(void)
+{
+ int c;
+ c = ch();
+ if(c == EOF)
+ return noval;
+ if(c == '\'')
+ return cons(quote, cons(readsxp(), nil));
+ if(c == '#'){
+ c = ch();
+ if(c == '\'')
+ return cons(function, cons(readsxp(), nil));
+ err("expected '");
+ }
+ if(c == ')')
+ err("error: unexpected ')'");
+ if(c == '(')
+ return readlist();
+ nextc = c;
+ return readatom();
+}
+
+/*
+ * 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 *t;
+ t = nil;
+ for(; c != nil; c = c->d)
+ t = eval(c->a, a);
+ return t;
+}
+
+C*
+evcon(C *c, C *a)
+{
+ C *tt;
+ int spdp;
+ spdp = pdp;
+ push(c);
+ push(a);
+ for(; c != nil; c = c->d){
+ tt = eval(c->a->a, a);
+ if(tt != nil){
+ pdp = spdp;
+ return evbody(c->a->d, a);
+ }
+ }
+ err("error: no cond clause");
+ return nil; /* make compiler happy */
+}
+
+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))
+ return form;
+ if(atom(form)){
+ if(tt = getx(form, value), tt != nil)
+ return tt->a;
+ if(tt = assq(form, a), tt == nil)
+ err("error: no value");
+ return tt->d;
+ }
+ if(form->a == cond)
+ return evcon(form->d, a);
+ spdp = pdp;
+ push(form);
+ push(a);
+ if(atom(form->a)){
+ if(form->a == nil || numberp(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)
+lprint(form),
+ err("error: no function");
+ form = cons(tt->d, form->d);
+ pdp = spdp;
+ goto tail;
+ }
+ 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;
+ int spdp;
+ Arglist al, ll;
+
+ if(atom(fn)){
+ if(fn == nil || numberp(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)
+lprint(fn),
+ err("error: no function");
+ return apply(tt->d, args, a);
+ }
+ spdp = pdp;
+ push(fn);
+ push(args);
+ push(a);
+ if(fn->a == label){
+ tt = cons(fn->d->a, fn->d->d->a);
+ a = cons(tt, 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 && atom(fn->d->a)){
+ tt = cons(fn->d->a, mkfix(length(args)));
+ pdp = spdp;
+ al = spread(args);
+ ll = largs;
+ largs.nargs = nargs;
+ largs.alist = alist-1;
+ tt = evbody(fn->d->d, cons(tt, 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;
+
+ sysin = stdin;
+ sysout = stdout;
+ syserr = stderr;
+
+ gc();
+
+ /* init oblist so we can use intern */
+ pname = cons(Atom, nil);
+ pname->d = cons(pname, makepname("PNAME"));
+ oblist = cons(pname, nil);
+
+ /* 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);
+ }
+ plus = intern("+");
+ minus = intern("-");
+
+ initsubr();
+
+ star = intern("*");
+}
+
+void
+eval_repl(void)
+{
+ C *e;
+
+ putprop(star, star, value);
+ for(;;){
+ putc('\n', sysout);
+ princ(eval(star, nil));
+ putc('\n', sysout);
+ e = readsxp();
+ 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();
+ if(e == noval)
+ return;
+ eval(e, nil);
+ }
+}
+
+void
+load(char *filename)
+{
+ FILE *oldin, *f;
+ f = fopen(filename, "r");
+ if(f == nil)
+ return;
+ oldin = sysin;
+ sysin = f;
+ if(setjmp(tljmp))
+ exit(1);
+ eval_file();
+ sysin = oldin;
+ 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
+
+ init();
+
+ load("lib.l");
+
+// lprint(oblist);
+// fprintf(sysout, "\n");
+
+ if(setjmp(tljmp))
+ fprintf(sysout, "→\n");
+ pdp = 0;
+ alist = nil;
+ memset(&temlis, 0, sizeof(temlis));
+
+ eval_repl();
+#ifdef PLAN9
+ exits(nil);
+#else
+ return 0;
+#endif
+}
--- /dev/null
+++ b/lisp.h
@@ -1,0 +1,189 @@
+#ifdef PLAN9
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include <ctype.h>
+typedef uintptr uintptr_t;
+typedef u32int uint32_t;
+typedef s32int int32_t;
+typedef u64int uint64_t;
+typedef s64int int64_t;
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <setjmp.h>
+#include <assert.h>
+
+#define nil NULL
+#endif
+
+#define FIXMIN (-FIXMAX-1)
+
+/* basic data types */
+typedef uintptr_t P;
+#ifdef LISP32
+/* assume we're running on 32 bits!!! */
+typedef uint32_t word;
+typedef int32_t fixnum;
+typedef float flonum;
+#define FIXMAX ((fixnum)0x7FFFFFFF)
+#define FLOMAX 3.40282347E+38f
+enum
+{
+ B2W = 32, /* bits per word */
+ C2W = B2W/8, /* character per word */
+};
+#else
+/* assume we're running on 64 bits!!! */
+typedef uint64_t word;
+typedef int64_t fixnum;
+typedef double flonum;
+#define FIXMAX ((fixnum)0x7FFFFFFFFFFFFFFF)
+#define FLOMAX 1.7976931348623157E+308
+enum
+{
+ B2W = 64, /* bits per word */
+ C2W = B2W/8, /* character per word */
+};
+#endif
+
+extern FILE *sysin, *sysout, *syserr;
+
+/* static storage sizes */
+enum
+{
+ NUMCONS = 32*1024,
+ NUMFW = 32*1024,
+ PDLSZ = 1024,
+};
+
+
+typedef struct C C;
+typedef union F F;
+
+/* A cons cell */
+struct C
+{
+ union {
+ C *a;
+ F *af;
+ P ap;
+ };
+ union {
+ C *d;
+ F *df;
+ P dp;
+
+ fixnum fix;
+ flonum flo;
+ };
+};
+
+/* CAR bits */
+enum
+{
+ CAR_MARK = 1,
+ CAR_ATOM = 2,
+ CAR_FIX = 4,
+ CAR_FLO = 8,
+ CAR_NUM = CAR_FIX | CAR_FLO
+};
+
+
+/* A full word */
+union F
+{
+ word fw;
+ char c[C2W];
+ F *p;
+ fixnum n;
+ C *(*subr)(void);
+};
+
+
+/* free storage */
+extern C *fclist;
+extern F *fflist;
+
+/* push down list */
+extern C *pdl[PDLSZ];
+extern int pdp;
+
+/* Temporary variables automatically saved */
+typedef struct Temlis Temlis;
+struct Temlis
+{
+ /* temp */
+ void *a, *b, *c;
+ /* arguments to cons */
+ void *ca;
+ void *cd;
+ /* pname */
+ void *pn;
+};
+extern Temlis temlis;
+extern C **alist;
+extern int nargs;
+extern C *oblist;
+
+typedef struct Arglist Arglist;
+struct Arglist {
+ int nargs;
+ C **alist;
+ int pdp;
+};
+extern Arglist largs; /* LEXPR/LSUBR args */
+
+extern C *noval;
+extern C *t;
+extern C *value;
+extern C *expr;
+extern C *subr;
+extern C *lsubr;
+extern C *fexpr;
+extern C *fsubr;
+extern C *macro;
+extern C *funarg;
+extern C *cond;
+extern C *set;
+extern C *setq;
+extern C *go;
+extern C *retrn;
+
+void err(char *fmt, ...);
+void panic(char *fmt, ...);
+C **push(C *c);
+C *pop(void);
+
+C *cons(void *a, C *d);
+F *consw(word fw);
+C *mkfix(fixnum fix);
+C *mkflo(flonum flo);
+C *mksubr(C *(*subr)(void), int n);
+int atom(C *c);
+int fixnump(C *c);
+int flonump(C *c);
+int numberp(C *c);
+int listp(C *c);
+fixnum length(C *c);
+C *get(C *l, C *p);
+C *assq(C *x, C *y);
+C *putprop(C *l, C *p, C *ind);
+C *pair(C *x, C *y);
+C *intern(char *name);
+C *readsxp(void);
+void lprint(C *c);
+void princ(C *c);
+void printatom(C *c, void (*pnm)(C *c));
+C *eval(C *form, C *a);
+C *evlis(C *m, C *a);
+C *apply(C *fn, C *args, C *a);
+
+void gc(void);
+
+void initsubr(void);
--- /dev/null
+++ b/mem.c
@@ -1,0 +1,92 @@
+#include "lisp.h"
+
+C cstore[NUMCONS];
+F fstore[NUMFW];
+word fmark[NUMFW/B2W];
+
+void
+mark(C *c)
+{
+ C *a;
+ F *f;
+ int n;
+
+tail:
+ if(c == nil)
+ return;
+
+ /* Mark full word */
+ f = (F*)c;
+ if(f >= &fstore[0] && f < &fstore[NUMFW]){
+ n = f - fstore;
+ fmark[n/B2W] |= (word)1 << n%B2W;
+ return;
+ }
+
+ /* Must be a cons cell */
+ if(c >= &cstore[0] && c < &cstore[NUMCONS]){
+ if(c->ap & CAR_MARK)
+ return;
+ a = c->a;
+ c->ap |= CAR_MARK;
+ if(c->ap & CAR_ATOM){
+ if(c->ap & CAR_NUM)
+ return;
+ }else
+ mark(a);
+ c = c->d;
+ goto tail;
+ }
+
+ panic("invalid ptr: %p\n", c);
+}
+
+void
+gc(void)
+{
+ int i, j;
+ C *c, **cp;
+ F *f;
+ word m;
+ int nc, nf;
+
+ /* Mark */
+ mark(oblist);
+ for(i = 0; i < pdp; i++)
+ mark(pdl[i]);
+ for(cp = (C**)&temlis; cp < (C**)(&temlis+1); cp++)
+ mark(*cp);
+
+ /* Sweep */
+ fclist = nil;
+ nc = 0;
+ for(c = cstore; c < &cstore[NUMCONS]; c++){
+ if(c->ap & CAR_MARK)
+ c->ap &= ~CAR_MARK;
+ else{
+ c->a = nil;
+ c->d = fclist;
+ fclist = c;
+ nc++;
+ }
+ }
+
+ fflist = nil;
+ f = fstore;
+ nf = 0;
+ for(i = 0; i < NUMFW/B2W; i++){
+ m = fmark[i];
+ fmark[i] = 0;
+ for(j = 0; j < B2W; j++){
+ if(!(m&1)){
+ f->p = fflist;
+ fflist = f;
+ nf++;
+ }
+ m >>= 1;
+ f++;
+ }
+ }
+
+// fprintf(syserr, "reclaimed: %d %d\n", nc, nf);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,18 @@
+</$objtype/mkfile
+BIN=/$objtype/bin
+CFLAGS=$CFLAGS -DPLAN9 -DLISP$bits
+
+TARG=lisp
+OFILES=\
+ lisp.$O\
+ subr.$O\
+ mem.$O
+
+HFILES=lisp.h
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/subr.c
@@ -1,0 +1,1213 @@
+#include "lisp.h"
+/*
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+*/
+
+int
+floeq(flonum x, flonum y)
+{
+ return fabs(x-y) < 0.000003;
+}
+
+int
+equal(C *a, C *b)
+{
+ 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);
+ return a == b;
+ }
+ return equal(a->a, b->a)
+ && equal(a->d, b->d);
+}
+
+/* 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;
+}
+
+/* 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_subr(void){
+ C *l;
+ for(l = alist[1]; l != nil; l = l->d){
+ if(atom(l))
+ err("error: no list");
+ if(equal(l->a, alist[0]))
+ return t;
+ }
+ return nil;
+}
+C *memq_subr(void){
+ C *l;
+ for(l = alist[1]; l != nil; l = l->d){
+ if(atom(l))
+ err("error: no list");
+ if(l->a == alist[0])
+ return t;
+ }
+ return nil;
+}
+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;
+}
+
+/* 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, *l, *last;
+ last = nil;
+ for(l = alist[0]; l != nil; l = l->d->d){
+ tt = l->a;
+ if(!atom(tt))
+ err("error: need atom");
+ tt = assq(tt, alist[1]);
+ if(tt == nil)
+ err("error: undefined");
+ tt->d = last = eval(l->d->a, alist[1]);
+ }
+ return last;
+}
+/* Has to be FSUBR here, also extended syntax */
+C *set_fsubr(void){
+ C *tt, *l, *last;
+ last = nil;
+ for(l = alist[0]; l != nil; l = l->d->d){
+ tt = eval(l->a, alist[1]);
+ if(!atom(tt))
+ err("error: need atom");
+ tt = assq(tt, alist[1]);
+ if(tt == nil)
+ err("error: undefined");
+ tt->d = last = eval(l->d->a, alist[1]);
+ }
+ return last;
+}
+
+/* slightly advanced cset functions */
+C *cset_subr(void){
+ return putprop(alist[0], alist[1], value);
+}
+C *csetq_fsubr(void){
+ C *l;
+ for(l = alist[0]; l != nil; l = l->d->d){
+ if(!atom(l->a))
+ err("error: need atom");
+ if(l->d == nil){
+ putprop(l->a, nil, value);
+ break;
+ }
+ putprop(l->a, eval(l->d->a, alist[1]), value);
+ }
+ return noval;
+}
+
+/* Property list */
+
+C *get_subr(void){
+ return get(alist[0], alist[1]);
+/*
+ C *l;
+ for(l = alist[0]; l != nil; l = l->d)
+ if(l->a == alist[1])
+ return l->d->a;
+ 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;
+}
+
+/* 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 */
+
+C *maplist_subr(void){
+ C *l, *c, **p;
+ p = push(nil);
+ for(l = alist[1]; l != nil; l = l->d){
+ push(c = cons(l, nil));
+ c->a = apply(alist[0], c, nil);
+ c->d = nil;
+ *p = pop();
+ p = &(*p)->d;
+ }
+ return pop();
+}
+C *mapcar_subr(void){
+ C *l, *c, **p;
+ p = push(nil);
+ for(l = alist[1]; l != nil; l = l->d){
+ push(c = cons(l->a, nil));
+ c->a = apply(alist[0], c, nil);
+ c->d = nil;
+ *p = pop();
+ p = &(*p)->d;
+ }
+ return pop();
+}
+C *map_subr(void){
+ C *l, *a;
+ push(a = cons(nil, nil));
+ for(l = alist[1]; l != nil; l = l->d){
+ a->a = l;
+ a->d = nil;
+ apply(alist[0], a, nil);
+ }
+ pop();
+ return nil;
+}
+C *mapc_subr(void){
+ C *l, *a;
+ push(a = cons(nil, nil));
+ for(l = alist[1]; l != nil; l = l->d){
+ a->a = l->a;
+ a->d = nil;
+ apply(alist[0], a, nil);
+ }
+ pop();
+ return nil;
+}
+C *mapcon_subr(void){
+ C *l, *a, **p;
+ p = push(nil);
+ push(a = cons(nil, nil));
+ for(l = alist[1]; l != nil; l = l->d){
+ a->a = l;
+ a->d = nil;
+ *p = apply(alist[0], a, nil);
+ if(*p == nil)
+ err("error: nil in mapcon");
+ for(; *p != nil; p = &(*p)->d)
+ if(atom(*p))
+ err("error: no list");
+ }
+ pop();
+ return pop();
+}
+C *mapcan_subr(void){
+ C *l, *a, **p;
+ p = push(nil);
+ push(a = cons(nil, nil));
+ for(l = alist[1]; l != nil; l = l->d){
+ a->a = l->a;
+ a->d = nil;
+ *p = apply(alist[0], a, nil);
+ if(*p == nil)
+ err("error: nil in mapcon");
+ for(; *p != nil; p = &(*p)->d)
+ if(atom(*p))
+ err("error: no list");
+ }
+ pop();
+ return pop();
+}
+
+/* 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;
+}
+
+
+/*
+ * LISP 1.5 leftover
+ */
+
+C *attrib_subr(void){
+ C *l;
+ for(l = alist[0]; l != nil; l = l->d){
+// if(atom(l)) // have to allow this for p-lists
+ if(numberp(l))
+ err("error: no list");
+ if(l->d == nil){
+ l->d = alist[1];
+ break;
+ }
+ }
+ return alist[1];
+}
+C *prop_subr(void){
+ C *l;
+ for(l = alist[0]; l != nil; l = l->d)
+ if(l->a == alist[1])
+ return l->d;
+ return apply(alist[2], nil, nil);
+}
+C *pair_subr(void){
+ return pair(alist[0], alist[1]);
+}
+C *copy_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;
+ }
+ l = temlis.a;
+ temlis.a = nil;
+ return l;
+}
+C *efface_subr(void){
+ C *l, **p;
+ p = &alist[1];
+ for(l = alist[1]; l != nil; l = l->d){
+ if(atom(l))
+ err("error: no list");
+ if(equal(l->a, alist[0])){
+ *p = l->d;
+ break;
+ }
+ p = &(*p)->d;
+ }
+ return alist[1];
+}
+
+
+
+/* Prog feature */
+
+typedef struct Prog Prog;
+struct Prog
+{
+ C *a;
+ C *go;
+ C *pc;
+ C *ret;
+};
+
+void
+setq_prog(Prog *prog, C *form)
+{
+ C *tt;
+ if(form == nil)
+ err("error: arg count");
+ if(!atom(form->a))
+ err("error: no atom");
+ tt = assq(form->a, prog->a);
+ if(tt == nil)
+ err("error: undefined");
+ tt->d = eval(form->d->a, prog->a);
+}
+
+void
+set_prog(Prog *prog, C *form)
+{
+ C *tt;
+ if(form == nil)
+ err("error: arg count");
+ tt = eval(form->a, prog->a);
+ if(!atom(tt))
+ err("error: no atom");
+ tt = assq(tt, prog->a);
+ if(tt == nil)
+ err("error: undefined");
+ tt->d = eval(form->d->a, prog->a);
+}
+
+void
+progstmt(Prog *prog, C *form)
+{
+ C *tt;
+ C *pc;
+
+ if(atom(form))
+ {}
+ else if(form->a == setq)
+ setq_prog(prog, form->d);
+ else if(form->a == set)
+ set_prog(prog, form->d);
+ else if(form->a == cond){
+ for(form = form->d; form != nil; form = form->d)
+ if(eval(form->a->a, prog->a) != nil){
+ for(pc = form->a->d; pc != nil; pc = pc->d)
+ progstmt(prog, pc->a);
+ return;
+ }
+ }else if(form->a == go){
+ if(form->d == nil)
+ err("error: arg count");
+ if(tt = assq(form->d->a, prog->go), tt == nil)
+ err("error: undefined label");
+ prog->pc = tt->d;
+ }else if(form->a == retrn){
+ if(form->d == nil)
+ prog->ret = nil;
+ else
+ prog->ret = eval(form->d->a, prog->a);
+ prog->pc = nil;
+ }else
+ eval(form, prog->a);
+}
+
+C *prog_fsubr(void){
+ Prog prog;
+
+ C *p;
+ C **ap;
+
+ 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];
+ alist[1] = temlis.a;
+ prog.a = alist[1];
+ temlis.a = nil;
+
+ /* build go-list */
+ for(p = prog.pc; p != nil; p = p->d)
+ if(atom(p->a))
+ temlis.a = cons(p, temlis.a);
+ prog.go = temlis.a;
+ temlis.a = nil;
+ alist[nargs++] = prog.go;
+
+ /* execute */
+ prog.ret = nil;
+ while(prog.pc != nil){
+ p = prog.pc->a;
+ prog.pc = prog.pc->d;
+ progstmt(&prog, p);
+ }
+
+ return prog.ret;
+}
+
+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("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)
+
+ FSUBR("AND", and_fsubr)
+ FSUBR("OR", or_fsubr)
+ FSUBR("PROG", prog_fsubr)
+
+ FSUBR("SETQ", setq_fsubr)
+ FSUBR("SET", set_fsubr)
+ SUBR("CSET", cset_subr, 2)
+ FSUBR("CSETQ", csetq_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)
+
+ SUBR("MAPLIST", maplist_subr, 2)
+ SUBR("MAPCAR", mapcar_subr, 2)
+ SUBR("MAP", map_subr, 2)
+ SUBR("MAPC", mapc_subr, 2)
+ SUBR("MAPCON", mapcon_subr, 2)
+ SUBR("MAPCAN", mapcan_subr, 2)
+
+ 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)
+
+
+
+
+ SUBR("ATTRIB", attrib_subr, 2)
+ SUBR("PROP", prop_subr, 3)
+ SUBR("PAIR", pair_subr, 2)
+ SUBR("COPY", copy_subr, 1)
+ SUBR("EFFACE", efface_subr, 2)
+}