Faking Recursive Calls in Avenue

Klaus Reinken reports an elegant alternative to the approaches discussed below:

theScript = Script.Make(Script.The.asString)
result = theScript.DoIt( { ... arguments ...} )

While porting some routines from C to Avenue, I encountered a problem: two of the routines were recursive (i.e. they called themselves). Because recursive calls are not allowed in Avenue, I had to find a way to re-engineer these routines without compromising the logic.

The first routine was simple to solve because only one call was made per iteration:

int locate_endpoint(v, vo, r)
     point_t *v;
     point_t *vo;
     int r;
{
  node_t *rptr = &qs[r];
  switch (rptr->nodetype)
    {
    case T_SINK:
      return rptr->trnum;
    case T_Y:
      if (_greater_than(v, &rptr->yval)) /* above */
        return locate_endpoint(v, vo, rptr->right);
      else if (_equal_to(v, &rptr->yval)) /* the point is already */
        {                                 /* inserted. */
          if (_greater_than(vo, &rptr->yval)) /* above */
            return locate_endpoint(v, vo, rptr->right);
          else
            return locate_endpoint(v, vo, rptr->left); /* below */
        }
      else
        return locate_endpoint(v, vo, rptr->left); /* below */
    ...
}
The approach I took was to set up an infinite loop, reassigning the value of variable "r" as necessary:

v = SELF.Get(0)
vo = SELF.Get(1)
r = SELF.Get(2)
while (TRUE)
   rptr = _qs.Get(r)
   nt = rptr.Get(0)
   if (nt = "T_SINK") then
      return rptr.Get(3)
   elseif (nt = "T_Y") then
      if (av.Run("P.GT",{v,rptr.Get(2)})) then
         r = rptr.Get(6)
      elseif (v = rptr.Get(2)) then
         if (av.Run("P.GT",{vo,rptr.Get(2)})) then
            r = rptr.Get(6)
         else
            r = rptr.Get(5)
         end
      else
         r = rptr.Get(5)
      end
   ...
end
The other routine was more difficult to solve because it made multiple calls per iteration:

static int traverse_polygon(mcur, trnum, from, dir)
     int mcur;
     int trnum;
     int from;
     int dir;
{
  ...
  int do_switch = FALSE;
  if ((trnum <= 0) || visited[trnum])
    return 0;
  visited[trnum] = TRUE;
  if ((t->u0 <= 0) && (t->u1 <= 0))
    {
      if ((t->d0 > 0) && (t->d1 > 0)) /* downward opening triangle */
        {
          v0 = tr[t->d1].lseg;
          v1 = t->lseg;
          if (from == t->d1)
            {
              do_switch = TRUE;
              mnew = make_new_monotone_poly(mcur, v1, v0);
              traverse_polygon(mcur, t->d1, trnum, TR_FROM_UP);
              traverse_polygon(mnew, t->d0, trnum, TR_FROM_UP);
            }
  ...
}
I solved the problem by using a Stack object to store arguments:

'**** initialize stack

theStack = Stack.Make
theStack.Push({SELF.Get(0),SELF.Get(1),SELF.Get(2),SELF.GET(3)})
retval = ""
...
while (TRUE)
   if (theStack.IsEmpty) then
      return retval
   end
   arglist = theStack.Pop
   mcur = arglist.Get(0)
   trnum = arglist.Get(1)
   from = arglist.Get(2)
   dir = arglist.Get(3)
   do_switch = FALSE
   cond = FALSE
   if (trnum <= 0) then
      cond = TRUE
   elseif (visited.Get(trnum)) then
      cond = TRUE
   else
      t = _tr.Get(trnum)
   end
   if (cond) then
      retval = 0
      continue
   end
   visited.Set(trnum,TRUE)
   sn = sn + 1
   av.SetStatus(100*sn/sd)
   if ((t.Get(4) <= 0) and (t.Get(5) <= 0)) then

      if ((t.Get(6) > 0) and (t.Get(7) > 0)) then

         '**** downward opening triangle

         v0 = _tr.Get(t.Get(7)).Get(0)
         v1 = t.Get(0)
         if (from = t.Get(7)) then
            do_switch = TRUE
            mnew = av.Run("P.NewMonotone",{mcur, v1, v0})
            theStack.Push({mnew,t.Get(6),trnum,"TR_FROM_UP"})
            theStack.Push({mcur,t.Get(7),trnum,"TR_FROM_UP"})
   ...
end
Note that because of the nature of Stacks (Last-In-First-Out), the order of the Push requests is reversed.

If you want to take a better look at the routines, you can access them via the Web. The original C code may be downloaded at:

http://www.cs.unc.edu/~dm/CODE/GEM/chapter.html

and the Avenue code may be downloaded at:

http://www.pierssen.com/arcview/esoteric.htm


Back to AVTips