968 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			968 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* storage.c:  Code and data storage manipulations.  This includes labels. */
 | 
						|
 | 
						|
/*  This file is part of bc written for MINIX.
 | 
						|
    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
 | 
						|
 | 
						|
    This program is free software; you can redistribute it and/or modify
 | 
						|
    it under the terms of the GNU General Public License as published by
 | 
						|
    the Free Software Foundation; either version 2 of the License , or
 | 
						|
    (at your option) any later version.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public License
 | 
						|
    along with this program; see the file COPYING.  If not, write to
 | 
						|
    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
 | 
						|
    You may contact the author by:
 | 
						|
       e-mail:  phil@cs.wwu.edu
 | 
						|
      us-mail:  Philip A. Nelson
 | 
						|
                Computer Science Department, 9062
 | 
						|
                Western Washington University
 | 
						|
                Bellingham, WA 98226-9062
 | 
						|
       
 | 
						|
*************************************************************************/
 | 
						|
 | 
						|
#include "bcdefs.h"
 | 
						|
#include "global.h"
 | 
						|
#include "proto.h"
 | 
						|
 | 
						|
 | 
						|
/* Initialize the storage at the beginning of the run. */
 | 
						|
 | 
						|
void
 | 
						|
init_storage ()
 | 
						|
{
 | 
						|
 | 
						|
  /* Functions: we start with none and ask for more. */
 | 
						|
  f_count = 0;
 | 
						|
  more_functions ();
 | 
						|
  f_names[0] = "(main)";
 | 
						|
 | 
						|
  /* Variables. */
 | 
						|
  v_count = 0;
 | 
						|
  more_variables ();
 | 
						|
  
 | 
						|
  /* Arrays. */
 | 
						|
  a_count = 0;
 | 
						|
  more_arrays ();
 | 
						|
 | 
						|
  /* Other things... */
 | 
						|
  ex_stack = NULL;
 | 
						|
  fn_stack = NULL;
 | 
						|
  i_base = 10;
 | 
						|
  o_base = 10;
 | 
						|
  scale  = 0;
 | 
						|
  c_code = FALSE;
 | 
						|
  init_numbers();
 | 
						|
}
 | 
						|
 | 
						|
/* Three functions for increasing the number of functions, variables, or
 | 
						|
   arrays that are needed.  This adds another 32 of the requested object. */
 | 
						|
 | 
						|
void
 | 
						|
