/***************************************************************
  
        bwb_stc.c       Commands Related to Structured Programming
                        for Bywater BASIC Interpreter
  
                        Copyright (c) 1993, Ted A. Campbell
                        Bywater Software
  
                        email: tcamp@delphi.com
  
        Copyright and Permissions Information:
  
        All U.S. and international rights are claimed by the author,
        Ted A. Campbell.
  
   This software is released under the terms of the GNU General
   Public License (GPL), which is distributed with this software
   in the file "COPYING".  The GPL specifies the terms under
   which users may copy and use the software in this distribution.
  
   A separate license is available for commercial distribution,
   for information on which you should contact the author.
  
***************************************************************/

/*---------------------------------------------------------------*/
/* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */
/* 11/1995 (eidetics@cerf.net).                                  */
/*                                                               */
/* Those additionally marked with "DD" were at the suggestion of */
/* Dale DePriest (daled@cadence.com).                            */
/*                                                               */
/* Version 3.00 by Howard Wulf, AF5NE                            */
/*                                                               */
/*---------------------------------------------------------------*/



#include "bwbasic.h"




/* prototypes */

static int
fslt_clear(void);
static int
scan_readargs(struct fslte * f,
         struct bwb_line * line, int *position);
struct bwb_variable *
bwb_etov(struct bwb_variable * dst, struct exp_ese * src);
int
fslt_addlocalvar(struct fslte * f, struct bwb_variable * v);

/***************************************************************
  
        FUNCTION:       bwb_scan()
  
        DESCRIPTION: This function scans all lines of the
            program in memory and creates a FUNCTION-
            SUB lookup table (fslt) for the program.
  
***************************************************************/

int
bwb_scan(void)
{
   struct bwb_line *current;
   int             Indention = 0;

   bwx_DEBUG(__FUNCTION__);


   /* int position; */

   if (CURTASK rescan != TRUE)
   {
      return FALSE;
   }
   /* program needs to be scanned again, because a line was added or
    * deleted */



   /* first run through the FUNCTION - SUB loopkup table and free any
    * existing memory */

   fslt_clear();


   for (current = CURTASK bwb_start.next; current != &CURTASK bwb_end; current = current->next)
   {
      current->OtherLine = NULL;
   }
   /* run through the list of lines and identify SUB and FUNCTION
    * statements */

   for (current = CURTASK bwb_start.next; current != &CURTASK bwb_end; current = current->next)
   {

      /* position = current->Startpos; */
      switch (current->cmdnum)
      {
      case C_DEF:
      case C_FUNCTION:
      case C_SUB:
      case C_USER_LBL:
         fslt_add(current);
      case C_DO:
      case C_DO_WHILE:
      case C_DO_UNTIL:
      case C_WHILE:
      case C_UNTIL:
      case C_FOR:
      case C_IF:
      case C_SELECT_CASE:
         find_BottomLineInCode(current);
         break;
      }

      switch (current->cmdnum)
      {
      case C_NEXT:
      case C_LOOP:
      case C_LOOP_UNTIL:
      case C_LOOP_WHILE:
      case C_UEND:
      case C_WEND:
      case C_ELSEIF:
      case C_ELSE:
      case C_END_IF:
      case C_CASE:
      case C_CASE_IF:
      case C_CASE_IS:
      case C_CASE_ELSE:
      case C_END_SELECT:
      case C_END_SUB:
      case C_END_FUNCTION:
         Indention--;
         break;
      }
      if (Indention < 0)
      {
         Indention = 0;
      }
      current->Indention = Indention;
      switch (current->cmdnum)
      {
      case C_FOR:
      case C_DO:
      case C_DO_UNTIL:
      case C_DO_WHILE:
      case C_UNTIL:
      case C_WHILE:
      case C_IF_THEN:
      case C_ELSEIF:
      case C_ELSE:
      case C_SELECT_CASE:
      case C_CASE:
      case C_CASE_IF:
      case C_CASE_IS:
      case C_CASE_ELSE:
      case C_FUNCTION:
      case C_SUB:
         Indention++;
         break;
      }
   }


   /* return */

   CURTASK         rescan = FALSE;
   return TRUE;

}

