/*
 * File: omisc.c
 *  Contents: refresh, size, tabmat, toby
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"


/*
 * ^x - return an entry block for co-expression x from the refresh block.
 */

OpDcl(refresh,1,"^")
   {

#ifdef Coexpr
   register struct b_coexpr *sblkp;
   register struct b_refresh *rblkp;
   register dptr dp, dsp;
   register word *newsp;
   int na, nl, i;

   /*
    * Be sure a co-expression is being refreshed.
    */
   if (Qual(Arg1) || Arg1.dword != D_Coexpr) 
      RunErr(118, &Arg1);

   /*
    * Get a new co-expression stack and initialize.
    */
   if ((sblkp = alccoexp()) == NULL) 
      RunErr(0, NULL);
   sblkp->freshblk = BlkLoc(Arg1)->coexpr.freshblk;
   if (ChkNull(sblkp->freshblk))	/* &main cannot be refreshed */
      RunErr(215, &Arg1);

   /*
    * The interpreter stack starts at word after co-expression stack block.
    *  C stack starts at end of stack region on machines with down-growing C
    *  stacks and somewhere in the middle of the region.
    *
    * The C stack is aligned on a doubleword boundary.	For upgrowing
    *  stacks, the C stack starts in the middle of the stack portion
    *  of the static block.  For downgrowing stacks, the C stack starts
    *  at the last word of the static block.
    */
   newsp = (word *)((word)(char *)sblkp + sizeof(struct b_coexpr));

#ifdef UpStack
   sblkp->cstate[0] =
      ((word)((word)(char *)sblkp + (stksize - sizeof(*sblkp))/2)
       &~(WordSize*StackAlign-1));
#else					/* UpStack */
   sblkp->cstate[0] =
    ((word)((word)(char *)sblkp + stksize - WordSize)&~(WordSize*StackAlign-1));
#endif					/* UpStack */

#ifdef CoProcesses
   sblkp->cstate[1] = 0;
#endif


   sblkp->es_argp = (dptr)newsp;

   /*
    * Get pointer to refresh block and get number of arguments and locals.
    */

   rblkp = (struct b_refresh *)BlkLoc(sblkp->freshblk);
   na = (rblkp->pfmkr).pf_nargs + 1;
   nl = (int)rblkp->numlocals;

   /*
    * Copy arguments onto new stack.
    */
   dp = &rblkp->elems[0];
   dsp = (dptr)newsp;
   for (i = 1; i <= na; i++)
      *dsp++ = *dp++;

   /*
    * Copy procedure frame to new stack and point dsp to word after frame.
    */
   *((struct pf_marker *)dsp) = rblkp->pfmkr;
   sblkp->es_pfp = (struct pf_marker *)dsp;
/*   dsp = (dptr)((word *)dsp + Vwsizeof(*pfp)); */
   dsp = (dptr)((word)dsp + sizeof(word) * Vwsizeof(*pfp));
   sblkp->es_ipc.opnd = rblkp->ep;
   sblkp->es_gfp = 0;
   sblkp->es_efp = 0;
   sblkp->tvalloc = NULL;
   sblkp->es_ilevel = 0;

   /*
    * Copy locals to new stack and refresh block.
    */
   for (i = 1; i <= nl; i++)
      *dsp++ = *dp++;

   /*
    * Push two null descriptors on the stack.
    */
   *dsp++ = nulldesc;
   *dsp++ = nulldesc;

   sblkp->es_sp = (word *)dsp - 1;

   /*
    * Return the new co-expression.
    */
   Arg0.dword = D_Coexpr;
   BlkLoc(Arg0) = (union block *) sblkp;
   Return;

#else					/* Coexpr */
   RunErr(-401, NULL);
#endif					/* Coexpr */

   }

/*
 * *x - return size of string or object x.
 */

