/*
 * File: oasgn.c
 *  Contents: asgn, rasgn, rswap, swap
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"


/*
 * x := y - assign y to x.
 */

OpDcl(asgn,2,":=")
   {
   /*
    * Make sure that Arg1 is a variable.
    */
   if (!Var(Arg1)) 
      RunErr(111, &Arg1);

   /*
    * The returned result is the variable to which assignment is being
    *  made.
    */
   Arg0 = Arg1;

   /*
    * All the work is done by doasgn.  Note that Arg1 is known
    *  to be a variable.
    */
   switch (doasgn(&Arg1, &Arg2)) {
      case Success:
         Return;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   }

/*
 * x <- y - assign y to x.
 * Reverses assignment if resumed.
 */

OpDcl(rasgn,2,"<-")
   {

   /*
    * Arg1 must be a variable.
    */
   if (!Var(Arg1)) 
      RunErr(111, &Arg1);

   /*
    * The return value is the variable Arg1, so make a copy of it before
    *  it is dereferenced.
    */
   Arg0 = Arg1;
   if (DeRef(Arg1) == Error) 
      RunErr(0, NULL);

   /*
    * Assign Arg2 to Arg1 and suspend.
    */
   switch (doasgn(&Arg0, &Arg2)) {
      case Success:
         Suspend;
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   /*
    * Reverse the assignment by assigning the old value
    *  of back and fail.
    */
   if (doasgn(&Arg0, &Arg1) == Error) 
      RunErr(0, NULL);
   Fail;
   }

/*
 * x <-> y - swap values of x and y.
 * Reverses swap if resumed.
 */

OpDcl(rswap,2,"<->")
   {
   register union block *bp1, *bp2;
   word adj1, adj2;

   /*
    * Arg1 and Arg2 must be variables.
    */
   if (!Var(Arg1)) {
      RunErr(111, &Arg1);
      }
   if (!Var(Arg2)) {
      RunErr(111, &Arg2);
      }

   /*
    * Make copies of Arg1 and Arg2 as variables in Arg0 and Arg3.
    */
   Arg0 = Arg1;
   Arg3 = Arg2;
   adj1 = adj2 = 0;
   if (Arg1.dword == D_Tvsubs && Arg2.dword == D_Tvsubs) {
      bp1 = BlkLoc(Arg1);
      bp2 = BlkLoc(Arg2);
      if (VarLoc(bp1->tvsubs.ssvar) == VarLoc(bp2->tvsubs.ssvar) &&
	  Offset(bp1->tvsubs.ssvar) == Offset(bp2->tvsubs.ssvar)) {
         /*
          * Arg1 and Arg2 are both substrings of the same string; set
          *  adj1 and adj2 for use in locating the substrings after
          *  an assignment has been made.  If Arg1 is to the right of Arg2,
          *  set adj1 := *Arg1 - *Arg2, otherwise if Arg2 is to the right of
          *  Arg1, set adj2 := *Arg2 - *Arg1.  Note that the adjustment values
          *  may be negative.
          */
         if (bp1->tvsubs.sspos > bp2->tvsubs.sspos)
            adj1 = bp1->tvsubs.sslen - bp2->tvsubs.sslen;
         else if (bp2->tvsubs.sspos > bp1->tvsubs.sspos)
            adj2 = bp2->tvsubs.sslen - bp1->tvsubs.sslen;
            }
      }
   if (DeRef(Arg1) == Error) {
      RunErr(0, NULL);
      }
   if (DeRef(Arg2) == Error) {
      RunErr(0, NULL);
      }
   /*
    * Do Arg1 := Arg2
    */
   switch (doasgn(&Arg0, &Arg2)) {
      case Success:
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   if (adj2 != 0)
      /*
       * Arg2 is to the right of Arg1 and the assignment Arg := Arg2 has
       *  shifted the position of Arg2.  Add adj2 to the position of Arg2
       *  to account for the replacement of Arg1 by Arg2.
       */
      BlkLoc(Arg3)->tvsubs.sspos += adj2;
   /*
    * Do Arg2 := Arg1
    */
   switch (doasgn(&Arg3, &Arg1)) {
      case Success:
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   if (adj1 != 0)
      /*
       * Arg1 is to the right of Arg2 and the assignment Arg2 := Arg1 has
       *  shifted  the position of Arg1.  Add adj2 to the position of Arg1
       *  to account for the replacement of Arg2 by Arg1.
       */
      BlkLoc(Arg0)->tvsubs.sspos += adj1;
   /*
    * Suspend Arg1 with the assignment in effect.
    */
   Suspend;
   /*
    * If resumed, the assignments are undone.  Note that the string position
    *  adjustments are opposite those done earlier.
    */
   switch (doasgn(&Arg0, &Arg1)) {        /* restore Arg1 */
      case Success:
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   if (adj2 != 0)
      BlkLoc(Arg3)->tvsubs.sspos -= adj2;
   switch (doasgn(&Arg3, &Arg2))  {       /* restore Arg2 */
      case Success:
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   if (adj1 != 0)
      BlkLoc(Arg0)->tvsubs.sspos -= adj1;
   Fail;
   }

/*
 * x :=: y - swap values of x and y.
 */

OpDcl(swap,2,":=:")
   {
   register union block *bp1, *bp2;
   word adj1, adj2;

   /*
    * Arg1 and Arg2 must be variables.
    */
   if (!Var(Arg1)) {
      RunErr(111, &Arg1);
      }
   if (!Var(Arg2)) {
      RunErr(111, &Arg2);
      }
   /*
    * Make copies of Arg1 and Arg2 as variables in Arg0 and Arg3.
    */
   Arg0 = Arg1;
   Arg3 = Arg2;
   adj1 = adj2 = 0;
   if (Arg1.dword == D_Tvsubs && Arg2.dword == D_Tvsubs) {
      bp1 = BlkLoc(Arg1);
      bp2 = BlkLoc(Arg2);
      if (VarLoc(bp1->tvsubs.ssvar) == VarLoc(bp2->tvsubs.ssvar) &&
	  Offset(bp1->tvsubs.ssvar) == Offset(bp2->tvsubs.ssvar)) {
         /*
	  * Arg1 and Arg2 are both substrings of the same string, set
	  *  adj1 and adj2 for use in locating the substrings after
	  *  an assignment has been made.  If Arg1 is to the right of Arg2,
	  *  set adj1 := *Arg1 - *Arg2, otherwise if Arg2 is to the right of
          *  Arg1, set adj2 := *Arg2 - *Arg1.  Note that the adjustment
          *  values may be negative.
	  */
         if (bp1->tvsubs.sspos > bp2->tvsubs.sspos)
            adj1 = bp1->tvsubs.sslen - bp2->tvsubs.sslen;
         else if (bp2->tvsubs.sspos > bp1->tvsubs.sspos)
            adj2 = bp2->tvsubs.sslen - bp1->tvsubs.sslen;
   	 }
      }
   if (DeRef(Arg1) == Error) {
      RunErr(0, NULL);
      }
   if (DeRef(Arg2) == Error) {
      RunErr(0, NULL);
      }
   /*
    * Do Arg1 := Arg2
    */
   switch (doasgn(&Arg0, &Arg2)) {
      case Success:
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   if (adj2 != 0)
      /*
       * Arg2 is to the right of Arg1 and the assignment Arg1 := Arg2 has
       *  shifted the position of Arg2.  Add adj2 to the position of Arg2
       *  to account for the replacement of Arg1 by Arg2.
       */
      BlkLoc(Arg3)->tvsubs.sspos += adj2;
   /*
    * Do Arg2 := Arg1
    */
   switch (doasgn(&Arg3, &Arg1)) {
      case Success:
         break;
      case Failure:
         Fail;
      case Error:
         RunErr(0, NULL);
      }
   if (adj1 != 0)
      /*
       * Arg1 is to the right of Arg2 and the assignment Arg2 := Arg1 has
       *  shifted the position of Arg1.  Add adj2 to the position of Arg1 to
       *  account for the replacement of Arg2 by Arg1.
       */
      BlkLoc(Arg0)->tvsubs.sspos += adj1;
   Return;
   }