more_functions (VOID)
 | 
						|
{
 | 
						|
  int old_count;
 | 
						|
  int indx1, indx2;
 | 
						|
  bc_function *old_f;
 | 
						|
  bc_function *f;
 | 
						|
  char **old_names;
 | 
						|
 | 
						|
  /* Save old information. */
 | 
						|
  old_count = f_count;
 | 
						|
  old_f = functions;
 | 
						|
  old_names = f_names;
 | 
						|
 | 
						|
  /* Add a fixed amount and allocate new space. */
 | 
						|
  f_count += STORE_INCR;
 | 
						|
  functions = (bc_function *) bc_malloc (f_count*sizeof (bc_function));
 | 
						|
  f_names = (char **) bc_malloc (f_count*sizeof (char *));
 | 
						|
 | 
						|
  /* Copy old ones. */
 | 
						|
  for (indx1 = 0; indx1 < old_count; indx1++)
 | 
						|
    {
 | 
						|
      functions[indx1] = old_f[indx1];
 | 
						|
      f_names[indx1] = old_names[indx1];
 | 
						|
    }
 | 
						|
 | 
						|
  /* Initialize the new ones. */
 | 
						|
  for (; indx1 < f_count; indx1++)
 | 
						|
    {
 | 
						|
      f = &functions[indx1];
 | 
						|
      f->f_defined = FALSE;
 | 
						|
      for (indx2 = 0; indx2 < BC_MAX_SEGS; indx2++)
 | 
						|
	f->f_body [indx2] = NULL;
 | 
						|
      f->f_code_size = 0;
 | 
						|
      f->f_label = NULL;
 | 
						|
      f->f_autos = NULL;
 | 
						|
      f->f_params = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Free the old elements. */
 | 
						|
  if (old_count != 0)
 | 
						|
    {
 | 
						|
      free (old_f);
 | 
						|
      free (old_names);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
more_variables ()
 | 
						|
{
 | 
						|
  int indx;
 | 
						|
  int old_count;
 | 
						|
  bc_var **old_var;
 | 
						|
  char **old_names;
 | 
						|
 | 
						|
  /* Save the old values. */
 | 
						|
  old_count = v_count;
 | 
						|
  old_var = variables;
 | 
						|
  old_names = v_names;
 | 
						|
 | 
						|
  /* Increment by a fixed amount and allocate. */
 | 
						|
  v_count += STORE_INCR;
 | 
						|
  variables = (bc_var **) bc_malloc (v_count*sizeof(bc_var *));
 | 
						|
  v_names = (char **) bc_malloc (v_count*sizeof(char *));
 | 
						|
 | 
						|
  /* Copy the old variables. */
 | 
						|
  for (indx = 3; indx < old_count; indx++)
 | 
						|
    variables[indx] = old_var[indx];
 | 
						|
 | 
						|
  /* Initialize the new elements. */
 | 
						|
  for (; indx < v_count; indx++)
 | 
						|
    variables[indx] = NULL;
 | 
						|
 | 
						|
  /* Free the old elements. */
 | 
						|
  if (old_count != 0)
 | 
						|
    {
 | 
						|
      free (old_var);
 | 
						|
      free (old_names);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
more_arrays ()
 | 
						|
{
 | 
						|
  int indx;
 | 
						|
  int old_count;
 | 
						|
  bc_var_array **old_ary;
 | 
						|
  char **old_names;
 | 
						|
 | 
						|
  /* Save the old values. */
 | 
						|
  old_count = a_count;
 | 
						|
  old_ary = arrays;
 | 
						|
  old_names = a_names;
 | 
						|
 | 
						|
  /* Increment by a fixed amount and allocate. */
 | 
						|
  a_count += STORE_INCR;
 | 
						|
  arrays = (bc_var_array **) bc_malloc (a_count*sizeof(bc_var_array *));
 | 
						|
  a_names = (char **) bc_malloc (a_count*sizeof(char *));
 | 
						|
 | 
						|
  /* Copy the old arrays. */
 | 
						|
  for (indx = 1; indx < old_count; indx++)
 | 
						|
    arrays[indx] = old_ary[indx];
 | 
						|
 | 
						|
 | 
						|
  /* Initialize the new elements. */
 | 
						|
  for (; indx < v_count; indx++)
 | 
						|
    arrays[indx] = NULL;
 | 
						|
 | 
						|
  /* Free the old elements. */
 | 
						|
  if (old_count != 0)
 | 
						|
    {
 | 
						|
      free (old_ary);
 | 
						|
      free (old_names);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* clear_func clears out function FUNC and makes it ready to redefine. */
 | 
						|
 | 
						|
void
 | 
						|
clear_func (func)
 | 
						|
     char func;
 | 
						|
{
 | 
						|
  bc_function *f;
 | 
						|
  int indx;
 | 
						|
  bc_label_group *lg;
 | 
						|
 | 
						|
  /* Set the pointer to the function. */
 | 
						|
  f = &functions[func];
 | 
						|
  f->f_defined = FALSE;
 | 
						|
 | 
						|
  /* Clear the code segments. */
 | 
						|
  for (indx = 0; indx < BC_MAX_SEGS; indx++)
 | 
						|
    {
 | 
						|
      if (f->f_body[indx] != NULL)
 | 
						|
	{
 | 
						|
	  free (f->f_body[indx]);
 | 
						|
	  f->f_body[indx] = NULL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  f->f_code_size = 0;
 | 
						|
  if (f->f_autos != NULL)
 | 
						|
    {
 | 
						|
      free_args (f->f_autos);
 | 
						|
      f->f_autos = NULL;
 | 
						|
    }
 | 
						|
  if (f->f_params != NULL)
 | 
						|
    {
 | 
						|
      free_args (f->f_params);
 | 
						|
      f->f_params = NULL;
 | 
						|
    }
 | 
						|
  while (f->f_label != NULL)
 | 
						|
    {
 | 
						|
      lg = f->f_label->l_next;
 | 
						|
      free (f->f_label);
 | 
						|
      f->f_label = lg;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*  Pop the function execution stack and return the top. */
 | 
						|
 | 
						|
int
 | 
						|
fpop()
 | 
						|
{
 | 
						|
  fstack_rec *temp;
 | 
						|
  int retval;
 | 
						|
  
 | 
						|
  if (fn_stack != NULL)
 | 
						|
    {
 | 
						|
      temp = fn_stack;
 | 
						|
      fn_stack = temp->s_next;
 | 
						|
      retval = temp->s_val;
 | 
						|
      free (temp);
 | 
						|
    }
 | 
						|
  return (retval);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Push VAL on to the function stack. */
 | 
						|
 | 
						|
void
 | 
						|
fpush (val)
 | 
						|
     int val;
 | 
						|
{
 | 
						|
  fstack_rec *temp;
 | 
						|
  
 | 
						|
  temp = (fstack_rec *) bc_malloc (sizeof (fstack_rec));
 | 
						|
  temp->s_next = fn_stack;
 | 
						|
  temp->s_val = val;
 | 
						|
  fn_stack = temp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Pop and discard the top element of the regular execution stack. */
 | 
						|
 | 
						|
void
 | 
						|
pop ()
 | 
						|
{
 | 
						|
  estack_rec *temp;
 | 
						|
  
 | 
						|
  if (ex_stack != NULL)
 | 
						|
    {
 | 
						|
      temp = ex_stack;
 | 
						|
      ex_stack = temp->s_next;
 | 
						|
      free_num (&temp->s_num);
 | 
						|
      free (temp);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Push a copy of NUM on to the regular execution stack. */
 | 
						|
 | 
						|
void
 | 
						|
push_copy (num)
 | 
						|
     bc_num num;
 | 
						|
{
 | 
						|
  estack_rec *temp;
 | 
						|
 | 
						|
  temp = (estack_rec *) bc_malloc (sizeof (estack_rec));
 | 
						|
  temp->s_num = copy_num (num);
 | 
						|
  temp->s_next = ex_stack;
 | 
						|
  ex_stack = temp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Push NUM on to the regular execution stack.  Do NOT push a copy. */
 | 
						|
 | 
						|
void
 | 
						|
push_num (num)
 | 
						|
     bc_num num;
 | 
						|
{
 | 
						|
  estack_rec *temp;
 | 
						|
 | 
						|
  temp = (estack_rec *) bc_malloc (sizeof (estack_rec));
 | 
						|
  temp->s_num = num;
 | 
						|
  temp->s_next = ex_stack;
 | 
						|
  ex_stack = temp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Make sure the ex_stack has at least DEPTH elements on it.
 | 
						|
   Return TRUE if it has at least DEPTH elements, otherwise
 | 
						|
   return FALSE. */
 | 
						|
 | 
						|
char
 | 
						|
check_stack (depth)
 | 
						|
     int depth;
 | 
						|
{
 | 
						|
  estack_rec *temp;
 | 
						|
 | 
						|
  temp = ex_stack;
 | 
						|
  while ((temp != NULL) && (depth > 0))
 | 
						|
    {
 | 
						|
      temp = temp->s_next;
 | 
						|
      depth--;
 | 
						|
    }
 | 
						|
  if (depth > 0)
 | 
						|
    {
 | 
						|
      rt_error ("Stack error.");
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* The following routines manipulate simple variables and
 | 
						|
   array variables. */
 | 
						|
 | 
						|
/* get_var returns a pointer to the variable VAR_NAME.  If one does not
 | 
						|
   exist, one is created. */
 | 
						|
 | 
						|
bc_var *
 | 
						|
get_var (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_var *var_ptr;
 | 
						|
 | 
						|
  var_ptr = variables[var_name];
 | 
						|
  if (var_ptr == NULL)
 | 
						|
    {
 | 
						|
      var_ptr = variables[var_name] = (bc_var *) bc_malloc (sizeof (bc_var));
 | 
						|
      init_num (&var_ptr->v_value);
 | 
						|
    }
 | 
						|
  return var_ptr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* get_array_num returns the address of the bc_num in the array
 | 
						|
   structure.  If more structure is requried to get to the index,
 | 
						|
   this routine does the work to create that structure. VAR_INDEX
 | 
						|
   is a zero based index into the arrays storage array. INDEX is
 | 
						|
   the index into the bc array. */
 | 
						|
 | 
						|
bc_num *
 | 
						|
get_array_num (var_index, index)
 | 
						|
     int var_index;
 | 
						|
     long  index;
 | 
						|
{
 | 
						|
  bc_var_array *ary_ptr;
 | 
						|
  bc_array *a_var;
 | 
						|
  bc_array_node *temp;
 | 
						|
  int log, ix, ix1;
 | 
						|
  int sub [NODE_DEPTH];
 | 
						|
 | 
						|
  /* Get the array entry. */
 | 
						|
  ary_ptr = arrays[var_index];
 | 
						|
  if (ary_ptr == NULL)
 | 
						|
    {
 | 
						|
      ary_ptr = arrays[var_index] =
 | 
						|
	(bc_var_array *) bc_malloc (sizeof (bc_var_array));
 | 
						|
      ary_ptr->a_value = NULL;
 | 
						|
      ary_ptr->a_next = NULL;
 | 
						|
      ary_ptr->a_param = FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  a_var = ary_ptr->a_value;
 | 
						|
  if (a_var == NULL) {
 | 
						|
    a_var = ary_ptr->a_value = (bc_array *) bc_malloc (sizeof (bc_array));
 | 
						|
    a_var->a_tree = NULL;
 | 
						|
    a_var->a_depth = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Get the index variable. */
 | 
						|
  sub[0] = index & NODE_MASK;
 | 
						|
  ix = index >> NODE_SHIFT;
 | 
						|
  log = 1;
 | 
						|
  while (ix > 0 || log < a_var->a_depth)
 | 
						|
    {
 | 
						|
      sub[log] = ix & NODE_MASK;
 | 
						|
      ix >>= NODE_SHIFT;
 | 
						|
      log++;
 | 
						|
    }
 | 
						|
  
 | 
						|
  /* Build any tree that is necessary. */
 | 
						|
  while (log > a_var->a_depth)
 | 
						|
    {
 | 
						|
      temp = (bc_array_node *) bc_malloc (sizeof(bc_array_node));
 | 
						|
      if (a_var->a_depth != 0)
 | 
						|
	{
 | 
						|
	  temp->n_items.n_down[0] = a_var->a_tree;
 | 
						|
	  for (ix=1; ix < NODE_SIZE; ix++)
 | 
						|
	    temp->n_items.n_down[ix] = NULL;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  for (ix=0; ix < NODE_SIZE; ix++)
 | 
						|
	    temp->n_items.n_num[ix] = copy_num(_zero_);
 | 
						|
	}
 | 
						|
      a_var->a_tree = temp;
 | 
						|
      a_var->a_depth++;
 | 
						|
    }
 | 
						|
  
 | 
						|
  /* Find the indexed variable. */
 | 
						|
  temp = a_var->a_tree;
 | 
						|
  while ( log-- > 1)
 | 
						|
    {
 | 
						|
      ix1 = sub[log];
 | 
						|
      if (temp->n_items.n_down[ix1] == NULL)
 | 
						|
	{
 | 
						|
	  temp->n_items.n_down[ix1] =
 | 
						|
	    (bc_array_node *) bc_malloc (sizeof(bc_array_node));
 | 
						|
	  temp = temp->n_items.n_down[ix1];
 | 
						|
	  if (log > 1)
 | 
						|
	    for (ix=0; ix < NODE_SIZE; ix++)
 | 
						|
	      temp->n_items.n_down[ix] = NULL;
 | 
						|
	  else
 | 
						|
	    for (ix=0; ix < NODE_SIZE; ix++)
 | 
						|
	      temp->n_items.n_num[ix] = copy_num(_zero_);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	temp = temp->n_items.n_down[ix1];
 | 
						|
    }
 | 
						|
  
 | 
						|
  /* Return the address of the indexed variable. */
 | 
						|
  return &(temp->n_items.n_num[sub[0]]);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Store the top of the execution stack into VAR_NAME.  
 | 
						|
   This includes the special variables ibase, obase, and scale. */
 | 
						|
 | 
						|
void
 | 
						|
store_var (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_var *var_ptr;
 | 
						|
  long temp;
 | 
						|
  char toobig;
 | 
						|
 | 
						|
  if (var_name > 2)
 | 
						|
    {
 | 
						|
      /* It is a simple variable. */
 | 
						|
      var_ptr = get_var (var_name);
 | 
						|
      if (var_ptr != NULL)
 | 
						|
	{
 | 
						|
	  free_num(&var_ptr->v_value);
 | 
						|
	  var_ptr->v_value = copy_num (ex_stack->s_num);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* It is a special variable... */
 | 
						|
      toobig = FALSE;
 | 
						|
      if (is_neg (ex_stack->s_num))
 | 
						|
	{
 | 
						|
	  switch (var_name)
 | 
						|
	    {
 | 
						|
	    case 0:
 | 
						|
	      rt_warn ("negative ibase, set to 2");
 | 
						|
	      temp = 2;
 | 
						|
	      break;
 | 
						|
	    case 1:
 | 
						|
	      rt_warn ("negative obase, set to 2");
 | 
						|
	      temp = 2;
 | 
						|
	      break;
 | 
						|
	    case 2:
 | 
						|
	      rt_warn ("negative scale, set to 0");
 | 
						|
	      temp = 0;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  temp = num2long (ex_stack->s_num);
 | 
						|
	  if (!is_zero (ex_stack->s_num) && temp == 0)
 | 
						|
	    toobig = TRUE;
 | 
						|
	}
 | 
						|
      switch (var_name)
 | 
						|
	{
 | 
						|
	case 0:
 | 
						|
	  if (temp < 2 && !toobig)
 | 
						|
	    {
 | 
						|
	      i_base = 2;
 | 
						|
	      rt_warn ("ibase too small, set to 2");
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    if (temp > 16 || toobig)
 | 
						|
	      {
 | 
						|
		i_base = 16;
 | 
						|
		rt_warn ("ibase too large, set to 16");
 | 
						|
	      }
 | 
						|
	    else
 | 
						|
	      i_base = (int) temp;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case 1:
 | 
						|
	  if (temp < 2 && !toobig)
 | 
						|
	    {
 | 
						|
	      o_base = 2;
 | 
						|
	      rt_warn ("obase too small, set to 2");
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    if (temp > BC_BASE_MAX || toobig)
 | 
						|
	      {
 | 
						|
		o_base = BC_BASE_MAX;
 | 
						|
		rt_warn ("obase too large, set to %d", BC_BASE_MAX);
 | 
						|
	      }
 | 
						|
	    else
 | 
						|
	      o_base = (int) temp;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case 2:
 | 
						|
	  /*  WARNING:  The following if statement may generate a compiler
 | 
						|
	      warning if INT_MAX == LONG_MAX.  This is NOT a problem. */
 | 
						|
	  if (temp > BC_SCALE_MAX || toobig )
 | 
						|
	    {
 | 
						|
	      scale = BC_SCALE_MAX;
 | 
						|
	      rt_warn ("scale too large, set to %d", BC_SCALE_MAX);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    scale = (int) temp;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Store the top of the execution stack into array VAR_NAME. 
 | 
						|
   VAR_NAME is the name of an array, and the next to the top
 | 
						|
   of stack for the index into the array. */
 | 
						|
 | 
						|
void
 | 
						|
store_array (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_num *num_ptr;
 | 
						|
  long index;
 | 
						|
 | 
						|
  if (!check_stack(2)) return;
 | 
						|
  index = num2long (ex_stack->s_next->s_num);
 | 
						|
  if (index < 0 || index > BC_DIM_MAX ||
 | 
						|
      (index == 0 && !is_zero(ex_stack->s_next->s_num))) 
 | 
						|
    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      num_ptr = get_array_num (var_name, index);
 | 
						|
      if (num_ptr != NULL)
 | 
						|
	{
 | 
						|
	  free_num (num_ptr);
 | 
						|
	  *num_ptr = copy_num (ex_stack->s_num);
 | 
						|
	  free_num (&ex_stack->s_next->s_num);
 | 
						|
	  ex_stack->s_next->s_num = ex_stack->s_num;
 | 
						|
	  init_num (&ex_stack->s_num);
 | 
						|
	  pop();
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*  Load a copy of VAR_NAME on to the execution stack.  This includes
 | 
						|
    the special variables ibase, obase and scale.  */
 | 
						|
 | 
						|
void
 | 
						|
load_var (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_var *var_ptr;
 | 
						|
 | 
						|
  switch (var_name)
 | 
						|
    {
 | 
						|
 | 
						|
    case 0:
 | 
						|
      /* Special variable ibase. */
 | 
						|
      push_copy (_zero_);
 | 
						|
      int2num (&ex_stack->s_num, i_base);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 1:
 | 
						|
      /* Special variable obase. */
 | 
						|
      push_copy (_zero_);
 | 
						|
      int2num (&ex_stack->s_num, o_base);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 2:
 | 
						|
      /* Special variable scale. */
 | 
						|
      push_copy (_zero_);
 | 
						|
      int2num (&ex_stack->s_num, scale);
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      /* It is a simple variable. */
 | 
						|
      var_ptr = variables[var_name];
 | 
						|
      if (var_ptr != NULL)
 | 
						|
	push_copy (var_ptr->v_value);
 | 
						|
      else
 | 
						|
	push_copy (_zero_);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*  Load a copy of VAR_NAME on to the execution stack.  This includes
 | 
						|
    the special variables ibase, obase and scale.  */
 | 
						|
 | 
						|
void
 | 
						|
load_array (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_num *num_ptr;
 | 
						|
  long   index;
 | 
						|
 | 
						|
  if (!check_stack(1)) return;
 | 
						|
  index = num2long (ex_stack->s_num);
 | 
						|
  if (index < 0 || index > BC_DIM_MAX ||
 | 
						|
     (index == 0 && !is_zero(ex_stack->s_num))) 
 | 
						|
    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      num_ptr = get_array_num (var_name, index);
 | 
						|
      if (num_ptr != NULL)
 | 
						|
	{
 | 
						|
	  pop();
 | 
						|
	  push_copy (*num_ptr);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Decrement VAR_NAME by one.  This includes the special variables
 | 
						|
   ibase, obase, and scale. */
 | 
						|
 | 
						|
void
 | 
						|
decr_var (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_var *var_ptr;
 | 
						|
 | 
						|
  switch (var_name)
 | 
						|
    {
 | 
						|
 | 
						|
    case 0: /* ibase */
 | 
						|
      if (i_base > 2)
 | 
						|
	i_base--;
 | 
						|
      else
 | 
						|
	rt_warn ("ibase too small in --");
 | 
						|
      break;
 | 
						|
      
 | 
						|
    case 1: /* obase */
 | 
						|
      if (o_base > 2)
 | 
						|
	o_base--;
 | 
						|
      else
 | 
						|
	rt_warn ("obase too small in --");
 | 
						|
      break;
 | 
						|
 | 
						|
    case 2: /* scale */
 | 
						|
      if (scale > 0)
 | 
						|
	scale--;
 | 
						|
      else
 | 
						|
	rt_warn ("scale can not be negative in -- ");
 | 
						|
      break;
 | 
						|
 | 
						|
    default: /* It is a simple variable. */
 | 
						|
      var_ptr = get_var (var_name);
 | 
						|
      if (var_ptr != NULL)
 | 
						|
	bc_sub (var_ptr->v_value,_one_,&var_ptr->v_value);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Decrement VAR_NAME by one.  VAR_NAME is an array, and the top of
 | 
						|
   the execution stack is the index and it is popped off the stack. */
 | 
						|
 | 
						|
void
 | 
						|
decr_array (var_name)
 | 
						|
     char var_name;
 | 
						|
{
 | 
						|
  bc_num *num_ptr;
 | 
						|
  long   index;
 | 
						|
 | 
						|
  /* It is an array variable. */
 | 
						|
  if (!check_stack (1)) return;
 | 
						|
  index = num2long (ex_stack->s_num);
 | 
						|
  if (index < 0 || index > BC_DIM_MAX ||
 | 
						|
     (index == 0 && !is_zero (ex_stack->s_num))) 
 | 
						|
    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      num_ptr = get_array_num (var_name, index);
 | 
						|
      if (num_ptr != NULL)
 | 
						|
	{
 | 
						|
	  pop ();
 | 
						|
	  bc_sub (*num_ptr, _one_, num_ptr);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Increment VAR_NAME by one.  This includes the special variables
 | 
						|
   ibase, obase, and scale. */
 | 
						|
 | 
						|
void
 | 
						|
incr_var (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_var *var_ptr;
 | 
						|
 | 
						|
  switch (var_name)
 | 
						|
    {
 | 
						|
 | 
						|
    case 0: /* ibase */
 | 
						|
      if (i_base < 16)
 | 
						|
	i_base++;
 | 
						|
      else
 | 
						|
	rt_warn ("ibase too big in ++");
 | 
						|
      break;
 | 
						|
 | 
						|
    case 1: /* obase */
 | 
						|
      if (o_base < BC_BASE_MAX)
 | 
						|
	o_base++;
 | 
						|
      else
 | 
						|
	rt_warn ("obase too big in ++");
 | 
						|
      break;
 | 
						|
 | 
						|
    case 2:
 | 
						|
      if (scale < BC_SCALE_MAX)
 | 
						|
	scale++;
 | 
						|
      else
 | 
						|
	rt_warn ("Scale too big in ++");
 | 
						|
      break;
 | 
						|
 | 
						|
    default:  /* It is a simple variable. */
 | 
						|
      var_ptr = get_var (var_name);
 | 
						|
      if (var_ptr != NULL)
 | 
						|
	bc_add (var_ptr->v_value, _one_, &var_ptr->v_value);
 | 
						|
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Increment VAR_NAME by one.  VAR_NAME is an array and top of
 | 
						|
   execution stack is the index and is popped off the stack. */
 | 
						|
 | 
						|
void
 | 
						|
incr_array (var_name)
 | 
						|
     int var_name;
 | 
						|
{
 | 
						|
  bc_num *num_ptr;
 | 
						|
  long   index;
 | 
						|
 | 
						|
  if (!check_stack (1)) return;
 | 
						|
  index = num2long (ex_stack->s_num);
 | 
						|
  if (index < 0 || index > BC_DIM_MAX ||
 | 
						|
      (index == 0 && !is_zero (ex_stack->s_num))) 
 | 
						|
    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      num_ptr = get_array_num (var_name, index);
 | 
						|
      if (num_ptr != NULL)
 | 
						|
	{
 | 
						|
	  pop ();
 | 
						|
	  bc_add (*num_ptr, _one_, num_ptr);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Routines for processing autos variables and parameters. */
 | 
						|
 | 
						|
/* NAME is an auto variable that needs to be pushed on its stack. */
 | 
						|
 | 
						|
void
 | 
						|
auto_var (name)
 | 
						|
     int name;
 | 
						|
{
 | 
						|
  bc_var *v_temp;
 | 
						|
  bc_var_array *a_temp;
 | 
						|
  int ix;
 | 
						|
 | 
						|
  if (name > 0)
 | 
						|
    {
 | 
						|
      /* A simple variable. */
 | 
						|
      ix = name;
 | 
						|
      v_temp = (bc_var *) bc_malloc (sizeof (bc_var));
 | 
						|
      v_temp->v_next = variables[ix];
 | 
						|
      init_num (&v_temp->v_value);
 | 
						|
      variables[ix] = v_temp;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* An array variable. */
 | 
						|
      ix = -name;
 | 
						|
      a_temp = (bc_var_array *) bc_malloc (sizeof (bc_var_array));
 | 
						|
      a_temp->a_next = arrays[ix];
 | 
						|
      a_temp->a_value = NULL;
 | 
						|
      a_temp->a_param = FALSE;
 | 
						|
      arrays[ix] = a_temp;
 | 
						|
    } 
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Free_a_tree frees everything associated with an array variable tree.
 | 
						|
   This is used when popping an array variable off its auto stack.  */
 | 
						|
 | 
						|
void
 | 
						|
free_a_tree ( root, depth )
 | 
						|
     bc_array_node *root;
 | 
						|
     int depth;
 | 
						|
{
 | 
						|
  int ix;
 | 
						|
 | 
						|
  if (root != NULL)
 | 
						|
    {
 | 
						|
      if (depth > 1)
 | 
						|
	for (ix = 0; ix < NODE_SIZE; ix++)
 | 
						|
	  free_a_tree (root->n_items.n_down[ix], depth-1);
 | 
						|
      else
 | 
						|
	for (ix = 0; ix < NODE_SIZE; ix++)
 | 
						|
	  free_num ( &(root->n_items.n_num[ix]));
 | 
						|
      free (root);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* LIST is an NULL terminated list of varible names that need to be
 | 
						|
   popped off their auto stacks. */
 | 
						|
 | 
						|
void
 | 
						|
pop_vars (list)
 | 
						|
     arg_list *list;
 | 
						|
{
 | 
						|
  bc_var *v_temp;
 | 
						|
  bc_var_array *a_temp;
 | 
						|
  int    ix;
 | 
						|
 | 
						|
  while (list != NULL)
 | 
						|
    {
 | 
						|
      ix = list->av_name;
 | 
						|
      if (ix > 0)
 | 
						|
	{
 | 
						|
	  /* A simple variable. */
 | 
						|
	  v_temp = variables[ix];
 | 
						|
	  if (v_temp != NULL)
 | 
						|
	    {
 | 
						|
	      variables[ix] = v_temp->v_next;
 | 
						|
	      free_num (&v_temp->v_value);
 | 
						|
	      free (v_temp);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* An array variable. */
 | 
						|
	  ix = -ix;
 | 
						|
	  a_temp = arrays[ix];
 | 
						|
	  if (a_temp != NULL)
 | 
						|
	    {
 | 
						|
	      arrays[ix] = a_temp->a_next;
 | 
						|
	      if (!a_temp->a_param && a_temp->a_value != NULL)
 | 
						|
		{
 | 
						|
		  free_a_tree (a_temp->a_value->a_tree,
 | 
						|
			       a_temp->a_value->a_depth);
 | 
						|
		  free (a_temp->a_value);
 | 
						|
		}
 | 
						|
	      free (a_temp);
 | 
						|
	    }
 | 
						|
	} 
 | 
						|
      list = list->next;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* A call is being made to FUNC.  The call types are at PC.  Process
 | 
						|
   the parameters by doing an auto on the parameter variable and then
 | 
						|
   store the value at the new variable or put a pointer the the array
 | 
						|
   variable. */
 | 
						|
 | 
						|
void
 | 
						|
process_params (pc, func)
 | 
						|
     program_counter *pc;
 | 
						|
     int func;
 | 
						|
{
 | 
						|
  char ch;
 | 
						|
  arg_list *params;
 | 
						|
  char warned = FALSE;
 | 
						|
  int ix, ix1;
 | 
						|
  bc_var *v_temp;
 | 
						|
  bc_var_array *a_src, *a_dest;
 | 
						|
  bc_num *n_temp;
 | 
						|
  
 | 
						|
  /* Get the parameter names from the function. */
 | 
						|
  params = functions[func].f_params;
 | 
						|
 | 
						|
  while ((ch = byte(pc)) != ':')
 | 
						|
    {
 | 
						|
      if (params != NULL)
 | 
						|
	{
 | 
						|
	  if ((ch == '0') && params->av_name > 0)
 | 
						|
	    {
 | 
						|
	      /* A simple variable. */
 | 
						|
	      ix = params->av_name;
 | 
						|
	      v_temp = (bc_var *) bc_malloc (sizeof(bc_var));
 | 
						|
	      v_temp->v_next = variables[ix];
 | 
						|
	      v_temp->v_value = ex_stack->s_num;
 | 
						|
	      init_num (&ex_stack->s_num);
 | 
						|
	      variables[ix] = v_temp;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    if ((ch == '1') && (params->av_name < 0))
 | 
						|
	      {
 | 
						|
		/* The variables is an array variable. */
 | 
						|
	
 | 
						|
		/* Compute source index and make sure some structure exists. */
 | 
						|
		ix = (int) num2long (ex_stack->s_num);
 | 
						|
		n_temp = get_array_num (ix, 0);    
 | 
						|
	
 | 
						|
		/* Push a new array and Compute Destination index */
 | 
						|
		auto_var (params->av_name);  
 | 
						|
		ix1 = -params->av_name;
 | 
						|
 | 
						|
		/* Set up the correct pointers in the structure. */
 | 
						|
		if (ix == ix1) 
 | 
						|
		  a_src = arrays[ix]->a_next;
 | 
						|
		else
 | 
						|
		  a_src = arrays[ix];
 | 
						|
		a_dest = arrays[ix1];
 | 
						|
		a_dest->a_param = TRUE;
 | 
						|
		a_dest->a_value = a_src->a_value;
 | 
						|
	      }
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		if (params->av_name < 0)
 | 
						|
		  rt_error ("Parameter type mismatch parameter %s.",
 | 
						|
			    a_names[-params->av_name]);
 | 
						|
		else
 | 
						|
		  rt_error ("Parameter type mismatch, parameter %s.",
 | 
						|
			    v_names[params->av_name]);
 | 
						|
		params++;
 | 
						|
	      }
 | 
						|
	  pop ();
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (!warned)
 | 
						|
	    {
 | 
						|
	      rt_error ("Parameter number mismatch");
 | 
						|
	      warned = TRUE;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      params = params->next;
 | 
						|
    }
 | 
						|
  if (params != NULL) 
 | 
						|
    rt_error ("Parameter number mismatch");
 | 
						|
}
 |