/***************************************************************
  
        FUNCTION:       fslt_clear()
  
        DESCRIPTION: This C function clears all existing memory
            in the FUNCTION-SUB lookup table.
  
***************************************************************/

static int
fslt_clear(void)
{
   struct fslte   *current, *next;
   struct bwb_variable *c, *n;

   bwx_DEBUG(__FUNCTION__);

   /* run through table and clear memory */

   next = CURTASK fslt_start.next;
   for (current = CURTASK fslt_start.next; current != &CURTASK fslt_end;
        current = next)
   {

      /* check for local variables and free them */

      c = current->local_variable;
      while (c != NULL)
      {
         n = c->next;

         /* Revised to FREE pass-thru call by JBV */
         FREE(c, "fslt_clear");
         c = n;
      }

      next = current->next;

      /* Revised to FREE pass-thru calls by JBV */
      if (current->name != NULL)
      {
         FREE(current->name, "fslt_clear");  /* JBV */
         current->name = NULL;   /* JBV */
      }
      FREE(current, "fslt_clear");
      current = NULL;   /* JBV */
   }

   /* reset linkage */

   CURTASK         fslt_start.next = &CURTASK fslt_end;

   return TRUE;
}


/***************************************************************
  
        FUNCTION:       fslt_add()
  
        DESCRIPTION: This C function adds an entry to the
            FUNCTION-SUB lookup table.
  
***************************************************************/

int
fslt_add(struct bwb_line * line /* , int *position , int code */ )
{
   char            tbuf[BasicStringLengthMax + 1];
   char           *name;
   struct fslte   *f, *n;
   int             p;
   int            *position;
   position = &p;
   p = line->Startpos;

   bwx_DEBUG(__FUNCTION__);

   /* get the element for name */

   if ( /* code == EXEC_LABEL */ line->cmdnum == C_USER_LBL)
   {
      p = 0;
      scan_element(line->buffer, &p, tbuf);
      tbuf[strlen(tbuf) - 1] = '\0';   /* remove trailing colon */
   }
   else
   {
      adv_ws(line->buffer, position);
      exp_getvfname(&(line->buffer[*position]), tbuf);
      *position += strlen(tbuf);
   }


   /* get memory for name buffer */

   /* Revised to CALLOC pass-thru call by JBV */
   if ((name = CALLOC(1, strlen(tbuf) + 1, "fslt_add")) == NULL)
   {
      bwb_error("in fslt_add(): failed to get memory for name buffer");
      return FALSE;
   }
   strcpy(name, tbuf);

   /* get memory for fslt structure */

   if ((f = CALLOC(1, sizeof(struct fslte), "fslt_add")) == NULL)
   {
      bwb_error("in fslt_add(): failed to get memory for fslt structure");
      return FALSE;
   }
   /* fill in structure */

   f->line = line;
   f->name = name;
   f->local_variable = NULL;
   f->ParameterCount = 0;  /* 0..32, 255 == VARIAMT */
   f->ParameterTypes = 0;  /* bit 0 is first parameter */







   /* read arguments */

   adv_ws(line->buffer, position);

   f->startpos = *position;



   if (line->buffer[*position] == '(')
   {
      scan_readargs(f, line, position);
   }
   /* establish linkages */

   n = CURTASK fslt_start.next;
   CURTASK         fslt_start.next = f;
   f->next = n;



   fnc_add_deffn(f);



   return TRUE;

}

/***************************************************************
  
        FUNCTION:       scan_readargs()
  
        DESCRIPTION: This C function reads arguments (variable
            names for an entry added to the FUNCTION-
         SUB lookup table.
  
***************************************************************/

