/* * Copyright (c) 2001 by The XFree86 Project, Inc. * * 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 XFREE86 PROJECT 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. * * Except as contained in this notice, the name of the XFree86 Project shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from the * XFree86 Project. * * Author: Paulo César Pereira de Andrade */ /* $XFree86: xc/programs/xedit/lisp/core.c,v 1.71tsi Exp $ */ #include "lisp/io.h" #include "lisp/core.h" #include "lisp/format.h" #include "lisp/helper.h" #include "lisp/package.h" #include "lisp/private.h" #include "lisp/write.h" /* * Types */ typedef struct _SeqInfo { LispType type; union { LispObj *list; LispObj **vector; unsigned char *string; } data; } SeqInfo; #define SETSEQ(seq, object) \ switch (seq.type = XOBJECT_TYPE(object)) { \ case LispString_t: \ seq.data.string = (unsigned char*)THESTR(object); \ break; \ case LispCons_t: \ seq.data.list = object; \ break; \ default: \ seq.data.list = object->data.array.list; \ break; \ } #ifdef __UNIXOS2__ # define finite(x) isfinite(x) #endif #ifdef NEED_SETENV extern int setenv(const char *name, const char *value, int overwrite); extern void unsetenv(const char *name); #endif /* * Prototypes */ #define NONE 0 #define REMOVE 1 #define SUBSTITUTE 2 #define DELETE 3 #define NSUBSTITUTE 4 #define ASSOC 1 #define MEMBER 2 #define FIND 1 #define POSITION 2 #define IF 1 #define IFNOT 2 #define UNION 1 #define INTERSECTION 2 #define SETDIFFERENCE 3 #define SETEXCLUSIVEOR 4 #define SUBSETP 5 #define NSETDIFFERENCE 6 #define NINTERSECTION 7 #define NUNION 8 #define NSETEXCLUSIVEOR 9 #define COPY_LIST 1 #define COPY_ALIST 2 #define COPY_TREE 3 #define EVERY 1 #define SOME 2 #define NOTEVERY 3 #define NOTANY 4 /* Call directly LispObjectCompare() if possible */ #define FCODE(predicate) \ predicate == Oeql ? FEQL : \ predicate == Oequal ? FEQUAL : \ predicate == Oeq ? FEQ : \ predicate == Oequalp ? FEQUALP : 0 #define FCOMPARE(predicate, left, right, code) \ code == FEQ ? left == right : \ code ? LispObjectCompare(left, right, code) != NIL : \ APPLY2(predicate, left, right) != NIL #define FUNCTION_CHECK(predicate) \ if (FUNCTIONP(predicate)) \ predicate = (predicate)->data.atom->object #define CHECK_TEST_0() \ if (test != UNSPEC && test_not != UNSPEC) \ LispDestroy("%s: specify either :TEST or :TEST-NOT", \ STRFUN(builtin)) #define CHECK_TEST() \ CHECK_TEST_0(); \ if (test_not == UNSPEC) { \ if (test == UNSPEC) \ lambda = Oeql; \ else \ lambda = test; \ expect = 1; \ } \ else { \ lambda = test_not; \ expect = 0; \ } \ FUNCTION_CHECK(lambda); \ code = FCODE(lambda) static LispObj *LispAdjoin(LispBuiltin*, LispObj*, LispObj*, LispObj*, LispObj*, LispObj*); static LispObj *LispAssocOrMember(LispBuiltin*, int, int); static LispObj *LispEverySomeAnyNot(LispBuiltin*, int); static LispObj *LispFindOrPosition(LispBuiltin*, int, int); static LispObj *LispDeleteOrRemoveDuplicates(LispBuiltin*, int); static LispObj *LispDeleteRemoveXSubstitute(LispBuiltin*, int, int); static LispObj *LispListSet(LispBuiltin*, int); static LispObj *LispMapc(LispBuiltin*, int); static LispObj *LispMapl(LispBuiltin*, int); static LispObj *LispMapnconc(LispObj*); extern LispObj *LispRunSetf(LispArgList*, LispObj*, LispObj*, LispObj*); extern LispObj *LispRunSetfMacro(LispAtom*, LispObj*, LispObj*); static LispObj *LispMergeSort(LispObj*, LispObj*, LispObj*, int); static LispObj *LispXReverse(LispBuiltin*, int); static LispObj *LispCopyList(LispBuiltin*, LispObj*, int); static LispObj *LispValuesList(LispBuiltin*, int); static LispObj *LispTreeEqual(LispObj*, LispObj*, LispObj*, int); static LispDocType_t LispDocumentationType(LispBuiltin*, LispObj*); extern void LispSetAtomObjectProperty(LispAtom*, LispObj*); /* * Initialization */ LispObj *Oeq, *Oeql, *Oequal, *Oequalp, *Omake_array, *Kinitial_contents, *Osetf, *Ootherwise, *Oquote; LispObj *Ogensym_counter; Atom_id Svariable, Sstructure, Stype, Ssetf; /* * Implementation */ void LispCoreInit(void) { Oeq = STATIC_ATOM("EQ"); Oeql = STATIC_ATOM("EQL"); Oequal = STATIC_ATOM("EQUAL"); Oequalp = STATIC_ATOM("EQUALP"); Omake_array = STATIC_ATOM("MAKE-ARRAY"); Kinitial_contents = KEYWORD("INITIAL-CONTENTS"); Osetf = STATIC_ATOM("SETF"); Ootherwise = STATIC_ATOM("OTHERWISE"); LispExportSymbol(Ootherwise); Oquote = STATIC_ATOM("QUOTE"); LispExportSymbol(Oquote); Svariable = GETATOMID("VARIABLE"); Sstructure = GETATOMID("STRUCTURE"); Stype = GETATOMID("TYPE"); /* Create as a constant so that only the C code should change the value */ Ogensym_counter = STATIC_ATOM("*GENSYM-COUNTER*"); LispDefconstant(Ogensym_counter, FIXNUM(0), NIL); LispExportSymbol(Ogensym_counter); Ssetf = ATOMID(Osetf); } LispObj * Lisp_Acons(LispBuiltin *builtin) /* acons key datum alist */ { LispObj *key, *datum, *alist; alist = ARGUMENT(2); datum = ARGUMENT(1); key = ARGUMENT(0); return (CONS(CONS(key, datum), alist)); } static LispObj * LispAdjoin(LispBuiltin*builtin, LispObj *item, LispObj *list, LispObj *key, LispObj *test, LispObj *test_not) { GC_ENTER(); int code, expect, value; LispObj *lambda, *compare, *object; CHECK_LIST(list); CHECK_TEST(); if (key != UNSPEC) { item = APPLY1(key, item); /* Result is not guaranteed to be gc protected */ GC_PROTECT(item); } /* Check if item is not already in place */ for (object = list; CONSP(object); object = CDR(object)) { compare = CAR(object); if (key != UNSPEC) { compare = APPLY1(key, compare); GC_PROTECT(compare); value = FCOMPARE(lambda, item, compare, code); /* Unprotect compare... */ --lisp__data.protect.length; } else value = FCOMPARE(lambda, item, compare, code); if (value == expect) { /* Item is already in list */ GC_LEAVE(); return (list); } } GC_LEAVE(); return (CONS(item, list)); } LispObj * Lisp_Adjoin(LispBuiltin *builtin) /* adjoin item list &key key test test-not */ { LispObj *item, *list, *key, *test, *test_not; test_not = ARGUMENT(4); test = ARGUMENT(3); key = ARGUMENT(2); list = ARGUMENT(1); item = ARGUMENT(0); return (LispAdjoin(builtin, item, list, key, test, test_not)); } LispObj * Lisp_Append(LispBuiltin *builtin) /* append &rest lists */ { GC_ENTER(); LispObj *result, *cons, *list; LispObj *lists; lists = ARGUMENT(0); /* no arguments */ if (!CONSP(lists)) return (NIL); /* skip initial nil lists */ for (; CONSP(CDR(lists)) && CAR(lists) == NIL; lists = CDR(lists)) ; /* last argument is not copied (even if it is the single argument) */ if (!CONSP(CDR(lists))) return (CAR(lists)); /* make sure result is a list */ list = CAR(lists); CHECK_CONS(list); result = cons = CONS(CAR(list), NIL); GC_PROTECT(result); for (list = CDR(list); CONSP(list); list = CDR(list)) { RPLACD(cons, CONS(CAR(list), NIL)); cons = CDR(cons); } lists = CDR(lists); /* copy intermediate lists */ for (; CONSP(CDR(lists)); lists = CDR(lists)) { list = CAR(lists); if (list == NIL) continue; /* intermediate elements must be lists */ CHECK_CONS(list); for (; CONSP(list); list = CDR(list)) { RPLACD(cons, CONS(CAR(list), NIL)); cons = CDR(cons); } } /* add last element */ RPLACD(cons, CAR(lists)); GC_LEAVE(); return (result); } LispObj * Lisp_Aref(LispBuiltin *builtin) /* aref array &rest subscripts */ { long c, count, idx, seq; LispObj *obj, *dim; LispObj *array, *subscripts; subscripts = ARGUMENT(1); array = ARGUMENT(0); /* accept strings also */ if (STRINGP(array) && CONSP(subscripts) && CDR(subscripts) == NIL) { long offset, length = STRLEN(array); CHECK_INDEX(CAR(subscripts)); offset = FIXNUM_VALUE(CAR(subscripts)); if (offset >= length) LispDestroy("%s: index %ld too large for sequence length %ld", STRFUN(builtin), offset, length); return (SCHAR(THESTR(array)[offset])); } CHECK_ARRAY(array); for (count = 0, dim = subscripts, obj = array->data.array.dim; CONSP(dim); count++, dim = CDR(dim), obj = CDR(obj)) { if (count >= array->data.array.rank) LispDestroy("%s: too many subscripts %s", STRFUN(builtin), STROBJ(subscripts)); if (!INDEXP(CAR(dim)) || FIXNUM_VALUE(CAR(dim)) >= FIXNUM_VALUE(CAR(obj))) LispDestroy("%s: %s is out of range or a bad index", STRFUN(builtin), STROBJ(CAR(dim))); } if (count < array->data.array.rank) LispDestroy("%s: too few subscripts %s", STRFUN(builtin), STROBJ(subscripts)); for (count = seq = 0, dim = subscripts; CONSP(dim); dim = CDR(dim), seq++) { for (idx = 0, obj = array->data.array.dim; idx < seq; obj = CDR(obj), ++idx) ; for (c = 1, obj = CDR(obj); obj != NIL; obj = CDR(obj)) c *= FIXNUM_VALUE(CAR(obj)); count += c * FIXNUM_VALUE(CAR(dim)); } for (array = array->data.array.list; count > 0; array = CDR(array), count--) ; return (CAR(array)); } static LispObj * LispAssocOrMember(LispBuiltin *builtin, int function, int comparison) /* assoc item list &key test test-not key assoc-if predicate list &key key assoc-if-not predicate list &key key member item list &key test test-not key member-if predicate list &key key member-if-not predicate list &key key */ { int code = 0, expect, value; LispObj *lambda, *result, *compare; LispObj *item, *list, *test, *test_not, *key; if (comparison == NONE) { key = ARGUMENT(4); test_not = ARGUMENT(3); test = ARGUMENT(2); list = ARGUMENT(1); item = ARGUMENT(0); lambda = NIL; } else { key = ARGUMENT(2); list = ARGUMENT(1); lambda = ARGUMENT(0); test = test_not = UNSPEC; item = NIL; } if (list == NIL) return (NIL); CHECK_CONS(list); /* Resolve compare function, and expected result of comparison */ if (comparison == NONE) { CHECK_TEST(); } else expect = comparison == IFNOT ? 0 : 1; result = NIL; for (; CONSP(list); list = CDR(list)) { compare = CAR(list); if (function == ASSOC) { if (!CONSP(compare)) continue; compare = CAR(compare); } if (key != UNSPEC) compare = APPLY1(key, compare); if (comparison == NONE) value = FCOMPARE(lambda, item, compare, code); else value = APPLY1(lambda, compare) != NIL; if (value == expect) { result = list; if (function == ASSOC) result = CAR(result); break; } } if (function == MEMBER) { CHECK_LIST(list); } return (result); } LispObj * Lisp_Assoc(LispBuiltin *builtin) /* assoc item list &key test test-not key */ { return (LispAssocOrMember(builtin, ASSOC, NONE)); } LispObj * Lisp_AssocIf(LispBuiltin *builtin) /* assoc-if predicate list &key key */ { return (LispAssocOrMember(builtin, ASSOC, IF)); } LispObj * Lisp_AssocIfNot(LispBuiltin *builtin) /* assoc-if-not predicate list &key key */ { return (LispAssocOrMember(builtin, ASSOC, IFNOT)); } LispObj * Lisp_And(LispBuiltin *builtin) /* and &rest args */ { LispObj *result = T, *args; args = ARGUMENT(0); for (; CONSP(args); args = CDR(args)) { result = EVAL(CAR(args)); if (result == NIL) break; } return (result); } LispObj * Lisp_Apply(LispBuiltin *builtin) /* apply function arg &rest more-args */ { GC_ENTER(); LispObj *result, *arguments; LispObj *function, *arg, *more_args; more_args = ARGUMENT(2); arg = ARGUMENT(1); function = ARGUMENT(0); if (more_args == NIL) { CHECK_LIST(arg); arguments = arg; for (; CONSP(arg); arg = CDR(arg)) ; CHECK_LIST(arg); } else { LispObj *cons; CHECK_CONS(more_args); arguments = cons = CONS(arg, NIL); GC_PROTECT(arguments); for (arg = CDR(more_args); CONSP(arg); more_args = arg, arg = CDR(arg)) { RPLACD(cons, CONS(CAR(more_args), NIL)); cons = CDR(cons); } more_args = CAR(more_args); if (more_args != NIL) { for (arg = more_args; CONSP(arg); arg = CDR(arg)) ; CHECK_LIST(arg); RPLACD(cons, more_args); } } result = APPLY(function, arguments); GC_LEAVE(); return (result); } LispObj * Lisp_Atom(LispBuiltin *builtin) /* atom object */ { LispObj *object; object = ARGUMENT(0); return (CONSP(object) ? NIL : T); } LispObj * Lisp_Block(LispBuiltin *builtin) /* block name &rest body */ { int did_jump, *pdid_jump = &did_jump; LispObj *res, **pres = &res; LispBlock *block; LispObj *name, *body; body = ARGUMENT(1); name = ARGUMENT(0); if (!SYMBOLP(name) && name != NIL && name != T) LispDestroy("%s: %s cannot name a block", STRFUN(builtin), STROBJ(name)); *pres = NIL; *pdid_jump = 1; block = LispBeginBlock(name, LispBlockTag); if (setjmp(block->jmp) == 0) { for (; CONSP(body); body = CDR(body)) res = EVAL(CAR(body)); *pdid_jump = 0; } LispEndBlock(block); if (*pdid_jump) *pres = lisp__data.block.block_ret; return (res); } LispObj * Lisp_Boundp(LispBuiltin *builtin) /* boundp symbol */ { LispAtom *atom; LispObj *symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); atom = symbol->data.atom; if (atom->package == lisp__data.keyword || (atom->a_object && atom->property->value != UNBOUND)) return (T); return (NIL); } LispObj * Lisp_Butlast(LispBuiltin *builtin) /* butlast list &optional count */ { GC_ENTER(); long length, count; LispObj *result, *cons, *list, *ocount; ocount = ARGUMENT(1); list = ARGUMENT(0); CHECK_LIST(list); if (ocount == UNSPEC) count = 1; else { CHECK_INDEX(ocount); count = FIXNUM_VALUE(ocount); } length = LispLength(list); if (count == 0) return (list); else if (count >= length) return (NIL); length -= count + 1; result = cons = CONS(CAR(list), NIL); GC_PROTECT(result); for (list = CDR(list); length > 0; list = CDR(list), length--) { RPLACD(cons, CONS(CAR(list), NIL)); cons = CDR(cons); } GC_LEAVE(); return (result); } LispObj * Lisp_Nbutlast(LispBuiltin *builtin) /* nbutlast list &optional count */ { long length, count; LispObj *result, *list, *ocount; ocount = ARGUMENT(1); list = ARGUMENT(0); CHECK_LIST(list); if (ocount == UNSPEC) count = 1; else { CHECK_INDEX(ocount); count = FIXNUM_VALUE(ocount); } length = LispLength(list); if (count == 0) return (list); else if (count >= length) return (NIL); length -= count + 1; result = list; for (; length > 0; list = CDR(list), length--) ; RPLACD(list, NIL); return (result); } LispObj * Lisp_Car(LispBuiltin *builtin) /* car list */ { LispObj *list, *result = NULL; list = ARGUMENT(0); if (list == NIL) result = NIL; else { CHECK_CONS(list); result = CAR(list); } return (result); } LispObj * Lisp_Case(LispBuiltin *builtin) /* case keyform &rest body */ { LispObj *result, *code, *keyform, *body, *form; body = ARGUMENT(1); keyform = ARGUMENT(0); result = NIL; keyform = EVAL(keyform); for (; CONSP(body); body = CDR(body)) { code = CAR(body); CHECK_CONS(code); form = CAR(code); if (form == T || form == Ootherwise) { if (CONSP(CDR(body))) LispDestroy("%s: %s must be the last clause", STRFUN(builtin), STROBJ(CAR(code))); result = CDR(code); break; } else if (CONSP(form)) { for (; CONSP(form); form = CDR(form)) if (XEQL(keyform, CAR(form)) == T) { result = CDR(code); break; } if (CONSP(form)) /* if found match */ break; } else if (XEQL(keyform, form) == T) { result = CDR(code); break; } } for (body = result; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); return (result); } LispObj * Lisp_Catch(LispBuiltin *builtin) /* catch tag &rest body */ { int did_jump, *pdid_jump = &did_jump; LispObj *res, **pres = &res; LispBlock *block; LispObj *tag, *body; body = ARGUMENT(1); tag = ARGUMENT(0); *pres = NIL; *pdid_jump = 1; block = LispBeginBlock(tag, LispBlockCatch); if (setjmp(block->jmp) == 0) { for (; CONSP(body); body = CDR(body)) res = EVAL(CAR(body)); *pdid_jump = 0; } LispEndBlock(block); if (*pdid_jump) *pres = lisp__data.block.block_ret; return (res); } LispObj * Lisp_Coerce(LispBuiltin *builtin) /* coerce object result-type */ { LispObj *object, *result_type; result_type = ARGUMENT(1); object = ARGUMENT(0); return (LispCoerce(builtin, object, result_type)); } LispObj * Lisp_Cdr(LispBuiltin *builtin) /* cdr list */ { LispObj *list, *result = NULL; list = ARGUMENT(0); if (list == NIL) result = NIL; else { CHECK_CONS(list); result = CDR(list); } return (result); } LispObj * Lisp_C_r(LispBuiltin *builtin) /* c[ad]{2,4}r list */ { char *desc; LispObj *list, *result = NULL; list = ARGUMENT(0); result = list; desc = STRFUN(builtin); while (desc[1] != 'R') ++desc; while (*desc != 'C') { if (result == NIL) break; CHECK_CONS(result); result = *desc == 'A' ? CAR(result) : CDR(result); --desc; } return (result); } LispObj * Lisp_Cond(LispBuiltin *builtin) /* cond &rest body */ { LispObj *result, *code, *body; body = ARGUMENT(0); result = NIL; for (; CONSP(body); body = CDR(body)) { code = CAR(body); CHECK_CONS(code); result = EVAL(CAR(code)); if (result == NIL) continue; for (code = CDR(code); CONSP(code); code = CDR(code)) result = EVAL(CAR(code)); break; } return (result); } static LispObj * LispCopyList(LispBuiltin *builtin, LispObj *list, int function) { GC_ENTER(); LispObj *result, *cons; if (list == NIL) return (list); CHECK_CONS(list); result = cons = CONS(NIL, NIL); GC_PROTECT(result); if (CONSP(CAR(list))) { switch (function) { case COPY_LIST: RPLACA(result, CAR(list)); break; case COPY_ALIST: RPLACA(result, CONS(CAR(CAR(list)), CDR(CAR(list)))); break; case COPY_TREE: RPLACA(result, LispCopyList(builtin, CAR(list), COPY_TREE)); break; } } else RPLACA(result, CAR(list)); for (list = CDR(list); CONSP(list); list = CDR(list)) { CDR(cons) = CONS(NIL, NIL); cons = CDR(cons); if (CONSP(CAR(list))) { switch (function) { case COPY_LIST: RPLACA(cons, CAR(list)); break; case COPY_ALIST: RPLACA(cons, CONS(CAR(CAR(list)), CDR(CAR(list)))); break; case COPY_TREE: RPLACA(cons, LispCopyList(builtin, CAR(list), COPY_TREE)); break; } } else RPLACA(cons, CAR(list)); } /* in case list is dotted */ RPLACD(cons, list); GC_LEAVE(); return (result); } LispObj * Lisp_CopyAlist(LispBuiltin *builtin) /* copy-alist list */ { LispObj *list; list = ARGUMENT(0); return (LispCopyList(builtin, list, COPY_ALIST)); } LispObj * Lisp_CopyList(LispBuiltin *builtin) /* copy-list list */ { LispObj *list; list = ARGUMENT(0); return (LispCopyList(builtin, list, COPY_LIST)); } LispObj * Lisp_CopyTree(LispBuiltin *builtin) /* copy-tree list */ { LispObj *list; list = ARGUMENT(0); return (LispCopyList(builtin, list, COPY_TREE)); } LispObj * Lisp_Cons(LispBuiltin *builtin) /* cons car cdr */ { LispObj *car, *cdr; cdr = ARGUMENT(1); car = ARGUMENT(0); return (CONS(car, cdr)); } LispObj * Lisp_Consp(LispBuiltin *builtin) /* consp object */ { LispObj *object; object = ARGUMENT(0); return (CONSP(object) ? T : NIL); } LispObj * Lisp_Constantp(LispBuiltin *builtin) /* constantp form &optional environment */ { LispObj *form; form = ARGUMENT(0); /* not all self-evaluating objects are considered constants */ if (!POINTERP(form) || NUMBERP(form) || XQUOTEP(form) || (XCONSP(form) && CAR(form) == Oquote) || (XSYMBOLP(form) && form->data.atom->constant) || XSTRINGP(form) || XARRAYP(form)) return (T); return (NIL); } LispObj * Lisp_Defconstant(LispBuiltin *builtin) /* defconstant name initial-value &optional documentation */ { LispObj *name, *initial_value, *documentation; documentation = ARGUMENT(2); initial_value = ARGUMENT(1); name = ARGUMENT(0); CHECK_SYMBOL(name); if (documentation != UNSPEC) { CHECK_STRING(documentation); } else documentation = NIL; LispDefconstant(name, EVAL(initial_value), documentation); return (name); } LispObj * Lisp_Defmacro(LispBuiltin *builtin) /* defmacro name lambda-list &rest body */ { LispArgList *alist; LispObj *lambda, *name, *lambda_list, *body; body = ARGUMENT(2); lambda_list = ARGUMENT(1); name = ARGUMENT(0); CHECK_SYMBOL(name); alist = LispCheckArguments(LispMacro, lambda_list, ATOMID(name)->value, 0); if (CONSP(body) && STRINGP(CAR(body))) { LispAddDocumentation(name, CAR(body), LispDocFunction); body = CDR(body); } lambda_list = LispListProtectedArguments(alist); lambda = LispNewLambda(name, body, lambda_list, LispMacro); if (name->data.atom->a_builtin || name->data.atom->a_compiled) { if (name->data.atom->a_builtin) { ERROR_CHECK_SPECIAL_FORM(name->data.atom); } /* redefining these may cause surprises if bytecode * compiled functions references them */ LispWarning("%s: %s is being redefined", STRFUN(builtin), ATOMID(name)->value); LispRemAtomBuiltinProperty(name->data.atom); } LispSetAtomFunctionProperty(name->data.atom, lambda, alist); LispUseArgList(alist); return (name); } LispObj * Lisp_Defun(LispBuiltin *builtin) /* defun name lambda-list &rest body */ { LispArgList *alist; LispObj *lambda, *name, *lambda_list, *body; body = ARGUMENT(2); lambda_list = ARGUMENT(1); name = ARGUMENT(0); CHECK_SYMBOL(name); alist = LispCheckArguments(LispFunction, lambda_list, ATOMID(name)->value, 0); if (CONSP(body) && STRINGP(CAR(body))) { LispAddDocumentation(name, CAR(body), LispDocFunction); body = CDR(body); } lambda_list = LispListProtectedArguments(alist); lambda = LispNewLambda(name, body, lambda_list, LispFunction); if (name->data.atom->a_builtin || name->data.atom->a_compiled) { if (name->data.atom->a_builtin) { ERROR_CHECK_SPECIAL_FORM(name->data.atom); } /* redefining these may cause surprises if bytecode * compiled functions references them */ LispWarning("%s: %s is being redefined", STRFUN(builtin), ATOMID(name)->value); LispRemAtomBuiltinProperty(name->data.atom); } LispSetAtomFunctionProperty(name->data.atom, lambda, alist); LispUseArgList(alist); return (name); } LispObj * Lisp_Defsetf(LispBuiltin *builtin) /* defsetf function lambda-list &rest body */ { LispArgList *alist; LispObj *obj; LispObj *lambda, *function, *lambda_list, *store, *body; body = ARGUMENT(2); lambda_list = ARGUMENT(1); function = ARGUMENT(0); CHECK_SYMBOL(function); if (body == NIL || (CONSP(body) && STRINGP(CAR(body)))) { if (!SYMBOLP(lambda_list)) LispDestroy("%s: syntax error %s %s", STRFUN(builtin), STROBJ(function), STROBJ(lambda_list)); if (body != NIL) LispAddDocumentation(function, CAR(body), LispDocSetf); LispSetAtomSetfProperty(function->data.atom, lambda_list, NULL); return (function); } alist = LispCheckArguments(LispSetf, lambda_list, ATOMID(function)->value, 0); store = CAR(body); if (!CONSP(store)) LispDestroy("%s: %s is a bad store value", STRFUN(builtin), STROBJ(store)); for (obj = store; CONSP(obj); obj = CDR(obj)) { CHECK_SYMBOL(CAR(obj)); } body = CDR(body); if (CONSP(body) && STRINGP(CAR(body))) { LispAddDocumentation(function, CAR(body), LispDocSetf); body = CDR(body); } lambda = LispNewLambda(function, body, store, LispSetf); LispSetAtomSetfProperty(function->data.atom, lambda, alist); LispUseArgList(alist); return (function); } LispObj * Lisp_Defparameter(LispBuiltin *builtin) /* defparameter name initial-value &optional documentation */ { LispObj *name, *initial_value, *documentation; documentation = ARGUMENT(2); initial_value = ARGUMENT(1); name = ARGUMENT(0); CHECK_SYMBOL(name); if (documentation != UNSPEC) { CHECK_STRING(documentation); } else documentation = NIL; LispProclaimSpecial(name, EVAL(initial_value), documentation); return (name); } LispObj * Lisp_Defvar(LispBuiltin *builtin) /* defvar name &optional initial-value documentation */ { LispObj *name, *initial_value, *documentation; documentation = ARGUMENT(2); initial_value = ARGUMENT(1); name = ARGUMENT(0); CHECK_SYMBOL(name); if (documentation != UNSPEC) { CHECK_STRING(documentation); } else documentation = NIL; LispProclaimSpecial(name, initial_value != UNSPEC ? EVAL(initial_value) : NULL, documentation); return (name); } LispObj * Lisp_Delete(LispBuiltin *builtin) /* delete item sequence &key from-end test test-not start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, DELETE, NONE)); } LispObj * Lisp_DeleteIf(LispBuiltin *builtin) /* delete-if predicate sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, DELETE, IF)); } LispObj * Lisp_DeleteIfNot(LispBuiltin *builtin) /* delete-if-not predicate sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, DELETE, IFNOT)); } LispObj * Lisp_DeleteDuplicates(LispBuiltin *builtin) /* delete-duplicates sequence &key from-end test test-not start end key */ { return (LispDeleteOrRemoveDuplicates(builtin, DELETE)); } LispObj * Lisp_Do(LispBuiltin *builtin) /* do init test &rest body */ { return (LispDo(builtin, 0)); } LispObj * Lisp_DoP(LispBuiltin *builtin) /* do* init test &rest body */ { return (LispDo(builtin, 1)); } static LispDocType_t LispDocumentationType(LispBuiltin *builtin, LispObj *type) { Atom_id atom; LispDocType_t doc_type = LispDocVariable; CHECK_SYMBOL(type); atom = ATOMID(type); if (atom == Svariable) doc_type = LispDocVariable; else if (atom == Sfunction) doc_type = LispDocFunction; else if (atom == Sstructure) doc_type = LispDocStructure; else if (atom == Stype) doc_type = LispDocType; else if (atom == Ssetf) doc_type = LispDocSetf; else { LispDestroy("%s: unknown documentation type %s", STRFUN(builtin), STROBJ(type)); /*NOTREACHED*/ } return (doc_type); } LispObj * Lisp_Documentation(LispBuiltin *builtin) /* documentation symbol type */ { LispObj *symbol, *type; type = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); /* type is checked in LispDocumentationType() */ return (LispGetDocumentation(symbol, LispDocumentationType(builtin, type))); } LispObj * Lisp_DoList(LispBuiltin *builtin) { return (LispDoListTimes(builtin, 0)); } LispObj * Lisp_DoTimes(LispBuiltin *builtin) { return (LispDoListTimes(builtin, 1)); } LispObj * Lisp_Elt(LispBuiltin *builtin) /* elt sequence index svref sequence index */ { long offset, length; LispObj *result, *sequence, *oindex; oindex = ARGUMENT(1); sequence = ARGUMENT(0); length = LispLength(sequence); CHECK_INDEX(oindex); offset = FIXNUM_VALUE(oindex); if (offset >= length) LispDestroy("%s: index %ld too large for sequence length %ld", STRFUN(builtin), offset, length); if (STRINGP(sequence)) result = SCHAR(THESTR(sequence)[offset]); else { if (ARRAYP(sequence)) sequence = sequence->data.array.list; for (; offset > 0; offset--, sequence = CDR(sequence)) ; result = CAR(sequence); } return (result); } LispObj * Lisp_Endp(LispBuiltin *builtin) /* endp object */ { LispObj *object; object = ARGUMENT(0); if (object == NIL) return (T); CHECK_CONS(object); return (NIL); } LispObj * Lisp_Eq(LispBuiltin *builtin) /* eq left right */ { LispObj *left, *right; right = ARGUMENT(1); left = ARGUMENT(0); return (XEQ(left, right)); } LispObj * Lisp_Eql(LispBuiltin *builtin) /* eql left right */ { LispObj *left, *right; right = ARGUMENT(1); left = ARGUMENT(0); return (XEQL(left, right)); } LispObj * Lisp_Equal(LispBuiltin *builtin) /* equal left right */ { LispObj *left, *right; right = ARGUMENT(1); left = ARGUMENT(0); return (XEQUAL(left, right)); } LispObj * Lisp_Equalp(LispBuiltin *builtin) /* equalp left right */ { LispObj *left, *right; right = ARGUMENT(1); left = ARGUMENT(0); return (XEQUALP(left, right)); } LispObj * Lisp_Error(LispBuiltin *builtin) /* error control-string &rest arguments */ { LispObj *string, *arglist; LispObj *control_string, *arguments; arguments = ARGUMENT(1); control_string = ARGUMENT(0); arglist = CONS(NIL, CONS(control_string, arguments)); GC_PROTECT(arglist); string = APPLY(Oformat, arglist); LispDestroy("%s", THESTR(string)); /*NOTREACHED*/ /* No need to call GC_ENTER() and GC_LEAVE() macros */ return (NIL); } LispObj * Lisp_Eval(LispBuiltin *builtin) /* eval form */ { int lex; LispObj *form, *result; form = ARGUMENT(0); /* make sure eval form will not access local variables */ lex = lisp__data.env.lex; lisp__data.env.lex = lisp__data.env.length; result = EVAL(form); lisp__data.env.lex = lex; return (result); } static LispObj * LispEverySomeAnyNot(LispBuiltin *builtin, int function) /* every predicate sequence &rest more-sequences some predicate sequence &rest more-sequences notevery predicate sequence &rest more-sequences notany predicate sequence &rest more-sequences */ { GC_ENTER(); long i, j, length, count; LispObj *result, *list, *item, *arguments, *acons, *value; SeqInfo stk[8], *seqs; LispObj *predicate, *sequence, *more_sequences; more_sequences = ARGUMENT(2); sequence = ARGUMENT(1); predicate = ARGUMENT(0); count = 1; length = LispLength(sequence); for (list = more_sequences; CONSP(list); list = CDR(list), count++) { i = LispLength(CAR(list)); if (i < length) length = i; } result = function == EVERY || function == NOTANY ? T : NIL; /* if at least one sequence has length zero */ if (length == 0) return (result); if (count > sizeof(stk) / sizeof(stk[0])) seqs = LispMalloc(count * sizeof(SeqInfo)); else seqs = &stk[0]; /* build information about sequences */ SETSEQ(seqs[0], sequence); for (i = 1, list = more_sequences; CONSP(list); list = CDR(list), i++) { item = CAR(list); SETSEQ(seqs[i], item); } /* prepare argument list */ arguments = acons = CONS(NIL, NIL); GC_PROTECT(arguments); for (i = 1; i < count; i++) { RPLACD(acons, CONS(NIL, NIL)); acons = CDR(acons); } /* loop applying predicate in sequence elements */ for (i = 0; i < length; i++) { /* build argument list */ for (acons = arguments, j = 0; j < count; acons = CDR(acons), j++) { if (seqs[j].type == LispString_t) item = SCHAR(*seqs[j].data.string++); else { item = CAR(seqs[j].data.list); seqs[j].data.list = CDR(seqs[j].data.list); } RPLACA(acons, item); } /* apply predicate */ value = APPLY(predicate, arguments); /* check if needs to terminate loop */ if (value == NIL) { if (function == EVERY) { result = NIL; break; } if (function == NOTEVERY) { result = T; break; } } else { if (function == SOME) { result = value; break; } if (function == NOTANY) { result = NIL; break; } } } GC_LEAVE(); if (seqs != &stk[0]) LispFree(seqs); return (result); } LispObj * Lisp_Every(LispBuiltin *builtin) /* every predicate sequence &rest more-sequences */ { return (LispEverySomeAnyNot(builtin, EVERY)); } LispObj * Lisp_Some(LispBuiltin *builtin) /* some predicate sequence &rest more-sequences */ { return (LispEverySomeAnyNot(builtin, SOME)); } LispObj * Lisp_Notevery(LispBuiltin *builtin) /* notevery predicate sequence &rest more-sequences */ { return (LispEverySomeAnyNot(builtin, NOTEVERY)); } LispObj * Lisp_Notany(LispBuiltin *builtin) /* notany predicate sequence &rest more-sequences */ { return (LispEverySomeAnyNot(builtin, NOTANY)); } LispObj * Lisp_Fboundp(LispBuiltin *builtin) /* fboundp symbol */ { LispAtom *atom; LispObj *symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); atom = symbol->data.atom; if (atom->a_function || atom->a_builtin || atom->a_compiled) return (T); return (NIL); } LispObj * Lisp_Find(LispBuiltin *builtin) /* find item sequence &key from-end test test-not start end key */ { return (LispFindOrPosition(builtin, FIND, NONE)); } LispObj * Lisp_FindIf(LispBuiltin *builtin) /* find-if predicate sequence &key from-end start end key */ { return (LispFindOrPosition(builtin, FIND, IF)); } LispObj * Lisp_FindIfNot(LispBuiltin *builtin) /* find-if-not predicate sequence &key from-end start end key */ { return (LispFindOrPosition(builtin, FIND, IFNOT)); } LispObj * Lisp_Fill(LispBuiltin *builtin) /* fill sequence item &key start end */ { long i, start, end, length; LispObj *sequence, *item, *ostart, *oend; oend = ARGUMENT(3); ostart = ARGUMENT(2); item = ARGUMENT(1); sequence = ARGUMENT(0); LispCheckSequenceStartEnd(builtin, sequence, ostart, oend, &start, &end, &length); if (STRINGP(sequence)) { int ch; char *string = THESTR(sequence); CHECK_STRING_WRITABLE(sequence); CHECK_SCHAR(item); ch = SCHAR_VALUE(item); for (i = start; i < end; i++) string[i] = ch; } else { LispObj *list; if (CONSP(sequence)) list = sequence; else list = sequence->data.array.list; for (i = 0; i < start; i++, list = CDR(list)) ; for (; i < end; i++, list = CDR(list)) RPLACA(list, item); } return (sequence); } LispObj * Lisp_Fmakunbound(LispBuiltin *builtin) /* fmkaunbound symbol */ { LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); if (symbol->data.atom->a_function) LispRemAtomFunctionProperty(symbol->data.atom); else if (symbol->data.atom->a_builtin) LispRemAtomBuiltinProperty(symbol->data.atom); else if (symbol->data.atom->a_compiled) LispRemAtomCompiledProperty(symbol->data.atom); return (symbol); } LispObj * Lisp_Funcall(LispBuiltin *builtin) /* funcall function &rest arguments */ { LispObj *result; LispObj *function, *arguments; arguments = ARGUMENT(1); function = ARGUMENT(0); result = APPLY(function, arguments); return (result); } LispObj * Lisp_Functionp(LispBuiltin *builtin) /* functionp object */ { LispObj *object; object = ARGUMENT(0); return (FUNCTIONP(object) || LAMBDAP(object) ? T : NIL); } LispObj * Lisp_Get(LispBuiltin *builtin) /* get symbol indicator &optional default */ { LispObj *result; LispObj *symbol, *indicator, *defalt; defalt = ARGUMENT(2); indicator = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); result = LispGetAtomProperty(symbol->data.atom, indicator); if (result != NIL) result = CAR(result); else result = defalt == UNSPEC ? NIL : defalt; return (result); } /* * ext::getenv */ LispObj * Lisp_Getenv(LispBuiltin *builtin) /* getenv name */ { char *value; LispObj *name; name = ARGUMENT(0); CHECK_STRING(name); value = getenv(THESTR(name)); return (value ? STRING(value) : NIL); } LispObj * Lisp_Gc(LispBuiltin *builtin) /* gc &optional car cdr */ { LispObj *car, *cdr; cdr = ARGUMENT(1); car = ARGUMENT(0); LispGC(car, cdr); return (NIL); } LispObj * Lisp_Gensym(LispBuiltin *builtin) /* gensym &optional arg */ { char *preffix = "G", name[132]; long counter = LONGINT_VALUE(Ogensym_counter->data.atom->property->value); LispObj *symbol; LispObj *arg; arg = ARGUMENT(0); if (arg != UNSPEC) { if (STRINGP(arg)) preffix = THESTR(arg); else { CHECK_INDEX(arg); counter = FIXNUM_VALUE(arg); } } snprintf(name, sizeof(name), "%s%ld", preffix, counter); if (strlen(name) >= 128) LispDestroy("%s: name %s too long", STRFUN(builtin), name); Ogensym_counter->data.atom->property->value = INTEGER(counter + 1); symbol = UNINTERNED_ATOM(name); symbol->data.atom->unreadable = !LispCheckAtomString(name); return (symbol); } LispObj * Lisp_Go(LispBuiltin *builtin) /* go tag */ { unsigned blevel = lisp__data.block.block_level; LispObj *tag; tag = ARGUMENT(0); while (blevel) { LispBlock *block = lisp__data.block.block[--blevel]; if (block->type == LispBlockClosure) /* if reached a function call */ break; if (block->type == LispBlockBody) { lisp__data.block.block_ret = tag; LispBlockUnwind(block); BLOCKJUMP(block); } } LispDestroy("%s: no visible tagbody for %s", STRFUN(builtin), STROBJ(tag)); /*NOTREACHED*/ return (NIL); } LispObj * Lisp_If(LispBuiltin *builtin) /* if test then &optional else */ { LispObj *result, *test, *then, *oelse; oelse = ARGUMENT(2); then = ARGUMENT(1); test = ARGUMENT(0); test = EVAL(test); if (test != NIL) result = EVAL(then); else if (oelse != UNSPEC) result = EVAL(oelse); else result = NIL; return (result); } LispObj * Lisp_IgnoreErrors(LispBuiltin *builtin) /* ignore-erros &rest body */ { LispObj *result; int i, jumped; LispBlock *block; /* interpreter state */ GC_ENTER(); int stack, lex, length; /* memory allocation */ int mem_level; void **mem; LispObj *body; body = ARGUMENT(0); /* Save environment information */ stack = lisp__data.stack.length; lex = lisp__data.env.lex; length = lisp__data.env.length; /* Save memory allocation information */ mem_level = lisp__data.mem.level; mem = LispMalloc(mem_level * sizeof(void*)); memcpy(mem, lisp__data.mem.mem, mem_level * sizeof(void*)); ++lisp__data.ignore_errors; result = NIL; jumped = 1; block = LispBeginBlock(NIL, LispBlockProtect); if (setjmp(block->jmp) == 0) { for (; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); jumped = 0; } LispEndBlock(block); if (!lisp__data.destroyed && jumped) result = lisp__data.block.block_ret; if (lisp__data.destroyed) { /* Restore environment */ lisp__data.stack.length = stack; lisp__data.env.lex = lex; lisp__data.env.head = lisp__data.env.length = length; GC_LEAVE(); /* Check for possible leaks due to ignoring errors */ for (i = 0; i < mem_level; i++) { if (lisp__data.mem.mem[i] && mem[i] != lisp__data.mem.mem[i]) LispFree(lisp__data.mem.mem[i]); } for (; i < lisp__data.mem.level; i++) { if (lisp__data.mem.mem[i]) LispFree(lisp__data.mem.mem[i]); } lisp__data.destroyed = 0; result = NIL; RETURN_COUNT = 1; RETURN(0) = lisp__data.error_condition; } LispFree(mem); --lisp__data.ignore_errors; return (result); } LispObj * Lisp_Intersection(LispBuiltin *builtin) /* intersection list1 list2 &key test test-not key */ { return (LispListSet(builtin, INTERSECTION)); } LispObj * Lisp_Nintersection(LispBuiltin *builtin) /* nintersection list1 list2 &key test test-not key */ { return (LispListSet(builtin, NINTERSECTION)); } LispObj * Lisp_Keywordp(LispBuiltin *builtin) /* keywordp object */ { LispObj *object; object = ARGUMENT(0); return (KEYWORDP(object) ? T : NIL); } LispObj * Lisp_Lambda(LispBuiltin *builtin) /* lambda lambda-list &rest body */ { GC_ENTER(); LispObj *name; LispArgList *alist; LispObj *lambda, *lambda_list, *body; body = ARGUMENT(1); lambda_list = ARGUMENT(0); alist = LispCheckArguments(LispLambda, lambda_list, Snil->value, 0); name = OPAQUE(alist, LispArgList_t); lambda_list = LispListProtectedArguments(alist); GC_PROTECT(name); GC_PROTECT(lambda_list); lambda = LispNewLambda(name, body, lambda_list, LispLambda); LispUseArgList(alist); GC_LEAVE(); return (lambda); } LispObj * Lisp_Last(LispBuiltin *builtin) /* last list &optional count */ { long count, length; LispObj *list, *ocount; ocount = ARGUMENT(1); list = ARGUMENT(0); if (!CONSP(list)) return (list); length = LispLength(list); if (ocount == UNSPEC) count = 1; else { CHECK_INDEX(ocount); count = FIXNUM_VALUE(ocount); } if (count >= length) return (list); length -= count; for (; length > 0; length--) list = CDR(list); return (list); } LispObj * Lisp_Length(LispBuiltin *builtin) /* length sequence */ { LispObj *sequence; sequence = ARGUMENT(0); return (FIXNUM(LispLength(sequence))); } LispObj * Lisp_Let(LispBuiltin *builtin) /* let init &rest body */ { GC_ENTER(); int head = lisp__data.env.length; LispObj *init, *body, *pair, *result, *list, *cons = NIL; body = ARGUMENT(1); init = ARGUMENT(0); CHECK_LIST(init); for (list = NIL; CONSP(init); init = CDR(init)) { LispObj *symbol, *value; pair = CAR(init); if (SYMBOLP(pair)) { symbol = pair; value = NIL; } else { CHECK_CONS(pair); symbol = CAR(pair); CHECK_SYMBOL(symbol); pair = CDR(pair); if (CONSP(pair)) { value = CAR(pair); if (CDR(pair) != NIL) LispDestroy("%s: too much arguments to initialize %s", STRFUN(builtin), STROBJ(symbol)); value = EVAL(value); } else value = NIL; } pair = CONS(symbol, value); if (list == NIL) { list = cons = CONS(pair, NIL); GC_PROTECT(list); } else { RPLACD(cons, CONS(pair, NIL)); cons = CDR(cons); } } /* Add variables */ for (; CONSP(list); list = CDR(list)) { pair = CAR(list); CHECK_CONSTANT(CAR(pair)); LispAddVar(CAR(pair), CDR(pair)); ++lisp__data.env.head; } /* Values of symbols are now protected */ GC_LEAVE(); /* execute body */ for (result = NIL; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); lisp__data.env.head = lisp__data.env.length = head; return (result); } LispObj * Lisp_LetP(LispBuiltin *builtin) /* let* init &rest body */ { int head = lisp__data.env.length; LispObj *init, *body, *pair, *result; body = ARGUMENT(1); init = ARGUMENT(0); CHECK_LIST(init); for (; CONSP(init); init = CDR(init)) { LispObj *symbol, *value; pair = CAR(init); if (SYMBOLP(pair)) { symbol = pair; value = NIL; } else { CHECK_CONS(pair); symbol = CAR(pair); CHECK_SYMBOL(symbol); pair = CDR(pair); if (CONSP(pair)) { value = CAR(pair); if (CDR(pair) != NIL) LispDestroy("%s: too much arguments to initialize %s", STRFUN(builtin), STROBJ(symbol)); value = EVAL(value); } else value = NIL; } CHECK_CONSTANT(symbol); LispAddVar(symbol, value); ++lisp__data.env.head; } /* execute body */ for (result = NIL; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); lisp__data.env.head = lisp__data.env.length = head; return (result); } LispObj * Lisp_List(LispBuiltin *builtin) /* list &rest args */ { LispObj *args; args = ARGUMENT(0); return (args); } LispObj * Lisp_ListP(LispBuiltin *builtin) /* list* object &rest more-objects */ { GC_ENTER(); LispObj *result, *cons; LispObj *object, *more_objects; more_objects = ARGUMENT(1); object = ARGUMENT(0); if (!CONSP(more_objects)) return (object); result = cons = CONS(object, CAR(more_objects)); GC_PROTECT(result); for (more_objects = CDR(more_objects); CONSP(more_objects); more_objects = CDR(more_objects)) { object = CAR(more_objects); RPLACD(cons, CONS(CDR(cons), object)); cons = CDR(cons); } GC_LEAVE(); return (result); } /* "classic" list-length */ LispObj * Lisp_ListLength(LispBuiltin *builtin) /* list-length list */ { long length; LispObj *fast, *slow; LispObj *list; list = ARGUMENT(0); CHECK_LIST(list); for (fast = slow = list, length = 0; CONSP(slow); slow = CDR(slow), length += 2) { if (fast == NIL) break; CHECK_CONS(fast); fast = CDR(fast); if (fast == NIL) { ++length; break; } CHECK_CONS(fast); fast = CDR(fast); if (slow == fast) /* circular list */ return (NIL); } return (FIXNUM(length)); } LispObj * Lisp_Listp(LispBuiltin *builtin) /* listp object */ { LispObj *object; object = ARGUMENT(0); return (object == NIL || CONSP(object) ? T : NIL); } static LispObj * LispListSet(LispBuiltin *builtin, int function) /* intersection list1 list2 &key test test-not key nintersection list1 list2 &key test test-not key set-difference list1 list2 &key test test-not key nset-difference list1 list2 &key test test-not key set-exclusive-or list1 list2 &key test test-not key nset-exclusive-or list1 list2 &key test test-not key subsetp list1 list2 &key test test-not key union list1 list2 &key test test-not key nunion list1 list2 &key test test-not key */ { GC_ENTER(); int code, expect, value, inplace, check_list2, intersection, setdifference, xunion, setexclusiveor; LispObj *lambda, *result, *cmp, *cmp1, *cmp2, *item, *clist1, *clist2, *cons, *cdr; LispObj *list1, *list2, *test, *test_not, *key; key = ARGUMENT(4); test_not = ARGUMENT(3); test = ARGUMENT(2); list2 = ARGUMENT(1); list1 = ARGUMENT(0); /* Check if arguments are valid lists */ CHECK_LIST(list1); CHECK_LIST(list2); setdifference = intersection = xunion = setexclusiveor = inplace = 0; switch (function) { case NSETDIFFERENCE: inplace = 1; case SETDIFFERENCE: setdifference = 1; break; case NINTERSECTION: inplace = 1; case INTERSECTION: intersection = 1; break; case NUNION: inplace = 1; case UNION: xunion = 1; break; case NSETEXCLUSIVEOR: inplace = 1; case SETEXCLUSIVEOR: setexclusiveor = 1; break; } /* Check for fast return */ if (list1 == NIL) return (setdifference || intersection ? NIL : function == SUBSETP ? T : list2); if (list2 == NIL) return (intersection || xunion || function == SUBSETP ? NIL : list1); CHECK_TEST(); clist1 = cdr = NIL; /* Make a copy of list2 with the key predicate applied */ if (key != UNSPEC) { result = cons = CONS(APPLY1(key, CAR(list2)), NIL); GC_PROTECT(result); for (cmp2 = CDR(list2); CONSP(cmp2); cmp2 = CDR(cmp2)) { item = APPLY1(key, CAR(cmp2)); RPLACD(cons, CONS(APPLY1(key, CAR(cmp2)), NIL)); cons = CDR(cons); } /* check if list2 is a proper list */ CHECK_LIST(cmp2); clist2 = result; check_list2 = 0; } else { clist2 = list2; check_list2 = 1; } result = cons = NIL; /* Compare elements of lists * Logic: * UNION * 1) Walk list1 and if CAR(list1) not in list2, add it to result * 2) Add list2 to result * INTERSECTION * 1) Walk list1 and if CAR(list1) in list2, add it to result * SET-DIFFERENCE * 1) Walk list1 and if CAR(list1) not in list2, add it to result * SET-EXCLUSIVE-OR * 1) Walk list1 and if CAR(list1) not in list2, add it to result * 2) Walk list2 and if CAR(list2) not in list1, add it to result * SUBSETP * 1) Walk list1 and if CAR(list1) not in list2, return NIL * 2) Return T */ value = 0; for (cmp1 = list1; CONSP(cmp1); cmp1 = CDR(cmp1)) { item = CAR(cmp1); /* Apply key predicate if required */ if (key != UNSPEC) { cmp = APPLY1(key, item); if (setexclusiveor) { if (clist1 == NIL) { clist1 = cdr = CONS(cmp, NIL); GC_PROTECT(clist1); } else { RPLACD(cdr, CONS(cmp, NIL)); cdr = CDR(cdr); } } } else cmp = item; /* Compare against list2 */ for (cmp2 = clist2; CONSP(cmp2); cmp2 = CDR(cmp2)) { value = FCOMPARE(lambda, cmp, CAR(cmp2), code); if (value == expect) break; } if (check_list2 && value != expect) { /* check if list2 is a proper list */ CHECK_LIST(cmp2); check_list2 = 0; } if (function == SUBSETP) { /* Element of list1 not in list2? */ if (value != expect) { GC_LEAVE(); return (NIL); } } /* If need to add item to result */ else if (((setdifference || xunion || setexclusiveor) && value != expect) || (intersection && value == expect)) { if (inplace) { if (result == NIL) result = cons = cmp1; else { if (setexclusiveor) { /* don't remove elements yet, will need * to check agains't list2 later */ for (cmp2 = cons; CDR(cmp2) != cmp1; cmp2 = CDR(cmp2)) ; if (cmp2 != cons) { RPLACD(cmp2, list1); list1 = cmp2; } } RPLACD(cons, cmp1); cons = cmp1; } } else { if (result == NIL) { result = cons = CONS(item, NIL); GC_PROTECT(result); } else { RPLACD(cons, CONS(item, NIL)); cons = CDR(cons); } } } } /* check if list1 is a proper list */ CHECK_LIST(cmp1); if (function == SUBSETP) { GC_LEAVE(); return (T); } else if (xunion) { /* Add list2 to tail of result */ if (result == NIL) result = list2; else RPLACD(cons, list2); } else if (setexclusiveor) { LispObj *result2, *cons2; result2 = cons2 = NIL; for (cmp2 = list2; CONSP(cmp2); cmp2 = CDR(cmp2)) { item = CAR(cmp2); if (key != UNSPEC) { cmp = CAR(clist2); /* XXX changing clist2 */ clist2 = CDR(clist2); cmp1 = clist1; } else { cmp = item; cmp1 = list1; } /* Compare against list1 */ for (; CONSP(cmp1); cmp1 = CDR(cmp1)) { value = FCOMPARE(lambda, cmp, CAR(cmp1), code); if (value == expect) break; } if (value != expect) { if (inplace) { if (result2 == NIL) result2 = cons2 = cmp2; else { RPLACD(cons2, cmp2); cons2 = cmp2; } } else { if (result == NIL) { result = cons = CONS(item, NIL); GC_PROTECT(result); } else { RPLACD(cons, CONS(item, NIL)); cons = CDR(cons); } } } } if (inplace) { if (CONSP(cons2)) RPLACD(cons2, NIL); if (result == NIL) result = result2; else RPLACD(cons, result2); } } else if ((function == NSETDIFFERENCE || function == NINTERSECTION) && CONSP(cons)) RPLACD(cons, NIL); GC_LEAVE(); return (result); } LispObj * Lisp_Loop(LispBuiltin *builtin) /* loop &rest body */ { LispObj *code, *result; LispBlock *block; LispObj *body; body = ARGUMENT(0); result = NIL; block = LispBeginBlock(NIL, LispBlockTag); if (setjmp(block->jmp) == 0) { for (;;) for (code = body; CONSP(code); code = CDR(code)) (void)EVAL(CAR(code)); } LispEndBlock(block); result = lisp__data.block.block_ret; return (result); } /* XXX This function is broken, needs a review (being delayed until true array/vectors be implemented) */ LispObj * Lisp_MakeArray(LispBuiltin *builtin) /* make-array dimensions &key element-type initial-element initial-contents adjustable fill-pointer displaced-to displaced-index-offset */ { long rank = 0, count = 1, offset, zero, c; LispObj *obj, *dim, *array; LispType type; LispObj *dimensions, *element_type, *initial_element, *initial_contents, *displaced_to, *displaced_index_offset; dim = array = NIL; type = LispNil_t; displaced_index_offset = ARGUMENT(7); displaced_to = ARGUMENT(6); initial_contents = ARGUMENT(3); initial_element = ARGUMENT(2); element_type = ARGUMENT(1); dimensions = ARGUMENT(0); if (INDEXP(dimensions)) { dim = CONS(dimensions, NIL); rank = 1; count = FIXNUM_VALUE(dimensions); } else if (CONSP(dimensions)) { dim = dimensions; for (rank = 0; CONSP(dim); rank++, dim = CDR(dim)) { obj = CAR(dim); CHECK_INDEX(obj); count *= FIXNUM_VALUE(obj); } dim = dimensions; } else if (dimensions == NIL) { dim = NIL; rank = count = 0; } else LispDestroy("%s: %s is a bad array dimension", STRFUN(builtin), STROBJ(dimensions)); /* check element-type */ if (element_type != UNSPEC) { if (element_type == T) type = LispNil_t; else if (!SYMBOLP(element_type)) LispDestroy("%s: unsupported element type %s", STRFUN(builtin), STROBJ(element_type)); else { Atom_id atom = ATOMID(element_type); if (atom == Satom) type = LispAtom_t; else if (atom == Sinteger) type = LispInteger_t; else if (atom == Scharacter) type = LispSChar_t; else if (atom == Sstring) type = LispString_t; else if (atom == Slist) type = LispCons_t; else if (atom == Sopaque) type = LispOpaque_t; else LispDestroy("%s: unsupported element type %s", STRFUN(builtin), ATOMID(element_type)->value); } } /* check initial-contents */ if (rank) { CHECK_LIST(initial_contents); } /* check displaced-to */ if (displaced_to != UNSPEC) { CHECK_ARRAY(displaced_to); } /* check displaced-index-offset */ offset = -1; if (displaced_index_offset != UNSPEC) { CHECK_INDEX(displaced_index_offset); offset = FIXNUM_VALUE(displaced_index_offset); } c = 0; if (initial_element != UNSPEC) ++c; if (initial_contents != UNSPEC) ++c; if (displaced_to != UNSPEC || offset >= 0) ++c; if (c > 1) LispDestroy("%s: more than one initialization specified", STRFUN(builtin)); if (initial_element == UNSPEC) initial_element = NIL; zero = count == 0; if (displaced_to != UNSPEC) { CHECK_ARRAY(displaced_to); if (offset < 0) offset = 0; for (c = 1, obj = displaced_to->data.array.dim; obj != NIL; obj = CDR(obj)) c *= FIXNUM_VALUE(CAR(obj)); if (c < count + offset) LispDestroy("%s: array-total-size + displaced-index-offset " "exceeds total size", STRFUN(builtin)); for (c = 0, array = displaced_to->data.array.list; c < offset; c++) array = CDR(array); } else if (initial_contents != UNSPEC) { CHECK_CONS(initial_contents); if (rank == 0) array = initial_contents; else if (rank == 1) { for (array = initial_contents, c = 0; c < count; array = CDR(array), c++) if (!CONSP(array)) LispDestroy("%s: bad argument or size %s", STRFUN(builtin), STROBJ(array)); if (array != NIL) LispDestroy("%s: bad argument or size %s", STRFUN(builtin), STROBJ(array)); array = initial_contents; } else { LispObj *err = NIL; /* check if list matches */ int i, j, k, *dims, *loop; /* create iteration variables */ dims = LispMalloc(sizeof(int) * rank); loop = LispCalloc(1, sizeof(int) * (rank - 1)); for (i = 0, obj = dim; CONSP(obj); i++, obj = CDR(obj)) dims[i] = FIXNUM_VALUE(CAR(obj)); /* check if list matches specified dimensions */ while (loop[0] < dims[0]) { for (obj = initial_contents, i = 0; i < rank - 1; i++) { for (j = 0; j < loop[i]; j++) obj = CDR(obj); err = obj; if (!CONSP(obj = CAR(obj))) goto make_array_error; err = obj; } --i; for (;;) { ++loop[i]; if (i && loop[i] >= dims[i]) loop[i] = 0; else break; --i; } for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) { if (!CONSP(obj)) goto make_array_error; } if (obj == NIL) continue; make_array_error: LispFree(dims); LispFree(loop); LispDestroy("%s: bad argument or size %s", STRFUN(builtin), STROBJ(err)); } /* list is correct, use it to fill initial values */ /* reset loop */ memset(loop, 0, sizeof(int) * (rank - 1)); GCDisable(); /* fill array with supplied values */ array = NIL; while (loop[0] < dims[0]) { for (obj = initial_contents, i = 0; i < rank - 1; i++) { for (j = 0; j < loop[i]; j++) obj = CDR(obj); obj = CAR(obj); } --i; for (;;) { ++loop[i]; if (i && loop[i] >= dims[i]) loop[i] = 0; else break; --i; } for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) { if (array == NIL) array = CONS(CAR(obj), NIL); else { RPLACD(array, CONS(CAR(array), CDR(array))); RPLACA(array, CAR(obj)); } } } LispFree(dims); LispFree(loop); array = LispReverse(array); GCEnable(); } } else { GCDisable(); /* allocate array */ if (count) { --count; array = CONS(initial_element, NIL); while (count) { RPLACD(array, CONS(CAR(array), CDR(array))); RPLACA(array, initial_element); count--; } } GCEnable(); } obj = LispNew(array, dim); obj->type = LispArray_t; obj->data.array.list = array; obj->data.array.dim = dim; obj->data.array.rank = rank; obj->data.array.type = type; obj->data.array.zero = zero; return (obj); } LispObj * Lisp_MakeList(LispBuiltin *builtin) /* make-list size &key initial-element */ { GC_ENTER(); long count; LispObj *result, *cons; LispObj *size, *initial_element; initial_element = ARGUMENT(1); size = ARGUMENT(0); CHECK_INDEX(size); count = FIXNUM_VALUE(size); if (count == 0) return (NIL); if (initial_element == UNSPEC) initial_element = NIL; result = cons = CONS(initial_element, NIL); GC_PROTECT(result); for (; count > 1; count--) { RPLACD(cons, CONS(initial_element, NIL)); cons = CDR(cons); } GC_LEAVE(); return (result); } LispObj * Lisp_MakeSymbol(LispBuiltin *builtin) /* make-symbol name */ { LispObj *name, *symbol; name = ARGUMENT(0); CHECK_STRING(name); symbol = UNINTERNED_ATOM(THESTR(name)); symbol->data.atom->unreadable = !LispCheckAtomString(THESTR(name)); return (symbol); } LispObj * Lisp_Makunbound(LispBuiltin *builtin) /* makunbound symbol */ { LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); LispUnsetVar(symbol); return (symbol); } LispObj * Lisp_Mapc(LispBuiltin *builtin) /* mapc function list &rest more-lists */ { return (LispMapc(builtin, 0)); } LispObj * Lisp_Mapcar(LispBuiltin *builtin) /* mapcar function list &rest more-lists */ { return (LispMapc(builtin, 1)); } /* Like nconc but ignore non list arguments */ LispObj * LispMapnconc(LispObj *list) { LispObj *result = NIL; if (CONSP(list)) { LispObj *cons, *head, *tail; cons = NIL; for (; CONSP(CDR(list)); list = CDR(list)) { head = CAR(list); if (CONSP(head)) { for (tail = head; CONSP(CDR(tail)); tail = CDR(tail)) ; if (cons != NIL) RPLACD(cons, head); else result = head; cons = tail; } } head = CAR(list); if (CONSP(head)) { if (cons != NIL) RPLACD(cons, head); else result = head; } } return (result); } LispObj * Lisp_Mapcan(LispBuiltin *builtin) /* mapcan function list &rest more-lists */ { return (LispMapnconc(LispMapc(builtin, 1))); } static LispObj * LispMapc(LispBuiltin *builtin, int mapcar) { GC_ENTER(); long i, offset, count, length; LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value; LispObj *stk[8], **cdrs; LispObj *function, *list, *more_lists; more_lists = ARGUMENT(2); list = ARGUMENT(1); function = ARGUMENT(0); /* Result will be no longer than this */ for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist)) ; /* If first argument is not a list... */ if (length == 0) return (NIL); /* At least one argument will be passed to function, count how many * extra arguments will be used, and calculate result length. */ count = 0; for (rest = more_lists; CONSP(rest); rest = CDR(rest), count++) { /* Check if extra list is really a list, and if it is smaller * than the first list */ for (i = 0, alist = CAR(rest); i < length && CONSP(alist); i++, alist = CDR(alist)) ; /* If it is not a true list */ if (i == 0) return (NIL); /* If it is smaller than the currently calculated result length */ if (i < length) length = i; } if (mapcar) { /* Initialize gc protected object cells for resulting list */ result = cons = CONS(NIL, NIL); GC_PROTECT(result); } else result = cons = list; if (count >= sizeof(stk) / sizeof(stk[0])) cdrs = LispMalloc(count * sizeof(LispObj*)); else cdrs = &stk[0]; for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest)) cdrs[i] = CAR(rest); /* Initialize gc protected object cells for argument list */ arguments = acons = CONS(NIL, NIL); GC_PROTECT(arguments); /* Allocate space for extra arguments */ for (i = 0; i < count; i++) { RPLACD(acons, CONS(NIL, NIL)); acons = CDR(acons); } /* For every element of the list that will be used */ for (offset = 0;; list = CDR(list)) { acons = arguments; /* Add first argument */ RPLACA(acons, CAR(list)); acons = CDR(acons); /* For every extra list argument */ for (i = 0; i < count; i++) { alist = cdrs[i]; cdrs[i] = CDR(cdrs[i]); /* Add element to argument list */ RPLACA(acons, CAR(alist)); acons = CDR(acons); } value = APPLY(function, arguments); if (mapcar) { /* Store result */ RPLACA(cons, value); /* Allocate new result cell */ if (++offset < length) { RPLACD(cons, CONS(NIL, NIL)); cons = CDR(cons); } else break; } else if (++offset >= length) break; } /* Unprotect argument and result list */ GC_LEAVE(); if (cdrs != &stk[0]) LispFree(cdrs); return (result); } static LispObj * LispMapl(LispBuiltin *builtin, int maplist) { GC_ENTER(); long i, offset, count, length; LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value; LispObj *stk[8], **cdrs; LispObj *function, *list, *more_lists; more_lists = ARGUMENT(2); list = ARGUMENT(1); function = ARGUMENT(0); /* count is the number of lists, length is the length of the result */ for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist)) ; /* first argument is not a list */ if (length == 0) return (NIL); /* check remaining arguments */ for (count = 0, rest = more_lists; CONSP(rest); rest = CDR(rest), count++) { for (i = 0, alist = CAR(rest); i < length && CONSP(alist); i++, alist = CDR(alist)) ; /* argument is not a list */ if (i == 0) return (NIL); /* result will have the length of the smallest list */ if (i < length) length = i; } /* result will be a list */ if (maplist) { result = cons = CONS(NIL, NIL); GC_PROTECT(result); } else result = cons = list; if (count >= sizeof(stk) / sizeof(stk[0])) cdrs = LispMalloc(count * sizeof(LispObj*)); else cdrs = &stk[0]; for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest)) cdrs[i] = CAR(rest); /* initialize argument list */ arguments = acons = CONS(NIL, NIL); GC_PROTECT(arguments); for (i = 0; i < count; i++) { RPLACD(acons, CONS(NIL, NIL)); acons = CDR(acons); } /* for every used list element */ for (offset = 0;; list = CDR(list)) { acons = arguments; /* first argument */ RPLACA(acons, list); acons = CDR(acons); /* for every extra list */ for (i = 0; i < count; i++) { RPLACA(acons, cdrs[i]); cdrs[i] = CDR(cdrs[i]); acons = CDR(acons); } value = APPLY(function, arguments); if (maplist) { /* store result */ RPLACA(cons, value); /* allocate new cell */ if (++offset < length) { RPLACD(cons, CONS(NIL, NIL)); cons = CDR(cons); } else break; } else if (++offset >= length) break; } GC_LEAVE(); if (cdrs != &stk[0]) LispFree(cdrs); return (result); } LispObj * Lisp_Mapl(LispBuiltin *builtin) /* mapl function list &rest more-lists */ { return (LispMapl(builtin, 0)); } LispObj * Lisp_Maplist(LispBuiltin *builtin) /* maplist function list &rest more-lists */ { return (LispMapl(builtin, 1)); } LispObj * Lisp_Mapcon(LispBuiltin *builtin) /* mapcon function list &rest more-lists */ { return (LispMapnconc(LispMapl(builtin, 1))); } LispObj * Lisp_Member(LispBuiltin *builtin) /* member item list &key test test-not key */ { int code, expect; LispObj *compare, *lambda; LispObj *item, *list, *test, *test_not, *key; key = ARGUMENT(4); test_not = ARGUMENT(3); test = ARGUMENT(2); list = ARGUMENT(1); item = ARGUMENT(0); if (list == NIL) return (NIL); CHECK_CONS(list); CHECK_TEST(); if (key == UNSPEC) { if (code == FEQ) { for (; CONSP(list); list = CDR(list)) if (item == CAR(list)) return (list); } else { for (; CONSP(list); list = CDR(list)) if (FCOMPARE(lambda, item, CAR(list), code) == expect) return (list); } } else { if (code == FEQ) { for (; CONSP(list); list = CDR(list)) if (item == APPLY1(key, CAR(list))) return (list); } else { for (; CONSP(list); list = CDR(list)) { compare = APPLY1(key, CAR(list)); if (FCOMPARE(lambda, item, compare, code) == expect) return (list); } } } /* check if is a proper list */ CHECK_LIST(list); return (NIL); } LispObj * Lisp_MemberIf(LispBuiltin *builtin) /* member-if predicate list &key key */ { return (LispAssocOrMember(builtin, MEMBER, IF)); } LispObj * Lisp_MemberIfNot(LispBuiltin *builtin) /* member-if-not predicate list &key key */ { return (LispAssocOrMember(builtin, MEMBER, IFNOT)); } LispObj * Lisp_MultipleValueBind(LispBuiltin *builtin) /* multiple-value-bind symbols values &rest body */ { int i, head = lisp__data.env.length; LispObj *result, *symbol, *value; LispObj *symbols, *values, *body; body = ARGUMENT(2); values = ARGUMENT(1); symbols = ARGUMENT(0); result = EVAL(values); for (i = -1; CONSP(symbols); symbols = CDR(symbols), i++) { symbol = CAR(symbols); CHECK_SYMBOL(symbol); CHECK_CONSTANT(symbol); if (i >= 0 && i < RETURN_COUNT) value = RETURN(i); else if (i < 0) value = result; else value = NIL; LispAddVar(symbol, value); ++lisp__data.env.head; } /* Execute code with binded variables (if any) */ for (result = NIL; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); lisp__data.env.head = lisp__data.env.length = head; return (result); } LispObj * Lisp_MultipleValueCall(LispBuiltin *builtin) /* multiple-value-call function &rest form */ { GC_ENTER(); int i; LispObj *arguments, *cons, *result; LispObj *function, *form; form = ARGUMENT(1); function = ARGUMENT(0); /* build argument list */ arguments = cons = NIL; for (; CONSP(form); form = CDR(form)) { RETURN_COUNT = 0; result = EVAL(CAR(form)); if (RETURN_COUNT >= 0) { if (arguments == NIL) { arguments = cons = CONS(result, NIL); GC_PROTECT(arguments); } else { RPLACD(cons, CONS(result, NIL)); cons = CDR(cons); } for (i = 0; i < RETURN_COUNT; i++) { RPLACD(cons, CONS(RETURN(i), NIL)); cons = CDR(cons); } } } /* apply function */ if (POINTERP(function) && !XSYMBOLP(function) && !XFUNCTIONP(function)) { function = EVAL(function); GC_PROTECT(function); } result = APPLY(function, arguments); GC_LEAVE(); return (result); } LispObj * Lisp_MultipleValueProg1(LispBuiltin *builtin) /* multiple-value-prog1 first-form &rest form */ { GC_ENTER(); int i, count; LispObj *values, *cons; LispObj *first_form, *form; form = ARGUMENT(1); first_form = ARGUMENT(0); values = EVAL(first_form); if (!CONSP(form)) return (values); cons = NIL; count = RETURN_COUNT; if (count < 0) values = NIL; else if (count == 0) { GC_PROTECT(values); } else { values = cons = CONS(values, NIL); GC_PROTECT(values); for (i = 0; i < count; i++) { RPLACD(cons, CONS(RETURN(i), NIL)); cons = CDR(cons); } } for (; CONSP(form); form = CDR(form)) EVAL(CAR(form)); RETURN_COUNT = count; if (count > 0) { for (i = 0, cons = CDR(values); CONSP(cons); cons = CDR(cons), i++) RETURN(i) = CAR(cons); values = CAR(values); } GC_LEAVE(); return (values); } LispObj * Lisp_MultipleValueList(LispBuiltin *builtin) /* multiple-value-list form */ { int i; GC_ENTER(); LispObj *form, *result, *cons; form = ARGUMENT(0); result = EVAL(form); if (RETURN_COUNT < 0) return (NIL); result = cons = CONS(result, NIL); GC_PROTECT(result); for (i = 0; i < RETURN_COUNT; i++) { RPLACD(cons, CONS(RETURN(i), NIL)); cons = CDR(cons); } GC_LEAVE(); return (result); } LispObj * Lisp_MultipleValueSetq(LispBuiltin *builtin) /* multiple-value-setq symbols form */ { int i; LispObj *result, *symbol, *value; LispObj *symbols, *form; form = ARGUMENT(1); symbols = ARGUMENT(0); CHECK_LIST(symbols); result = EVAL(form); if (CONSP(symbols)) { symbol = CAR(symbols); CHECK_SYMBOL(symbol); CHECK_CONSTANT(symbol); LispSetVar(symbol, result); symbols = CDR(symbols); } for (i = 0; CONSP(symbols); symbols = CDR(symbols), i++) { symbol = CAR(symbols); CHECK_SYMBOL(symbol); CHECK_CONSTANT(symbol); if (i < RETURN_COUNT && RETURN_COUNT > 0) value = RETURN(i); else value = NIL; LispSetVar(symbol, value); } return (result); } LispObj * Lisp_Nconc(LispBuiltin *builtin) /* nconc &rest lists */ { LispObj *list, *lists, *cons, *head, *tail; lists = ARGUMENT(0); /* skip any initial empty lists */ for (; CONSP(lists); lists = CDR(lists)) if (CAR(lists) != NIL) break; /* don't check if a proper list */ if (!CONSP(lists)) return (lists); /* setup to concatenate lists */ list = CAR(lists); CHECK_CONS(list); for (cons = list; CONSP(CDR(cons)); cons = CDR(cons)) ; /* if only two lists */ lists = CDR(lists); if (!CONSP(lists)) { RPLACD(cons, lists); return (list); } /* concatenate */ for (; CONSP(CDR(lists)); lists = CDR(lists)) { head = CAR(lists); if (head == NIL) continue; CHECK_CONS(head); for (tail = head; CONSP(CDR(tail)); tail = CDR(tail)) ; RPLACD(cons, head); cons = tail; } /* add last list */ RPLACD(cons, CAR(lists)); return (list); } LispObj * Lisp_Nreverse(LispBuiltin *builtin) /* nreverse sequence */ { return (LispXReverse(builtin, 1)); } LispObj * Lisp_NsetDifference(LispBuiltin *builtin) /* nset-difference list1 list2 &key test test-not key */ { return (LispListSet(builtin, NSETDIFFERENCE)); } LispObj * Lisp_Nsubstitute(LispBuiltin *builtin) /* nsubstitute newitem olditem sequence &key from-end test test-not start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, NONE)); } LispObj * Lisp_NsubstituteIf(LispBuiltin *builtin) /* nsubstitute-if newitem test sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IF)); } LispObj * Lisp_NsubstituteIfNot(LispBuiltin *builtin) /* nsubstitute-if-not newitem test sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IFNOT)); } LispObj * Lisp_Nth(LispBuiltin *builtin) /* nth index list */ { long position; LispObj *oindex, *list; list = ARGUMENT(1); oindex = ARGUMENT(0); CHECK_INDEX(oindex); position = FIXNUM_VALUE(oindex); if (list == NIL) return (NIL); CHECK_CONS(list); for (; position > 0; position--) { if (!CONSP(list)) return (NIL); list = CDR(list); } return (CONSP(list) ? CAR(list) : NIL); } LispObj * Lisp_Nthcdr(LispBuiltin *builtin) /* nthcdr index list */ { long position; LispObj *oindex, *list; list = ARGUMENT(1); oindex = ARGUMENT(0); CHECK_INDEX(oindex); position = FIXNUM_VALUE(oindex); if (list == NIL) return (NIL); CHECK_CONS(list); for (; position > 0; position--) { if (!CONSP(list)) return (NIL); list = CDR(list); } return (list); } LispObj * Lisp_NthValue(LispBuiltin *builtin) /* nth-value index form */ { long i; LispObj *oindex, *form, *result; form = ARGUMENT(1); oindex = ARGUMENT(0); oindex = EVAL(oindex); CHECK_INDEX(oindex); i = FIXNUM_VALUE(oindex) - 1; result = EVAL(form); if (RETURN_COUNT < 0 || i >= RETURN_COUNT) result = NIL; else if (i >= 0) result = RETURN(i); return (result); } LispObj * Lisp_Null(LispBuiltin *builtin) /* null list */ { LispObj *list; list = ARGUMENT(0); return (list == NIL ? T : NIL); } LispObj * Lisp_Or(LispBuiltin *builtin) /* or &rest args */ { LispObj *result = NIL, *args; args = ARGUMENT(0); for (; CONSP(args); args = CDR(args)) { result = EVAL(CAR(args)); if (result != NIL) break; } return (result); } LispObj * Lisp_Pairlis(LispBuiltin *builtin) /* pairlis key data &optional alist */ { LispObj *result, *cons; LispObj *key, *data, *alist; alist = ARGUMENT(2); data = ARGUMENT(1); key = ARGUMENT(0); if (CONSP(key) && CONSP(data)) { GC_ENTER(); result = cons = CONS(CONS(CAR(key), CAR(data)), NIL); GC_PROTECT(result); key = CDR(key); data = CDR(data); for (; CONSP(key) && CONSP(data); key = CDR(key), data = CDR(data)) { RPLACD(cons, CONS(CONS(CAR(key), CAR(data)), NIL)); cons = CDR(cons); } if (CONSP(key) || CONSP(data)) LispDestroy("%s: different length lists", STRFUN(builtin)); GC_LEAVE(); if (alist != UNSPEC) RPLACD(cons, alist); } else result = alist == UNSPEC ? NIL : alist; return (result); } static LispObj * LispFindOrPosition(LispBuiltin *builtin, int function, int comparison) /* find item sequence &key from-end test test-not start end key find-if predicate sequence &key from-end start end key find-if-not predicate sequence &key from-end start end key position item sequence &key from-end test test-not start end key position-if predicate sequence &key from-end start end key position-if-not predicate sequence &key from-end start end key */ { int code = 0, istring, expect, value; char *string = NULL; long offset = -1, start, end, length, i = comparison == NONE ? 7 : 5; LispObj *cmp, *element, **objects = NULL; LispObj *item, *predicate, *sequence, *from_end, *test, *test_not, *ostart, *oend, *key; key = ARGUMENT(i); --i; oend = ARGUMENT(i); --i; ostart = ARGUMENT(i); --i; if (comparison == NONE) { test_not = ARGUMENT(i); --i; test = ARGUMENT(i); --i; } else test_not = test = UNSPEC; from_end = ARGUMENT(i); --i; if (from_end == UNSPEC) from_end = NIL; sequence = ARGUMENT(i); --i; if (comparison == NONE) { item = ARGUMENT(i); predicate = Oeql; } else { predicate = ARGUMENT(i); item = NIL; } LispCheckSequenceStartEnd(builtin, sequence, ostart, oend, &start, &end, &length); if (sequence == NIL) return (NIL); /* Cannot specify both :test and :test-not */ if (test != UNSPEC && test_not != UNSPEC) LispDestroy("%s: specify either :TEST or :TEST-NOT", STRFUN(builtin)); expect = 1; if (comparison == NONE) { if (test != UNSPEC) predicate = test; else if (test_not != UNSPEC) { predicate = test_not; expect = 0; } FUNCTION_CHECK(predicate); code = FCODE(predicate); } cmp = element = NIL; istring = STRINGP(sequence); if (istring) string = THESTR(sequence); else { if (!CONSP(sequence)) sequence = sequence->data.array.list; for (i = 0; i < start; i++) sequence = CDR(sequence); } if ((length = end - start) == 0) return (NIL); if (from_end != NIL && !istring) { objects = LispMalloc(sizeof(LispObj*) * length); for (i = length - 1; i >= 0; i--, sequence = CDR(sequence)) objects[i] = CAR(sequence); } for (i = 0; i < length; i++) { if (istring) element = SCHAR(string[from_end == NIL ? i + start : end - i - 1]); else element = from_end == NIL ? CAR(sequence) : objects[i]; if (key != UNSPEC) cmp = APPLY1(key, element); else cmp = element; /* Update list */ if (!istring && from_end == NIL) sequence = CDR(sequence); if (comparison == NONE) value = FCOMPARE(predicate, item, cmp, code); else value = APPLY1(predicate, cmp) != NIL; if ((!value && (comparison == IFNOT || (comparison == NONE && !expect))) || (value && (comparison == IF || (comparison == NONE && expect)))) { offset = from_end == NIL ? i + start : end - i - 1; break; } } if (from_end != NIL && !istring) LispFree(objects); return (offset == -1 ? NIL : function == FIND ? element : FIXNUM(offset)); } LispObj * Lisp_Pop(LispBuiltin *builtin) /* pop place */ { LispObj *result, *value; LispObj *place; place = ARGUMENT(0); if (SYMBOLP(place)) { result = LispGetVar(place); if (result == NULL) LispDestroy("EVAL: the variable %s is unbound", STROBJ(place)); CHECK_CONSTANT(place); if (result != NIL) { CHECK_CONS(result); value = CDR(result); result = CAR(result); } else value = NIL; LispSetVar(place, value); } else { GC_ENTER(); LispObj quote; result = EVAL(place); if (result != NIL) { CHECK_CONS(result); value = CDR(result); GC_PROTECT(value); result = CAR(result); } else value = NIL; quote.type = LispQuote_t; quote.data.quote = value; APPLY2(Osetf, place, "e); GC_LEAVE(); } return (result); } LispObj * Lisp_Position(LispBuiltin *builtin) /* position item sequence &key from-end test test-not start end key */ { return (LispFindOrPosition(builtin, POSITION, NONE)); } LispObj * Lisp_PositionIf(LispBuiltin *builtin) /* position-if predicate sequence &key from-end start end key */ { return (LispFindOrPosition(builtin, POSITION, IF)); } LispObj * Lisp_PositionIfNot(LispBuiltin *builtin) /* position-if-not predicate sequence &key from-end start end key */ { return (LispFindOrPosition(builtin, POSITION, IFNOT)); } LispObj * Lisp_Proclaim(LispBuiltin *builtin) /* proclaim declaration */ { LispObj *arguments, *object; char *operation; LispObj *declaration; declaration = ARGUMENT(0); CHECK_CONS(declaration); arguments = declaration; object = CAR(arguments); CHECK_SYMBOL(object); operation = ATOMID(object)->value; if (strcmp(operation, "SPECIAL") == 0) { for (arguments = CDR(arguments); CONSP(arguments); arguments = CDR(arguments)) { object = CAR(arguments); CHECK_SYMBOL(object); LispProclaimSpecial(object, NULL, NIL); } } else if (strcmp(operation, "TYPE") == 0) { /* XXX no type checking yet, but should be added */ } /* else do nothing */ return (NIL); } LispObj * Lisp_Prog1(LispBuiltin *builtin) /* prog1 first &rest body */ { GC_ENTER(); LispObj *result; LispObj *first, *body; body = ARGUMENT(1); first = ARGUMENT(0); result = EVAL(first); GC_PROTECT(result); for (; CONSP(body); body = CDR(body)) (void)EVAL(CAR(body)); GC_LEAVE(); return (result); } LispObj * Lisp_Prog2(LispBuiltin *builtin) /* prog2 first second &rest body */ { GC_ENTER(); LispObj *result; LispObj *first, *second, *body; body = ARGUMENT(2); second = ARGUMENT(1); first = ARGUMENT(0); (void)EVAL(first); result = EVAL(second); GC_PROTECT(result); for (; CONSP(body); body = CDR(body)) (void)EVAL(CAR(body)); GC_LEAVE(); return (result); } LispObj * Lisp_Progn(LispBuiltin *builtin) /* progn &rest body */ { LispObj *result = NIL; LispObj *body; body = ARGUMENT(0); for (; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); return (result); } /* * This does what I believe is the expected behaviour (or at least * acceptable for the the interpreter), if the code being executed * ever tries to change/bind a progv symbol, the symbol state will * be restored when exiting the progv block, so, code like: * (progv '(*x*) '(1) (defvar *x* 10)) * when exiting the block, will have *x* unbound, and not a dynamic * symbol; if it was already bound, will have the old value. * Symbols already dynamic can be freely changed, even unbounded in * the progv block. */ LispObj * Lisp_Progv(LispBuiltin *builtin) /* progv symbols values &rest body */ { GC_ENTER(); int head = lisp__data.env.length, i, count, ostk[32], *offsets; LispObj *result, *list, *symbol, *value; int jumped; char fstk[32], *flags; LispBlock *block; LispAtom *atom; LispObj *symbols, *values, *body; /* Possible states */ #define DYNAMIC_SYMBOL 1 #define GLOBAL_SYMBOL 2 #define UNBOUND_SYMBOL 3 body = ARGUMENT(2); values = ARGUMENT(1); symbols = ARGUMENT(0); /* get symbol names */ symbols = EVAL(symbols); GC_PROTECT(symbols); /* get symbol values */ values = EVAL(values); GC_PROTECT(values); /* count/check symbols and allocate space to remember symbol state */ for (count = 0, list = symbols; CONSP(list); count++, list = CDR(list)) { symbol = CAR(list); CHECK_SYMBOL(symbol); CHECK_CONSTANT(symbol); } if (count > sizeof(fstk)) { flags = LispMalloc(count); offsets = LispMalloc(count * sizeof(int)); } else { flags = &fstk[0]; offsets = &ostk[0]; } /* store flags and save old value if required */ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) { atom = CAR(list)->data.atom; if (atom->dyn) flags[i] = DYNAMIC_SYMBOL; else if (atom->a_object) { flags[i] = GLOBAL_SYMBOL; offsets[i] = lisp__data.protect.length; GC_PROTECT(atom->property->value); } else flags[i] = UNBOUND_SYMBOL; } /* bind the symbols */ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) { symbol = CAR(list); atom = symbol->data.atom; if (CONSP(values)) { value = CAR(values); values = CDR(values); } else value = NIL; if (flags[i] != DYNAMIC_SYMBOL) { if (!atom->a_object) LispSetAtomObjectProperty(atom, value); else SETVALUE(atom, value); } else LispAddVar(symbol, value); } /* bind dynamic symbols */ lisp__data.env.head = lisp__data.env.length; jumped = 0; result = NIL; block = LispBeginBlock(NIL, LispBlockProtect); if (setjmp(block->jmp) == 0) { for (; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); } /* restore symbols */ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) { symbol = CAR(list); atom = symbol->data.atom; if (flags[i] != DYNAMIC_SYMBOL) { if (flags[i] == UNBOUND_SYMBOL) LispUnsetVar(symbol); else { /* restore global symbol value */ LispSetAtomObjectProperty(atom, lisp__data.protect.objects [offsets[i]]); atom->dyn = 0; } } } /* unbind dynamic symbols */ lisp__data.env.head = lisp__data.env.length = head; GC_LEAVE(); if (count > sizeof(fstk)) { LispFree(flags); LispFree(offsets); } LispEndBlock(block); if (!lisp__data.destroyed) { if (jumped) result = lisp__data.block.block_ret; } else { /* check if there is an unwind-protect block */ LispBlockUnwind(NULL); /* no unwind-protect block, return to the toplevel */ LispDestroy("."); } return (result); } LispObj * Lisp_Provide(LispBuiltin *builtin) /* provide module */ { LispObj *module, *obj; module = ARGUMENT(0); CHECK_STRING(module); for (obj = MOD; obj != NIL; obj = CDR(obj)) { if (STRLEN(CAR(obj)) == STRLEN(module) && memcmp(THESTR(CAR(obj)), THESTR(module), STRLEN(module)) == 0) return (module); } if (MOD == NIL) MOD = CONS(module, NIL); else { RPLACD(MOD, CONS(CAR(MOD), CDR(MOD))); RPLACA(MOD, module); } LispSetVar(lisp__data.modules, MOD); return (MOD); } LispObj * Lisp_Push(LispBuiltin *builtin) /* push item place */ { LispObj *result, *list; LispObj *item, *place; place = ARGUMENT(1); item = ARGUMENT(0); item = EVAL(item); if (SYMBOLP(place)) { list = LispGetVar(place); if (list == NULL) LispDestroy("EVAL: the variable %s is unbound", STROBJ(place)); CHECK_CONSTANT(place); LispSetVar(place, result = CONS(item, list)); } else { GC_ENTER(); LispObj quote; list = EVAL(place); result = CONS(item, list); GC_PROTECT(result); quote.type = LispQuote_t; quote.data.quote = result; APPLY2(Osetf, place, "e); GC_LEAVE(); } return (result); } LispObj * Lisp_Pushnew(LispBuiltin *builtin) /* pushnew item place &key key test test-not */ { GC_ENTER(); LispObj *result, *list; LispObj *item, *place, *key, *test, *test_not; test_not = ARGUMENT(4); test = ARGUMENT(3); key = ARGUMENT(2); place = ARGUMENT(1); item = ARGUMENT(0); /* Evaluate place */ if (SYMBOLP(place)) { list = LispGetVar(place); if (list == NULL) LispDestroy("EVAL: the variable %s is unbound", STROBJ(place)); /* Do error checking now. */ CHECK_CONSTANT(place); } else /* It is possible that list is not gc protected? */ list = EVAL(place); item = EVAL(item); GC_PROTECT(item); if (key != UNSPEC) { key = EVAL(key); GC_PROTECT(key); } if (test != UNSPEC) { test = EVAL(test); GC_PROTECT(test); } else if (test_not != UNSPEC) { test_not = EVAL(test_not); GC_PROTECT(test_not); } result = LispAdjoin(builtin, item, list, key, test, test_not); /* Item already in list */ if (result == list) { GC_LEAVE(); return (result); } if (SYMBOLP(place)) { CHECK_CONSTANT(place); LispSetVar(place, result); } else { LispObj quote; GC_PROTECT(result); quote.type = LispQuote_t; quote.data.quote = result; APPLY2(Osetf, place, "e); } GC_LEAVE(); return (result); } LispObj * Lisp_Quit(LispBuiltin *builtin) /* quit &optional status */ { int status = 0; LispObj *ostatus; ostatus = ARGUMENT(0); if (FIXNUMP(ostatus)) status = (int)FIXNUM_VALUE(ostatus); else if (ostatus != UNSPEC) LispDestroy("%s: bad exit status argument %s", STRFUN(builtin), STROBJ(ostatus)); exit(status); } LispObj * Lisp_Quote(LispBuiltin *builtin) /* quote object */ { LispObj *object; object = ARGUMENT(0); return (object); } LispObj * Lisp_Replace(LispBuiltin *builtin) /* replace sequence1 sequence2 &key start1 end1 start2 end2 */ { long length, length1, length2, start1, end1, start2, end2; LispObj *sequence1, *sequence2, *ostart1, *oend1, *ostart2, *oend2; oend2 = ARGUMENT(5); ostart2 = ARGUMENT(4); oend1 = ARGUMENT(3); ostart1 = ARGUMENT(2); sequence2 = ARGUMENT(1); sequence1 = ARGUMENT(0); LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1, &start1, &end1, &length1); LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2, &start2, &end2, &length2); if (start1 == end1 || start2 == end2) return (sequence1); length = end1 - start1; if (length > end2 - start2) length = end2 - start2; if (STRINGP(sequence1)) { CHECK_STRING_WRITABLE(sequence1); if (!STRINGP(sequence2)) LispDestroy("%s: cannot store %s in %s", STRFUN(builtin), STROBJ(sequence2), THESTR(sequence1)); memmove(THESTR(sequence1) + start1, THESTR(sequence2) + start2, length); } else { int i; LispObj *from, *to; if (ARRAYP(sequence1)) sequence1 = sequence1->data.array.list; if (ARRAYP(sequence2)) sequence2 = sequence2->data.array.list; /* adjust pointers */ for (i = 0, from = sequence2; i < start2; i++, from = CDR(from)) ; for (i = 0, to = sequence1; i < start1; i++, to = CDR(to)) ; /* copy data */ for (i = 0; i < length; i++, from = CDR(from), to = CDR(to)) RPLACA(to, CAR(from)); } return (sequence1); } static LispObj * LispDeleteOrRemoveDuplicates(LispBuiltin *builtin, int function) /* delete-duplicates sequence &key from-end test test-not start end key remove-duplicates sequence &key from-end test test-not start end key */ { GC_ENTER(); int code, expect, value = 0; long i, j, start, end, length, count; LispObj *lambda, *result, *cons, *compare; LispObj *sequence, *from_end, *test, *test_not, *ostart, *oend, *key; key = ARGUMENT(6); oend = ARGUMENT(5); ostart = ARGUMENT(4); test_not = ARGUMENT(3); test = ARGUMENT(2); from_end = ARGUMENT(1); if (from_end == UNSPEC) from_end = NIL; sequence = ARGUMENT(0); LispCheckSequenceStartEnd(builtin, sequence, ostart, oend, &start, &end, &length); /* Check if need to do something */ if (start == end) return (sequence); CHECK_TEST(); /* Initialize */ count = 0; result = cons = NIL; if (STRINGP(sequence)) { char *ptr, *string, *buffer = LispMalloc(length + 1); /* Use same code, update start/end offsets */ if (from_end != NIL) { i = length - start; start = length - end; end = i; } if (from_end == NIL) string = THESTR(sequence); else { /* Make a reversed copy of the sequence */ string = LispMalloc(length + 1); for (ptr = THESTR(sequence) + length - 1, i = 0; i < length; i++) string[i] = *ptr--; string[i] = '\0'; } ptr = buffer; /* Copy leading bytes */ for (i = 0; i < start; i++) *ptr++ = string[i]; compare = SCHAR(string[i]); if (key != UNSPEC) compare = APPLY1(key, compare); result = cons = CONS(compare, NIL); GC_PROTECT(result); for (++i; i < end; i++) { compare = SCHAR(string[i]); if (key != UNSPEC) compare = APPLY1(key, compare); RPLACD(cons, CONS(compare, NIL)); cons = CDR(cons); } for (i = start; i < end; i++, result = CDR(result)) { compare = CAR(result); for (j = i + 1, cons = CDR(result); j < end; j++, cons = CDR(cons)) { value = FCOMPARE(lambda, compare, CAR(cons), code); if (value == expect) break; } if (value != expect) *ptr++ = string[i]; else ++count; } if (count) { /* Copy ending bytes */ for (; i <= length; i++) /* Also copy the ending nul */ *ptr++ = string[i]; if (from_end == NIL) ptr = buffer; else { for (i = 0, ptr = buffer + strlen(buffer); ptr > buffer; i++) string[i] = *--ptr; string[i] = '\0'; ptr = string; LispFree(buffer); } if (function == REMOVE) result = STRING2(ptr); else { CHECK_STRING_WRITABLE(sequence); result = sequence; free(THESTR(result)); THESTR(result) = ptr; LispMused(ptr); } } else { result = sequence; if (from_end != NIL) LispFree(string); } } else { long xlength = end - start; LispObj *list, *object, **kobjects = NULL, **xobjects; LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength); if (!CONSP(sequence)) object = sequence->data.array.list; else object = sequence; list = object; for (i = 0; i < start; i++) object = CDR(object); /* Put data in a vector */ if (from_end == NIL) { for (i = 0; i < xlength; i++, object = CDR(object)) objects[i] = CAR(object); } else { for (i = xlength - 1; i >= 0; i--, object = CDR(object)) objects[i] = CAR(object); } /* Apply key predicate if required */ if (key != UNSPEC) { kobjects = LispMalloc(sizeof(LispObj*) * xlength); for (i = 0; i < xlength; i++) { kobjects[i] = APPLY1(key, objects[i]); GC_PROTECT(kobjects[i]); } xobjects = kobjects; } else xobjects = objects; /* Check if needs to remove something */ for (i = 0; i < xlength; i++) { compare = xobjects[i]; for (j = i + 1; j < xlength; j++) { value = FCOMPARE(lambda, compare, xobjects[j], code); if (value == expect) { objects[i] = NULL; ++count; break; } } } if (count) { /* Create/set result list */ object = list; if (start) { /* Skip first elements of resulting list */ if (function == REMOVE) { result = cons = CONS(CAR(object), NIL); GC_PROTECT(result); for (i = 1, object = CDR(object); i < start; i++, object = CDR(object)) { RPLACD(cons, CONS(CAR(object), NIL)); cons = CDR(cons); } } else { result = cons = object; for (i = 1; i < start; i++, cons = CDR(cons)) ; } } else if (function == DELETE) result = list; /* Skip initial removed elements */ if (function == REMOVE) { for (i = 0; objects[i] == NULL && i < xlength; i++) ; } else i = 0; if (i < xlength) { int xstart, xlimit, xinc; if (from_end == NIL) { xstart = i; xlimit = xlength; xinc = 1; } else { xstart = xlength - 1; xlimit = i - 1; xinc = -1; } if (function == REMOVE) { for (i = xstart; i != xlimit; i += xinc) { if (objects[i] != NULL) { if (result == NIL) { result = cons = CONS(objects[i], NIL); GC_PROTECT(result); } else { RPLACD(cons, CONS(objects[i], NIL)); cons = CDR(cons); } } } } else { /* Delete duplicates */ for (i = xstart; i != xlimit; i += xinc) { if (objects[i] == NULL) { if (cons == NIL) { if (CONSP(CDR(result))) { RPLACA(result, CADR(result)); RPLACD(result, CDDR(result)); } else { RPLACA(result, CDR(result)); RPLACD(result, NIL); } } else { if (CONSP(CDR(cons))) RPLACD(cons, CDDR(cons)); else RPLACD(cons, NIL); } } else { if (cons == NIL) cons = result; else cons = CDR(cons); } } } } if (end < length && function == REMOVE) { for (i = start; i < end; i++, object = CDR(object)) ; if (result == NIL) { result = cons = CONS(CAR(object), NIL); GC_PROTECT(result); ++i; object = CDR(object); } for (; i < length; i++, object = CDR(object)) { RPLACD(cons, CONS(CAR(object), NIL)); cons = CDR(cons); } } } else result = sequence; LispFree(objects); if (key != UNSPEC) LispFree(kobjects); if (count && !CONSP(sequence)) { if (function == REMOVE) result = VECTOR(result); else { length = FIXNUM_VALUE(CAR(sequence->data.array.dim)) - count; CAR(sequence->data.array.dim) = FIXNUM(length); result = sequence; } } } GC_LEAVE(); return (result); } LispObj * Lisp_RemoveDuplicates(LispBuiltin *builtin) /* remove-duplicates sequence &key from-end test test-not start end key */ { return (LispDeleteOrRemoveDuplicates(builtin, REMOVE)); } static LispObj * LispDeleteRemoveXSubstitute(LispBuiltin *builtin, int function, int comparison) /* delete item sequence &key from-end test test-not start end count key delete-if predicate sequence &key from-end start end count key delete-if-not predicate sequence &key from-end start end count key remove item sequence &key from-end test test-not start end count key remove-if predicate sequence &key from-end start end count key remove-if-not predicate sequence &key from-end start end count key substitute newitem olditem sequence &key from-end test test-not start end count key substitute-if newitem test sequence &key from-end start end count key substitute-if-not newitem test sequence &key from-end start end count key nsubstitute newitem olditem sequence &key from-end test test-not start end count key nsubstitute-if newitem test sequence &key from-end start end count key nsubstitute-if-not newitem test sequence &key from-end start end count key */ { GC_ENTER(); int code, expect, value, inplace, substitute; long i, j, start, end, length, copy, count, xstart, xend, xinc, xlength; LispObj *result, *compare; LispObj *item, *newitem, *lambda, *sequence, *from_end, *test, *test_not, *ostart, *oend, *ocount, *key; substitute = function == SUBSTITUTE || function == NSUBSTITUTE; if (!substitute) i = comparison == NONE ? 8 : 6; else /* substitute */ i = comparison == NONE ? 9 : 7; /* Get function arguments */ key = ARGUMENT(i); --i; ocount = ARGUMENT(i); --i; oend = ARGUMENT(i); --i; ostart = ARGUMENT(i); --i; if (comparison == NONE) { test_not = ARGUMENT(i); --i; test = ARGUMENT(i); --i; } else test_not = test = UNSPEC; from_end = ARGUMENT(i); --i; if (from_end == UNSPEC) from_end = NIL; sequence = ARGUMENT(i); --i; if (comparison != NONE) { lambda = ARGUMENT(i); --i; if (substitute) newitem = ARGUMENT(0); else newitem = NIL; item = NIL; } else { lambda = NIL; if (substitute) { item = ARGUMENT(1); newitem = ARGUMENT(0); } else { item = ARGUMENT(0); newitem = NIL; } } /* Check if argument is a valid sequence, and if start/end * are correctly specified. */ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend, &start, &end, &length); /* Check count argument */ if (ocount == UNSPEC) { count = length; /* Doesn't matter, but left to right should be slightly faster */ from_end = NIL; } else { CHECK_INDEX(ocount); count = FIXNUM_VALUE(ocount); } /* Check if need to do something */ if (start == end || count == 0) return (sequence); CHECK_TEST_0(); /* Resolve comparison function, and expected result of comparison */ if (comparison == NONE) { if (test_not == UNSPEC) { if (test == UNSPEC) lambda = Oeql; else lambda = test; expect = 1; } else { lambda = test_not; expect = 0; } FUNCTION_CHECK(lambda); } else expect = comparison == IFNOT ? 0 : 1; /* Check for fast path to comparison function */ code = FCODE(lambda); /* Initialize for loop */ copy = count; result = sequence; inplace = function == DELETE || function == NSUBSTITUTE; xlength = end - start; /* String is easier */ if (STRINGP(sequence)) { char *buffer, *string; if (comparison == NONE) { CHECK_SCHAR(item); } if (substitute) { CHECK_SCHAR(newitem); } if (from_end == NIL) { xstart = start; xend = end; xinc = 1; } else { xstart = end - 1; xend = start - 1; xinc = -1; } string = THESTR(sequence); buffer = LispMalloc(length + 1); /* Copy leading bytes, if any */ for (i = 0; i < start; i++) buffer[i] = string[i]; for (j = xstart; i != xend && count > 0; i += xinc) { compare = SCHAR(string[i]); if (key != UNSPEC) { compare = APPLY1(key, compare); /* Value returned by the key predicate may not be protected */ GC_PROTECT(compare); if (comparison == NONE) value = FCOMPARE(lambda, item, compare, code); else value = APPLY1(lambda, compare) != NIL; /* Unprotect value returned by the key predicate */ GC_LEAVE(); } else { if (comparison == NONE) value = FCOMPARE(lambda, item, compare, code); else value = APPLY1(lambda, compare) != NIL; } if (value != expect) { buffer[j] = string[i]; j += xinc; } else { if (substitute) { buffer[j] = SCHAR_VALUE(newitem); j += xinc; } else --count; } } if (count != copy && from_end != NIL) memmove(buffer + start, buffer + copy - count, count); /* Copy remaining bytes, if any */ for (; i < length; i++, j++) buffer[j] = string[i]; buffer[j] = '\0'; xlength = length - (copy - count); if (inplace) { CHECK_STRING_WRITABLE(sequence); /* result is a pointer to sequence */ LispFree(THESTR(sequence)); LispMused(buffer); THESTR(sequence) = buffer; STRLEN(sequence) = xlength; } else result = LSTRING2(buffer, xlength); } /* If inplace, need to update CAR and CDR of sequence */ else { LispObj *list, *object; LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength); if (!CONSP(sequence)) list = sequence->data.array.list; else list = sequence; /* Put data in a vector */ for (i = 0, object = list; i < start; i++) object = CDR(object); for (i = 0; i < xlength; i++, object = CDR(object)) objects[i] = CAR(object); if (from_end == NIL) { xstart = 0; xend = xlength; xinc = 1; } else { xstart = xlength - 1; xend = -1; xinc = -1; } /* Check if needs to remove something */ for (i = xstart; i != xend && count > 0; i += xinc) { compare = objects[i]; if (key != UNSPEC) { compare = APPLY1(key, compare); GC_PROTECT(compare); if (comparison == NONE) value = FCOMPARE(lambda, item, compare, code); else value = APPLY1(lambda, compare) != NIL; GC_LEAVE(); } else { if (comparison == NONE) value = FCOMPARE(lambda, item, compare, code); else value = APPLY1(lambda, compare) != NIL; } if (value == expect) { if (substitute) objects[i] = newitem; else objects[i] = NULL; --count; } } if (copy != count) { LispObj *cons = NIL; i = 0; object = list; if (inplace) { /* While result is NIL, skip initial elements of sequence */ result = start ? list : NIL; /* Skip initial elements, if any */ for (; i < start; i++, cons = object, object = CDR(object)) ; } /* Copy initial elements, if any */ else { result = NIL; if (start) { result = cons = CONS(CAR(list), NIL); GC_PROTECT(result); for (++i, object = CDR(list); i < start; i++, object = CDR(object)) { RPLACD(cons, CONS(CAR(object), NIL)); cons = CDR(cons); } } } /* Skip initial removed elements, if any */ for (i = 0; i < xlength && objects[i] == NULL; i++) ; for (i = 0; i < xlength; i++, object = CDR(object)) { if (objects[i]) { if (inplace) { if (result == NIL) result = cons = object; else { RPLACD(cons, object); cons = CDR(cons); } if (function == NSUBSTITUTE) RPLACA(cons, objects[i]); } else { if (result == NIL) { result = cons = CONS(objects[i], NIL); GC_PROTECT(result); } else { RPLACD(cons, CONS(objects[i], NIL)); cons = CDR(cons); } } } } if (inplace) { if (result == NIL) result = object; else RPLACD(cons, object); if (!CONSP(sequence)) { result = sequence; CAR(result)->data.array.dim = FIXNUM(length - (copy - count)); } } else if (end < length) { i = end; /* Copy ending elements, if any */ if (result == NIL) { result = cons = CONS(CAR(object), NIL); GC_PROTECT(result); object = CDR(object); i++; } for (; i < length; i++, object = CDR(object)) { RPLACD(cons, CONS(CAR(object), NIL)); cons = CDR(cons); } } } /* Release comparison vector */ LispFree(objects); } GC_LEAVE(); return (result); } LispObj * Lisp_Remove(LispBuiltin *builtin) /* remove item sequence &key from-end test test-not start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, REMOVE, NONE)); } LispObj * Lisp_RemoveIf(LispBuiltin *builtin) /* remove-if predicate sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IF)); } LispObj * Lisp_RemoveIfNot(LispBuiltin *builtin) /* remove-if-not predicate sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IFNOT)); } LispObj * Lisp_Remprop(LispBuiltin *builtin) /* remprop symbol indicator */ { LispObj *symbol, *indicator; indicator = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); return (LispRemAtomProperty(symbol->data.atom, indicator)); } LispObj * Lisp_Return(LispBuiltin *builtin) /* return &optional result */ { unsigned blevel = lisp__data.block.block_level; LispObj *result; result = ARGUMENT(0); while (blevel) { LispBlock *block = lisp__data.block.block[--blevel]; if (block->type == LispBlockClosure) /* if reached a function call */ break; if (block->type == LispBlockTag && block->tag == NIL) { lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result); LispBlockUnwind(block); BLOCKJUMP(block); } } LispDestroy("%s: no visible NIL block", STRFUN(builtin)); /*NOTREACHED*/ return (NIL); } LispObj * Lisp_ReturnFrom(LispBuiltin *builtin) /* return-from name &optional result */ { unsigned blevel = lisp__data.block.block_level; LispObj *name, *result; result = ARGUMENT(1); name = ARGUMENT(0); if (name != NIL && name != T && !SYMBOLP(name)) LispDestroy("%s: %s is not a valid block name", STRFUN(builtin), STROBJ(name)); while (blevel) { LispBlock *block = lisp__data.block.block[--blevel]; if (name == block->tag && (block->type == LispBlockTag || block->type == LispBlockClosure)) { lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result); LispBlockUnwind(block); BLOCKJUMP(block); } if (block->type == LispBlockClosure) /* can use return-from only in the current function */ break; } LispDestroy("%s: no visible block named %s", STRFUN(builtin), STROBJ(name)); /*NOTREACHED*/ return (NIL); } static LispObj * LispXReverse(LispBuiltin *builtin, int inplace) /* nreverse sequence reverse sequence */ { long length; LispObj *list, *result = NIL; LispObj *sequence; sequence = ARGUMENT(0); /* Do error checking for arrays and object type. */ length = LispLength(sequence); if (length <= 1) return (sequence); switch (XOBJECT_TYPE(sequence)) { case LispString_t: { long i; char *from, *to; from = THESTR(sequence) + length - 1; if (inplace) { char temp; CHECK_STRING_WRITABLE(sequence); to = THESTR(sequence); for (i = 0; i < length / 2; i++) { temp = to[i]; to[i] = from[-i]; from[-i] = temp; } result = sequence; } else { to = LispMalloc(length + 1); to[length] = '\0'; for (i = 0; i < length; i++) to[i] = from[-i]; result = STRING2(to); } } return (result); case LispCons_t: if (inplace) { long i, j; LispObj *temp; /* For large lists this can be very slow, but for small * amounts of data, this avoid allocating a buffer to * to store the CAR of the sequence. This is only done * to not destroy the contents of a variable. */ for (i = 0, list = sequence; i < (length + 1) / 2; i++, list = CDR(list)) ; length /= 2; for (i = 0; i < length; i++, list = CDR(list)) { for (j = length - i - 1, result = sequence; j > 0; j--, result = CDR(result)) ; temp = CAR(list); RPLACA(list, CAR(result)); RPLACA(result, temp); } return (sequence); } list = sequence; break; case LispArray_t: if (inplace) { sequence->data.array.list = LispReverse(sequence->data.array.list); return (sequence); } list = sequence->data.array.list; break; default: /* LispNil_t */ return (result); } { GC_ENTER(); LispObj *cons; result = cons = CONS(CAR(list), NIL); GC_PROTECT(result); for (list = CDR(list); CONSP(list); list = CDR(list)) { RPLACD(cons, CONS(CAR(list), NIL)); cons = CDR(cons); } result = LispReverse(result); GC_LEAVE(); } if (ARRAYP(sequence)) { list = result; result = LispNew(list, NIL); result->type = LispArray_t; result->data.array.list = list; result->data.array.dim = sequence->data.array.dim; result->data.array.rank = sequence->data.array.rank; result->data.array.type = sequence->data.array.type; result->data.array.zero = sequence->data.array.zero; } return (result); } LispObj * Lisp_Reverse(LispBuiltin *builtin) /* reverse sequence */ { return (LispXReverse(builtin, 0)); } LispObj * Lisp_Rplaca(LispBuiltin *builtin) /* rplaca place value */ { LispObj *place, *value; value = ARGUMENT(1); place = ARGUMENT(0); CHECK_CONS(place); RPLACA(place, value); return (place); } LispObj * Lisp_Rplacd(LispBuiltin *builtin) /* rplacd place value */ { LispObj *place, *value; value = ARGUMENT(1); place = ARGUMENT(0); CHECK_CONS(place); RPLACD(place, value); return (place); } LispObj * Lisp_Search(LispBuiltin *builtin) /* search sequence1 sequence2 &key from-end test test-not key start1 start2 end1 end2 */ { int code = 0, expect, value; long start1, start2, end1, end2, length1, length2, off1, off2, offset = -1; LispObj *cmp1, *cmp2, *list1 = NIL, *lambda; SeqInfo seq1, seq2; LispObj *sequence1, *sequence2, *from_end, *test, *test_not, *key, *ostart1, *ostart2, *oend1, *oend2; oend2 = ARGUMENT(9); oend1 = ARGUMENT(8); ostart2 = ARGUMENT(7); ostart1 = ARGUMENT(6); key = ARGUMENT(5); test_not = ARGUMENT(4); test = ARGUMENT(3); from_end = ARGUMENT(2); sequence2 = ARGUMENT(1); sequence1 = ARGUMENT(0); LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1, &start1, &end1, &length1); LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2, &start2, &end2, &length2); /* Check for special conditions */ if (start1 == end1) return (FIXNUM(end2)); else if (start2 == end2) return (start1 == end1 ? FIXNUM(start2) : NIL); CHECK_TEST(); if (from_end == UNSPEC) from_end = NIL; SETSEQ(seq1, sequence1); SETSEQ(seq2, sequence2); length1 = end1 - start1; length2 = end2 - start2; /* update start of sequences */ if (start1) { if (seq1.type == LispString_t) seq1.data.string += start1; else { for (cmp1 = seq1.data.list; start1; cmp1 = CDR(cmp1), --start1) ; seq1.data.list = cmp1; } end1 = length1; } if (start2) { if (seq2.type == LispString_t) seq2.data.string += start2; else { for (cmp2 = seq2.data.list; start2; cmp2 = CDR(cmp2), --start2) ; seq2.data.list = cmp2; } end2 = length2; } /* easier case */ if (from_end == NIL) { LispObj *list2 = NIL; /* while a match is possible */ while (end2 - start2 >= length1) { /* prepare to search */ off1 = 0; off2 = start2; if (seq1.type != LispString_t) list1 = seq1.data.list; if (seq2.type != LispString_t) list2 = seq2.data.list; /* for every element that must match in sequence1 */ while (off1 < length1) { if (seq1.type == LispString_t) cmp1 = SCHAR(seq1.data.string[off1]); else cmp1 = CAR(list1); if (seq2.type == LispString_t) cmp2 = SCHAR(seq2.data.string[off2]); else cmp2 = CAR(list2); if (key != UNSPEC) { cmp1 = APPLY1(key, cmp1); cmp2 = APPLY1(key, cmp2); } /* compare elements */ value = FCOMPARE(lambda, cmp1, cmp2, code); if (value != expect) break; /* update offsets/sequence pointers */ ++off1; ++off2; if (seq1.type != LispString_t) list1 = CDR(list1); if (seq2.type != LispString_t) list2 = CDR(list2); } /* if everything matched */ if (off1 == end1) { offset = off2 - length1; break; } /* update offset/sequence2 pointer */ ++start2; if (seq2.type != LispString_t) seq2.data.list = CDR(seq2.data.list); } } else { /* allocate vector if required, only list2 requires it. * list1 can be traversed forward */ if (seq2.type != LispString_t) { cmp2 = seq2.data.list; seq2.data.vector = LispMalloc(sizeof(LispObj*) * length2); for (off2 = 0; off2 < end2; off2++, cmp2 = CDR(cmp2)) seq2.data.vector[off2] = CAR(cmp2); } /* while a match is possible */ while (end2 >= length1) { /* prepare to search */ off1 = 0; off2 = end2 - length1; if (seq1.type != LispString_t) list1 = seq1.data.list; /* for every element that must match in sequence1 */ while (off1 < end1) { if (seq1.type == LispString_t) cmp1 = SCHAR(seq1.data.string[off1]); else cmp1 = CAR(list1); if (seq2.type == LispString_t) cmp2 = SCHAR(seq2.data.string[off2]); else cmp2 = seq2.data.vector[off2]; if (key != UNSPEC) { cmp1 = APPLY1(key, cmp1); cmp2 = APPLY1(key, cmp2); } /* Compare elements */ value = FCOMPARE(lambda, cmp1, cmp2, code); if (value != expect) break; /* Update offsets */ ++off1; ++off2; if (seq1.type != LispString_t) list1 = CDR(list1); } /* If all elements matched */ if (off1 == end1) { offset = off2 - length1; break; } /* Update offset */ --end2; } if (seq2.type != LispString_t) LispFree(seq2.data.vector); } return (offset == -1 ? NIL : FIXNUM(offset)); } /* * ext::getenv */ LispObj * Lisp_Setenv(LispBuiltin *builtin) /* setenv name value &optional overwrite */ { char *name, *value; LispObj *oname, *ovalue, *overwrite; overwrite = ARGUMENT(2); ovalue = ARGUMENT(1); oname = ARGUMENT(0); CHECK_STRING(oname); name = THESTR(oname); CHECK_STRING(ovalue); value = THESTR(ovalue); setenv(name, value, overwrite != UNSPEC && overwrite != NIL); value = getenv(name); return (value ? STRING(value) : NIL); } LispObj * Lisp_Set(LispBuiltin *builtin) /* set symbol value */ { LispAtom *atom; LispObj *symbol, *value; value = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); atom = symbol->data.atom; if (atom->dyn) LispSetVar(symbol, value); else if (atom->watch || !atom->a_object) LispSetAtomObjectProperty(atom, value); else { CHECK_CONSTANT(symbol); SETVALUE(atom, value); } return (value); } LispObj * Lisp_SetDifference(LispBuiltin *builtin) /* set-difference list1 list2 &key test test-not key */ { return (LispListSet(builtin, SETDIFFERENCE)); } LispObj * Lisp_SetExclusiveOr(LispBuiltin *builtin) /* set-exclusive-or list1 list2 &key test test-not key */ { return (LispListSet(builtin, SETEXCLUSIVEOR)); } LispObj * Lisp_NsetExclusiveOr(LispBuiltin *builtin) /* nset-exclusive-or list1 list2 &key test test-not key */ { return (LispListSet(builtin, NSETEXCLUSIVEOR)); } LispObj * Lisp_SetQ(LispBuiltin *builtin) /* setq &rest form */ { LispObj *result, *variable, *form; form = ARGUMENT(0); result = NIL; for (; CONSP(form); form = CDR(form)) { variable = CAR(form); CHECK_SYMBOL(variable); CHECK_CONSTANT(variable); form = CDR(form); if (!CONSP(form)) LispDestroy("%s: odd number of arguments", STRFUN(builtin)); result = EVAL(CAR(form)); LispSetVar(variable, result); } return (result); } LispObj * Lisp_Psetq(LispBuiltin *builtin) /* psetq &rest form */ { GC_ENTER(); int base = gc__protect; LispObj *value, *symbol, *list, *form; form = ARGUMENT(0); /* parallel setq, first pass evaluate values and basic error checking */ for (list = form; CONSP(list); list = CDR(list)) { symbol = CAR(list); CHECK_SYMBOL(symbol); list = CDR(list); if (!CONSP(list)) LispDestroy("%s: odd number of arguments", STRFUN(builtin)); value = EVAL(CAR(list)); GC_PROTECT(value); } /* second pass, assign values */ for (; CONSP(form); form = CDDR(form)) { symbol = CAR(form); CHECK_CONSTANT(symbol); LispSetVar(symbol, lisp__data.protect.objects[base++]); } GC_LEAVE(); return (NIL); } LispObj * Lisp_Setf(LispBuiltin *builtin) /* setf &rest form */ { LispAtom *atom; LispObj *setf, *place, *value, *result = NIL, *data; LispObj *form; form = ARGUMENT(0); for (; CONSP(form); form = CDR(form)) { place = CAR(form); form = CDR(form); if (!CONSP(form)) LispDestroy("%s: odd number of arguments", STRFUN(builtin)); value = CAR(form); if (!POINTERP(place)) goto invalid_place; if (XSYMBOLP(place)) { CHECK_CONSTANT(place); result = EVAL(value); (void)LispSetVar(place, result); } else if (XCONSP(place)) { /* it really should not be required to protect any object * evaluated here, but is done for safety in case one of * the evaluated forms returns data not gc protected, what * could cause surprises if the object is garbage collected * before finishing setf. */ GC_ENTER(); setf = CAR(place); if (!SYMBOLP(setf)) goto invalid_place; if (!CONSP(CDR(place))) goto invalid_place; value = EVAL(value); GC_PROTECT(value); atom = setf->data.atom; if (atom->a_defsetf == 0) { if (atom->a_defstruct && atom->property->structure.function >= 0) { /* Use a default setf method for the structure field, as * if this definition have been done * (defsetf THE-STRUCT-FIELD (struct) (value) * `(lisp::struct-store 'THE-STRUCT-FIELD ,struct ,value)) */ place = CDR(place); data = CAR(place); if (CONSP(CDR(place))) goto invalid_place; data = EVAL(data); GC_PROTECT(data); result = APPLY3(Ostruct_store, setf, data, value); GC_LEAVE(); continue; } /* Must also expand macros */ else if (atom->a_function && atom->property->fun.function->funtype == LispMacro) { result = LispRunSetfMacro(atom, CDR(place), value); continue; } goto invalid_place; } place = CDR(place); setf = setf->data.atom->property->setf; if (SYMBOLP(setf)) { LispObj *arguments, *cons; if (!CONSP(CDR(place))) { arguments = EVAL(CAR(place)); GC_PROTECT(arguments); result = APPLY2(setf, arguments, value); } else if (!CONSP(CDDR(place))) { arguments = EVAL(CAR(place)); GC_PROTECT(arguments); cons = EVAL(CADR(place)); GC_PROTECT(cons); result = APPLY3(setf, arguments, cons, value); } else { arguments = cons = CONS(EVAL(CAR(place)), NIL); GC_PROTECT(arguments); for (place = CDR(place); CONSP(place); place = CDR(place)) { RPLACD(cons, CONS(EVAL(CAR(place)), NIL)); cons = CDR(cons); } RPLACD(cons, CONS(value, NIL)); result = APPLY(setf, arguments); } } else result = LispRunSetf(atom->property->salist, setf, place, value); GC_LEAVE(); } else goto invalid_place; } return (result); invalid_place: LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place)); /*NOTREACHED*/ return (NIL); } LispObj * Lisp_Psetf(LispBuiltin *builtin) /* psetf &rest form */ { int base; GC_ENTER(); LispAtom *atom; LispObj *setf, *place = NIL, *value, *data; LispObj *form; form = ARGUMENT(0); /* parallel setf, first pass evaluate values and basic error checking */ base = gc__protect; for (setf = form; CONSP(setf); setf = CDR(setf)) { if (!POINTERP(CAR(setf))) goto invalid_place; setf = CDR(setf); if (!CONSP(setf)) LispDestroy("%s: odd number of arguments", STRFUN(builtin)); value = EVAL(CAR(setf)); GC_PROTECT(value); } /* second pass, assign values */ for (; CONSP(form); form = CDDR(form)) { place = CAR(form); value = lisp__data.protect.objects[base++]; if (XSYMBOLP(place)) { CHECK_CONSTANT(place); (void)LispSetVar(place, value); } else if (XCONSP(place)) { LispObj *arguments, *cons; int xbase = lisp__data.protect.length; setf = CAR(place); if (!SYMBOLP(setf)) goto invalid_place; if (!CONSP(CDR(place))) goto invalid_place; atom = setf->data.atom; if (atom->a_defsetf == 0) { if (atom->a_defstruct && atom->property->structure.function >= 0) { place = CDR(place); data = CAR(place); if (CONSP(CDR(place))) goto invalid_place; data = EVAL(data); GC_PROTECT(data); (void)APPLY3(Ostruct_store, setf, data, value); lisp__data.protect.length = xbase; continue; } else if (atom->a_function && atom->property->fun.function->funtype == LispMacro) { (void)LispRunSetfMacro(atom, CDR(place), value); lisp__data.protect.length = xbase; continue; } goto invalid_place; } place = CDR(place); setf = setf->data.atom->property->setf; if (SYMBOLP(setf)) { if (!CONSP(CDR(place))) { arguments = EVAL(CAR(place)); GC_PROTECT(arguments); (void)APPLY2(setf, arguments, value); } else if (!CONSP(CDDR(place))) { arguments = EVAL(CAR(place)); GC_PROTECT(arguments); cons = EVAL(CADR(place)); GC_PROTECT(cons); (void)APPLY3(setf, arguments, cons, value); } else { arguments = cons = CONS(EVAL(CAR(place)), NIL); GC_PROTECT(arguments); for (place = CDR(place); CONSP(place); place = CDR(place)) { RPLACD(cons, CONS(EVAL(CAR(place)), NIL)); cons = CDR(cons); } RPLACD(cons, CONS(value, NIL)); (void)APPLY(setf, arguments); } lisp__data.protect.length = xbase; } else (void)LispRunSetf(atom->property->salist, setf, place, value); } else goto invalid_place; } GC_LEAVE(); return (NIL); invalid_place: LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place)); /*NOTREACHED*/ return (NIL); } LispObj * Lisp_Sleep(LispBuiltin *builtin) /* sleep seconds */ { long sec, msec; double value, dsec; LispObj *seconds; seconds = ARGUMENT(0); value = -1.0; switch (OBJECT_TYPE(seconds)) { case LispFixnum_t: value = FIXNUM_VALUE(seconds); break; case LispDFloat_t: value = DFLOAT_VALUE(seconds); break; default: break; } if (value < 0.0 || value > MOST_POSITIVE_FIXNUM) LispDestroy("%s: %s is not a positive fixnum", STRFUN(builtin), STROBJ(seconds)); msec = modf(value, &dsec) * 1e6; sec = dsec; if (sec) sleep(sec); if (msec) usleep(msec); return (NIL); } /* * This function is called recursively, but the contents of "list2" are * kept gc protected until it returns to LispSort. This is required partly * because the "gc protection logic" protects an object, not the contents * of the c pointer. */ static LispObj * LispMergeSort(LispObj *list, LispObj *predicate, LispObj *key, int code) { int protect; LispObj *list1, *list2, *left, *right, *result, *cons; /* Check if list length is larger than 1 */ if (!CONSP(list) || !CONSP(CDR(list))) return (list); list1 = list2 = list; for (;;) { list = CDR(list); if (!CONSP(list)) break; list = CDR(list); if (!CONSP(list)) break; list2 = CDR(list2); } cons = list2; list2 = CDR(list2); RPLACD(cons, NIL); protect = 0; if (lisp__data.protect.length + 2 >= lisp__data.protect.space) LispMoreProtects(); lisp__data.protect.objects[lisp__data.protect.length++] = list2; list1 = LispMergeSort(list1, predicate, key, code); list2 = LispMergeSort(list2, predicate, key, code); left = CAR(list1); right = CAR(list2); if (key != UNSPEC) { protect = lisp__data.protect.length; left = APPLY1(key, left); lisp__data.protect.objects[protect] = left; right = APPLY1(key, right); lisp__data.protect.objects[protect + 1] = right; } result = NIL; for (;;) { if ((FCOMPARE(predicate, left, right, code)) == 0 && (FCOMPARE(predicate, right, left, code)) == 1) { /* right is "smaller" */ if (result == NIL) result = list2; else RPLACD(cons, list2); cons = list2; list2 = CDR(list2); if (!CONSP(list2)) { RPLACD(cons, list1); break; } right = CAR(list2); if (key != UNSPEC) { right = APPLY1(key, right); lisp__data.protect.objects[protect + 1] = right; } } else { /* left is "smaller" */ if (result == NIL) result = list1; else RPLACD(cons, list1); cons = list1; list1 = CDR(list1); if (!CONSP(list1)) { RPLACD(cons, list2); break; } left = CAR(list1); if (key != UNSPEC) { left = APPLY1(key, left); lisp__data.protect.objects[protect] = left; } } } if (key != UNSPEC) lisp__data.protect.length = protect; return (result); } /* XXX The first version made a copy of the list and then adjusted * the CARs of the list. To minimize GC time now it is now doing * the sort inplace. So, instead of writing just (sort variable) * now it is required to write (setq variable (sort variable)) * if the variable should always keep all elements. */ LispObj * Lisp_Sort(LispBuiltin *builtin) /* sort sequence predicate &key key */ { GC_ENTER(); int istring, code; long length; char *string; LispObj *list, *work, *cons = NULL; LispObj *sequence, *predicate, *key; key = ARGUMENT(2); predicate = ARGUMENT(1); sequence = ARGUMENT(0); length = LispLength(sequence); if (length < 2) return (sequence); list = sequence; istring = XSTRINGP(sequence); if (istring) { CHECK_STRING_WRITABLE(sequence); /* Convert string to list */ string = THESTR(sequence); work = cons = CONS(SCHAR(string[0]), NIL); GC_PROTECT(work); for (++string; *string; ++string) { RPLACD(cons, CONS(SCHAR(*string), NIL)); cons = CDR(cons); } } else if (ARRAYP(list)) work = list->data.array.list; else work = list; FUNCTION_CHECK(predicate); code = FCODE(predicate); work = LispMergeSort(work, predicate, key, code); if (istring) { /* Convert list to string */ string = THESTR(sequence); for (; CONSP(work); ++string, work = CDR(work)) *string = SCHAR_VALUE(CAR(work)); } else if (ARRAYP(list)) list->data.array.list = work; else sequence = work; GC_LEAVE(); return (sequence); } LispObj * Lisp_Subseq(LispBuiltin *builtin) /* subseq sequence start &optional end */ { long start, end, length, seqlength; LispObj *sequence, *ostart, *oend, *result; oend = ARGUMENT(2); ostart = ARGUMENT(1); sequence = ARGUMENT(0); LispCheckSequenceStartEnd(builtin, sequence, ostart, oend, &start, &end, &length); seqlength = end - start; if (sequence == NIL) result = NIL; else if (XSTRINGP(sequence)) { char *string = LispMalloc(seqlength + 1); memcpy(string, THESTR(sequence) + start, seqlength); string[seqlength] = '\0'; result = STRING2(string); } else { GC_ENTER(); LispObj *object; if (end > start) { /* list or array */ int count; LispObj *cons; if (ARRAYP(sequence)) object = sequence->data.array.list; else object = sequence; /* goto first element to copy */ for (count = 0; count < start; count++, object = CDR(object)) ; result = cons = CONS(CAR(object), NIL); GC_PROTECT(result); for (++count, object = CDR(object); count < end; count++, object = CDR(object)) { RPLACD(cons, CONS(CAR(object), NIL)); cons = CDR(cons); } } else result = NIL; if (ARRAYP(sequence)) { object = LispNew(NIL, NIL); GC_PROTECT(object); object->type = LispArray_t; object->data.array.list = result; object->data.array.dim = CONS(FIXNUM(seqlength), NIL); object->data.array.rank = 1; object->data.array.type = sequence->data.array.type; object->data.array.zero = length == 0; result = object; } GC_LEAVE(); } return (result); } LispObj * Lisp_Subsetp(LispBuiltin *builtin) /* subsetp list1 list2 &key test test-not key */ { return (LispListSet(builtin, SUBSETP)); } LispObj * Lisp_Substitute(LispBuiltin *builtin) /* substitute newitem olditem sequence &key from-end test test-not start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, NONE)); } LispObj * Lisp_SubstituteIf(LispBuiltin *builtin) /* substitute-if newitem test sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IF)); } LispObj * Lisp_SubstituteIfNot(LispBuiltin *builtin) /* substitute-if-not newitem test sequence &key from-end start end count key */ { return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IFNOT)); } LispObj * Lisp_Symbolp(LispBuiltin *builtin) /* symbolp object */ { LispObj *object; object = ARGUMENT(0); return (SYMBOLP(object) ? T : NIL); } LispObj * Lisp_SymbolFunction(LispBuiltin *builtin) /* symbol-function symbol */ { LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); return (LispSymbolFunction(symbol)); } LispObj * Lisp_SymbolName(LispBuiltin *builtin) /* symbol-name symbol */ { LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); return (LispSymbolName(symbol)); } LispObj * Lisp_SymbolPackage(LispBuiltin *builtin) /* symbol-package symbol */ { LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); symbol = symbol->data.atom->package; return (symbol ? symbol : NIL); } LispObj * Lisp_SymbolPlist(LispBuiltin *builtin) /* symbol-plist symbol */ { LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); return (symbol->data.atom->a_property ? symbol->data.atom->property->properties : NIL); } LispObj * Lisp_SymbolValue(LispBuiltin *builtin) /* symbol-value symbol */ { LispAtom *atom; LispObj *symbol; symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); atom = symbol->data.atom; if (!atom->a_object || atom->property->value == UNBOUND) { if (atom->package == lisp__data.keyword) return (symbol); LispDestroy("%s: the symbol %s has no value", STRFUN(builtin), STROBJ(symbol)); } return (atom->dyn ? LispGetVar(symbol) : atom->property->value); } LispObj * Lisp_Tagbody(LispBuiltin *builtin) /* tagbody &rest body */ { GC_ENTER(); int stack, lex, length; LispObj *list, *body, *ptr, *tag, *labels, *map, **p_body; LispBlock *block; body = ARGUMENT(0); /* Save environment information */ stack = lisp__data.stack.length; lex = lisp__data.env.lex; length = lisp__data.env.length; /* Since the body may be large, and the code may iterate several * thousand times, it is not a bad idea to avoid checking all * elements of the body to verify if it is a tag. */ for (labels = map = NIL, ptr = body; CONSP(ptr); ptr = CDR(ptr)) { tag = CAR(ptr); switch (OBJECT_TYPE(tag)) { case LispNil_t: case LispAtom_t: case LispFixnum_t: /* Don't allow duplicated labels */ for (list = labels; CONSP(list); list = CDDR(list)) { if (CAR(list) == tag) LispDestroy("%s: tag %s specified more than once", STRFUN(builtin), STROBJ(tag)); } if (labels == NIL) { labels = CONS(tag, CONS(NIL, NIL)); map = CDR(labels); GC_PROTECT(labels); } else { RPLACD(map, CONS(tag, CONS(NIL, NIL))); map = CDDR(map); } break; case LispCons_t: /* Restart point for tag */ if (map != NIL && CAR(map) == NIL) RPLACA(map, ptr); break; default: break; } } /* Check for consecutive labels without code between them */ for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) { if (CADR(ptr) == NIL) { for (map = CDDR(ptr); CONSP(map); map = CDDR(map)) { if (CADR(map) != NIL) { RPLACA(CDR(ptr), CADR(map)); break; } } } } /* Initialize */ list = body; p_body = &body; block = LispBeginBlock(NIL, LispBlockBody); /* Loop */ if (setjmp(block->jmp) != 0) { /* Restore environment */ lisp__data.stack.length = stack; lisp__data.env.lex = lex; lisp__data.env.head = lisp__data.env.length = length; tag = lisp__data.block.block_ret; for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) { map = CAR(ptr); if (map == tag) break; } if (!CONSP(ptr)) LispDestroy("%s: no such tag %s", STRFUN(builtin), STROBJ(tag)); *p_body = CADR(ptr); } /* Execute code */ for (; CONSP(body); body = CDR(body)) { LispObj *form = CAR(body); if (CONSP(form)) EVAL(form); } /* If got here, (go) not called, else, labels will be candidate to gc * when GC_LEAVE() be called by the code in the bottom of the stack. */ GC_LEAVE(); /* Finished */ LispEndBlock(block); /* Always return NIL */ return (NIL); } LispObj * Lisp_The(LispBuiltin *builtin) /* the value-type form */ { LispObj *value_type, *form; form = ARGUMENT(1); value_type = ARGUMENT(0); form = EVAL(form); return (LispCoerce(builtin, form, value_type)); } LispObj * Lisp_Throw(LispBuiltin *builtin) /* throw tag result */ { unsigned blevel = lisp__data.block.block_level; LispObj *tag, *result; result = ARGUMENT(1); tag = ARGUMENT(0); tag = EVAL(tag); if (blevel == 0) LispDestroy("%s: not within a block", STRFUN(builtin)); while (blevel) { LispBlock *block = lisp__data.block.block[--blevel]; if (block->type == LispBlockCatch && tag == block->tag) { lisp__data.block.block_ret = EVAL(result); LispBlockUnwind(block); BLOCKJUMP(block); } } LispDestroy("%s: %s is not a valid tag", STRFUN(builtin), STROBJ(tag)); /*NOTREACHED*/ return (NIL); } static LispObj * LispTreeEqual(LispObj *left, LispObj *right, LispObj *test, int expect) { LispObj *cmp_left, *cmp_right; if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right))) return (NIL); if (CONSP(left)) { for (; CONSP(left) && CONSP(right); left = CDR(left), right = CDR(right)) { cmp_left = CAR(left); cmp_right = CAR(right); if ((OBJECT_TYPE(cmp_left)) ^ (OBJECT_TYPE(cmp_right))) return (NIL); if (CONSP(cmp_left)) { if (LispTreeEqual(cmp_left, cmp_right, test, expect) == NIL) return (NIL); } else { if (POINTERP(cmp_left) && (XQUOTEP(cmp_left) || XBACKQUOTEP(cmp_left))) { cmp_left = cmp_left->data.quote; cmp_right = cmp_right->data.quote; } else if (COMMAP(cmp_left)) { cmp_left = cmp_left->data.comma.eval; cmp_right = cmp_right->data.comma.eval; } if ((APPLY2(test, cmp_left, cmp_right) != NIL) != expect) return (NIL); } } if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right))) return (NIL); } if (POINTERP(left) && (XQUOTEP(left) || XBACKQUOTEP(left))) { left = left->data.quote; right = right->data.quote; } else if (COMMAP(left)) { left = left->data.comma.eval; right = right->data.comma.eval; } return ((APPLY2(test, left, right) != NIL) == expect ? T : NIL); } LispObj * Lisp_TreeEqual(LispBuiltin *builtin) /* tree-equal tree-1 tree-2 &key test test-not */ { int expect; LispObj *compare; LispObj *tree_1, *tree_2, *test, *test_not; test_not = ARGUMENT(3); test = ARGUMENT(2); tree_2 = ARGUMENT(1); tree_1 = ARGUMENT(0); CHECK_TEST_0(); if (test_not != UNSPEC) { expect = 0; compare = test_not; } else { if (test == UNSPEC) test = Oeql; expect = 1; compare = test; } return (LispTreeEqual(tree_1, tree_2, compare, expect)); } LispObj * Lisp_Typep(LispBuiltin *builtin) /* typep object type */ { LispObj *result = NULL; LispObj *object, *type; type = ARGUMENT(1); object = ARGUMENT(0); if (SYMBOLP(type)) { Atom_id atom = ATOMID(type); if (OBJECT_TYPE(object) == LispStruct_t) result = ATOMID(CAR(object->data.struc.def)) == atom ? T : NIL; else if (type->data.atom->a_defstruct && type->data.atom->property->structure.function == STRUCT_NAME) result = NIL; else if (atom == Snil) result = object == NIL ? T : NIL; else if (atom == St) result = object == T ? T : NIL; else if (atom == Satom) result = !CONSP(object) ? T : NIL; else if (atom == Ssymbol) result = SYMBOLP(object) || object == NIL || object == T ? T : NIL; else if (atom == Sinteger) result = INTEGERP(object) ? T : NIL; else if (atom == Srational) result = RATIONALP(object) ? T : NIL; else if (atom == Scons || atom == Slist) result = CONSP(object) ? T : NIL; else if (atom == Sstring) result = STRINGP(object) ? T : NIL; else if (atom == Scharacter) result = SCHARP(object) ? T : NIL; else if (atom == Scomplex) result = COMPLEXP(object) ? T : NIL; else if (atom == Svector || atom == Sarray) result = ARRAYP(object) ? T : NIL; else if (atom == Skeyword) result = KEYWORDP(object) ? T : NIL; else if (atom == Sfunction) result = LAMBDAP(object) ? T : NIL; else if (atom == Spathname) result = PATHNAMEP(object) ? T : NIL; else if (atom == Sopaque) result = OPAQUEP(object) ? T : NIL; } else if (CONSP(type)) { if (OBJECT_TYPE(object) == LispStruct_t && SYMBOLP(CAR(type)) && ATOMID(CAR(type)) == Sstruct && SYMBOLP(CAR(CDR(type))) && CDR(CDR(type)) == NIL) { result = ATOMID(CAR(object->data.struc.def)) == ATOMID(CAR(CDR(type))) ? T : NIL; } } else if (type == NIL) result = object == NIL ? T : NIL; else if (type == T) result = object == T ? T : NIL; if (result == NULL) LispDestroy("%s: bad type specification %s", STRFUN(builtin), STROBJ(type)); return (result); } LispObj * Lisp_Union(LispBuiltin *builtin) /* union list1 list2 &key test test-not key */ { return (LispListSet(builtin, UNION)); } LispObj * Lisp_Nunion(LispBuiltin *builtin) /* nunion list1 list2 &key test test-not key */ { return (LispListSet(builtin, NUNION)); } LispObj * Lisp_Unless(LispBuiltin *builtin) /* unless test &rest body */ { LispObj *result, *test, *body; body = ARGUMENT(1); test = ARGUMENT(0); result = NIL; test = EVAL(test); RETURN_COUNT = 0; if (test == NIL) { for (; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); } return (result); } /* * ext::until */ LispObj * Lisp_Until(LispBuiltin *builtin) /* until test &rest body */ { LispObj *result, *test, *body, *prog; body = ARGUMENT(1); test = ARGUMENT(0); result = NIL; for (;;) { if ((result = EVAL(test)) == NIL) { for (prog = body; CONSP(prog); prog = CDR(prog)) (void)EVAL(CAR(prog)); } else break; } return (result); } LispObj * Lisp_UnwindProtect(LispBuiltin *builtin) /* unwind-protect protect &rest cleanup */ { LispObj *result, **presult = &result; int did_jump, *pdid_jump = &did_jump, destroyed; LispBlock *block; LispObj *protect, *cleanup, **pcleanup = &cleanup; cleanup = ARGUMENT(1); protect = ARGUMENT(0); /* run protected code */ *presult = NIL; *pdid_jump = 1; block = LispBeginBlock(NIL, LispBlockProtect); if (setjmp(block->jmp) == 0) { *presult = EVAL(protect); *pdid_jump = 0; } LispEndBlock(block); if (!lisp__data.destroyed && *pdid_jump) *presult = lisp__data.block.block_ret; destroyed = lisp__data.destroyed; lisp__data.destroyed = 0; /* run cleanup, unprotected code */ if (CONSP(*pcleanup)) for (; CONSP(cleanup); cleanup = CDR(cleanup)) (void)EVAL(CAR(cleanup)); if (destroyed) { /* in case there is another unwind-protect */ LispBlockUnwind(NULL); /* if not, just return to the toplevel */ lisp__data.destroyed = 1; LispDestroy("."); } return (result); } static LispObj * LispValuesList(LispBuiltin *builtin, int check_list) { long i, count; LispObj *result; LispObj *list; list = ARGUMENT(0); count = LispLength(list) - 1; if (count >= 0) { result = CAR(list); if ((RETURN_CHECK(count)) != count) LispDestroy("%s: too many values", STRFUN(builtin)); RETURN_COUNT = count; for (i = 0, list = CDR(list); count && CONSP(list); count--, i++, list = CDR(list)) RETURN(i) = CAR(list); if (check_list) { CHECK_LIST(list); } } else { RETURN_COUNT = -1; result = NIL; } return (result); } LispObj * Lisp_Values(LispBuiltin *builtin) /* values &rest objects */ { return (LispValuesList(builtin, 0)); } LispObj * Lisp_ValuesList(LispBuiltin *builtin) /* values-list list */ { return (LispValuesList(builtin, 1)); } LispObj * Lisp_Vector(LispBuiltin *builtin) /* vector &rest objects */ { LispObj *objects; objects = ARGUMENT(0); return (VECTOR(objects)); } LispObj * Lisp_When(LispBuiltin *builtin) /* when test &rest body */ { LispObj *result, *test, *body; body = ARGUMENT(1); test = ARGUMENT(0); result = NIL; test = EVAL(test); RETURN_COUNT = 0; if (test != NIL) { for (; CONSP(body); body = CDR(body)) result = EVAL(CAR(body)); } return (result); } /* * ext::while */ LispObj * Lisp_While(LispBuiltin *builtin) /* while test &rest body */ { LispObj *test, *body, *prog; body = ARGUMENT(1); test = ARGUMENT(0); for (;;) { if (EVAL(test) != NIL) { for (prog = body; CONSP(prog); prog = CDR(prog)) (void)EVAL(CAR(prog)); } else break; } return (NIL); } /* * ext::unsetenv */ LispObj * Lisp_Unsetenv(LispBuiltin *builtin) /* unsetenv name */ { char *name; LispObj *oname; oname = ARGUMENT(0); CHECK_STRING(oname); name = THESTR(oname); unsetenv(name); return (NIL); } LispObj * Lisp_XeditEltStore(LispBuiltin *builtin) /* lisp::elt-store sequence index value */ { int length, offset; LispObj *sequence, *oindex, *value; value = ARGUMENT(2); oindex = ARGUMENT(1); sequence = ARGUMENT(0); CHECK_INDEX(oindex); offset = FIXNUM_VALUE(oindex); length = LispLength(sequence); if (offset >= length) LispDestroy("%s: index %d too large for sequence length %d", STRFUN(builtin), offset, length); if (STRINGP(sequence)) { int ch; CHECK_STRING_WRITABLE(sequence); CHECK_SCHAR(value); ch = SCHAR_VALUE(value); if (ch < 0 || ch > 255) LispDestroy("%s: cannot represent character %d", STRFUN(builtin), ch); THESTR(sequence)[offset] = ch; } else { if (ARRAYP(sequence)) sequence = sequence->data.array.list; for (; offset > 0; offset--, sequence = CDR(sequence)) ; RPLACA(sequence, value); } return (value); } LispObj * Lisp_XeditPut(LispBuiltin *builtin) /* lisp::put symbol indicator value */ { LispObj *symbol, *indicator, *value; value = ARGUMENT(2); indicator = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); return (CAR(LispPutAtomProperty(symbol->data.atom, indicator, value))); } LispObj * Lisp_XeditSetSymbolPlist(LispBuiltin *builtin) /* lisp::set-symbol-plist symbol list */ { LispObj *symbol, *list; list = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); return (LispReplaceAtomPropertyList(symbol->data.atom, list)); } LispObj * Lisp_XeditVectorStore(LispBuiltin *builtin) /* lisp::vector-store array &rest values */ { LispObj *value, *list, *object; long rank, count, sequence, offset, accum; LispObj *array, *values; values = ARGUMENT(1); array = ARGUMENT(0); /* check for errors */ for (rank = 0, list = values; CONSP(list) && CONSP(CDR(list)); list = CDR(list), rank++) { CHECK_INDEX(CAR(values)); } if (rank == 0) LispDestroy("%s: too few subscripts", STRFUN(builtin)); value = CAR(list); if (STRINGP(array) && rank == 1) { long ch; long length = STRLEN(array); long offset = FIXNUM_VALUE(CAR(values)); CHECK_SCHAR(value); CHECK_STRING_WRITABLE(array); ch = SCHAR_VALUE(value); if (offset >= length) LispDestroy("%s: index %ld too large for sequence length %ld", STRFUN(builtin), offset, length); if (ch < 0 || ch > 255) LispDestroy("%s: cannot represent character %ld", STRFUN(builtin), ch); THESTR(array)[offset] = ch; return (value); } CHECK_ARRAY(array); if (rank != array->data.array.rank) LispDestroy("%s: too %s subscripts", STRFUN(builtin), rank < array->data.array.rank ? "few" : "many"); for (list = values, object = array->data.array.dim; CONSP(CDR(list)); list = CDR(list), object = CDR(object)) { if (FIXNUM_VALUE(CAR(list)) >= FIXNUM_VALUE(CAR(object))) LispDestroy("%s: %ld is out of range, index %ld", STRFUN(builtin), FIXNUM_VALUE(CAR(list)), FIXNUM_VALUE(CAR(object))); } for (count = sequence = 0, list = values; CONSP(CDR(list)); list = CDR(list), sequence++) { for (offset = 0, object = array->data.array.dim; offset < sequence; object = CDR(object), offset++) ; for (accum = 1, object = CDR(object); CONSP(object); object = CDR(object)) accum *= FIXNUM_VALUE(CAR(object)); count += accum * FIXNUM_VALUE(CAR(list)); } for (array = array->data.array.list; count > 0; array = CDR(array), count--) ; RPLACA(array, value); return (value); } LispObj * Lisp_XeditDocumentationStore(LispBuiltin *builtin) /* lisp::documentation-store symbol type string */ { LispDocType_t doc_type; LispObj *symbol, *type, *string; string = ARGUMENT(2); type = ARGUMENT(1); symbol = ARGUMENT(0); CHECK_SYMBOL(symbol); /* type is checked in LispDocumentationType() */ doc_type = LispDocumentationType(builtin, type); if (string == NIL) /* allow explicitly releasing memory used for documentation */ LispRemDocumentation(symbol, doc_type); else { CHECK_STRING(string); LispAddDocumentation(symbol, string, doc_type); } return (string); }