/* Scheme In One Defun, but in C this time. * COPYRIGHT (c) 1989 BY * * PARADIGM ASSOCIATES INCORPORATED, CAMBRIDGE, MASSACHUSETTS. * * ALL RIGHTS RESERVED * Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Paradigm Associates Inc not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. PARADIGM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL PARADIGM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* gjc@paradigm.com Paradigm Associates Inc Phone: 617-492-6079 29 Putnam Ave, Suite 6 Cambridge, MA 02138 Release 1.0: 24-APR-88 Release 1.1: 25-APR-88, added: macros, predicates, load. With additions by Barak.Pearlmutter@DOGHEN.BOLTZ.CS.CMU.EDU: Full flonum recognizer, cleaned up uses of NULL/0. Now distributed with siod.scm. Release 1.2: 28-APR-88, name changes as requested by JAR@AI.AI.MIT.EDU, plus some bug fixes. Release 1.3: 1-MAY-88, changed env to use frames instead of alist. define now works properly. vms specific function edit. Release 1.4 20-NOV-89. Minor Cleanup and remodularization. Now in 3 files, siod.h, slib.c, siod.c. Makes it easier to write your own main loops. Some short-int changes for lightspeed C included. Release 1.5 29-NOV-89. Added startup flag -g, select stop and copy or mark-and-sweep garbage collection, which assumes that the stack/register marking code is correct for your architecture. Release 2.0 1-DEC-89. Added repl_hooks, Catch, Throw. This is significantly different enough (from 1.3) now that I'm calling it a major release. Release 2.1 4-DEC-89. Small reader features, dot, backquote, comma. Release 2.2 5-DEC-89. gc,read,print,eval, hooks for user defined datatypes. Release 2.3 6-DEC-89. save_forms, obarray intern mechanism. comment char. Release 2.3a......... minor speed-ups. i/o interrupt considerations. Release 2.4 27-APR-90 gen_readr, for read-from-string. */ #include #include #include #include #include #include #ifdef vms #include #endif #include "siod.h" LISP heap_1,heap_2; LISP heap,heap_end,heap_org; long heap_size = 5000; long old_heap_used; long which_heap; long gc_status_flag = 1; char *init_file = (char *) NULL; char tkbuffer[TKBUFFERN]; long gc_kind_copying = 1; long gc_cells_allocated = 0; double gc_time_taken; LISP *stack_start_ptr; LISP freelist; jmp_buf errjmp; long errjmp_ok = 0; long nointerrupt = 1; long interrupt_differed = 0; LISP oblistvar = NIL; LISP truth = NIL; LISP eof_val = NIL; LISP sym_errobj = NIL; LISP sym_progn = NIL; LISP sym_lambda = NIL; LISP sym_quote = NIL; LISP sym_dot = NIL; LISP open_files = NIL; LISP unbound_marker = NIL; LISP *obarray; long obarray_dim = 100; struct catch_frame {LISP tag; LISP retval; jmp_buf cframe; struct catch_frame *next;}; struct gc_protected {LISP *location; long length; struct gc_protected *next;}; struct catch_frame *catch_framep = (struct catch_frame *) NULL; process_cla(argc,argv,warnflag) int argc,warnflag; char **argv; {int k; for(k=1;k #include struct tms time_buffer; double myruntime() {times(&time_buffer); return(time_buffer.tms_utime/60.0);} #else #ifdef vms #include double myruntime() {return(clock() * 1.0e-2);} #else double myruntime() {long x; long time(); time(&x); return((double) x);} #endif #endif void (*repl_puts)() = NULL; LISP (*repl_read)() = NULL; LISP (*repl_eval)() = NULL; void (*repl_print)() = NULL; void set_repl_hooks(puts_f,read_f,eval_f,print_f) void (*puts_f)(); LISP (*read_f)(); LISP (*eval_f)(); void (*print_f)(); {repl_puts = puts_f; repl_read = read_f; repl_eval = eval_f; repl_print = print_f;} fput_st(f,st) FILE *f; char *st; {long flag; flag = no_interrupt(1); fprintf(f,"%s",st); no_interrupt(flag);} put_st(st) char *st; {fput_st(stdout,st);} grepl_puts(st) char *st; {if (repl_puts == NULL) put_st(st); else (*repl_puts)(st);} repl() {LISP x,cw; double rt; while(1) {if ((gc_kind_copying == 1) && ((gc_status_flag) || heap >= heap_end)) {rt = myruntime(); gc_stop_and_copy(); sprintf(tkbuffer, "GC took %g seconds, %ld compressed to %ld, %ld free\n", myruntime()-rt,old_heap_used,heap-heap_org,heap_end-heap); grepl_puts(tkbuffer);} grepl_puts("> "); if (repl_read == NULL) x = lread(); else x = (*repl_read)(); if EQ(x,eof_val) break; rt = myruntime(); if (gc_kind_copying == 1) cw = heap; else {gc_cells_allocated = 0; gc_time_taken = 0.0;} if (repl_eval == NULL) x = leval(x,NIL); else x = (*repl_eval)(); if (gc_kind_copying == 1) sprintf(tkbuffer, "Evaluation took %g seconds %ld cons work\n", myruntime()-rt, heap-cw); else sprintf(tkbuffer, "Evaluation took %g seconds (%g in gc) %ld cons work\n", myruntime()-rt, gc_time_taken, gc_cells_allocated); grepl_puts(tkbuffer); if (repl_print == NULL) lprint(x); else (*repl_print)(x);}} err(message,x) char *message; LISP x; {nointerrupt = 1; if NNULLP(x) printf("ERROR: %s (see errobj)\n",message); else printf("ERROR: %s\n",message); if (errjmp_ok == 1) {setvar(sym_errobj,x,NIL); longjmp(errjmp,1);} printf("FATAL ERROR DURING STARTUP OR CRITICAL CODE SECTION\n"); exit(1);} LISP lerr(message,x) LISP message,x; {if NSYMBOLP(message) err("argument to error not a symbol",message); err(PNAME(message),x); return(NIL);} void gc_fatal_error() {err("ran out of storage",NIL);} #define NEWCELL(_into,_type) \ {if (gc_kind_copying == 1) \ {if ((_into = heap) >= heap_end) \ gc_fatal_error(); \ heap = _into+1;} \ else \ {if NULLP(freelist) \ gc_for_newcell(); \ _into = freelist; \ freelist = CDR(freelist); \ ++gc_cells_allocated;} \ (*_into).gc_mark = 0; \ (*_into).type = _type;} LISP newcell(type) long type; {LISP z; NEWCELL(z,type); return(z);} LISP cons(x,y) LISP x,y; {LISP z; NEWCELL(z,tc_cons); CAR(z) = x; CDR(z) = y; return(z);} LISP consp(x) LISP x; {if CONSP(x) return(truth); else return(NIL);} LISP car(x) LISP x; {switch TYPE(x) {case tc_nil: return(NIL); case tc_cons: return(CAR(x)); default: err("wta to car",x);}} LISP cdr(x) LISP x; {switch TYPE(x) {case tc_nil: return(NIL); case tc_cons: return(CDR(x)); default: err("wta to cdr",x);}} LISP setcar(cell,value) LISP cell, value; {if NCONSP(cell) err("wta to setcar",cell); return(CAR(cell) = value);} LISP setcdr(cell,value) LISP cell, value; {if NCONSP(cell) err("wta to setcdr",cell); return(CDR(cell) = value);} LISP flocons(x) double x; {LISP z; NEWCELL(z,tc_flonum); FLONM(z) = x; return(z);} LISP numberp(x) LISP x; {if FLONUMP(x) return(truth); else return(NIL);} LISP plus(x,y) LISP x,y; {LISP z; if NFLONUMP(x) err("wta(1st) to plus",x); if NFLONUMP(y) err("wta(2nd) to plus",y); NEWCELL(z,tc_flonum); FLONM(z) = FLONM(x) + FLONM(y); return(z);} LISP ltimes(x,y) LISP x,y; {LISP z; if NFLONUMP(x) err("wta(1st) to times",x); if NFLONUMP(y) err("wta(2nd) to times",y); NEWCELL(z,tc_flonum); FLONM(z) = FLONM(x)*FLONM(y); return(z);} LISP difference(x,y) LISP x,y; {LISP z; if NFLONUMP(x) err("wta(1st) to difference",x); if NFLONUMP(y) err("wta(2nd) to difference",y); NEWCELL(z,tc_flonum); FLONM(z) = FLONM(x) - FLONM(y); return(z);} LISP quotient(x,y) LISP x,y; {LISP z; if NFLONUMP(x) err("wta(1st) to quotient",x); if NFLONUMP(y) err("wta(2nd) to quotient",y); NEWCELL(z,tc_flonum); FLONM(z) = FLONM(x)/FLONM(y); return(z);} LISP greaterp(x,y) LISP x,y; {if NFLONUMP(x) err("wta(1st) to greaterp",x); if NFLONUMP(y) err("wta(2nd) to greaterp",y); if (FLONM(x)>FLONM(y)) return(truth); return(NIL);} LISP lessp(x,y) LISP x,y; {if NFLONUMP(x) err("wta(1st) to lessp",x); if NFLONUMP(y) err("wta(2nd) to lessp",y); if (FLONM(x) 1) {hash = 0; n = obarray_dim; cname = name; while(c = *cname++) hash = ((hash * 17) ^ c) % n; sl = obarray[hash];} else sl = oblistvar; for(l=sl;NNULLP(l);l=CDR(l)) if (strcmp(name,PNAME(CAR(l))) == 0) {no_interrupt(flag); return(CAR(l));} if (copyp == 1) {cname = must_malloc(strlen(name)+1); strcpy(cname,name);} else cname = name; sym = symcons(cname,unbound_marker); if (obarray_dim > 1) obarray[hash] = cons(sym,sl); oblistvar = cons(sym,oblistvar); no_interrupt(flag); return(sym);} LISP cintern(name) char *name; {return(gen_intern(name,0));} LISP rintern(name) char *name; {return(gen_intern(name,1));} LISP subrcons(type,name,f) long type; char *name; LISP (*f)(); {LISP z; NEWCELL(z,type); (*z).storage_as.subr.name = name; (*z).storage_as.subr.f = f; return(z);} LISP closure(env,code) LISP env,code; {LISP z; NEWCELL(z,tc_closure); (*z).storage_as.closure.env = env; (*z).storage_as.closure.code = code; return(z);} struct gc_protected *protected_registers = NULL; void gc_protect(location) LISP *location; {gc_protect_n(location,1);} void gc_protect_n(location,n) LISP *location; long n; {struct gc_protected *reg; reg = (struct gc_protected *) must_malloc(sizeof(struct gc_protected)); (*reg).location = location; (*reg).length = n; (*reg).next = protected_registers; protected_registers = reg;} void gc_protect_sym(location,st) LISP *location; char *st; {*location = cintern(st); gc_protect(location);} scan_registers() {struct gc_protected *reg; LISP *location; long j,n; for(reg = protected_registers; reg; reg = (*reg).next) {location = (*reg).location; n = (*reg).length; for(j=0;j 1) {obarray = (LISP *) must_malloc(sizeof(LISP) * obarray_dim); for(j=0;j= heap_end) gc_fatal_error(); heap = new+1; memcpy(new,x,sizeof(struct obj)); break; case tc_user_1: case tc_user_2: case tc_user_3: case tc_user_4: case tc_user_5: if (user_gc_relocate != NULL) {new = (*user_gc_relocate)(x); break;} default: err("BUG IN GARBAGE COLLECTOR gc_relocate",NIL);} (*x).gc_mark = 1; CAR(x) = new; return(new);} LISP get_newspace() {LISP newspace; if (which_heap == 1) {newspace = heap_2; which_heap = 2;} else {newspace = heap_1; which_heap = 1;} heap = newspace; heap_org = heap; heap_end = heap + heap_size; return(newspace);} scan_newspace(newspace) LISP newspace; {LISP ptr; for(ptr=newspace; ptr < heap; ++ptr) {switch TYPE(ptr) {case tc_cons: case tc_closure: CAR(ptr) = gc_relocate(CAR(ptr)); CDR(ptr) = gc_relocate(CDR(ptr)); break; case tc_symbol: VCELL(ptr) = gc_relocate(VCELL(ptr)); break; case tc_user_1: case tc_user_2: case tc_user_3: case tc_user_4: case tc_user_5: if (user_gc_scan != NULL) (*user_gc_scan)(ptr); break; default: break;}}} gc_stop_and_copy() {LISP newspace; long flag; flag = no_interrupt(1); errjmp_ok = 0; old_heap_used = heap - heap_org; newspace = get_newspace(); scan_registers(); scan_newspace(newspace); errjmp_ok = 1; no_interrupt(flag);} gc_for_newcell() {long flag; if (errjmp_ok == 0) gc_fatal_error(); flag = no_interrupt(1); errjmp_ok = 0; gc_mark_and_sweep(); errjmp_ok = 1; no_interrupt(flag); if NULLP(freelist) gc_fatal_error();} jmp_buf save_regs_gc_mark; gc_mark_and_sweep() {LISP stack_end; gc_ms_stats_start(); /* This assumes that all registers are saved into the jmp_buff */ setjmp(save_regs_gc_mark); mark_locations((LISP *) save_regs_gc_mark, (LISP *) ((char *) save_regs_gc_mark) + sizeof(save_regs_gc_mark)); mark_protected_registers(); mark_locations((LISP *) stack_start_ptr, (LISP *) &stack_end); #if THINK_C mark_locations((LISP *) ((char *) stack_start_ptr + 2), (LISP *) ((char *) &stack_end + 2)); #endif gc_sweep(); gc_ms_stats_end();} double gc_rt; long gc_cells_collected; gc_ms_stats_start() {gc_rt = myruntime(); gc_cells_collected = 0; if (gc_status_flag) printf("[starting GC]\n");} gc_ms_stats_end() {gc_rt = myruntime() - gc_rt; gc_time_taken = gc_time_taken + gc_rt; if (gc_status_flag) printf("[GC took %g cpu seconds, %ld cells collected]\n", gc_rt, gc_cells_collected);} void gc_mark(ptr) LISP ptr; {gc_mark_loop: if NULLP(ptr) return; if ((*ptr).gc_mark) return; (*ptr).gc_mark = 1; switch ((*ptr).type) {case tc_flonum: break; case tc_cons: gc_mark(CAR(ptr)); ptr = CDR(ptr); goto gc_mark_loop; case tc_symbol: ptr = VCELL(ptr); goto gc_mark_loop; case tc_closure: gc_mark((*ptr).storage_as.closure.code); ptr = (*ptr).storage_as.closure.env; goto gc_mark_loop; case tc_subr_0: case tc_subr_1: case tc_subr_2: case tc_subr_3: case tc_lsubr: case tc_fsubr: case tc_msubr: return; case tc_user_1: case tc_user_2: case tc_user_3: case tc_user_4: case tc_user_5: if (user_gc_mark != NULL) {ptr = (*user_gc_mark)(ptr); goto gc_mark_loop;} default: err("BUG IN GARBAGE COLLECTOR gc_mark",NIL);}} mark_protected_registers() {struct gc_protected *reg; LISP *location; long j,n; for(reg = protected_registers; reg; reg = (*reg).next) {location = (*reg).location; n = (*reg).length; for(j=0;j end) {tmp = start; start = end; end = tmp;} n = end - start; mark_locations_array(start,n);} mark_locations_array(x,n) LISP x[]; long n; {int j; LISP p; for(j=0;j= heap_org) && (p < heap_end) && (((((char *)p) - ((char *)heap_org)) % sizeof(struct obj)) == 0) && NTYPEP(p,tc_free_cell)) gc_mark(p);}} gc_sweep() {LISP ptr,end,nfreelist; long n; end = heap_end; n = 0; nfreelist = freelist; for(ptr=heap_org; ptr < end; ++ptr) if (((*ptr).gc_mark == 0)) switch((*ptr).type) {case tc_free_cell: break; case tc_user_1: case tc_user_2: case tc_user_3: case tc_user_4: case tc_user_5: if (user_gc_free != NULL) (*user_gc_free)(ptr); default: ++n; (*ptr).type = tc_free_cell; CDR(ptr) = nfreelist; nfreelist = ptr;} else (*ptr).gc_mark = 0; gc_cells_collected = n; freelist = nfreelist;} LISP user_gc(args) LISP args; {long old_status_flag,flag; if (gc_kind_copying == 1) err("implementation cannot GC at will with stop-and-copy\n", NIL); flag = no_interrupt(1); errjmp_ok = 0; old_status_flag = gc_status_flag; if NNULLP(args) if NULLP(car(args)) gc_status_flag = 0; else gc_status_flag = 1; gc_mark_and_sweep(); gc_status_flag = old_status_flag; errjmp_ok = 1; no_interrupt(flag); return(NIL);} LISP gc_status(args) LISP args; {LISP l; int n; if NNULLP(args) if NULLP(car(args)) gc_status_flag = 0; else gc_status_flag = 1; if (gc_kind_copying == 1) {if (gc_status_flag) put_st("garbage collection is on\n"); else put_st("garbage collection is off\n"); sprintf(tkbuffer,"%ld allocated %ld free\n", heap - heap_org, heap_end - heap); put_st(tkbuffer);} else {if (gc_status_flag) put_st("garbage collection verbose\n"); else put_st("garbage collection silent\n"); {for(n=0,l=freelist;NNULLP(l); ++n) l = CDR(l); sprintf(tkbuffer,"%ld allocated %ld free\n", (heap_end - heap_org) - n,n); put_st(tkbuffer);}} return(NIL);} LISP leval_args(l,env) LISP l,env; {LISP result,v1,v2,tmp; if NULLP(l) return(NIL); if NCONSP(l) err("bad syntax argument list",l); result = cons(leval(CAR(l),env),NIL); for(v1=result,v2=CDR(l); CONSP(v2); v1 = tmp, v2 = CDR(v2)) {tmp = cons(leval(CAR(v2),env),NIL); CDR(v1) = tmp;} if NNULLP(v2) err("bad syntax argument list",l); return(result);} LISP extend_env(actuals,formals,env) LISP actuals,formals,env; {if SYMBOLP(formals) return(cons(cons(cons(formals,NIL),cons(actuals,NIL)),env)); return(cons(cons(formals,actuals),env));} LISP envlookup(var,env) LISP var,env; {LISP frame,al,fl,tmp; for(frame=env;CONSP(frame);frame=CDR(frame)) {tmp = CAR(frame); if NCONSP(tmp) err("damaged frame",tmp); for(fl=CAR(tmp),al=CDR(tmp); CONSP(fl); fl=CDR(fl),al=CDR(al)) {if NCONSP(al) err("too few arguments",tmp); if EQ(CAR(fl),var) return(al);}} if NNULLP(frame) err("damaged env",env); return(NIL);} LISP (*user_leval)() = NULL; void set_eval_hooks(fcn) LISP (*fcn)(); {user_leval = fcn;} LISP leval(x,env) LISP x,env; {LISP tmp,arg1; loop: switch TYPE(x) {case tc_symbol: tmp = envlookup(x,env); if NNULLP(tmp) return(CAR(tmp)); tmp = VCELL(x); if EQ(tmp,unbound_marker) err("unbound variable",x); return(tmp); case tc_cons: tmp = CAR(x); switch TYPE(tmp) {case tc_symbol: tmp = envlookup(tmp,env); if NNULLP(tmp) {tmp = CAR(tmp); break;} tmp = VCELL(CAR(x)); if EQ(tmp,unbound_marker) err("unbound variable",CAR(x)); break; case tc_cons: tmp = leval(tmp,env); break;} switch TYPE(tmp) {case tc_subr_0: return(SUBRF(tmp)()); case tc_subr_1: return(SUBRF(tmp)(leval(car(CDR(x)),env))); case tc_subr_2: x = CDR(x); arg1 = leval(car(x),env); x = NULLP(x) ? NIL : CDR(x); return(SUBRF(tmp)(arg1, leval(car(x),env))); case tc_subr_3: x = CDR(x); arg1 = leval(car(x),env); x = NULLP(x) ? NIL : CDR(x); return(SUBRF(tmp)(arg1, leval(car(x),env), leval(car(cdr(x)),env))); case tc_lsubr: return(SUBRF(tmp)(leval_args(CDR(x),env))); case tc_fsubr: return(SUBRF(tmp)(CDR(x),env)); case tc_msubr: if NULLP(SUBRF(tmp)(&x,&env)) return(x); goto loop; case tc_closure: env = extend_env(leval_args(CDR(x),env), car((*tmp).storage_as.closure.code), (*tmp).storage_as.closure.env); x = cdr((*tmp).storage_as.closure.code); goto loop; case tc_symbol: x = cons(tmp,cons(cons(sym_quote,cons(x,NIL)),NIL)); x = leval(x,NIL); goto loop; case tc_user_1: case tc_user_2: case tc_user_3: case tc_user_4: case tc_user_5: if (user_leval != NULL) {if NULLP((*user_leval)(tmp,&x,&env)) return(x); else goto loop;} default: err("bad function",tmp);} default: return(x);}} LISP setvar(var,val,env) LISP var,val,env; {LISP tmp; if NSYMBOLP(var) err("wta(non-symbol) to setvar",var); tmp = envlookup(var,env); if NULLP(tmp) return(VCELL(var) = val); return(CAR(tmp)=val);} LISP leval_setq(args,env) LISP args,env; {return(setvar(car(args),leval(car(cdr(args)),env),env));} LISP syntax_define(args) LISP args; {if SYMBOLP(car(args)) return(args); return(syntax_define( cons(car(car(args)), cons(cons(sym_lambda, cons(cdr(car(args)), cdr(args))), NIL))));} LISP leval_define(args,env) LISP args,env; {LISP tmp,var,val; tmp = syntax_define(args); var = car(tmp); if NSYMBOLP(var) err("wta(non-symbol) to define",var); val = leval(car(cdr(tmp)),env); tmp = envlookup(var,env); if NNULLP(tmp) return(CAR(tmp) = val); if NULLP(env) return(VCELL(var) = val); tmp = car(env); setcar(tmp,cons(var,car(tmp))); setcdr(tmp,cons(val,cdr(tmp))); return(val);} LISP leval_if(pform,penv) LISP *pform,*penv; {LISP args,env; args = cdr(*pform); env = *penv; if NNULLP(leval(car(args),env)) *pform = car(cdr(args)); else *pform = car(cdr(cdr(args))); return(truth);} LISP leval_lambda(args,env) LISP args,env; {LISP body; if NULLP(cdr(cdr(args))) body = car(cdr(args)); else body = cons(sym_progn,cdr(args)); return(closure(env,cons(arglchk(car(args)),body)));} LISP leval_progn(pform,penv) LISP *pform,*penv; {LISP env,l,next; env = *penv; l = cdr(*pform); next = cdr(l); while(NNULLP(next)) {leval(car(l),env);l=next;next=cdr(next);} *pform = car(l); return(truth);} LISP leval_or(pform,penv) LISP *pform,*penv; {LISP env,l,next,val; env = *penv; l = cdr(*pform); next = cdr(l); while(NNULLP(next)) {val = leval(car(l),env); if NNULLP(val) {*pform = val; return(NIL);} l=next;next=cdr(next);} *pform = car(l); return(truth);} LISP leval_and(pform,penv) LISP *pform,*penv; {LISP env,l,next; env = *penv; l = cdr(*pform); if NULLP(l) {*pform = truth; return(NIL);} next = cdr(l); while(NNULLP(next)) {if NULLP(leval(car(l),env)) {*pform = NIL; return(NIL);} l=next;next=cdr(next);} *pform = car(l); return(truth);} LISP leval_catch(args,env) LISP args,env; {struct catch_frame frame; int k; LISP l,val; frame.tag = leval(car(args),env); frame.next = catch_framep; k = setjmp(frame.cframe); catch_framep = &frame; if (k == 2) {catch_framep = frame.next; return(frame.retval);} for(l=cdr(args); NNULLP(l); l = cdr(l)) val = leval(car(l),env); catch_framep = frame.next; return(val);} LISP lthrow(tag,value) LISP tag,value; {struct catch_frame *l; for(l=catch_framep; l; l = (*l).next) if EQ((*l).tag,tag) {(*l).retval = value; longjmp((*l).cframe,2);} err("no *catch found with this tag",tag); return(NIL);} LISP leval_let(pform,penv) LISP *pform,*penv; {LISP env,l; l = cdr(*pform); env = *penv; *penv = extend_env(leval_args(car(cdr(l)),env),car(l),env); *pform = car(cdr(cdr(l))); return(truth);} LISP reverse(l) LISP l; {LISP n,p; n = NIL; for(p=l;NNULLP(p);p=cdr(p)) n = cons(car(p),n); return(n);} LISP let_macro(form) LISP form; {LISP p,fl,al,tmp; fl = NIL; al = NIL; for(p=car(cdr(form));NNULLP(p);p=cdr(p)) {tmp = car(p); if SYMBOLP(tmp) {fl = cons(tmp,fl); al = cons(NIL,al);} else {fl = cons(car(tmp),fl); al = cons(car(cdr(tmp)),al);}} p = cdr(cdr(form)); if NULLP(cdr(p)) p = car(p); else p = cons(sym_progn,p); setcdr(form,cons(reverse(fl),cons(reverse(al),cons(p,NIL)))); setcar(form,cintern("let-internal")); return(form);} LISP leval_quote(args,env) LISP args,env; {return(car(args));} LISP leval_tenv(args,env) LISP args,env; {return(env);} LISP symbolconc(args) LISP args; {long size; LISP l,s; size = 0; tkbuffer[0] = 0; for(l=args;NNULLP(l);l=cdr(l)) {s = car(l); if NSYMBOLP(s) err("wta(non-symbol) to symbolconc",s); size = size + strlen(PNAME(s)); if (size > TKBUFFERN) err("symbolconc buffer overflow",NIL); strcat(tkbuffer,PNAME(s));} return(rintern(tkbuffer));} void (*user_prin1)() = NULL; void set_print_hooks(fcn) void (*fcn)(); {user_prin1 = fcn;} LISP lprin1f(exp,f) LISP exp; FILE *f; {LISP tmp; switch TYPE(exp) {case tc_nil: fput_st(f,"()"); break; case tc_cons: fput_st(f,"("); lprin1f(car(exp),f); for(tmp=cdr(exp);CONSP(tmp);tmp=cdr(tmp)) {fput_st(f," ");lprin1f(car(tmp),f);} if NNULLP(tmp) {fput_st(f," . ");lprin1f(tmp,f);} fput_st(f,")"); break; case tc_flonum: sprintf(tkbuffer,"%g",FLONM(exp)); fput_st(f,tkbuffer); break; case tc_symbol: fput_st(f,PNAME(exp)); break; case tc_subr_0: case tc_subr_1: case tc_subr_2: case tc_subr_3: case tc_lsubr: case tc_fsubr: case tc_msubr: sprintf(tkbuffer,"#"); break; case tc_closure: fput_st(f,"#"); break; case tc_user_1: case tc_user_2: case tc_user_3: case tc_user_4: case tc_user_5: if (user_prin1 != NULL) {(*user_prin1)(exp,f); break;} default: sprintf(tkbuffer,"#",TYPE(exp),exp); fput_st(f,tkbuffer);} return(NIL);} LISP lprint(exp) LISP exp; {lprin1f(exp,stdout); put_st("\n"); return(NIL);} LISP lreadr(),lreadparen(),lreadtk(),lreadf(); LISP lread() {return(lreadf(stdin));} int f_getc(f) FILE *f; {long iflag,dflag; int c; iflag = no_interrupt(1); dflag = interrupt_differed; c = getc(f); #ifdef VMS if ((dflag == 0) & interrupt_differed & (f == stdin)) while((c != 0) & (c != EOF)) c = getc(f); #endif no_interrupt(iflag); return(c);} void f_ungetc(c,f) int c; FILE *f; {ungetc(c,f);} int flush_ws(f,eoferr) struct gen_readio *f; char *eoferr; {int c,commentp; commentp = 0; while(1) {c = GETC_FCN(f); if (c == EOF) if (eoferr) err(eoferr,NIL); else return(c); if (commentp) {if (c == '\n') commentp = 0;} else if (c == ';') commentp = 1; else if (!isspace(c)) return(c);}} LISP lreadf(f) FILE *f; {return(gen_read(f_getc,f_ungetc,f));} LISP readtl(f) struct gen_readio *f; {int c; c = flush_ws(f,(char *)NULL); if (c == EOF) return(eof_val); UNGETC_FCN(c,f); return(lreadr(f));} LISP gen_read(f1,f2,x) int (*f1)(); void (*f2)(); char *x; {struct gen_readio f; f.getc_fcn = f1; f.ungetc_fcn = f2; f.cb_argument = x; return(readtl(&f));} char *user_ch_readm = ""; char *user_te_readm = ""; LISP (*user_readm)() = NULL; LISP (*user_readt)() = NULL; void set_read_hooks(all_set,end_set,fcn1,fcn2) char *all_set,*end_set; LISP (*fcn1)(),(*fcn2)(); {user_ch_readm = all_set; user_te_readm = end_set; user_readm = fcn1; user_readt = fcn2;} LISP lreadr(f) struct gen_readio *f; {int c,j; char *p; c = flush_ws(f,"end of file inside read"); switch (c) {case '(': return(lreadparen(f)); case ')': err("unexpected close paren",NIL); case '\'': return(cons(sym_quote,cons(lreadr(f),NIL))); case '`': return(cons(cintern("+internal-backquote"),lreadr(f))); case ',': c = GETC_FCN(f); switch(c) {case '@': p = "+internal-comma-atsign"; break; case '.': p = "+internal-comma-dot"; break; default: p = "+internal-comma"; UNGETC_FCN(c,f);} return(cons(cintern(p),lreadr(f))); default: if ((user_readm != NULL) && strchr(user_ch_readm,c)) return((*user_readm)(c,f));} p = tkbuffer; *p++ = c; for(j = 1; j",tc_subr_2,greaterp); init_subr("<",tc_subr_2,lessp); init_subr("eq?",tc_subr_2,eq); init_subr("eqv?",tc_subr_2,eql); init_subr("assq",tc_subr_2,assq); init_subr("read",tc_subr_0,lread); init_subr("eof-val",tc_subr_0,get_eof_val); init_subr("print",tc_subr_1,lprint); init_subr("eval",tc_subr_2,leval); init_subr("define",tc_fsubr,leval_define); init_subr("lambda",tc_fsubr,leval_lambda); init_subr("if",tc_msubr,leval_if); init_subr("begin",tc_msubr,leval_progn); init_subr("set!",tc_fsubr,leval_setq); init_subr("or",tc_msubr,leval_or); init_subr("and",tc_msubr,leval_and); init_subr("*catch",tc_fsubr,leval_catch); init_subr("*throw",tc_subr_2,lthrow); init_subr("quote",tc_fsubr,leval_quote); init_subr("oblist",tc_subr_0,oblistfn); init_subr("copy-list",tc_subr_1,copy_list); init_subr("gc-status",tc_lsubr,gc_status); init_subr("gc",tc_lsubr,user_gc); init_subr("load",tc_subr_2,load); init_subr("pair?",tc_subr_1,consp); init_subr("symbol?",tc_subr_1,symbolp); init_subr("number?",tc_subr_1,numberp); init_subr("let-internal",tc_msubr,leval_let); init_subr("let-internal-macro",tc_subr_1,let_macro); init_subr("symbol-bound?",tc_subr_2,symbol_boundp); init_subr("symbol-value",tc_subr_2,symbol_value); init_subr("set-symbol-value!",tc_subr_3,setvar); init_subr("the-environment",tc_fsubr,leval_tenv); init_subr("error",tc_subr_2,lerr); init_subr("quit",tc_subr_0,quit); init_subr("not",tc_subr_1,nullp); init_subr("null?",tc_subr_1,nullp); init_subr("env-lookup",tc_subr_2,envlookup); init_subr("reverse",tc_subr_1,reverse); init_subr("symbolconc",tc_lsubr,symbolconc); init_subr("save-forms",tc_subr_3,save_forms);}