static int
scan_readargs(struct fslte * f, struct bwb_line * line, int *position)
{
   int             control_loop;
   struct bwb_variable *v;
   char            tbuf[BasicStringLengthMax + 1];

   bwx_DEBUG(__FUNCTION__);

   f->ParameterCount = 0;  /* 0..254, 255 == ... */
   f->ParameterTypes = 0;  /* bit 0 is first parameter */


   /* if we are at begin paren, advance */

   if (line->buffer[*position] == '(')
   {
      ++(*position);
   }
   /* loop through looking for arguments */

   control_loop = TRUE;
   adv_ws(line->buffer, position);


   if (strncasecmp(&(line->buffer[*position]), "...", 3) == 0)
   {
      /* FUNCTION FRED( ... ) */
      (*position) += 3;
      adv_ws(line->buffer, position);
      if (line->buffer[*position] == ')')
      {
         f->ParameterCount = 0xFF;  /* VARIANT */
         f->ParameterTypes = 0;
         ++(*position);
         f->startpos = *position;
         return TRUE;
      }
      bwb_error(err_syntax);
      return FALSE;
   }
   while (control_loop == TRUE)
   {


      if (line->buffer[*position] == '\0' || line->buffer[*position] == OptionCommentChar)
      {
         /* premature end of line */
         control_loop = FALSE;
         f->startpos = *position;
         bwb_error(err_syntax);
         return FALSE;
      }
      switch (line->buffer[*position])
      {
      case ')':   /* end of argument list */
         ++(*position);
         control_loop = FALSE;
         f->startpos = *position;
         return TRUE;


      case '=':   /* end of NO argument list */
         control_loop = FALSE;
         f->startpos = *position;
         return TRUE;


      default: /* presume beginning of argument == variable
             * name */

         exp_getvfname(&(line->buffer[*position]), tbuf);
         *position += strlen(tbuf);


         /* initialize the variable and add it to local chain */

         v = var_new(tbuf);
         fslt_addlocalvar(f, v);

         /* advance past the comma */

         if (line->buffer[*position] == ',')
         {
            ++(*position);
         }
         if (f->ParameterCount > 32)
         {
            /* should have been declared VARIANT */
            bwb_error(err_syntax);
            return FALSE;
         }
         else
         {
            if (v->type == STRING)
            {
               f->ParameterTypes |= (1 << f->ParameterCount);
            }
            f->ParameterCount++; /* 0..32, 255 == VARIANT */
         }

         break;
      }

      adv_ws(line->buffer, position);
   }

   return TRUE;

}


/***************************************************************
  
        FUNCTION:       bwb_def()
  
        DESCRIPTION:    This C function implements the BASIC
                        DEF statement. Since DEF and FUNCTION
         are equivalent, it simply passes execution
         to bwb_function().
  
   SYNTAX:     DEF FNname(arg...)] = expression
  
   NOTE:    It is not a strict requirement that the
         function name should begin with "FN".
  
***************************************************************/

struct bwb_line *
bwb_DEF(struct bwb_line * l)
{
   bwx_DEBUG(__FUNCTION__);

   /* this line will be executed by fnc_deffn() in bwb_fnc.c */
   adv_eos(l->buffer, &(l->position));

   return bwb_zline(l);
}



/***************************************************************
  
        FUNCTION:       bwb_call()
  
        DESCRIPTION: This C function implements the BASIC
            CALL subroutine command.
  
   SYNTAX:     CALL subroutine-name
  
***************************************************************/

struct bwb_line *
bwb_CALL(struct bwb_line * l)
{
   bwx_DEBUG(__FUNCTION__);

   /* Call the expression interpreter to evaluate the function */

   /* prn_xprintf(Buffer); */
   bwb_exp(l->buffer, FALSE, &l->position);
   if (ERROR_PENDING)
   {
      /* prn_xprintf("oops"); */
   }
   adv_eos(l->buffer, &(l->position));
   return bwb_zline(l);
#if 0
   return bwb_DEF_SUB(l);
#endif
}


/***************************************************************
  
   FUNCTION:   find_label()
  
   DESCRIPTION:   This C function finds a program line that
         begins with the label included in <buffer>.
  
***************************************************************/

