/* xlseq.c - xlisp sequence functions */ /* Written by Thomas Almy, based on code: Copyright (c) 1985, by David Michael Betz All Rights Reserved Permission is granted for unrestricted non-commercial use */ #include "xlisp.h" /* this is part of the COMMON LISP extension: */ /* (elt seq index) -- generic sequence reference function */ /* (map type fcn seq1 [seq2 ...]) -- generic sequence mapping function */ /* type is one of cons, array, string, or nil */ /* (some fcn seq1 [seq2 ...]) -- apply fcn until non-nil */ /* also every notany and notevery */ /* (concatenate type seq1 [seq2 ...]) -- sequence concatenation function */ /* type is one of cons, array, or string. */ /* (search seq1 seq1 &key :test :test-not :start1 :end1 :start2 :end2) -- generic sequence searching function. */ /* subseq reverse remove remove-if remove-if-not delete delete-if delete-if-not -- rewritten to allow all sequence types */ /* Position, position-if, position-if-not, count, count-if, count-if-not, find, find-if, find-if-not */ /* the keyword arguments :start and :end are now valid for the remove, delete, find position and count functions */ /* the keyword argument :key is also valid where appropriate */ /* The author, Tom Almy, appologizes for using "goto" several places in this code. */ /**** should add :FROM-END to countif, xsearch, xreduce */ /* Function prototypes */ LOCAL VOID getseqbounds P5H(unsigned *, unsigned *, unsigned, LVAL, LVAL); LOCAL LVAL map P1H(int); LOCAL LVAL xlmapwhile P1H(int); LOCAL LVAL remif P3H(int, int, int); LOCAL LVAL substituteif P3H(int, int, int); LOCAL LVAL xlkitchensink P3H(int, int, int); LOCAL unsigned calclength(V); LOCAL LVAL cattovector P1H(LVAL); LOCAL LVAL cattocons(V); #define getlength(x) (listp(x) ? llength(x) : gettvecsize(x)) LOCAL VOID getseqbounds P5C(unsigned *, start, unsigned *, end, unsigned, length, LVAL, startkey, LVAL, endkey) { LVAL arg; FIXTYPE temp; if (xlgkfixnum(startkey,&arg)) { temp = getfixnum(arg); if (temp < 0 || temp > (FIXTYPE)length ) goto rangeError; *start = (unsigned)temp; } else *start = 0; if (xlgetkeyarg(endkey, &arg) && !null(arg)) { if (!fixp(arg)) xlbadtype(arg); temp = getfixnum(arg); if (temp < (FIXTYPE)*start || temp > (FIXTYPE)length) goto rangeError; *end = (unsigned)temp; } else *end = length; return; /* else there is a range error */ rangeError: xlerror("range error",arg); } /* xelt - sequence reference function */ LVAL xelt(V) { LVAL seq,index; FIXTYPE i; /* get the sequence and the index */ seq = xlgetarg(); index = xlgafixnum(); i = getfixnum(index); if (i < 0) goto badindex; xllastarg(); if (listp(seq)) { /* do like nth, but check for in range */ /* find the ith element */ while (consp(seq)) { if (i-- == 0) return (car(seq)); seq = cdr(seq); } goto badindex; /* end of list reached first */ } switch (ntype(seq)) { case VECTOR: case STRING: case TVEC: if (i >= (FIXTYPE)gettvecsize(seq)) goto badindex; return (gettvecelement(seq,(int)i)); default: xlbadtype(seq); } badindex: xlerror("index out of bounds",index); return (NIL); /* eliminate warnings */ } LOCAL LVAL map P1C(int, into) { FRAMEP newfp; LVAL fun, lists, val, last = NULL, x, y, etype = NULL; unsigned len, rlen, temp, i; int argc, typ = 0; /* protect some pointers */ xlstkcheck(3); xlsave(fun); xlsave(lists); xlsave(val); /* get the type of resultant, and resultant for map-into */ val = xlgetarg(); if (null(val)) typ = 0; /* return nothing */ else { if (into) { switch (ntype(val)) { case SYMBOL: if (! null(val)) xlbadtype(val); typ = CONS; break; case CONS: typ = CONS; break; case VECTOR: typ = VECTOR; break; case STRING: typ = STRING; break; case TVEC: typ = TVEC; break; default: xlbadtype(val); } } else { if ((typ = xlcvttype(val)) != CONS && typ != STRING && typ != VECTOR && typ != DARRAY) { xlerror("invalid result type", val); } if (typ == STRING) etype = a_char; else if (consp(val) && consp(cdr(val))) /* very superficial check! */ etype = car(cdr(val)); else etype = s_true; } } /* get the function to apply and argument sequences */ fun = xlgetarg(); /* Initialization code bug fixed, Luke Tierney 3/94 */ if (into) { /* MAP-INTO */ len = getlength(val); if (moreargs()) { /* handle first argument */ x = xlgaseq(); if ((temp = getlength(x)) < len) len = temp; argc = 1; lists = last = consa(x); } else { lists = NIL; argc = 0; } } else { /* MAP */ val = NIL; lists = xlgaseq(); len = getlength(lists); lists = last = consa(lists); argc = 1; } /* build a list of argument lists */ for (; moreargs(); last = cdr(last)) { x = xlgaseq(); if ((temp = getlength(x)) < len) len = temp; argc++; rplacd(last,(consa(x))); } if (into) { rlen = getlength(val); if (rlen < len) len = rlen; } else if (typ != 0) { /* initialize the result list */ switch (typ) { case DARRAY: case VECTOR: case STRING: case TVEC: val = mktvec(len, etype); break; default: val = mklist(len, NIL); break; } } else val = NIL; /* loop through each of the argument lists */ for (i=0,last=val;iMAXSLEN) xltoolong();*/ /*check for overflow*/ } /* restore the argument list */ xlargv = saveargv; xlargc = saveargc; return (unsigned)len; } LOCAL LVAL cattovector P1C(LVAL, etype) { LVAL tmp,val; unsigned len,i, j; /* find resulting length -- also validates argument types */ len = calclength(); /* protect the result */ xlsave1(val); /* create the result vector */ val = mktvec(len, etype); /* combine the vectors */ for (j = 0; moreargs();) { tmp = nextarg(); if (!null(tmp)) switch (ntype(tmp)) { case VECTOR: case STRING: case TVEC: len = gettvecsize(tmp); for (i = 0; i < len; i++, j++) settvecelement(val, j, gettvecelement(tmp,i)); break; case CONS: for (; consp(tmp); tmp = cdr(tmp), j++) settvecelement(val, j, car(tmp)); break; } } xlpop(); /* return the new vector */ return (val); } LOCAL LVAL cattocons(V) { LVAL val,tmp,next,last=NIL; unsigned len,i; long n; xlsave1(val); /* protect against GC */ /* combine the lists */ while (moreargs()) { tmp = nextarg(); if (!null(tmp)) switch (ntype(tmp)) { case CONS: /* check for circular list (Added 5/6/94) */ next = tmp; for (n = 0; consp(next); next=cdr(next)) { if (n++ > nnodes) xlcircular(); /*DIRTY, but we loose anyway!*/ } while (consp(tmp)) { next = consa(car(tmp)); if (!null(val)) rplacd(last,next); else val = next; last = next; tmp = cdr(tmp); } break; case VECTOR: case STRING: case TVEC: len = gettvecsize(tmp); for (i = 0; i < len; i++) { next = consa(gettvecelement(tmp,i)); if (!null(val)) rplacd(last,next); else val = next; last = next; } break; default: xlbadtype(tmp); break; /* need default because no precheck*/ } } xlpop(); return (val); } LVAL xconcatenate(V) { LVAL type, etype; switch (xlcvttype(type = xlgetarg())) { /* target type of data */ case CONS: return cattocons(); case STRING: return cattovector(a_char); case DARRAY: case VECTOR: case TVEC: if (consp(type) && consp(cdr(type))) /* very superficial check! */ etype = car(cdr(type)); else etype = s_true; return cattovector(etype); default: xlerror("invalid result type", type); return (NIL); /* avoid warning */ } } /* xsubseq - return a subsequence -- new version */ LVAL xsubseq(V) { unsigned start,end=0,len,esize; FIXTYPE temp; int srctype; LVAL src,dst; LVAL next,last=NIL; /* get sequence */ src = xlgaseq(); if (listp(src)) srctype = CONS; else srctype=ntype(src); /* get length */ len = getlength(src); /* get the starting position */ dst = xlgafixnum(); temp = getfixnum(dst); if (temp < 0 || temp > len) xlerror("sequence index out of bounds",dst); start = (unsigned) temp; /* get the ending position */ if (moreargs()) { dst = nextarg(); if (null(dst)) end = len; else if (fixp(dst)) { temp = getfixnum(dst); if (temp < start || temp > len) xlerror("sequence index out of bounds",dst); end = (unsigned) temp; } else xlbadtype(dst); } else end = len; xllastarg(); len = end - start; switch (srctype) { /* do the subsequencing */ case STRING: dst = newstring(len); MEMCPY(getstring(dst), getstring(src)+start, len); dst->n_string[len] = 0; break; case VECTOR: dst = newvector(len); MEMCPY(dst->n_vdata, &src->n_vdata[start], sizeof(LVAL)*len); break; case TVEC: dst = mktvec(len, gettvecetype(src)); esize = gettveceltsize(src); MEMCPY(gettvecdata(dst), ((char *) gettvecdata(src)) + start * esize, esize * len); break; case CONS: xlsave1(dst); while (start--) src = cdr(src); while (len--) { next = consa(car(src)); if (!null(dst)) rplacd(last,next); else dst = next; last = next; src = cdr(src); } xlpop(); break; } /* return the substring */ return (dst); } /* xnreverse -- built-in function nreverse (destructive reverse) */ LVAL xlnreverse P1C(LVAL, seq) { LVAL val; if (null(seq)) return (NIL); /* empty argument */ switch (ntype(seq)) { case CONS: val = NIL; while (consp(seq)) { LVAL next = cdr(seq); rplacd(seq,val); val = seq; seq = next; } break; case DARRAY: seq = getdarraydata(seq); /* drop through */ case VECTOR: case STRING: case TVEC: { int esize, qstep; char *p, *q; esize = gettveceltsize(seq); p = (char *) gettvecdata(seq); q = p + (gettvecsize(seq) - 1) * esize; qstep = 2 * esize; while (p < q) { char *pp = p + esize; while (p < pp) { int ival = *p; *p++ = *q; *q++ = ival; } q -= qstep; } val = seq; } break; default: val = xlbadtype(seq); break; } /* return the sequence */ return (val); } LVAL xnreverse(V) { LVAL seq; /* get the sequence to reverse */ seq = xlgaseq(); xllastarg(); return xlnreverse(seq); } /* xreverse - built-in function reverse -- new version */ LVAL xreverse(V) { LVAL seq,val; unsigned i,len; /* get the sequence to reverse */ seq = xlgetarg(); xllastarg(); if (null(seq)) return (NIL); /* empty argument */ switch (ntype(seq)) { case CONS: /* protect pointer */ xlsave1(val); /* append each element to the head of the result list */ for (val = NIL; consp(seq); seq = cdr(seq)) val = cons(car(seq),val); /* restore the stack */ xlpop(); break; case VECTOR: len = getsize(seq); val = newvector(len); for (i = 0; i < len; i++) setelement(val,i,getelement(seq,len-i-1)); break; case STRING: len = getslength(seq); val = newstring(len); for (i = 0; i < len; i++) val->n_string[i] = seq->n_string[len-i-1]; val->n_string[len] = 0; break; case TVEC: { int esize; char *p, *q; len = gettvecsize(seq); val = mktvec(len, gettvecetype(seq)); esize = gettveceltsize(seq); p = (char *) gettvecdata(val); q = ((char *) gettvecdata(seq)) + (len - 1) * esize; for (i = 0; i < len; i++, p += esize, q -= esize) MEMCPY(p, q, esize); break; } default: xlbadtype(seq); break; } /* return the sequence */ return (val); } /* remif - common code for remove and delete functions */ #define remtest(x,elt,fcn,kfcn) \ ((expr?dotest2(x,elt,fcn,kfcn):dotest1(elt,fcn,kfcn)) == tresult) LOCAL LVAL remif P3C(int, tresult, int, expr, int, destruct) { LVAL x,seq,fcn,last,val,elt; long i,j,l; unsigned start,end; int esize; LVAL kfcn; int fromend=FALSE; /* process from the end */ FIXTYPE count=-1; if (expr) { /* get the expression to delete and the sequence */ x = xlgetarg(); seq = xlgaseq(); xltest(&fcn,&tresult); } else { /* get the function and the sequence */ fcn = xlgetarg(); seq = xlgaseq(); x = NIL; /* to keep compiler happy */ } getseqbounds(&start,&end,getlength(seq),k_start,k_end); if (xlgetkeyarg(k_fromend, &val) && !null(val)) fromend=TRUE; if (xlgetkeyarg(k_count, &val)) { if (!null(val)) { if (!fixp(val)) xlbadtype(val); count = getfixnum(val); if (count < 0) count = 0; } } if (count <= 0) fromend=FALSE; /* save some time */ kfcn = xlkey(); xllastkey(); if (null(seq)) return NIL; /* protect some pointers */ xlstkcheck(4); xlsave1(val); xlprotect(seq); xlprotect(kfcn); xlprotect(fcn); /* delete matches */ switch (ntype(seq)) { case CONS: if (! destruct) seq = copylist(seq); if (fromend) { unsigned temp; seq = xlnreverse(seq); l = getlength(seq); temp = l - end; end = l - start; start = temp; } end -= start; /* delete leading matches, only if start is 0 */ if (start == 0) { while (consp(seq) && end > 0) { end--; if (count == 0 || ! remtest(x,car(seq),fcn,kfcn)) break; seq = cdr(seq); count--; } } val = last = seq; /* delete embedded matches */ if (consp(seq) && end > 0) { /* skip the first non-matching element, start == 0 */ if (start == 0) seq = cdr(seq); /* skip first elements if start > 0, correct "last" */ for (; consp(seq) && start-- > 0; last = seq, seq = cdr(seq)); /* look for embedded matches */ for (; consp(seq) && end-- > 0; seq = cdr(seq)) { /* check to see if this element should be deleted */ if (count != 0) { if (remtest(x,car(seq),fcn,kfcn)) { rplacd(last,cdr(seq)); count--; } else last = seq; } } } if (fromend) val = xlnreverse(val); break; case VECTOR: case STRING: case TVEC: l = gettvecsize(seq); esize = gettveceltsize(seq); if (end > l) end = l; if (! destruct) seq = copyvector(seq); xlsave1(elt); /* just to be safe for typed arrays -- prob. not needed */ if (fromend) { for (i = j = l - 1; i != -1; i--) { elt = gettvecelement(seq,i); if (i < start || i >= end || /* copy if out of range */ count == (FIXTYPE)(j-i) || ! remtest(x,elt,fcn,kfcn)) { if (i != j) settvecelement(seq,j,elt); j--; } } if (++j != 0) { /* need new, shorter result -- too bad */ fcn = seq; /* save value in protected cell */ l -= j; /* new length */ seq = mktvec(l, gettvecetype(seq)); MEMCPY(gettvecdata(seq), ((char *) gettvecdata(fcn)) + j * esize, l * esize); } } else { for (i = j = 0; i < l; i++) { elt = gettvecelement(seq, i); if (i < start || i >= end || /* copy if out of range */ count == (FIXTYPE)(i-j) || ! remtest(x,elt,fcn,kfcn)) { if (i != j) settvecelement(seq,j,elt); j++; } } if (l != j) { /* need new, shorter result -- too bad */ fcn = seq; /* save value in protected cell */ seq = mktvec(j, gettvecetype(seq)); MEMCPY(gettvecdata(seq), gettvecdata(fcn), j * esize); } } xlpop(); val = seq; break; default: xlbadtype(seq); break; } /* restore the stack */ xlpopn(4); /* return the updated sequence */ return (val); } /* xremif - built-in function 'remove-if' -- enhanced version */ LVAL xremif(V) { return (remif(TRUE,FALSE,FALSE)); } /* xremifnot - built-in function 'remove-if-not' -- enhanced version */ LVAL xremifnot(V) { return (remif(FALSE,FALSE,FALSE)); } /* xremove - built-in function 'remove' -- enhanced version */ LVAL xremove(V) { return (remif(TRUE,TRUE,FALSE)); } /* xdelif - built-in function 'delete-if' -- enhanced version */ LVAL xdelif(V) { return (remif(TRUE,FALSE,TRUE)); } /* xdelifnot - built-in function 'delete-if-not' -- enhanced version */ LVAL xdelifnot(V) { return (remif(FALSE,FALSE,TRUE)); } /* xdelete - built-in function 'delete' -- enhanced version */ LVAL xdelete(V) { return (remif(TRUE,TRUE,TRUE)); } #ifdef SUBSTITUTE #define subtest(x,y,fcn,kfcn) \ ((expr?dotest2(x,y,fcn,kfcn):dotest1(y,fcn,kfcn)) == tresult) /* substituteif - common code for 'substitute*' functions */ LOCAL LVAL substituteif P3C(int, tresult, int, expr, int, destruct) { LVAL x,seq,fcn,val,next,repl; long i,l; unsigned start,end; LVAL kfcn; int fromend=FALSE; /* process from the end */ FIXTYPE count=-1; repl = xlgetarg(); /* replacement expression */ if (expr) { /* get the expression to substitute and the sequence */ x = xlgetarg(); seq = xlgaseq(); xltest(&fcn,&tresult); } else { /* get the function and the sequence */ fcn = xlgetarg(); seq = xlgaseq(); x = NIL; /* to keep compiler happy */ } getseqbounds(&start,&end,getlength(seq),k_start,k_end); if (xlgetkeyarg(k_fromend, &val) && !null(val)) fromend=TRUE; if (xlgetkeyarg(k_count, &val)) { if (!null(val)) { if (!fixp(val)) xlbadtype(val); count = getfixnum(val); if (count < 0) count = 0; } } kfcn = xlkey(); xllastkey(); if (null(seq) || count == 0) return seq; /* protect some pointers */ xlstkcheck(4); xlprotect(seq); xlprotect(kfcn); xlprotect(fcn); xlsave(val); /* substitute matches */ switch (ntype(seq)) { case CONS: if (destruct) val = seq; else seq = val = copylist(seq); if (fromend) { unsigned temp; seq = val = xlnreverse(seq); l = getlength(seq); temp = l - end; end = l - start; start = temp; } end -= start; /* skip first elements if start > 0 */ for (; consp(seq) && start-- > 0; seq = cdr(seq)); /* look for embedded matches */ for (; consp(seq) && end-- > 0 && count != 0; seq = cdr(seq)) { /* check to see if this element should be replaced */ if (subtest(x,car(seq),fcn,kfcn)) { rplaca(seq, repl); count--; } } if (fromend) val = xlnreverse(val); break; case VECTOR: case STRING: case TVEC: l = gettvecsize(seq); if (end > l) end = l; val = destruct ? seq : copyvector(seq); xlsave1(next); i = fromend ? end : ((int) start) - 1; while ((count != 0) && (fromend ? i-- > start : ++i < end)) { next = gettvecelement(val, i); if (subtest(x,next,fcn,kfcn)) { settvecelement(val, i, repl); count--; } } xlpop(); break; default: xlbadtype(seq); break; } /* restore the stack */ xlpopn(4); /* return the updated sequence */ return (val); } /* xsubstituteif - built-in function 'substitute-if' -- enhanced version */ LVAL xsubstituteif(V) { return (substituteif(TRUE,FALSE,FALSE)); } /* xsubstituteifnot - built-in function 'substitute-if-not' -- enhanced version */ LVAL xsubstituteifnot(V) { return (substituteif(FALSE,FALSE,FALSE)); } /* xsubstitute - built-in function 'substitute' -- enhanced version */ LVAL xsubstitute(V) { return (substituteif(TRUE,TRUE,FALSE)); } /* xnsubstituteif - built-in function 'nsubstitute-if' -- enhanced version */ LVAL xnsubstituteif(V) { return (substituteif(TRUE,FALSE,TRUE)); } /* xnsubstituteifnot - built-in function 'nsubstitute-if-not' -- enhanced version */ LVAL xnsubstituteifnot(V) { return (substituteif(FALSE,FALSE,TRUE)); } /* xnsubstitute - built-in function 'nsubstitute' -- enhanced version */ LVAL xnsubstitute(V) { return (substituteif(TRUE,TRUE,TRUE)); } #endif #ifdef POSFCNS /* TAA MOD -- This is a rewrite done 6/93 to incorporate missing variations */ #define CNTFCN 0 /* three different function types */ #define FNDFCN 1 #define POSFCN 2 /* This is the test expression for all cases */ #ifdef KEYARG #define bigtest(i) ((expr?dotest2(x,i,fcn,kfcn):dotest1(i,fcn,kfcn)) == tresult) #else #define bigtest(i) ((expr?dotest2(x,i,fcn):dotest1(i,fcn)) == tresult) #endif /* count*, position*, and find* are all done by the following function */ LOCAL LVAL xlkitchensink P3C(int, ftype, int, tresult, int, expr) { LVAL seq, fcn; /* sequence and function */ LVAL x; /* expression (when expr is TRUE) */ unsigned start, end; /* start and end positions */ unsigned counter=0; /* for CNTFCN */ int count; /* for POSFCN */ LVAL val; /* for FNDFCN */ int fromend, i; #ifdef KEYARG LVAL kfcn; #endif if (expr) { x = xlgetarg(); /* get expression */ seq = xlgaseq(); /* get sequence */ xltest(&fcn, &tresult); /* get test function and invert from keyargs*/ } else { fcn = xlgetarg(); /* get function */ seq = xlgaseq(); /* get sequence */ x = NIL; /* to keep compiler happy */ } getseqbounds(&start,&end,getlength(seq),k_start,k_end); #ifdef KEYARG kfcn = xlkey(); /* get :key keyword arg */ #endif if (xlgetkeyarg(k_fromend, &val) && !null(val)) fromend = TRUE; else fromend = FALSE; xllastkey(); if (null(seq)) /* nothing to do, return default result */ return (ftype==CNTFCN ? cvfixnum((FIXTYPE)0) : NIL); #ifdef KEYARG xlstkcheck(4); xlprotect(kfcn); #else xlstkcheck(3); #endif xlprotect(fcn); xlprotect(seq); xlsave(val); count = (fromend) ? end - 1 : start; /* examine arg and count */ switch (ntype(seq)) { case CONS: end -= start; for (; consp(seq) && start-- > 0; seq = cdr(seq)) ; if (fromend) { val = seq; for (seq = NIL, i = 0; i < end; i++, val = cdr(val)) seq = cons(car(val), seq); } for (; end-- > 0; seq = cdr(seq)) { val = car(seq); if (bigtest(val)) { if (ftype==CNTFCN) counter++; else goto fin; } if (fromend) count--; else count++; } break; case VECTOR: case STRING: case TVEC: if (fromend) { for (; count >= (int) start; count--) { val = gettvecelement(seq, count); if (bigtest(val)) { if (ftype==CNTFCN) counter++; else goto fin; } } } else { for (; count < end; count++) { val = gettvecelement(seq, count); if (bigtest(val)) { if (ftype==CNTFCN) counter++; else goto fin; } } } break; default: xlbadtype(seq); break; } #ifdef KEYARG xlpopn(4); #else xlpopn(3); #endif return (ftype==CNTFCN ? cvfixnum((FIXTYPE)counter) : NIL); fin: #ifdef KEYARG xlpopn(4); #else xlpopn(3); #endif return (ftype==POSFCN ? cvfixnum((FIXTYPE)count) : val); } /* nine different functions are done by xlkitchensink */ LVAL xcount(V) { /* count */ return(xlkitchensink(CNTFCN,TRUE,TRUE)); } LVAL xcountif(V) { /* count-if */ return(xlkitchensink(CNTFCN,TRUE,FALSE)); } LVAL xcountifnot(V) { /* count-if-not */ return(xlkitchensink(CNTFCN,FALSE,FALSE)); } LVAL xposition(V) { /* position */ return(xlkitchensink(POSFCN,TRUE,TRUE)); } LVAL xpositionif(V) { /* position-if */ return(xlkitchensink(POSFCN,TRUE,FALSE)); } LVAL xpositionifnot(V) { /* position-if-not */ return(xlkitchensink(POSFCN,FALSE,FALSE)); } LVAL xfind(V) { /* find */ return(xlkitchensink(FNDFCN,TRUE,TRUE)); } LVAL xfindif(V) { /* find-if */ return(xlkitchensink(FNDFCN,TRUE,FALSE)); } LVAL xfindifnot(V) { /* find-if-not */ return(xlkitchensink(FNDFCN,FALSE,FALSE)); } #endif #ifdef SRCHFCN /* xsearch -- search function */ LVAL xsearch(V) { LVAL seq1, seq2, fcn, temp1, temp2, elt1, elt2; unsigned start1, start2, end1, end2, len1, len2; unsigned i,j; int tresult,typ1, typ2; #ifdef KEYARG LVAL kfcn; #endif /* get the sequences */ seq1 = xlgaseq(); len1 = getlength(seq1); seq2 = xlgaseq(); len2 = getlength(seq2); /* test/test-not args? */ xltest(&fcn,&tresult); /* check for start/end keys */ getseqbounds(&start1,&end1,len1,k_1start,k_1end); getseqbounds(&start2,&end2,len2,k_2start,k_2end); #ifdef KEYARG kfcn = xlkey(); #endif xllastkey(); /* calculate the true final search string location that needs to be checked (end2) */ if (end2 - start2 < end1 - start1 /* nothing to compare */ || end2 - start2 == 0) return (NIL); len1 = end1 - start1; /* calc lengths of sequences to test */ end2 -= len1; /* we don't need to compare with start loc beyond this value */ typ1 = ntype(seq1); typ2 = ntype(seq2); #ifdef KEYARG xlstkcheck(4); xlprotect(kfcn); #else xlstkcheck(3); #endif xlprotect(fcn); xlsave(elt1); xlsave(elt2); if (typ1 == CONS) { /* skip leading section of sequence 1 if a cons */ j = start1; while (j--) seq1 = cdr(seq1); } if (typ2 == CONS) { /* second string is cons */ i = start2; /* skip leading section of string 2 */ while (start2--) seq2 = cdr(seq2); for (;i<=end2;i++) { temp2 = seq2; if (typ1 == CONS) { temp1 = seq1; for (j = start1; j < end1; j++) { #ifdef KEYARG if (dotest2s(car(temp1),car(temp2),fcn,kfcn) != tresult) goto next1; #else if (dotest2(car(temp1),car(temp2),fcn) != tresult) goto next1; #endif temp1 = cdr(temp1); temp2 = cdr(temp2); } } else { for (j = start1; j < end1; j++) { elt1 = gettvecelement(seq1, j); #ifdef KEYARG if (dotest2s(elt1, car(temp2), fcn, kfcn) !=tresult) #else if (dotest2(elt1, car(temp2), fcn)!=tresult) #endif goto next1; temp2 = cdr(temp2); } } #ifdef KEYARG xlpopn(4); #else xlpopn(3); #endif return cvfixnum(i); next1: /* continue */ seq2 = cdr(seq2); } } else for (i = start2; i <= end2 ; i++) { /* second string is array/string */ if (typ1 == CONS) { temp1 = seq1; for (j = 0; j < len1; j++) { elt2 = gettvecelement(seq2, i+j); #ifdef KEYARG if (dotest2s(car(temp1), elt2, fcn,kfcn) != tresult) #else if (dotest2(car(temp1), elt2, fcn) != tresult) #endif goto next2; temp1 = cdr(temp1); } } else for (j=start1; j < end1; j++) { elt1 = gettvecelement(seq1,j); elt2 = gettvecelement(seq2,i+j-start1); #ifdef KEYARG if (dotest2s(elt1, elt2, fcn, kfcn) != tresult) #else if (dotest2(elt1, elt2, fcn) != tresult) #endif goto next2; } #ifdef KEYARG xlpopn(4); #else xlpopn(3); #endif return cvfixnum(i); next2:; /* continue */ } #ifdef KEYARG xlpopn(4); #else xlpopn(3); #endif return (NIL); /*no match*/ } #endif /* The following is based on code with the following copyright message: */ /* XLISP-STAT 2.0 Copyright (c) 1988, by Luke Tierney */ /* All Rights Reserved */ /* Permission is granted for unrestricted non-commercial use */ /* Extended by Tom Almy to put in a single C function, allow :start and :end keywords, correctly handle case of null(seq), and case where sequence is a string */ /* Common Lisp REDUCE function */ LVAL xreduce(V) { LVAL fcn, seq, initial_value; LVAL next, args, result; int has_init; unsigned start, end; fcn = xlgetarg(); seq = xlgaseq(); has_init = xlgetkeyarg(k_ivalue, &initial_value); getseqbounds(&start, &end, getlength(seq), k_start, k_end); xllastkey(); /* protect some pointers */ xlstkcheck(4); xlsave(next); xlsave(args); xlsave(result); xlprotect(fcn); args = cons(NIL, cons(NIL,NIL)); if (null(seq) || start==end) { result = has_init ? initial_value : xlapply(pushargs(fcn, NIL)); } else switch (ntype(seq)) { case CONS: end -= start; while (start-- > 0) seq = cdr(seq); /* skip to start */ next = seq; if (has_init) result = initial_value; else { result = car(next); next = cdr(next); end--; } for (; end-- > 0; next = cdr(next)) { rplaca(args, result); rplaca(cdr(args), car(next)); result = xlapply(pushargs(fcn, args)); } break; case VECTOR: case STRING: case TVEC: if (has_init) result = initial_value; else { result = gettvecelement(seq, start); start++; } for (; start < end; start++) { rplaca(args, result); rplaca(cdr(args), gettvecelement(seq, start)); result = xlapply(pushargs(fcn, args)); } break; default: xlbadtype(seq); } /* restore the stack frame */ xlpopn(4); return(result); } /* Common Lisp REMOVE-DUPLICATES function */ /* by Tom Almy */ /* unlike xllist.c version, this one works on all sequences and allows the :start and :end keywords. */ LVAL xremove_duplicates(V) { LVAL seq,fcn,val,next,tmp,item; LVAL last=NULL,vstart=NIL; unsigned i,j,l,k; unsigned start,end,esize; int tresult, fromend; #ifdef KEYARG LVAL kfcn; #endif /* get the sequence */ seq = xlgaseq(); /* get any optional args */ xltest(&fcn,&tresult); getseqbounds(&start,&end,getlength(seq),k_start,k_end); #ifdef KEYARG kfcn = xlkey(); #endif if (xlgetkeyarg(k_fromend, &tmp) && !null(tmp)) fromend = TRUE; else fromend = FALSE; xllastkey(); if (null(seq)) return NIL; /* protect some pointers */ #ifdef KEYARG xlstkcheck(5); xlprotect(kfcn); #else xlstkcheck(4); #endif xlsave(item); xlsave(tmp); xlprotect(fcn); xlsave(val); /* remove matches */ switch (ntype(seq)) { case CONS: end -= start; /* length of valid subsequence */ while (start-- > 0) { /* copy leading part intact */ next = consa(car(seq)); if (!null(val)) rplacd(last,next); else val=next; last= next; } if (fromend) { for (; end-- > 0; seq = cdr(seq)) { /* check to see if this element should be deleted */ item = car(seq); #ifdef KEYARG if (!null(kfcn)) item = xlapp1(kfcn,item); for (tmp=vstart; consp(tmp); tmp = cdr(tmp)) if (dotest2(item,car(tmp),fcn,kfcn)==tresult) goto cons_noxfer_fromend; #else for (tmp=vstart; consp(tmp); tmp = cdr(tmp)) if (dotest2(item,car(tmp),fcn)==tresult) goto cons_noxfer_fromend; #endif next = consa(car(seq)); if (!null(val)) rplacd(last,next); else val = next; last = next; if (null(vstart)) vstart = next; cons_noxfer_fromend:; } } else { for (; end-- > 1; seq = cdr(seq)) { /* check to see if this element should be deleted */ item = car(seq); #ifdef KEYARG if (!null(kfcn)) item = xlapp1(kfcn,item); for (l=end,tmp=cdr(seq); l-- >0; tmp = cdr(tmp)) if (dotest2(item,car(tmp),fcn,kfcn)==tresult) goto cons_noxfer; #else for (l=end,tmp=cdr(seq); l-- >0; tmp = cdr(tmp)) if (dotest2(item,car(tmp),fcn)==tresult) goto cons_noxfer; #endif next = consa(car(seq)); if (!null(val)) rplacd(last,next); else val = next; last = next; cons_noxfer:; } } /* now copy to end */ while (consp(seq)) { next = consa(car(seq)); if (!null(val)) rplacd(last,next); else val = next; last = next; seq = cdr(seq); } break; case VECTOR: case STRING: case TVEC: val = mktvec(l=gettvecsize(seq), gettvecetype(seq)); esize = gettveceltsize(seq); if (start>0) MEMCPY(gettvecdata(val), gettvecdata(seq),start * esize); for (i=j=start; i < end; i++) { item = gettvecelement(seq,i); #ifdef KEYARG if (!null(kfcn)) item = xlapp1(kfcn,item); if (fromend) { for (k=start; k 0) { /* elements at end to copy */ MEMCPY(((char *) gettvecdata(val)) + j * esize, ((char *) gettvecdata(seq)) + end * esize, (l - end) * esize); j += l - end; } if (l != j) { /* need new, shorter result -- too bad */ fcn = val; /* save value in protected cell */ val = mktvec(j, gettvecetype(seq)); MEMCPY(gettvecdata(val), gettvecdata(fcn), j * esize); } break; default: xlbadtype(seq); break; } /* restore the stack */ #ifdef KEYARG xlpopn(5); #else xlpopn(4); #endif /* return the updated sequence */ return (val); } /* Common Lisp COPY-SEQ function */ LVAL xcopyseq(V) { LVAL x; x = xlgaseq(); xllastarg(); if (listp(x)) return(copylist(x)); else return(copyvector(x)); } LVAL xreplace(V) { LVAL seq1, seq2; unsigned start1, start2, end1, end2, len1, len2; /* get the sequences */ seq1 = xlgaseq(); len1 = getlength(seq1); seq2 = xlgaseq(); len2 = getlength(seq2); /* check for start/end keys */ getseqbounds(&start1,&end1,len1,k_1start,k_1end); getseqbounds(&start2,&end2,len2,k_2start,k_2end); xlreplace(seq1, seq2, start1, end1, start2, end2); return seq1; }