/* eval2.c Copyright (C) 1989-96 Codemist Ltd */ /* * Interpreter (part 2). apply & some special forms */ /* Signature: 589bce9e 17-Jan-1999 */ #include <stdarg.h> #include <string.h> #include <ctype.h> #include "machine.h" #include "tags.h" #include "cslerror.h" #include "externs.h" #include "entries.h" #ifdef TIMEOUT #include "timeout.h" #endif static Lisp_Object apply_lots(int nargs, n_args *f, Lisp_Object def) /* * Cases with 8 or more args are lifted out here into a subroutine * to make APPLY a bit shorter and because these cases should be * uncommon & not worth optimising much. The code that Microsoft C 6.00A * produced for this was utterly DREADFUL - maybe other C compilers will * make a mess of it too. Anyway I hope it will not be called very often. */ { switch(nargs) { case 9: return (*f)(def, 9, stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 10: return (*f)(def, 10, stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 11: return (*f)(def, 11, stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 12: return (*f)(def, 12, stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 13: return (*f)(def, 13, stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 14: return (*f)(def, 14, stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 15: return (*f)(def, 15, stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 16: return (*f)(def, 16, stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 17: return (*f)(def, 17, stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 18: return (*f)(def, 18, stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 19: return (*f)(def, 19, stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 20: return (*f)(def, 20, stack[-20], stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 21: return (*f)(def, 21, stack[-21], stack[-20], stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 22: return (*f)(def, 22, stack[-22], stack[-21], stack[-20], stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 23: return (*f)(def, 23, stack[-23], stack[-22], stack[-21], stack[-20], stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 24: return (*f)(def, 24, stack[-24], stack[-23], stack[-22], stack[-21], stack[-20], stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); case 25: return (*f)(def, 25, stack[-25], stack[-24], stack[-23], stack[-22], stack[-21], stack[-20], stack[-19], stack[-18], stack[-17], stack[-16], stack[-15], stack[-14], stack[-13], stack[-12], stack[-11], stack[-10], stack[-9], stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); default: /* * If more than 25 args are going to be passed I will arrange that the * final ones are built into a list - as if the 25th arg was specified * as a "&rest" one. Why? Because passing VERY large numbers of arguments * in C is not a good idea - ANSI C compilers are only obliged to support * up to 31 args, and one some machines this limit seems to really matter. * But Common Lisp can need more args than that. I will ignore the fact that * what I do here is slow. I will HOPE that calls with 25 or more args * are very uncommon. */ { int n = nargs; Lisp_Object w, *tsp = stack, nil = C_nil; #if (ARG_CUT_OFF != 25) if (ARG_CUT_OFF != 25) { fprintf(stderr, "\n+++ ARG_CUT_OFF incorrectly configured\n"); my_exit(EXIT_FAILURE); } #endif w = ncons(tsp[-1]); errexit(); tsp[-1] = w; while (n > ARG_CUT_OFF) { w = cons(tsp[-2], tsp[-1]); errexit(); tsp[-2] = w; tsp[-1] = tsp[0]; tsp--; n--; } return (*f)(def, nargs, tsp[-25], tsp[-24], tsp[-23], tsp[-22], tsp[-21], tsp[-20], tsp[-19], tsp[-18], tsp[-17], tsp[-16], tsp[-15], tsp[-14], tsp[-13], tsp[-12], tsp[-11], tsp[-10], tsp[-9], tsp[-8], tsp[-7], tsp[-6], tsp[-5], tsp[-4], tsp[-3], tsp[-2], tsp[-1]); } } } void push_args(va_list a, int nargs) /* * The unpacking here must match "apply_lots" as above. For up to * (and including) ARG_CUT_OFF (=25) args things are passed normally. * beyond that the first ARG_CUT_OFF-1 args are passed normally, and the * rest are in a list as a final actual arg. Note that this list will * have at least two elements. */ { int i; if (nargs <= ARG_CUT_OFF) { for (i = 0; i<nargs; i++) { Lisp_Object w = va_arg(a, Lisp_Object); push(w); } } else { Lisp_Object x; for (i = 0; i<(ARG_CUT_OFF-1); i++) { Lisp_Object w = va_arg(a, Lisp_Object); push(w); } x = va_arg(a, Lisp_Object); /* * Internal consistency should ensure that the list passed here is long * enough for the following unpacking operation. But if (as a result of * internal system muddles it is not maybe the fact that qcar(nil) = * qcdr(nil) = nil will tend to reduce the damage? */ for (; i<nargs; i++) { push(qcar(x)); x = qcdr(x); } } va_end(a); } void push_args_1(va_list a, int nargs) /* * This is very much like push_args(), but is for the (rather small number * of) cases where the first argument to a function must NOT be pushed on the * stack. See, for instance, "funcall" as an example. */ { int i; if (nargs <= ARG_CUT_OFF) { for (i = 1; i<nargs; i++) { Lisp_Object w = va_arg(a, Lisp_Object); push(w); } } else { Lisp_Object x; for (i = 1; i<(ARG_CUT_OFF-1); i++) { Lisp_Object w = va_arg(a, Lisp_Object); push(w); } x = va_arg(a, Lisp_Object); for (; i<nargs; i++) { push(qcar(x)); x = qcdr(x); } } va_end(a); } Lisp_Object apply(Lisp_Object fn, int nargs, Lisp_Object env, Lisp_Object name) /* * There are (nargs) arguments on the Lisp stack, and apply() must use them * then pop them off. They were pushed in the order push(arg1); push(arg2), * and so on, and the stack grows upwards. * If I return with an error I will hand back the value name rather than the * junk value normally used in such cases. */ { Lisp_Object def, nil = C_nil; for (;;) { if (symbolp(fn)) { def = qenv(fn); /* this is passed as arg1 to the called code */ /* * apply_lambda() will find arguments on the stack and is responsible for * popping them before it exits. */ { /* * Because there are nargs values pushed on the (upwards growing) stack, * &stack[1-nargs] points at the first value pushed, i.e. arg-1. At one stage * I had a machine-specific bit of code (called "ncall") to do the following, * arguing that maybe in assembly code it would be possible to do much better * than the really ugly switch statement shown now. My belief now (especially * given that ncall was used in just one place - here) is that the switch will * cost no more than the procedure call did, and that in-line code will help * speed up the common and critical cases of 0, 1, 2 and 3 args. Also apply * is otherwise a reasonably short function, so if this switch is needed * anywhere here is not too bad. */ push(name); switch (nargs) { /* * The Standard Lisp Report (Marti et al, Utah UUCS-78-101) only * requires support for 15 args. Common Lisp requires at least 50. * I deal with up to 8 args in-line here (I expect more than that to be * amazingly uncommon) so that this function is kept under contol. * Calls with more than 8 args go over to apply_lots, and within that * function calls with over 25 args have an even more clumsy treatment. */ case 0: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 0); break; case 1: #ifdef DEBUG if (qfn1(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfn1(fn))(def, stack[-1]); break; case 2: #ifdef DEBUG if (qfn2(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfn2(fn))(def, stack[-2], stack[-1]); break; case 3: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 3, stack[-3], stack[-2], stack[-1]); break; case 4: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 4, stack[-4], stack[-3], stack[-2], stack[-1]); break; case 5: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 5, stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); break; case 6: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 6, stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); break; case 7: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 7, stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); break; case 8: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = (*qfnn(fn))(def, 8, stack[-8], stack[-7], stack[-6], stack[-5], stack[-4], stack[-3], stack[-2], stack[-1]); break; default: #ifdef DEBUG if (qfnn(fn) == NULL) { term_printf("Illegal APPLY\n"); my_exit(EXIT_FAILURE); } #endif def = apply_lots(nargs, qfnn(fn), def); break; } /* * here I have to pop the stack by hand - note that popv does not * corrupt exit_count, which tells me how many results were being handed * back. */ pop(name); popv(nargs); nil = C_nil; if (exception_pending()) return name; else return def; } } else if (!is_cons(fn)) { popv(nargs); push(name); error(1, err_bad_fn, fn); pop(name); return name; } /* apply_lambda() will pop the args from the stack when it is done */ if ((def = qcar(fn)) == lambda) return apply_lambda(qcdr(fn), nargs, env, name); /* * A bytecoded funarg is stored as (cfunarg <actual fn> <env>) and any call * to it behaves as if the actual function was called with the environment * passed as a forced-in first argument. */ else if (def == cfunarg) { int i; push(nil); def = qcdr(fn); fn = qcar(def); for (i=0; i<nargs; i++) stack[-i] = stack[-i-1]; stack[-nargs] = qcdr(def); nargs++; continue; } else if (def == funarg) { def = qcdr(fn); if (consp(def)) return apply_lambda(qcdr(def), nargs, qcar(def), name); } break; } /* * Other cases are all errors. */ popv(nargs); push(name); error(1, err_bad_apply, fn); pop(name); return name; } /* * Now for implementation of all the special forms... */ static Lisp_Object and_fn(Lisp_Object args, Lisp_Object env) /* also needs to be a macro for Common Lisp */ { Lisp_Object nil = C_nil; stackcheck2(0, args, env); if (!consp(args)) return onevalue(lisp_true); for (;;) { Lisp_Object v = qcar(args); args = qcdr(args); if (!consp(args)) return eval(v, env); push2(args, env); v = eval(v, env); pop2(env, args); errexit(); if (v == nil) return onevalue(nil); } } /* * This is not used at present, but may be wanted sometime so I will * leave it here for now... * Lisp_Object append(Lisp_Object a, Lisp_Object b) { Lisp_Object nil = C_nil; if (!consp(a)) return b; else { stackcheck2(0, a, b); push(a); b = append(qcdr(a), b); pop(a); errexit(); return cons(qcar(a), b); } } */ static Lisp_Object block_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object p, nil = C_nil; if (!consp(args)) return onevalue(nil); stackcheck2(0, args, env); push3(qcar(args), /* my_tag */ qcdr(args), /* args */ env); #define env stack[0] #define args stack[-1] #define my_tag stack[-2] /* * I need to augment the (lexical) environment with the name of my * tag in such a way that return-from can throw out to exactly the * correct matching level. This is done by pushing (0 . tag) onto * the environment - the 0 marks this as a block name. */ my_tag = cons(fixnum_of_int(0), my_tag); errexitn(3); env = cons(my_tag, env); errexitn(3); p = nil; while (consp(args)) { p = qcar(args); p = eval(p, env); /* * one of the sorts of exit that may be activated by marking nil is * a return_from. Here I need to check to see if that is what * is going on. */ nil = C_nil; if (exception_pending()) { flip_exception(); /* Temp restore it */ qcar(my_tag) = fixnum_of_int(2); /* Invalidate */ if (exit_reason == UNWIND_RETURN && exit_tag == my_tag) { exit_reason = UNWIND_NULL; /* not strictly needed - but tidy */ popv(3); return nvalues(exit_value, exit_count); } if ((exit_reason & UNWIND_ERROR) != 0) { err_printf("\nEvaluating: "); loop_print_error(qcar(args)); ignore_exception(); } flip_exception(); /* re-instate exit condition */ popv(3); return nil; } args = qcdr(args); } popv(3); return p; #undef env #undef args #undef my_tag } static Lisp_Object catch_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object tag, nil = C_nil; if (!consp(args)) return onevalue(nil); stackcheck2(0, args, env); push2(args, env); tag = qcar(args); tag = eval(tag, env); errexit(); tag = catch_tags = cons(tag, catch_tags); pop2(env, args); errexit(); push(tag); { Lisp_Object v = progn_fn(qcdr(args), env); pop(tag); nil = C_nil; if (exception_pending()) { flip_exception(); catch_tags = qcdr(tag); qcar(tag) = tag; qcdr(tag) = nil; /* Invalidate the catch frame */ if (exit_reason == UNWIND_THROW && exit_tag == tag) { exit_reason = UNWIND_NULL; return nvalues(exit_value, exit_count); } flip_exception(); return nil; } catch_tags = qcdr(tag); qcar(tag) = tag; qcdr(tag) = nil; /* Invalidate the catch frame */ return v; } } #define BODY_LET 0 #define BODY_COMPILER_LET 1 #define BODY_PROG 2 Lisp_Object let_fn_1(Lisp_Object bvl, Lisp_Object body, Lisp_Object env, int compilerp) /* * This will have to look for (declare (special ...)). * compiler-let forces all of its bindings to be locally special. In * CSL mode I do not support local declarations, which simplifies and * speeds things up here. */ { Lisp_Object nil = C_nil; stackcheck3(0, bvl, body, env); push3(bvl, body, env); nil = C_nil; push5(nil, nil, env, nil, nil); #ifdef COMMON /* * I lose the name (for security) but leave the junk stack location * (because doing otherwise seems unduly complicated. */ #define local_decs stack[0] #endif #define specenv stack[-1] #define env1 stack[-2] #define p stack[-3] #define q stack[-4] #define env stack[-5] #define body stack[-6] #define bvl stack[-7] #define Return(v) { popv(8); return (v); } #ifdef COMMON /* * Find local declarations - it is necessary to macro-expand * items in the body to see if they turn into declarations. */ for (;;) { if (exception_pending() || !consp(body)) break; p = macroexpand(qcar(body), env); errexitn(8); body = qcdr(body); if (!consp(p)) { if (stringp(p) && consp(body)) continue; body = cons(p, body); nil = C_nil; break; } if (qcar(p) != declare_symbol) { body = cons(p, body); nil = C_nil; break; } for (p = qcdr(p); consp(p); p = qcdr(p)) { q = qcar(p); if (!consp(q) || qcar(q) != special_symbol) continue; /* here q says (special ...) */ for (q=qcdr(q); consp(q); q = qcdr(q)) { local_decs = cons(qcar(q), local_decs); nil = C_nil; if (exception_pending()) break; } if (exception_pending()) break; } } if (exception_pending()) Return(nil); #endif for (; consp(bvl); bvl=qcdr(bvl)) { Lisp_Object z; q = qcar(bvl); if (consp(q)) { z = qcdr(q); q = qcar(q); if (consp(z)) z = qcar(z); else z = nil; } else z = nil; if (!is_symbol(q)) { Lisp_Object qq = q; Return(error(1, err_bad_bvl, qq)); } else { #ifdef COMMON Header h = qheader(q); #endif if (z != nil) { z = eval(z, env); errexitn(8); } z = cons(q, z); errexitn(8); #ifdef COMMON if (compilerp == BODY_COMPILER_LET) { specenv = cons(z, specenv); errexitn(8); q = acons(q, work_symbol, env1); errexitn(8); env1 = q; /* Locally special */ } else #endif #ifndef COMMON specenv = cons(z, specenv); #else if (h & SYM_SPECIAL_VAR) specenv = cons(z, specenv); else { Lisp_Object w; for (w = local_decs; w!=nil; w = qcdr(w)) { if (q != qcar(w)) continue; qcar(w) = fixnum_of_int(0); /* The next few calls to cons() maybe lose w, but that is OK! */ specenv = cons(z, specenv); errexitn(8); q = acons(q, work_symbol, env1); errexitn(8); env1 = q; goto bound; } env1 = cons(z, env1); bound: ; } #endif errexitn(8); } } #ifdef COMMON while (local_decs!=nil) /* Pervasive special declarations */ { Lisp_Object q1 = qcar(local_decs); local_decs=qcdr(local_decs); if (!is_symbol(q1)) continue; q1 = acons(q1, work_symbol, env1); errexitn(8); env1 = q1; } #endif if (specenv == nil) { Lisp_Object bodyx = body, env1x = env1; /* * See expansion of Return() for an explanation of why body and env1 have * been moved into new local variables before the call.. */ if (compilerp == BODY_PROG) { Return(tagbody_fn(bodyx, env1x)); } else { Return(progn_fn(bodyx, env1x)); } } /* * I instate the special bindings after all values to bind have been collected */ for (p = specenv; p != nil; p = qcdr(p)) { Lisp_Object w = qcar(p), v = qcar(w), z = qcdr(w); Lisp_Object old = qvalue(v); qvalue(v) = z; qcdr(w) = old; } { if (compilerp == BODY_PROG) body = tagbody_fn(body, env1); else body = progn_fn(body, env1); nil = C_nil; if (exception_pending()) { flip_exception(); for (p = specenv; p != nil; p = qcdr(p)) { Lisp_Object w = qcar(p), v = qcar(w), z = qcdr(w); qvalue(v) = z; } flip_exception(); Return(nil); } else { for (p = specenv; p != nil; p = qcdr(p)) { Lisp_Object w = qcar(p), v = qcar(w), z = qcdr(w); qvalue(v) = z; } { Lisp_Object bodyx = body; Return(bodyx); } } } #ifdef COMMON #undef local_decs #endif #undef specenv #undef env1 #undef p #undef q #undef env #undef body #undef bvl #undef Return } #ifdef COMMON static Lisp_Object compiler_let_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object nil = C_nil; if (!consp(args)) return onevalue(nil); return let_fn_1(qcar(args), qcdr(args), env, BODY_COMPILER_LET); } #endif static Lisp_Object cond_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object nil = C_nil; stackcheck2(0, args, env); while (consp(args)) { Lisp_Object p = qcar(args); if (consp(p)) { Lisp_Object p1; push2(args, env); p1 = qcar(p); p1 = eval(p1, env); pop2(env, args); errexit(); if (p1 != nil) { args = qcdr(qcar(args)); /* Here I support the case "(cond (predicate) ...)" with no consequents */ if (!consp(args)) return onevalue(p1); else return progn_fn(args, env); } } args = qcdr(args); } return onevalue(nil); } #ifdef COMMON Lisp_Object declare_fn(Lisp_Object args, Lisp_Object env) /* * declarations can only properly occur at the heads of various * special forms, and so may NOT be evaluated in an ordinary manner. * Thus I am entitled (just about) to make this a no-op. It would * probably be better to arrange that (declare ...) never got evaluated * and then I could raise an error if this bit of code got activated. * Indeed (declare ...) probably does not ever get evaluated - still * a no-op here seems the safest bet. */ { Lisp_Object nil = C_nil; CSL_IGNORE(env); CSL_IGNORE(args); return onevalue(nil); } #endif #define flagged_lose(v) \ ((fv = qfastgets(v)) != nil && elt(fv, 1) != SPID_NOPROP) static Lisp_Object defun_fn(Lisp_Object args, Lisp_Object env) { /* * defun is eventually expected (required!) to be a macro rather than (maybe * as well as?) a special form. For bootstrap purposes it seems useful to * build it in as a special form. Also this special form is quite good enough * in CSL mode */ Lisp_Object fname, nil = C_nil; CSL_IGNORE(env); if (consp(args)) { fname = qcar(args); args = qcdr(args); if (is_symbol(fname)) { Lisp_Object fv; if (qheader(fname) & SYM_SPECIAL_FORM) return error(1, err_redef_special, fname); if ((qheader(fname) & (SYM_C_DEF | SYM_CODEPTR)) == (SYM_C_DEF | SYM_CODEPTR)) return onevalue(fname); if (flagged_lose(fname)) { debug_printf("\n+++ "); loop_print_debug(fname); debug_printf(" not defined because of LOSE flag\n"); return onevalue(nil); } qheader(fname) = qheader(fname) & ~SYM_MACRO; if ((qheader(fname) & SYM_C_DEF) != 0) lose_C_def(fname); if (qfn1(fname) != undefined1) { if (qvalue(redef_msg) != nil) { debug_printf("\n+++ "); loop_print_debug(fname); debug_printf(" redefined\n"); } errexit(); set_fns(fname, undefined1, undefined2, undefinedn); qenv(fname) = fname; } /* * qfn() can contain 'interpreted' for a function defined wrt the null * environment, or 'funarged' for one with an environment - in the latter * case the definition (in qenv()) is a pair (<def> . <env>) */ qenv(fname) = args; /* Sort of notional lambda present */ set_fns(fname, interpreted1, interpreted2, interpretedn); if (qvalue(comp_symbol) != nil && qfn1(compiler_symbol) != undefined1) { push(fname); args = ncons(fname); nil = C_nil; if (!exception_pending()) (qfn1(compiler_symbol))(qenv(compiler_symbol), args); pop(fname); } return onevalue(fname); } } return aerror("defun"); } static Lisp_Object defmacro_fn(Lisp_Object args, Lisp_Object env) { /* * defmacro is eventually expected (required!) to be a macro rather than (maybe * as well as?) a special form. For bootstrap purposes it seems useful to * build it in as a special form. */ Lisp_Object fname, nil = C_nil; CSL_IGNORE(env); if (consp(args)) { fname = qcar(args); args = qcdr(args); if (is_symbol(fname)) { if ((qheader(fname) & (SYM_C_DEF | SYM_CODEPTR)) == (SYM_C_DEF | SYM_CODEPTR)) return onevalue(fname); qheader(fname) |= SYM_MACRO; /* * Note that a name can have a definition as a macro and as a special form, * and in that case the qfn() cell gives the special form and the qenv() * cell the macro definition. Otherwise at present I put 'undefined' * in the qfn() cell, but in due course I will want something else as better * protection against compiled code improperly attempting to call a macro. * Note also that if the symbol was a special form before I do not want * to clear the C_DEF flag, since the special form must be re-instated when * I reload the system. */ if ((qheader(fname) & SYM_SPECIAL_FORM) == 0) { qheader(fname) &= ~SYM_C_DEF; if (qfn1(fname) != undefined1 && qvalue(redef_msg) != nil) { debug_printf("\n+++ "); loop_print_debug(fname); debug_printf(" redefined as a macro\n"); errexit(); } set_fns(fname, undefined1, undefined2, undefinedn); } qenv(fname) = args; /* Sort of notional lambda present */ if (qvalue(comp_symbol) != nil && qfn1(compiler_symbol) != undefined1) { Lisp_Object t1, t2; push(fname); if (!(consp(args) && consp(qcdr(args)) && qcdr(qcdr(args)) == nil && (t1 = qcar(args), t2 = qcdr(qcar(qcdr(args))), equal(t1, t2)))) { errexitn(1); fname = stack[0]; args = ncons(fname); nil = C_nil; if (!exception_pending()) (qfn1(compiler_symbol))(qenv(compiler_symbol), args); } pop(fname); errexit(); } return onevalue(fname); } } return aerror("defmacro"); } static Lisp_Object eval_when_fn(Lisp_Object args, Lisp_Object env) /* * When interpreted, eval-when just looks for the situation EVAL. */ { Lisp_Object situations, nil = C_nil; if (!consp(args)) return onevalue(nil); situations = qcar(args); args = qcdr(args); while (consp(situations)) { if (qcar(situations) == eval_symbol) return progn_fn(args, env); situations = qcdr(situations); } return onevalue(nil); } #ifdef COMMON static Lisp_Object flet_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object my_env, d, nil = C_nil; if (!consp(args)) return onevalue(nil); stackcheck2(0, args, env); my_env = env; d = qcar(args); /* The bunch of definitions */ args = qcdr(args); nil = C_nil; while (consp(d)) { Lisp_Object w = qcar(d); if (consp(w) && consp(qcdr(w))) { Lisp_Object w1; push4(args, d, env, w); w1 = list2star(funarg, my_env, qcdr(w)); pop(w); nil = C_nil; if (!exception_pending()) w1 = cons(w1, qcar(w)); pop(env); nil = C_nil; if (!exception_pending()) env = cons(w1, env); pop2(d, args); errexit(); } d = qcdr(d); } /* * Treat body as (let nil ...) to get (declare ...) recognized. */ return let_fn_1(nil, args, env, BODY_LET); } #endif Lisp_Object function_fn(Lisp_Object args, Lisp_Object env) { /* * For most things this behaves just like (quote xxx), but * (function (lambda (x) y)) gets converted to * (funarg env (x) y). */ Lisp_Object nil = C_nil; if (consp(args) && qcdr(args) == nil) { args = qcar(args); if (consp(args) && qcar(args) == lambda) args = list2star(funarg, env, qcdr(args)); return onevalue(args); } return aerror("function"); } static Lisp_Object go_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object p, tag, nil = C_nil; CSL_IGNORE(env); if (!consp(args)) return aerror("go"); else tag = qcar(args); for(p=env; consp(p); p=qcdr(p)) { Lisp_Object w = qcar(p), z; if (!consp(w)) continue; if (qcar(w) == fixnum_of_int(1) && (z = qcar(qcdr(w)), eql(z, tag))) { p = w; goto tag_found; } } return error(1, err_go_tag, tag); tag_found: exit_tag = p; exit_count = 0; exit_reason = UNWIND_GO; flip_exception(); /* Exceptional exit active */ return nil; } static Lisp_Object if_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object nil = C_nil; Lisp_Object p=nil, tr=nil, fs=nil; if (!consp(args)) return aerror("if"); p = qcar(args); args = qcdr(args); if (!consp(args)) return aerror("if"); tr = qcar(args); args = qcdr(args); if (!consp(args)) fs = nil; else { fs = qcar(args); args = qcdr(args); if (args != nil) return aerror("if"); } stackcheck4(0, p, env, tr, fs); push3(fs, tr, env); p = eval(p, env); pop3(env, tr, fs); errexit(); if (p == nil) return eval(fs, env); /* tail call on result */ else return eval(tr, env); /* ... passing back values */ } #ifdef COMMON static Lisp_Object labels_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object my_env, d, nil = C_nil; if (!consp(args)) return onevalue(nil); stackcheck2(0, args, env); my_env = env; d = qcar(args); /* The bunch of definitions */ while (consp(d)) { Lisp_Object w = qcar(d); if (consp(w) && consp(qcdr(w))) { Lisp_Object w1; push4(args, d, env, w); w1 = list2star(funarg, nil, qcdr(w)); pop(w); nil = C_nil; if (!exception_pending()) w1 = cons(w1, qcar(w)); pop(env); nil = C_nil; if (!exception_pending()) env = cons(w1, env); pop2(d, args); errexit(); } d = qcdr(d); } /* * Now patch up the environments stored with the local defs so as to * permit mutual recursion between them all. */ for (d=env; d!=my_env; d=qcdr(d)) qcar(qcdr(qcar(qcar(d)))) = env; return let_fn_1(nil, qcdr(args), env, BODY_LET); } #endif static Lisp_Object let_fn(Lisp_Object args, Lisp_Object env) { Lisp_Object nil = C_nil; if (!consp(args)) return onevalue(nil); return let_fn_1(qcar(args), qcdr(args), env, BODY_LET); } static Lisp_Object letstar_fn(Lisp_Object args, Lisp_Object env) /* * This will have to look for (declare (special ...)), unless * I am in CSL mode. */ { Lisp_Object nil = C_nil; if (!consp(args)) return onevalue(nil); stackcheck2(0, args, env); push3(qcar(args), qcdr(args), env); nil = C_nil; push5(nil, nil, /* p, q */ env, nil, nil); /* env1, specenv, local_decs */ #ifdef COMMON #define local_decs stack[0] #endif #define specenv stack[-1] #define env1 stack[-2] #define p stack[-3] #define q stack[-4] #define env stack[-5] #define body stack[-6] #define bvl stack[-7] #define Return(v) { popv(8); return (v); } #ifdef COMMON for (;;) { if (exception_pending() || !consp(body)) break; p = macroexpand(qcar(body), env); errexitn(8); body = qcdr(body); if (!consp(p)) { if (stringp(p) && consp(body)) continue; body = cons(p, body); nil = C_nil; break; } if (qcar(p) != declare_symbol) { body = cons(p, body); nil = C_nil; break; } for (p = qcdr(p); consp(p); p = qcdr(p)) { q = qcar(p); if (!consp(q) || qcar(q) != special_symbol) continue; /* here q says (special ...) */ for (q=qcdr(q); consp(q); q = qcdr(q)) { local_decs = cons(qcar(q), local_decs); nil = C_nil; if (exception_pending()) break; } if (exception_pending()) break; } } if (exception_pending()) Return(nil); #endif for (; consp(bvl); bvl=qcdr(bvl)) { Lisp_Object z; q = qcar(bvl); if (consp(q)) { z = qcdr(q); q = qcar(q); if (consp(z)) z = qcar(z); else z = nil; } else z = nil; if (!is_symbol(q)) { error(1, err_bad_bvl, q); goto unwind_special_bindings; } else { #ifdef COMMON Header h = qheader(q); #endif if (z != nil) { z = eval(z, env); nil = C_nil; if (exception_pending()) goto unwind_special_bindings; } #ifndef COMMON p = z; z = acons(q, qvalue(q), specenv); nil = C_nil; if (!exception_pending()) specenv = z; qvalue(q) = p; #else if (h & SYM_SPECIAL_VAR) { p = z; z = acons(q, qvalue(q), specenv); nil = C_nil; if (!exception_pending()) specenv = z; qvalue(q) = p; } else { for (p = local_decs; p!=nil; p = qcdr(p)) { Lisp_Object w; if (q != qcar(p)) continue; qcar(p) = fixnum_of_int(0); w = acons(q, qvalue(q), specenv); nil = C_nil; if (exception_pending()) goto unwind_special_bindings; specenv = w; w = acons(q, work_symbol, env); nil = C_nil; if (exception_pending()) goto unwind_special_bindings; env = w; qvalue(q) = z; goto bound; } q = acons(q, z, env); nil = C_nil; if (exception_pending()) goto unwind_special_bindings; env = q; bound: ; } #endif nil = C_nil; if (exception_pending()) goto unwind_special_bindings; } } #ifdef COMMON while (local_decs!=nil) /* Pervasive special declarations */ { q = qcar(local_decs); local_decs=qcdr(local_decs); if (!is_symbol(q)) continue; q = acons(q, work_symbol, env); nil = C_nil; if (!exception_pending()) env = q; else goto unwind_special_bindings; } #endif if (specenv == nil) { Lisp_Object bodyx = body, envx = env; Return(progn_fn(bodyx, envx)); /* beware Return macro! */ } { body = progn_fn(body, env); nil = C_nil; if (exception_pending()) goto unwind_special_bindings; for (bvl = specenv; bvl != nil; bvl = qcdr(bvl)) { Lisp_Object w = qcar(bvl), v = qcar(w), z = qcdr(w); qvalue(v) = z; } { Lisp_Object bodyx = body; Return(bodyx); } } unwind_special_bindings: flip_exception(); for (bvl = specenv; bvl != nil; bvl = qcdr(bvl)) { Lisp_Object w = qcar(bvl), v = qcar(w), z = qcdr(w); qvalue(v) = z; } flip_exception(); popv(8); return nil; #ifdef COMMON #undef local_decs #endif #undef specenv #undef env1 #undef p #undef q #undef env #undef body #undef bvl #undef Return } setup_type const eval2_setup[] = /* * A jolly curiosity - "function" and "declare" are ALSO set up in * restart.c (because handles are needed on the symbols). I leave * the redundant initialisation here too since I find it clearer that * way. */ { {"and", and_fn, bad_special2, bad_specialn}, {"catch", catch_fn, bad_special2, bad_specialn}, {"cond", cond_fn, bad_special2, bad_specialn}, /* * I am not over-enthusiastic about supporting eval-when in CSL, but * something of that sort seems needed by some bits of code that I have * come across... */ {"eval-when", eval_when_fn, bad_special2, bad_specialn}, {"function", function_fn, bad_special2, bad_specialn}, {"go", go_fn, bad_special2, bad_specialn}, {"if", if_fn, bad_special2, bad_specialn}, {"let*", letstar_fn, bad_special2, bad_specialn}, /* DE and DM are used as low level primitives in the Common Lisp bootstrap */ {"de", defun_fn, bad_special2, bad_specialn}, {"dm", defmacro_fn, bad_special2, bad_specialn}, #ifdef COMMON {"block", block_fn, bad_special2, bad_specialn}, {"compiler-let", compiler_let_fn, bad_special2, bad_specialn}, {"declare", declare_fn, bad_special2, bad_specialn}, {"flet", flet_fn, bad_special2, bad_specialn}, {"labels", labels_fn, bad_special2, bad_specialn}, {"let", let_fn, bad_special2, bad_specialn}, #else {"~block", block_fn, bad_special2, bad_specialn}, {"~let", let_fn, bad_special2, bad_specialn}, #endif {NULL, 0, 0, 0}}; /* end of eval2.c */