struct bwb_line *
find_label(char *buffer)
{
   bwx_DEBUG(__FUNCTION__);

   while (*buffer == ' ')
      buffer++;


   if (*buffer == 0)
   {
      bwb_error(err_noln);
   }
   else
   if (isdigit(*buffer))
   {
      /* LINE NUMBER */
      int             number;
      number = atoi(buffer);
      if (number > 0)
      {
         struct bwb_line *x;
         for (x = &CURTASK bwb_start; x != &CURTASK bwb_end; x = x->next)
         {
            if (x->number == number)
            {
               return x;
            }
         }
      }
      sprintf(bwb_ebuf, "Line number %d not found", number);
      bwb_error(bwb_ebuf);

   }
   else
   if (OptionFlags & OPTION_LABELS_ON)
   {
      /* LABEL */
      if (isalpha(*buffer))
      {
         struct fslte   *f;

         for (f = CURTASK fslt_start.next; f != &(CURTASK fslt_end); f = f->next)
         {
            if (strcasecmp(buffer, f->name) == 0)
            {
               return f->line;
            }
         }
      }
      sprintf(bwb_ebuf, "Line label '%s' not found", buffer);
      bwb_error(bwb_ebuf);
   }
   else
   {
      sprintf(bwb_ebuf, "Line label '%s' is not allowed", buffer);
      bwb_error(bwb_ebuf);
   }

   return NULL;

}

/***************************************************************
  
        FUNCTION:       bwb_exit()
  
   DESCRIPTION:    This C function implements the BASIC EXIT
         statement, calling subroutines for either
         EXIT FOR or EXIT DO.
  
   SYNTAX:     EXIT FOR|DO
  
***************************************************************/

struct bwb_line *
bwb_EXIT(struct bwb_line * l)
{
   bwx_DEBUG(__FUNCTION__);

   sprintf(bwb_ebuf, "in bwb_exit(): Nonsense or nothing following EXIT");
   bwb_error(bwb_ebuf);

   return bwb_zline(l);
}



/***************************************************************
  
   FUNCTION:       bwb_etov()
  
   DESCRIPTION:    This function assigns the value of a
         bwBASIC expression stack element (src)
         to the value of a bwBASIC variable (dst).
  
***************************************************************/

struct bwb_variable *
bwb_etov(struct bwb_variable * dst, struct exp_ese * src)
{

   if ((int) src->type != dst->type)
   {
      sprintf(bwb_ebuf, "in bwb_etov(): mismatch src <%d> dst <%d>",
         src->type, dst->type);
      bwb_error(bwb_ebuf);
      return NULL;
   }
   if (dst->type == NUMBER)
   {
      *var_findnval(dst, dst->array_pos) = exp_getnval(src);
   }
   else
   {
      str_btob(var_getsval(dst), exp_getsval(src));
   }

   return dst;
}





/***************************************************************
  
   FUNCTION:       fslt_addlocalvar()
  
   DESCRIPTION:    This function adds a local variable
         to the FUNCTION-SUB lookup table at
         a specific level.
  
***************************************************************/

int
fslt_addlocalvar(struct fslte * f, struct bwb_variable * v)
{
   bwx_DEBUG(__FUNCTION__);

   /* find end of local chain */

   if (f->local_variable == NULL)
   {
      f->local_variable = v;
   }
   else
   {
      struct bwb_variable *p;
      struct bwb_variable *c;

      p = f->local_variable;
      for (c = f->local_variable->next; c != NULL; c = c->next)
      {
         p = c;
      }
      p->next = v;
   }

   v->next = NULL;


   return TRUE;

}

/***************************************************************
  
   FUNCTION:       fslt_init()
  
   DESCRIPTION:    This function initializes the FUNCTION-SUB
         lookup table.
  
***************************************************************/

int
fslt_init(int task)
{
   bwx_DEBUG(__FUNCTION__);

   LOCALTASK       fslt_start.next = &(LOCALTASK fslt_end);
   return TRUE;
}


struct bwb_line *
bwb_USER_LBL(struct bwb_line * l)
{
   bwx_DEBUG(__FUNCTION__);

   adv_eos(l->buffer, &(l->position));

   return bwb_zline(l);
}





/* EOF */
