home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: oref.c
- * Contents: bang, random, sect, subsc
- */
-
- #include "../h/config.h"
- #include "../h/rt.h"
- #include "rproto.h"
-
-
- /*
- * !x - generate successive values from object x.
- */
-
- OpDcl(bang,1,"!")
- {
- register word i, j, slen, rlen;
- register union block *bp;
- register dptr dp;
- register char *sp;
- int typ1;
- char sbuf[MaxCvtLen];
- FILE *fd;
-
- #ifdef RecordIO
- word status;
- #endif /* RecordIO */
-
- Arg2 = Arg1;
-
- if (DeRef(Arg1) == Error)
- RunErr(0, NULL);
- if ((typ1 = cvstr(&Arg1, sbuf)) != CvtFail) {
- /*
- * A string is being banged.
- */
- i = 1;
- while (i <= StrLen(Arg1)) {
- /*
- * Loop through the string using i as an index.
- */
- if (typ1 == Cvt) {
- /*
- * Arg1 was converted to a string, thus, the resulting string
- * cannot be modified and a trapped variable is not needed.
- * Make a one-character string out of the next character
- * in Arg1 and suspend it.
- */
- if (strreq((word)1) == Error)
- RunErr(0, NULL);
- StrLen(Arg0) = 1;
- StrLoc(Arg0) = alcstr(StrLoc(Arg1)+i-1, (word)1);
- Suspend;
- }
- else {
- /*
- * Arg1 is a string and thus a trapped variable must be made
- * for the one character string being suspended.
- */
- if (blkreq((word)sizeof(struct b_tvsubs)) == Error)
- RunErr(0, NULL);
- mksubs(&Arg2, &Arg1, i, (word)1, &Arg0);
- Suspend;
- Arg1 = Arg2;
- if (DeRef(Arg1) == Error)
- RunErr(0, NULL);
- if (!Qual(Arg1))
- RunErr(103, &Arg1);
- }
- i++;
- }
- }
- else {
- /*
- * Arg1 is not a string.
- */
- switch (Type(Arg1)) {
- case T_List:
- /*
- * Arg1 is a list. Chain through each list element block and for
- * each one, suspend with a variable pointing to each
- * element contained in the block.
- */
- bp = BlkLoc(Arg1);
- for (bp = bp->list.listhead; bp != NULL; bp = bp->lelem.listnext) {
- for (i = 0; i < bp->lelem.nused; i++) {
- j = bp->lelem.first + i;
- if (j >= bp->lelem.nslots)
- j -= bp->lelem.nslots;
- dp = &bp->lelem.lslots[j];
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- BlkLoc(Arg1) = bp; /* save in Arg1 since bp is untended */
- Suspend;
- bp = BlkLoc(Arg1); /* bp is untended, must reset */
- }
- }
- break;
-
-
- case T_File:
- /*
- * Arg1 is a file. Read the next line into the string space
- * and suspend the newly allocated string.
- */
- fd = BlkLoc(Arg1)->file.fd;
-
- #ifdef RecordIO
- status = BlkLoc(Arg1)->file.status;
- #endif /* RecordIO */
-
- if ((BlkLoc(Arg1)->file.status & Fs_Read) == 0)
- RunErr(212, &Arg1);
-
- #ifdef StandardLib
- if (BlkLoc(Arg1)->file.status & Fs_Writing) {
- fseek(fd, 0L, SEEK_CUR);
- BlkLoc(Arg1)->file.status &= ~Fs_Writing;
- }
- BlkLoc(Arg1)->file.status |= Fs_Reading;
- #endif /* StandardLib */
-
- for (;;) {
- StrLen(Arg0) = 0;
- do {
-
- #ifdef RecordIO
- if ((slen = (status & Fs_Record ?
- getrec(sbuf, MaxCvtLen, fd) :
- getstrg(sbuf, MaxCvtLen, fd))) == -1)
- #else /* RecordIO */
- if ((slen = getstrg(sbuf,MaxCvtLen,fd)) == -1)
- #endif /* RecordIO */
- Fail;
- rlen = slen < 0 ? (word)MaxCvtLen : slen;
- if (strreq(rlen) == Error)
- RunErr(0, NULL);
- sp = alcstr(sbuf,rlen);
- if (StrLen(Arg0) == 0)
- StrLoc(Arg0) = sp;
- StrLen(Arg0) += rlen;
- } while (slen < 0);
- Suspend;
- }
- break;
-
- case T_Table:
- /*
- * Arg1 is a table. Generate the element values.
- */
- MakeInt(2, &Arg2); /* indicate that we want the values */
- Forward(hgener); /* go to the hash generator */
-
- case T_Set:
- /*
- * Arg1 is a set. Generate the element values.
- */
- MakeInt(0, &Arg2); /* indicate that we want set elements */
- Forward(hgener); /* go to the hash generator */
-
- case T_Record:
- /*
- * Arg1 is a record. Loop through the fields and suspend
- * a variable pointing to each one.
- */
- bp = BlkLoc(Arg1);
- j = bp->record.recdesc->proc.nfields;
- for (i = 0; i < j; i++) {
- dp = &bp->record.fields[i];
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- Suspend;
- bp = BlkLoc(Arg1); /* bp is untended, must reset */
- }
- break;
-
- default: /* This object can not be compromised. */
- RunErr(116, &Arg1);
- }
- }
-
- /*
- * Eventually fail.
- */
- Fail;
- }
-
- #define RandVal (RanScale*(k_random=(RandA*(long)k_random+RandC)&0x7fffffffL))
-
- /*
- * ?x - produce a randomly selected element of x.
- */
-
- OpDcl(random,1,"?")
- {
- register word val, i, j, n;
- register union block *bp, *ep;
- struct b_slots *seg;
- char sbuf[MaxCvtLen];
- dptr dp;
- double rval;
-
- Arg2 = Arg1;
- if (DeRef(Arg1) == Error)
- RunErr(0, NULL);
-
- if (Qual(Arg1)) {
- /*
- * Arg1 is a string, produce a random character in it as the result.
- * Note that a substring trapped variable is returned.
- */
- if ((val = StrLen(Arg1)) <= 0)
- Fail;
- if (blkreq((word)sizeof(struct b_tvsubs)) == Error)
- RunErr(0, NULL);
- rval = RandVal; /* This form is used to get around */
- rval *= val; /* a bug in a certain C compiler */
- mksubs(&Arg2, &Arg1, (word)rval + 1, (word)1, &Arg0);
- Return;
- }
-
- switch (Type(Arg1)) {
- case T_Cset:
- /*
- * Arg1 is a cset. Convert it to a string, select a random character
- * of that string and return it. Note that a substring trapped
- * variable is not needed.
- */
- cvstr(&Arg1, sbuf);
- if ((val = StrLen(Arg1)) <= 0)
- Fail;
- if (strreq((word)1) == Error)
- RunErr(0, NULL);
- StrLen(Arg0) = 1;
- rval = RandVal;
- rval *= val;
- StrLoc(Arg0) = alcstr(StrLoc(Arg1)+(word)rval, (word)1);
- Return;
-
-
- case T_List:
- /*
- * Arg1 is a list. Set i to a random number in the range [1,*Arg1],
- * failing if the list is empty.
- */
- bp = BlkLoc(Arg1);
- val = bp->list.size;
- if (val <= 0)
- Fail;
- rval = RandVal;
- rval *= val;
- i = (word)rval + 1;
- j = 1;
- /*
- * Work down chain list of list blocks and find the block that
- * contains the selected element.
- */
- bp = bp->list.listhead;
- while (i >= j + bp->lelem.nused) {
- j += bp->lelem.nused;
- bp = bp->lelem.listnext;
- if (bp == NULL)
- syserr("list reference out of bounds in random");
- }
- /*
- * Locate the appropriate element and return a variable
- * that points to it.
- */
- i += bp->lelem.first - j;
- if (i >= bp->lelem.nslots)
- i -= bp->lelem.nslots;
- dp = &bp->lelem.lslots[i];
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- Return;
-
- case T_Table:
- case T_Set:
- /*
- * Arg1 is a table or a set. Set n to a random number in the range
- * [1,*Arg1], failing if the structure is empty.
- */
- bp = BlkLoc(Arg1);
- val = bp->table.size;
- if (val <= 0)
- Fail;
- rval = RandVal;
- rval *= val;
- n = (word)rval + 1;
- /*
- * Walk down the hash chains to find and return the n'th element.
- */
- for (i = 0; i < HSegs && (seg = bp->table.hdir[i]) != NULL; i++)
- for (j = segsize[i] - 1; j >= 0; j--)
- for (ep = seg->hslots[j]; ep != NULL; ep = ep->telem.clink)
- if (--n <= 0) {
- if (Type(Arg1) == T_Set) {
- /*
- * For a set, return the element value.
- */
- Arg0 = ep->selem.setmem;
- }
- else {
- /*
- * For a table, return a variable pointing to the
- * selected element.
- */
- dp = &ep->telem.tval;
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- }
- Return;
- }
-
- case T_Record:
- /*
- * Arg1 is a record. Set val to a random number in the range
- * [1,*Arg1] (*Arg1 is the number of fields), failing if the
- * record has no fields.
- */
- bp = BlkLoc(Arg1);
- val = bp->record.recdesc->proc.nfields;
- if (val <= 0)
- Fail;
- /*
- * Locate the selected element and return a variable
- * that points to it
- */
- rval = RandVal;
- rval *= val;
- dp = &bp->record.fields[(word)rval];
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- Return;
-
- #ifdef LargeInts
- case T_Bignum:
- if (bigrand(&Arg1, &Arg0) == Error) /* alcbignum failed */
- RunErr(0, NULL);
- Return;
- #endif /* LargeInts */
-
- default:
- /*
- * Try converting it to an integer
- */
- switch (cvint(&Arg1)) {
-
- case T_Integer:
- /*
- * Arg1 is an integer, be sure that it's non-negative.
- */
- val = (word)IntVal(Arg1);
- if (val < 0)
- RunErr(205, &Arg1);
-
- /*
- * val contains the integer value of Arg1. If val is 0, return
- * a real in the range [0,1], else return an integer in the
- * range [1,val].
- */
- if (val == 0) {
- rval = RandVal;
- if (makereal(rval, &Arg0) == Error)
- RunErr(0, NULL);
- }
- else {
- rval = RandVal;
- rval *= val;
- MakeInt((long)rval + 1, &Arg0);
- }
- Return;
-
- default:
- /*
- * Arg1 is of a type for which random generation is not supported
- */
- RunErr(113, &Arg1);
- }
- }
- }
-
- /*
- * x[i:j] - form a substring or list section of x.
- */
-
- OpDcl(sect,3,"[:]")
- {
- register word i, j, t;
- int typ1;
- char sbuf[MaxCvtLen];
-
- if (blkreq((word)sizeof(struct b_tvsubs)) == Error)
- RunErr(0, NULL);
-
- if (cvint(&Arg2) == CvtFail)
- RunErr(101, &Arg2);
- if (cvint(&Arg3) == CvtFail)
- RunErr(101, &Arg3);
-
- Arg4 = Arg1;
- if (DeRef(Arg1) == Error)
- RunErr(0, NULL);
-
- if (Arg1.dword == D_List) {
- i = cvpos(IntVal(Arg2), BlkLoc(Arg1)->list.size);
- if (i == CvtFail)
- Fail;
- j = cvpos(IntVal(Arg3), BlkLoc(Arg1)->list.size);
- if (j == CvtFail)
- Fail;
- if (i > j) {
- t = i;
- i = j;
- j = t;
- }
- if (cplist(&Arg1, &Arg0, i, j) == Error)
- RunErr(0, NULL);
- Return;
- }
-
- if ((typ1 = cvstr(&Arg1, sbuf)) == CvtFail)
- RunErr(110, &Arg1);
-
- i = cvpos(IntVal(Arg2), StrLen(Arg1));
- if (i == CvtFail)
- Fail;
- j = cvpos(IntVal(Arg3), StrLen(Arg1));
- if (j == CvtFail)
- Fail;
- if (i > j) { /* convert section to substring */
- t = i;
- i = j;
- j = t - j;
- }
- else
- j = j - i;
-
- if (typ1 == Cvt) {
- /*
- * A string was created - just return a string
- */
- if (strreq(j) == Error)
- RunErr(0, NULL);
- StrLen(Arg0) = j;
- StrLoc(Arg0) = alcstr(StrLoc(Arg1)+i-1, j);
- }
- else /* else make a substring tv */
- mksubs(&Arg4, &Arg1, i, j, &Arg0);
- Return;
- }
-
- /*
- * x[y] - access yth character or element of x.
- */
-
- OpDcl(subsc,2,"[]")
- {
- register word i, j;
- register union block *bp;
- register uword hn;
- int typ1, res;
- dptr dp;
- union block **dp1;
- char sbuf[MaxCvtLen];
-
- /*
- * Make a copy of Arg1.
- */
- Arg3 = Arg1;
-
- if (DeRef(Arg1) == Error)
- RunErr(0, NULL);
- if ((typ1 = cvstr(&Arg1, sbuf)) != CvtFail) {
- /*
- * Arg1 is a string, make sure that Arg2 is an integer.
- */
- if (cvint(&Arg2) == CvtFail)
- RunErr(101, &Arg2);
-
- /*
- * Convert Arg2 to a position in Arg1 and fail if the position is out
- * of bounds.
- */
- i = cvpos(IntVal(Arg2), StrLen(Arg1));
- if (i == CvtFail || i > StrLen(Arg1))
- Fail;
- if (typ1 == Cvt) {
- /*
- * Arg1 was converted to a string, so it cannot be assigned back into.
- * Just return a string containing the selected character.
- */
- if (strreq((word)1) == Error)
- RunErr(0, NULL);
- StrLen(Arg0) = 1;
- StrLoc(Arg0) = alcstr(StrLoc(Arg1)+i-1, (word)1);
- }
- else {
- /*
- * Arg1 is a string, make a substring trapped variable for the one
- * character substring selected and return it.
- */
- if (blkreq((word)sizeof(struct b_tvsubs)) == Error)
- RunErr(0, NULL);
- mksubs(&Arg3, &Arg1, i, (word)1, &Arg0);
- }
- Return;
- }
-
- /*
- * Arg1 is not a string or convertible to one, see if it's an aggregate.
- */
- switch (Type(Arg1)) {
- case T_List:
- /*
- * Make sure that Arg2 is an integer and that the
- * subscript is in range.
- */
- if (cvint(&Arg2) == CvtFail)
- RunErr(101, &Arg2);
- i = cvpos(IntVal(Arg2), BlkLoc(Arg1)->list.size);
- if (i == CvtFail || i > BlkLoc(Arg1)->list.size)
- Fail;
-
- /*
- * Locate the list-element block containing the desired
- * element.
- */
- bp = BlkLoc(Arg1)->list.listhead;
- j = 1;
- while (bp != NULL && i >= j + bp->lelem.nused) {
- j += bp->lelem.nused;
- bp = bp->lelem.listnext;
- }
-
- /*
- * Locate the desired element and return a pointer to it.
- */
- i += bp->lelem.first - j;
- if (i >= bp->lelem.nslots)
- i -= bp->lelem.nslots;
- dp = &bp->lelem.lslots[i];
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- Return;
-
- case T_Table:
- /*
- * Arg1 is a table. Locate the appropriate bucket
- * based on the hash value.
- */
- if (blkreq((word)sizeof(struct b_tvtbl)) == Error)
- RunErr(0, NULL);
- hn = hash(&Arg2);
- dp1 = memb(BlkLoc(Arg1), &Arg2, hn, &res);
- if (res == 1) {
- bp = *dp1;
- dp = &bp->telem.tval;
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- }
- else {
- /*
- * Arg1[Arg2] is not in the table, make a table element trapped
- * variable and return it as the result.
- */
- Arg0.dword = D_Tvtbl;
- BlkLoc(Arg0) = (union block *)alctvtbl(&Arg1, &Arg2, hn);
- }
- Return;
-
- case T_Record:
- /*
- * Arg1 is a record. Convert Arg2 to an integer and be sure that it
- * it is in range as a field number.
- */
- if (cvint(&Arg2) == CvtFail)
- RunErr(101, &Arg2);
- bp = BlkLoc(Arg1);
- i = cvpos(IntVal(Arg2), (word)(bp->record.recdesc->proc.nfields));
- if (i == CvtFail || i > bp->record.recdesc->proc.nfields)
- Fail;
- /*
- * Locate the appropriate field and return a pointer to it.
- */
- dp = &bp->record.fields[i-1];
- Arg0.dword = D_Var + ((word *)dp - (word *)bp);
- VarLoc(Arg0) = (dptr)bp;
- Return;
-
- default:
- /*
- * Arg1 is of a type that cannot be subscripted.
- */
- RunErr(114, &Arg1);
- }
- }
-