OpDcl(size,1,"*")
   {
   char sbuf[MaxCvtLen];
   word i;
   int j;
   union block *bp;

   if (Qual(Arg1)) {
      /*
       * If Arg1 is a string, return the length of the string.
       */
      i = StrLen(Arg1);
      }

   else {
      /*
       * Arg1 is not a string.  For most types, the size is in the size
       *  field of the block.
       *  structure.
       */
      switch (Type(Arg1)) {
         case T_List:
            i = BlkLoc(Arg1)->list.size;
            break;

         case T_Table:
            i = BlkLoc(Arg1)->table.size;
            break;

         case T_Set:
            i = BlkLoc(Arg1)->set.size;
            break;

         case T_Cset: {
	    register unsigned int w;

            i = BlkLoc(Arg1)->cset.size;
            if (i >= 0)
               break;
            bp = (union block *)BlkLoc(Arg1);
            i = 0;
            for (j = 0; j < CsetSize; j++)
	       for (w=bp->cset.bits[j]; w; w >>= 1)
		  if (w & 01)
		     i++;
            bp->cset.size = i;
            break;
	    }

         case T_Record:
            i = BlkLoc(Arg1)->record.recdesc->proc.nfields;
            break;

         case T_Coexpr:

            i = BlkLoc(Arg1)->coexpr.size;
            break;

         default:
            /*
             * Try to convert it to a string.
             */
            if (cvstr(&Arg1, sbuf) == CvtFail) 
               RunErr(112, &Arg1);	/* no notion of size */
            i = StrLen(Arg1);
         }
      }
   MakeInt(i, &Arg0);
   Return;
   }

/*
 * =x - tab(match(x)).  Reverses effects if resumed.
 */

OpDcl(tabmat,1,"=")
   {
   register word l;
   register char *s1, *s2;
   word i, j;
   char sbuf[MaxCvtLen];
   int type;

   /*
    * Arg1 must be a string.
    */
   if ((type = cvstr(&Arg1,sbuf)) == CvtFail) 
      RunErr(103, &Arg1);

   /*
    * Make a copy of &pos.
    */
   i = k_pos;

   /*
    * Fail if &subject[&pos:0] is not of sufficient length to contain Arg1.
    */
   j = StrLen(k_subject) - i + 1;
   if (j < StrLen(Arg1))
      Fail;

   /*
    * Get pointers to Arg1 (s1) and &subject (s2).  Compare them on a bytewise
    *  basis and fail if s1 doesn't match s2 for *s1 characters.
    */
   s1 = StrLoc(Arg1);
   s2 = StrLoc(k_subject) + i - 1;
   l = StrLen(Arg1);
   while (l-- > 0) {
      if (*s1++ != *s2++)
         Fail;
      }

   /*
    * Increment &pos to tab over the matched string and suspend the
    *  matched string.
    */
   l = StrLen(Arg1);
   k_pos += l;
   Arg0 = Arg1;
   if (type == Cvt) {		/* string is in buffer, copy */
      if (strreq(StrLen(Arg0)) == Error) 
         RunErr(0, NULL);
      StrLoc(Arg0) = alcstr(StrLoc(Arg0), StrLen(Arg0));
      }
   Suspend;

   /*
    * tabmat has been resumed, restore &pos and fail.
    */
   if (i > StrLen(k_subject) + 1) {
      RunErr(205, &tvky_pos.kyval);
      }
   else
      k_pos = i;
   Fail;
   }

/*
 * i to j by k - generate successive values.
 */

OpDcl(toby,3,"...")
   {
   long from;

   /*
    * Arg1 (from), Arg2 (to), and Arg3 (by) must be integers.
    *  Also, Arg3 must not be zero.
    */
   if (cvint(&Arg1) == CvtFail) 
      RunErr(101, &Arg1);
   if (cvint(&Arg2) == CvtFail) 
      RunErr(101, &Arg2);
   if (cvint(&Arg3) == CvtFail) 
      RunErr(101, &Arg3);
   if (IntVal(Arg3) == 0) 
      RunErr(211, &Arg3);

   /*
    * Count up or down (depending on relationship of from and to) and
    *  suspend each value in sequence, failing when the limit has been
    *  exceeded.
    */
   from = IntVal(Arg1);
   if (IntVal(Arg3) > 0)
      for ( ; from <= IntVal(Arg2); from += IntVal(Arg3)) {
	 MakeInt(from, &Arg0);
         Suspend;
         }
   else
      for ( ; from >= IntVal(Arg2); from += IntVal(Arg3)) {
	 MakeInt(from, &Arg0);
         Suspend;
         }
   Fail;
   }
