/* 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" #include #ifdef COMMONLISP /* external variables */ extern LVAL k_start,k_end,k_1start,k_1end,k_2start,k_2end; extern LVAL true; /* 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. */ /* (position-if pred seq) -- returns position of first match */ /* (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 */ /* find-if count-if -- previous Common Lisp extension, rewritten to allow all sequence types */ /* the keyword arguments :start and :end are now valid for the remove, delete, find position and count functions */ /* The author, Tom Almy, appologizes for using "goto" several places in this code. */ #define MAXSIZE ((unsigned)-1) /* the maximum unsigned integer value */ #ifdef ANSI static void getseqbounds(unsigned *start, unsigned *end, unsigned length, LVAL *startkey, LVAL *endkey) #else LOCAL VOID getseqbounds(start,end,length,startkey,endkey) unsigned *start, *end, length; LVAL *startkey, *endkey; #endif { LVAL arg; FIXTYPE temp; if (xlgkfixnum(*startkey,&arg)) { temp = (long)getfixnum(arg); if (temp < 0 || temp > length ) goto rangeError; *start = (unsigned)temp; } else *start = 0; if (xlgetkeyarg(*endkey, &arg) && arg != NIL) { if (!fixp(arg)) xlbadtype(arg); temp = (long)getfixnum(arg); if (temp < *start || temp > length) goto rangeError; *end = (unsigned)temp; } else *end = length; return; /* else there is a range error */ rangeError: xlerror("range error",arg); } /* dotest1 - call a test function with one argument */ /* this function was in xllist.c */ #ifdef ANSI static int dotest1(LVAL arg, LVAL fun) #else LOCAL int dotest1(arg,fun) LVAL arg,fun; #endif { LVAL *newfp; /* create the new call frame */ newfp = xlsp; pusharg(cvfixnum((FIXTYPE)(newfp - xlfp))); pusharg(fun); pusharg(cvfixnum((FIXTYPE)1)); pusharg(arg); xlfp = newfp; /* return the result of applying the test function */ return (xlapply(1) != NIL); } /* xelt - sequence reference function */ LVAL xelt() { 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 */ } if (ntype(seq) == STRING) { if (i >= getslength(seq)-1) goto badindex; return (cvchar(getstringch(seq,i))); } if (ntype(seq)!=VECTOR) xlbadtype(seq); /* type must be array */ /* range check the index */ if (i >= getsize(seq)) goto badindex; /* return the array element */ return (getelement(seq,(int)i)); badindex: xlerror("index out of bounds",index); return (NIL); /* eliminate warnings */ } /* xmap -- map function */ #ifdef ANSI static unsigned getlength(LVAL seq) #else LOCAL unsigned getlength(seq) LVAL seq; #endif { unsigned len; if (seq == NIL) return 0; switch (ntype(seq)) { case STRING: return (unsigned)(getslength(seq) - 1); case VECTOR: return (unsigned)(getsize(seq)); case CONS: len = 0; while (consp(seq)) { len++; seq = cdr(seq); } return len; default: xlbadtype(seq); return (0); /* ha ha */ } } LVAL xmap() { LVAL *newfp, fun, lists, val, last, x, y; unsigned len,temp, i; int argc, typ; /* protect some pointers */ xlstkcheck(3); xlsave(fun); xlsave(lists); xlsave(val); /* get the type of resultant */ if ((last = xlgetarg()) == NIL) { /* nothing is returned */ typ = 0; } else if ((typ = xlcvttype(last)) != CONS && typ != STRING && typ != VECTOR) { xlerror("invalid result type", last); } /* get the function to apply and argument sequences */ fun = xlgetarg(); val = NIL; lists = xlgetarg(); len = getlength(lists); argc = 1; /* check for invalid result size (actually only needed when 16bit ints)*/ if (((int)len)<0 && (typ==STRING || typ==VECTOR)) { xlerror("too long",last); } /* build a list of argument lists */ for (lists = last = consa(lists); moreargs(); last = cdr(last)) { val = xlgetarg(); if ((temp = getlength(val)) < len) len = temp; argc++; rplacd(last,(cons(val,NIL))); } /* initialize the result list */ switch (typ) { case VECTOR: val = newvector(len); break; case STRING: val = newstring(len+1); break; default: val = NIL; break; } /* loop through each of the argument lists */ for (i=0;in_string[i] = getchcode(x); break; } } /* restore the stack */ xlpopn(3); /* return the last test expression value */ return (val); } /* every, some, notany, notevery */ #define EVERY 0 #define SOME 1 #define NOTEVERY 2 #define NOTANY 3 #ifdef ANSI static LVAL xlmapwhile(int cond) #else LOCAL LVAL xlmapwhile(cond) int cond; #endif { int exitcond; LVAL *newfp, fun, lists, val, last, x, y; unsigned len,temp,i; int argc; /* protect some pointers */ xlstkcheck(2); xlsave(fun); xlsave(lists); /* get the function to apply and argument sequences */ fun = xlgetarg(); lists = xlgetarg(); len = getlength(lists); argc = 1; /* build a list of argument lists */ for (lists = last = consa(lists); moreargs(); last = cdr(last)) { val = xlgetarg(); if ((temp = getlength(val)) < len) len = temp; argc++; rplacd(last,(cons(val,NIL))); } switch (cond) { case SOME: case NOTANY: exitcond = TRUE; val = NIL; break; case EVERY: case NOTEVERY: exitcond = FALSE; val = true; break; } /* loop through each of the argument lists */ for (i=0;in_vdata[0]; /* combine the vectors */ while (moreargs()) { tmp = nextarg(); if (tmp != NIL) switch (ntype(tmp)) { case VECTOR: len = getsize(tmp); memcpy(vect, &getelement(tmp,0), len*sizeof(LVAL)); vect += len; break; case STRING: len = getslength(tmp)-1; for (i = 0; i < len; i++) { *vect++ = cvchar(getstringch(tmp,i)); } break; case CONS: while (consp(tmp)) { *vect++ = car(tmp); tmp = cdr(tmp); } break; } } /* return the new vector */ return (val); } #ifdef ANSI static LVAL cattocons(void) #else LOCAL LVAL cattocons() #endif { LVAL val,tmp,next,last=NIL; int len,i; xlsave1(val); /* protect against GC */ /* combine the lists */ while (moreargs()) { tmp = nextarg(); if (tmp != NIL) switch (ntype(tmp)) { case CONS: while (consp(tmp)) { next = consa(car(tmp)); if (val) rplacd(last,next); else val = next; last = next; tmp = cdr(tmp); } break; case VECTOR: len = getsize(tmp); for (i = 0; i len) xlerror("sequence index out of bounds",dst); start = (unsigned) temp; /* get the ending position */ if (moreargs()) { dst = nextarg(); if (dst == NIL) end = len; else if (fixp(dst)) { temp = (int)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+1); 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 CONS: xlsave1(dst); while (start--) src = cdr(src); while (len--) { next = consa(car(src)); if (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 xnreverse() { LVAL seq,val,next; unsigned int i,j; int ival; /* get the sequence to reverse */ seq = xlgetarg(); xllastarg(); if (seq == NIL) return (NIL); /* empty argument */ switch (ntype(seq)) { case CONS: val = NIL; while (consp(seq)) { next = cdr(seq); rplacd(seq,val); val = seq; seq = next; } break; case VECTOR: for (i = 0, j = getlength(seq)-1; i < j; i++, j--) { val = getelement(seq,i); setelement(seq,i,getelement(seq,j)); setelement(seq,j,val); } return seq; break; case STRING: for (i = 0, j=getslength(seq)-2 ; i < j; i++, j--) { ival = seq->n_string[i]; seq->n_string[i] = seq->n_string[j]; seq->n_string[j] = ival; } return seq; break; default: xlbadtype(seq); break; } /* return the sequence */ return (val); } /* xreverse - built-in function reverse -- new version */ LVAL xreverse() { LVAL seq,val; int i,len; /* get the sequence to reverse */ seq = xlgetarg(); xllastarg(); if (seq == NIL) 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) - 1; val = newstring(len+1); for (i = 0; i < len; i++) val->n_string[i] = seq->n_string[len-i-1]; val->n_string[len] = 0; break; default: xlbadtype(seq); break; } /* return the sequence */ return (val); } /* remif - common code for 'remove', 'remove-if', and 'remove-if-not' */ #ifdef ANSI static LVAL remif(int tresult, int expr) #else LOCAL LVAL remif(tresult,expr) int tresult,expr; #endif { LVAL x,seq,fcn,val,last,next; unsigned i,j,l; unsigned start,end; if (expr) { /* get the expression to remove and the sequence */ x = xlgetarg(); seq = xlgetarg(); xltest(&fcn,&tresult); } else { /* get the function and the sequence */ fcn = xlgetarg(); seq = xlgetarg(); /* xllastarg(); */ } if (seq == NIL) return NIL; getseqbounds(&start,&end,MAXSIZE,&k_start,&k_end); /* protect some pointers */ xlstkcheck(2); xlprotect(fcn); xlsave(val); /* remove matches */ switch (ntype(seq)) { case CONS: for (; consp(seq); seq = cdr(seq)) { long s=start, l=end-start; /* check to see if this element should be deleted */ /* force copy if count, as specified by end, is exhausted */ if (s-- > 0 || l-- <= 0 || (expr?dotest2(x,car(seq),fcn) :dotest1(car(seq),fcn)) != tresult) { next = consa(car(seq)); if (val) rplacd(last,next); else val = next; last = next; } } break; case VECTOR: val = newvector(l=getlength(seq)); for (i=j=0; i < l; i++) { if (i < start || i >= end || /* copy if out of range */ (expr?dotest2(x,getelement(seq,i),fcn) :dotest1(getelement(seq,i),fcn)) != tresult) { setelement(val,j++,getelement(seq,i)); } } if (l != j) { /* need new, shorter result -- too bad */ fcn = val; /* save value in protected cell */ val = newvector(j); memcpy(val->n_vdata, fcn->n_vdata, j*sizeof(LVAL)); } break; case STRING: l = getslength(seq)-1; val = newstring(l+1); for (i=j=0; i < l; i++) { if (i < start || i >= end || /* copy if out of range */ (expr?dotest2(x,cvchar(getstringch(seq,i)),fcn) :dotest1(cvchar(getstringch(seq,i)),fcn)) != tresult) { val->n_string[j++] = seq->n_string[i]; } } if (l != j) { /* need new, shorter result -- too bad */ fcn = val; /* save value in protected cell */ val = newstring(j+1); memcpy(val->n_string, fcn->n_string, j*sizeof(char)); val->n_string[j] = 0; } break; default: xlbadtype(seq); break; } /* restore the stack */ xlpopn(2); /* return the updated sequence */ return (val); } /* xremif - built-in function 'remove-if' -- enhanced version */ LVAL xremif() { return (remif(TRUE,FALSE)); } /* xremifnot - built-in function 'remove-if-not' -- enhanced version */ LVAL xremifnot() { return (remif(FALSE,FALSE)); } /* xremove - built-in function 'remove' -- enhanced version */ LVAL xremove() { return (remif(TRUE,TRUE)); } /* delif - common code for 'delete', 'delete-if', and 'delete-if-not' */ #ifdef ANSI static LVAL delif(int tresult, int expr) #else LOCAL LVAL delif(tresult,expr) int tresult,expr; #endif { LVAL x,seq,fcn,last,val; unsigned i,j,l; unsigned start,end; if (expr) { /* get the expression to delete and the sequence */ x = xlgetarg(); seq = xlgetarg(); xltest(&fcn,&tresult); } else { /* get the function and the sequence */ fcn = xlgetarg(); seq = xlgetarg(); /* xllastarg(); */ } if (seq == NIL) return NIL; getseqbounds(&start,&end,MAXSIZE,&k_start,&k_end); /* protect a pointer */ xlstkcheck(1); xlprotect(fcn); /* delete matches */ switch (ntype(seq)) { case CONS: end -= start; /* gives length */ /* delete leading matches */ while (consp(seq)) { if (start-- > 0 || (expr?dotest2(x,car(seq),fcn) :dotest1(car(seq),fcn)) != tresult) break; seq = cdr(seq); } val = last = seq; /* delete embedded matches */ if (consp(seq)) { /* skip the first non-matching element */ seq = cdr(seq); for (;consp(seq) && start-- > 0;seq=cdr(seq)); /* look for embedded matches */ while (consp(seq)) { /* check to see if this element should be deleted */ if (end-- > 0 && (expr?dotest2(x,car(seq),fcn) :dotest1(car(seq),fcn)) == tresult) rplacd(last,cdr(seq)); else last = seq; /* move to the next element */ seq = cdr(seq); } } break; case VECTOR: l = getlength(seq); for (i=j=0; i < l; i++) { if (i < start || i >= end || /* copy if out of range */ (expr?dotest2(x,getelement(seq,i),fcn) :dotest1(getelement(seq,i),fcn)) != tresult) { if (i != j) setelement(seq,j,getelement(seq,i)); j++; } } if (l != j) { /* need new, shorter result -- too bad */ fcn = seq; /* save value in protected cell */ seq = newvector(j); memcpy(seq->n_vdata, fcn->n_vdata, j*sizeof(LVAL)); } val = seq; break; case STRING: l = getslength(seq)-1; for (i=j=0; i < l; i++) { if (i < start || i >= end || /* copy if out of range */ (expr?dotest2(x,cvchar(getstringch(seq,i)),fcn) :dotest1(cvchar(getstringch(seq,i)),fcn)) != tresult) { if (i != j) seq->n_string[j] = seq->n_string[i]; j++; } } if (l != j) { /* need new, shorter result -- too bad */ fcn = seq; /* save value in protected cell */ seq = newstring(j+1); memcpy(seq->n_string, fcn->n_string, j*sizeof(char)); seq->n_string[j] = 0; } val = seq; break; default: xlbadtype(seq); break; } /* restore the stack */ xlpop(); /* return the updated sequence */ return (val); } /* xdelif - built-in function 'delete-if' -- enhanced version */ LVAL xdelif() { return (delif(TRUE,FALSE)); } /* xdelifnot - built-in function 'delete-if-not' -- enhanced version */ LVAL xdelifnot() { return (delif(FALSE,FALSE)); } /* xdelete - built-in function 'delete' -- enhanced version */ LVAL xdelete() { return (delif(TRUE,TRUE)); } #ifdef ADDEDTAA /* xcountif - built-in function 'count-if TAA MOD addition */ LVAL xcountif() { unsigned counter=0; unsigned i,l; unsigned start,end; LVAL seq, fcn; /* get the arguments */ fcn = xlgetarg(); seq = xlgetarg(); /* xllastarg(); */ if (seq == NIL) return (cvfixnum((FIXTYPE)0)); getseqbounds(&start,&end,MAXSIZE,&k_start,&k_end); xlstkcheck(1); xlprotect(fcn); /* examine arg and count */ switch (ntype(seq)) { case CONS: end -= start; for (; consp(seq) && start-- > 0; seq = cdr(seq)); for (; consp(seq); seq = cdr(seq)) if (end-- > 0 && dotest1(car(seq),fcn)) counter++; break; case VECTOR: l = getlength(seq); if (end < l) l = end; for (i=start; i < l; i++) if (dotest1(getelement(seq,i),fcn)) counter++; break; case STRING: l = getslength(seq)-1; if (end < l) l = end; for (i=start; i < l; i++) if (dotest1(cvchar(getstringch(seq,i)),fcn)) counter++; break; default: xlbadtype(seq); break; } xlpop(); return (cvfixnum((FIXTYPE)counter)); } /* xfindif - built-in function 'find-if' TAA MOD */ LVAL xfindif() { LVAL seq, fcn, val; unsigned start,end; unsigned i,l; fcn = xlgetarg(); seq = xlgetarg(); /* xllastarg(); */ if (seq == NIL) return NIL; getseqbounds(&start,&end,MAXSIZE,&k_start,&k_end); xlstkcheck(1); xlprotect(fcn); switch (ntype(seq)) { case CONS: end -= start; for (; consp(seq) && start-- > 0; seq = cdr(seq)); for (; consp(seq); seq = cdr(seq)) { if (end-- > 0 && dotest1(val=car(seq), fcn)) goto fin; } break; case VECTOR: l = getlength(seq); if (end < l) l = end; for (i=start; i < l; i++) if (dotest1(val=getelement(seq,i),fcn)) goto fin; break; case STRING: l = getslength(seq)-1; if (end < l) l = end; for (i=start; i < l; i++) if (dotest1(val=cvchar(getstringch(seq,i)),fcn)) goto fin; break; default: xlbadtype(seq); break; } val = NIL; /* not found */ fin: xlpop(); return (val); } /* xpositionif - built-in function 'position-if' TAA MOD */ LVAL xpositionif() { LVAL seq, fcn; unsigned start,end; unsigned count; unsigned i,l; fcn = xlgetarg(); seq = xlgetarg(); /* xllastarg(); */ if (seq == NIL) return NIL; getseqbounds(&start,&end,MAXSIZE,&k_start,&k_end); xlstkcheck(1); xlprotect(fcn); switch (ntype(seq)) { case CONS: end -= start; count = start; for (; consp(seq) && start-- > 0; seq = cdr(seq)); for (; consp(seq); seq = cdr(seq)) { if ((end-- > 0) && dotest1(car(seq), fcn)) goto fin; count++; } break; case VECTOR: l = getlength(seq); if (end < l) l = end; for (i=start; i < l; i++) if (dotest1(getelement(seq,i),fcn)) { count = i; goto fin; } break; case STRING: l = getslength(seq)-1; if (end < l) l = end; for (i=start; i < l; i++) if (dotest1(cvchar(getstringch(seq,i)),fcn)) { count = i; goto fin; } break; default: xlbadtype(seq); break; } xlpop(); /* not found */ return(NIL); fin: /* found */ xlpop(); return (cvfixnum((FIXTYPE)count)); } #endif /* xsearch -- search function */ LVAL xsearch() { LVAL seq1, seq2, fcn, temp1, temp2; unsigned start1, start2, end1, end2, len1, len2; unsigned i,j; int tresult,typ1, typ2; /* get the sequences */ seq1 = xlgetarg(); len1 = getlength(seq1); seq2 = xlgetarg(); 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); if (end2 - 1 - (end1 - start1) > len2) { end2 = len2 + 1 + (end1 - start1); if (end2 < start2) end2 = start2; } len1 = end1 - start1; /* calc lengths of sequences to test */ typ1 = ntype(seq1); typ2 = ntype(seq2); xlstkcheck(1); xlprotect(fcn); 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