/* l2xixutl.c  LTX2X Executor utility routines  */
/*  Written by: Peter Wilson, CUA  pwilson@cme.nist.gov                */
/*  This code is partly based on algorithms presented by Ronald Mak in */
/*  "Writing Compilers & Interpreters", John Wiley & Sons, 1991        */

#include <stdio.h>
#include "l2xicmon.h"
#include "l2xierr.h"
#include "l2xiscan.h"
#include "l2xisymt.h"
#include "l2xiidbg.h"
#include "l2xiexec.h"

#include "listsetc.h"

/* EXTERNALS */

extern TOKEN_CODE token;
extern int line_number;;
extern int level;
 
extern BOOLEAN executed_return;      /* TRUE iff return statement executed */

/* GLOBALS */

ICT *code_buffer;             /* code buffer */
ICT *code_bufferp;            /* code buffer ptr */
ICT *code_segmentp;           /* code segment ptr */
ICT *code_segment_limit;      /* end of code segment */

ICT *statement_startp;        /* ptr to start of statement */
TOKEN_CODE ctoken;            /* token from code segment */
int exec_line_number;         /* no. of line executed */
long exec_stmt_count = 0;     /* count of executed statements */

STACK_ITEM *stack;                  /* runtime stack */
STACK_ITEM_PTR tos;                 /* ptr to top of runtime stack */
STACK_ITEM_PTR stack_frame_basep;   /* ptr to stack fame base */
STACK_ITEM_PTR maxtos;              /* current max top of runtime stack */

/* map from form type to stack type */
STACK_TYPE form2stack[] = {
#define fotc(a, b, c, d) a,
#define sotc(a, b, c, d) 
#define sftc(a, b, c, d) a,
#include "l2xisftc.h"
#undef fotc
#undef sotc
#undef sftc
};

/* map from stack type to form type */
TYPE_FORM stack2form[] = {
#define fotc(a, b, c, d) 
#define sotc(a, b, c, d) c,
#define sftc(a, b, c, d) c,
#include "l2xisftc.h"
#undef fotc
#undef sotc
#undef sftc
};

/* FORWARDS */

ADDRESS get_static_link();
ADDRESS get_dynamic_link();
ADDRESS get_return_address();
STACK_TYPE get_stackval_type();

/* CODE SEGMENT ROUTINES */


/***************************************************************************/
/* create_code_segment() Create a code segment and copy in the contents    */
/*                       of the code buffer. Reset the code buffer pointer */
/* return a pointer to the segment                                         */

ICT *create_code_segment()
{
  ICT *code_segment = alloc_array(ICT, (code_bufferp - code_buffer));
  entry_debug("create_code_segment");

  code_segment_limit = code_segment + (code_bufferp - code_buffer);
  code_bufferp = code_buffer;
  code_segmentp = code_segment;

  /* copy in the contents of the code buffer */
  while (code_segmentp != code_segment_limit) {
    *code_segmentp++ = *code_bufferp++;
  }
  /* reset the code buffer pointer */
  code_bufferp = code_buffer;

  code_segment_debug(code_segment, code_segment_limit);

  exit_debug("create_code_segment");
  return(code_segment);

}                                               /* end create_code_segment */
/***************************************************************************/




/***************************************************************************/
/* crunch_token()  Append the token code to the code buffer.               */
/*                Called by the scanner routine only while parsing a block */


crunch_token()
{
  int token_code = token;      /* integer sized token code */
  entry_debug("crunch_token");

  if ((code_bufferp - code_buffer) >= MAX_CODE_BUFFER_SIZE - sizeof(token_code)) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }

  *code_bufferp++ = (ICT) token_code;

  exit_debug("crunch_token");
  return;
}                                                      /* end crunch_token */
/***************************************************************************/


/***************************************************************************/
/* crunch_extra_token()  Append the token code to the code buffer.         */

crunch_extra_token(tok)
TOKEN_CODE tok;
{
  entry_debug("crunch_extra_token (l2xixutl.c)");

  if ((code_bufferp - code_buffer) >= MAX_CODE_BUFFER_SIZE - sizeof(tok)) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }

  *code_bufferp++ = (ICT) tok;

  exit_debug("crunch_extra_token");
  return;
}                                                /* end CRUNCH_EXTRA_TOKEN */
/***************************************************************************/


/***************************************************************************/
/* get_ctoken()     Gets next crunched token                               */

TOKEN_CODE get_ctoken()
{
  entry_debug("get_ctoken (l2xixutl.c)");

  code_segment_entry_debug(code_segmentp);
  ctoken = *code_segmentp++;

  exit_debug("get_ctoken");
  return(ctoken);
}                                                        /* end GET_CTOKEN */
/***************************************************************************/


/***************************************************************************/
/* change_crunched_token(newtok)  Replace the last token in the code       */
/*                                segment by newtok                        */

change_crunched_token(newtok)
int newtok;                    /* integer sized new token code */
{
  ICT *bp;
  entry_debug("change_crunched_token");

  bp = code_bufferp;
  bp--;

  *bp = (ICT) newtok;

  exit_debug("change_crunched_token");
  return;
}                                            /* end CHANGE_CRUNCHED_TOKEN */
/***************************************************************************/


/***************************************************************************/
/* backup_crunched()            prepare to write over last code entry      */
/*                                                                         */

backup_crunched()
{
  entry_debug("backup_crunched");

  code_bufferp--;

  exit_debug("backup_crunched");
  return;
}                                                   /* end BACKUP_CRUNCHED */
/***************************************************************************/


/***************************************************************************/
/* crunch_symtab_node_ptr(np)  Append a symbol table node pointer to the   */
/*                             code buffer                                 */

crunch_symtab_node_ptr(np)
SYMTAB_NODE_PTR np;              /* pointer to append */
{
/*  SYMTAB_NODE_PTR *npp = (SYMTAB_NODE_PTR *) code_bufferp; */
  ICT *npp = code_bufferp;

  entry_debug("crunch_symtab_node_ptr");

  if ((code_bufferp - code_buffer) >= 
       (MAX_CODE_BUFFER_SIZE - sizeof(SYMTAB_NODE_PTR))) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }
  else {
    *npp = (ICT) np; 
    code_bufferp++;
  }

  exit_debug("crunch_symtab_node_ptr");
  return;
}                                            /* end crunch_symtab_node_ptr */
/***************************************************************************/


/***************************************************************************/
/* get_symtab_cptr() Extract a symbol table node pointer from the current  */
/*                   code segment                                          */
/* return the symbol table node pointer                                    */

SYMTAB_NODE_PTR get_symtab_cptr()
{
  SYMTAB_NODE_PTR np;
  ICT *npp = code_segmentp;

  np = (SYMTAB_NODE_PTR) *npp;
/*  code_segmentp += sizeof(SYMTAB_NODE_PTR); */
  code_segmentp++;

  return(np);
}                                                   /* end get_symtab_cptr */
/***************************************************************************/




/***************************************************************************/
/* crunch_statement_marker()  Append a statement marker to the code buffer */
/*                                                                         */

crunch_statement_marker()
{
  entry_debug("crunch_statement_marker");

  if ((code_bufferp - code_buffer) >= MAX_CODE_BUFFER_SIZE) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }
  else {
  ICT save_code = *(--code_bufferp);

  *code_bufferp++ = STATEMENT_MARKER;
  *((int *) code_bufferp) = line_number;
  code_bufferp++;
  *code_bufferp++ = save_code;
  }

  exit_debug("crunch_statement_marker");
  return;
}                                           /* end crunch_statement_marker */
/***************************************************************************/



/***************************************************************************/
/* get_statement_cmarker()  Extract a statement marker from the current    */
/*                          code segment.                                  */
/* return its line number.                                                 */


int get_statement_cmarker()
{
  int line_num;
  entry_debug("get_statement_cmarker");


  if (ctoken == STATEMENT_MARKER) {
    line_num = *((int *) code_segmentp);
    code_segmentp++;
  }

  exit_debug("get_statement_cmarker");
  return(line_num);
}                                             /* end get_statement_cmarker */
/***************************************************************************/



/***************************************************************************/
/* crunch_address_marker(address)  Append a code address to the code       */
/*                                  buffer                                 */
/* return the address of the address                                       */

ICT *crunch_address_marker(address)
ADDRESS address;
{

  ICT *save_code_bufferp;

  if ((code_bufferp - code_buffer) >= MAX_CODE_BUFFER_SIZE - sizeof(ADDRESS)) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }
  else {
    ICT save_code = *(--code_bufferp);

    *code_bufferp++ = (ICT) ADDRESS_MARKER;
    save_code_bufferp = code_bufferp;
    *((ADDRESS *) code_bufferp) = address;
    code_bufferp++;
    *code_bufferp++ = save_code;
  }

  return(save_code_bufferp);
}                                             /* end crunch_address_marker */
/***************************************************************************/


/***************************************************************************/
/* get_address_cmarker  Extract an address marker from current code        */
/*                      segment. Add its offset value to the code segment  */
/*                      address.                                           */
/*  return new address                                                     */

ADDRESS get_address_cmarker()
{
  ADDRESS address;                   /* address to be returned */

  if (ctoken == ADDRESS_MARKER) {
    address = *((int *) code_segmentp) + code_segmentp - 1;
    code_segmentp++;
  }

  return(address);

}                                               /* end get_address_cmarker */
/***************************************************************************/


/***************************************************************************/
/*  fixup_address_marker(address) Fix up an address marker with the offset */
/*                                from the address marker to the current   */
/*                                code buffer address.                     */
/* return the old value of the address marker                              */

ADDRESS fixup_address_marker(address)
ADDRESS address;           /* address of marker to be fixed up */
{

/*   ADDRESS old_address = address; */
/*  int *old_address = *((ADDRESS *) address); */
    ADDRESS old_address = *((ADDRESS *) address);
  
  *((int *) address) = code_bufferp - address;
  return(old_address);

}                                              /* end fixup_address_marker */
/***************************************************************************/


/***************************************************************************/
/* crunch_integer(value)  Append an integer value to the code buffer       */

crunch_integer(value)
XPRSAINT value;
{

  if ((code_bufferp - code_buffer) >= MAX_CODE_BUFFER_SIZE - sizeof(XPRSAINT)) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }
  else {
    *code_bufferp++ = (ICT) value;
  }

}                                                    /* end crunch_integer */
/***************************************************************************/


/***************************************************************************/
/* get_cinteger    Extract an integer from the current code segment        */
/* return the value                                                        */

XPRSAINT get_cinteger()
{
  XPRSAINT value;

  value = (XPRSAINT) *code_segmentp++;

  return(value);

}                                                      /* end get_cinteger */
/***************************************************************************/


/***************************************************************************/
/* crunch_offset(address)  Append an integer value to the code that        */
/*                         represents the offset from the given address    */
/*                         to the current code buffer address              */

crunch_offset(address)
ADDRESS address;
{
  ICT *temp;

  if ((code_bufferp - code_buffer) >= MAX_CODE_BUFFER_SIZE - sizeof(int)) {
    error(CODE_SEGMENT_OVERFLOW);
    exit(-CODE_SEGMENT_OVERFLOW);
  }
  else {
    temp = code_bufferp;
    *code_bufferp++ = address - temp;
  }

}                                                     /* end crunch_offset */
/***************************************************************************/


/***************************************************************************/
/* get_caddress()  Extract an offset from the current code segment and     */
/*                 add it to the code segment address.                     */
/* return the new address                                                  */

ADDRESS get_caddress()
{
  ADDRESS address;

  address = *((int *) code_segmentp) + code_segmentp - 1;
  code_segmentp++;

  return(address);

}                                                      /* end get_caddress */
/***************************************************************************/


/* EXECUTOR UTILITIES */


/***************************************************************************/
/* get_element_type(tp)  Given an aggregate type, return the element type  */

TYPE_STRUCT_PTR get_element_type(agg_tp)
TYPE_STRUCT_PTR agg_tp;                   /* the aggregate type */
{
  TYPE_STRUCT_PTR et;

  if (is_array(agg_tp)) return(agg_tp->info.array.elmt_typep);
  else if (is_dynagg(agg_tp)) return(agg_tp->info.dynagg.elmt_typep);
  else return(agg_tp);

}                                                  /* end GET_ELEMENT_TYPE */
/***************************************************************************/



/***************************************************************************/
/* push_integer(item_value)  Push an integer onto the runtime stack        */

push_integer(item_value)
XPRSAINT item_value;
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_integer");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKINT;
  itemp->value.integer = item_value;

  stack_access_debug("Pushed", tos);
  exit_debug("push_integer");
  return;
}                                                      /* end push_integer */
/***************************************************************************/



/***************************************************************************/
/* put_integer(sptr, item_value)  Put an integer into the runtime stack    */

put_integer(sptr, item_value)
STACK_ITEM_PTR sptr;
XPRSAINT item_value;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_integer");

  itemp->type = STKINT;
  itemp->value.integer = item_value;

  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_integer");
  return;
}                                                      /* end put_integer */
/***************************************************************************/



/***************************************************************************/
/* int get_integer(sptr)          Get an integer from the runtime stack    */

XPRSAINT get_integer(sptr)
STACK_ITEM_PTR sptr;
{
  int item_value = 0;
  XPRSAREAL r1;
  STACK_ITEM_PTR itemp = sptr;
  STACK_TYPE stype;
  entry_debug("get_integer");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
    return(item_value);
  }

  stype = itemp->type;
  if (stype == STKINT) {
    item_value = itemp->value.integer;
  }
  else if (stype == STKREA) {          /* real value, return nearest integer */
    r1 = itemp->value.real;
    item_value = r1 > 0.0 ? (XPRSAINT) (r1 + 0.5)
                          : (XPRSAINT) (r1 - 0.5);
  }
  else {
     stack_warning(STKINT, stype);
   }

  exit_debug("get_integer");
  return(item_value);
}                                                      /* end get_integer */
/***************************************************************************/



/***************************************************************************/
/* push_real(item_value)  Push a real onto the runtime stack               */

push_real(item_value)
XPRSAREAL item_value;
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_real");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKREA;
  itemp->value.real = item_value;

  stack_access_debug("Pushed", tos);
  exit_debug("push_real");
  return;
}                                                         /* end push_real */
/***************************************************************************/



/***************************************************************************/
/* put_real(sptr, item_value)  Put a real into the runtime stack           */

put_real(sptr, item_value)
STACK_ITEM_PTR sptr;
XPRSAREAL item_value;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_real");


  itemp->type = STKREA;
  itemp->value.real = item_value;

  stack_access_debug("Put",itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_real");
  return;
}                                                         /* end put_real */
/***************************************************************************/



/***************************************************************************/
/* float get_real(sptr)  Get a real from the runtime stack                 */

XPRSAREAL get_real(sptr)
STACK_ITEM_PTR sptr;
{
  XPRSAREAL item_value = 0.0;
  STACK_ITEM_PTR itemp = sptr;
  STACK_TYPE stype;
  entry_debug("get_real");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
    return(item_value);
  }
  
  stype = itemp->type;
  if (stype == STKREA) {
    item_value = itemp->value.real;
  }
  else if (stype == STKINT) {                  /* convert integer to float */
    item_value = (XPRSAREAL) itemp->value.integer;
  }
  else {
    stack_warning(STKREA, stype);
  }

  exit_debug("get_real");
  return(item_value);
}                                                         /* end get_real */
/***************************************************************************/



/***************************************************************************/
/* push_address(item_value)  Push an address onto the runtime stack        */

push_address(address)
ADDRESS address;
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_address");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKADD;
  itemp->value.address = address;

  stack_access_debug("Pushed", tos);
  exit_debug("push_address");
  return;
}                                                      /* end push_address */
/***************************************************************************/



/***************************************************************************/
/* put_address(sptr, item_value)  Put an address into the runtime stack    */

put_address(sptr, address)
STACK_ITEM_PTR sptr;
ADDRESS address;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_address");


  itemp->type = STKADD;
  itemp->value.address = address;
 
  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_address");
  return;
}                                                      /* end put_address */
/***************************************************************************/



/***************************************************************************/
/* ADDRESS get_address(sptr)  Get an address from the runtime stack        */

ADDRESS get_address(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  ADDRESS address = NULL;
  STACK_TYPE stype;
  entry_debug("get_address");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
    return(address);
  }
  
  stype = get_stackval_type(itemp);
  if (stype == STKINT ||
      stype == STKREA ||
      stype == STKLOG ||
      stype == STKSTR ||
      stype == STKBAG ||
      stype == STKLST ||
      stype == STKSET ||
      stype == STKUDF) {
    stack_warning(STKADD, stype);
  }
  else {
    address = itemp->value.address;
  }

  exit_debug("get_address");
  return(address);
}                                                      /* end get_address */
/***************************************************************************/



/***************************************************************************/
/* push_address_type(item_value, type)  Push an address onto the runtime   */
/*                                                            stack        */

push_address_type(address, type)
ADDRESS address;
STACK_TYPE type;
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_address_type (l2xixutl.c)");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  switch (type) {
    case STKBAG:
    case STKLST:
    case STKSET: {
      itemp->type = type;
      itemp->value.head = (LBS_PTR) address;
      break;
    }
    case STKSTR: {
      itemp->type = type;
      itemp->value.string = (STRING) address;
      break;
    }
    default : {
      itemp->type = type;
      itemp->value.address = address;
      break;
    }
  }

  stack_access_debug("Pushed", tos);
  exit_debug("push_address_type");
  return;
}                                                      /* end push_address_type */
/***************************************************************************/



/***************************************************************************/
/* put_address_type(sptr, item_value, type)  Put an address into the runtime stack    */

put_address_type(sptr, address, type)
STACK_ITEM_PTR sptr;
ADDRESS address;
STACK_TYPE type;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_address_type (l2xixutl.c)");

  switch (type) {
    case STKBAG:
    case STKLST:
    case STKSET: {
      itemp->type = type;
      itemp->value.head = (LBS_PTR) address;
      break;
    }
    case STKSTR: {
      itemp->type = type;
      itemp->value.string = (STRING) address;
      break;
    }
    default : {
      itemp->type = type;
      itemp->value.address = address;
      break;
    }
  }
 
  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_address_type");
  return;
}                                                      /* end put_address_type */
/***************************************************************************/



/***************************************************************************/
/* ADDRESS get_address_type(sptr, type)  Get an address from the runtime stack        */

ADDRESS get_address_type(sptr, type)
STACK_ITEM_PTR sptr;
STACK_TYPE type;
{
  STACK_ITEM_PTR itemp = sptr;
  ADDRESS address = NULL;
  STACK_TYPE ftype;
  entry_debug("get_address_type (l2xixutl.c)");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
    return(NULL);
  }

  ftype = get_stackval_type(itemp);
  if (type != ftype) stack_warning(type, ftype);

  switch (ftype) {
    case STKBAG:
    case STKLST:
    case STKSET: {
      address = (ADDRESS) itemp->value.head;
      break;
    }
    case STKSTR: {
      address = (ADDRESS) itemp->value.string;
      break;
    }
    case STKADD:
    case STKARY:
    case STKENT: {
      address = itemp->value.address;
      break;
    }
  }

  exit_debug("get_address_type");
  return(address);
}                                                      /* end get_address_type */
/***************************************************************************/



/***************************************************************************/
/* get_stackval_type(sptr)  Returns the type of value in the stack         */

STACK_TYPE get_stackval_type(sptr)
STACK_ITEM_PTR sptr;
{
  entry_debug("get_stackval_type (l2xixutl.c)");

  if (sptr == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
  }

  exit_debug("get_stackval_type");
  return(sptr->type);
}                                                 /* end GET_STACKVAL_TYPE */
/***************************************************************************/



/***************************************************************************/
/* push_false()  Push false onto runtime stack                             */

push_false()
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_false");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKLOG;
  itemp->value.integer = FALSE_REP;

  stack_access_debug("Pushed", tos);
  exit_debug("push_false");
  return;
}                                                        /* end PUSH_FALSE */
/***************************************************************************/



/***************************************************************************/
/* push_unknown()  Push unknown onto runtime stack                         */

push_unknown()
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_unknown");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKLOG;
  itemp->value.integer = UNKNOWN_REP;

  stack_access_debug("Pushed", tos);
  exit_debug("push_unknown");
  return;
}                                                      /* end PUSH_UNKNOWN */
/***************************************************************************/



/***************************************************************************/
/* push_true()  Push true onto runtime stack                               */

push_true()
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_true");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKLOG;
  itemp->value.integer = TRUE_REP;

  stack_access_debug("Pushed", tos);
  exit_debug("push_true");
  return;
}                                                         /* end PUSH_TRUE */
/***************************************************************************/



/***************************************************************************/
/* put_false()  Put false onto runtime stack                               */

put_false(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_false");

  itemp->type = STKLOG;
  itemp->value.integer = FALSE_REP;

  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_false");
  return;
}                                                         /* end PUT_FALSE */
/***************************************************************************/



/***************************************************************************/
/* put_unknown()  Put unknown onto runtime stack                           */

put_unknown(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_unknown");

  itemp->type = STKLOG;
  itemp->value.integer = UNKNOWN_REP;

  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_unknown");
  return;
}                                                       /* end PUT_UNKNOWN */
/***************************************************************************/



/***************************************************************************/
/* put_true()  Put true onto runtime stack                                 */

put_true(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_true");

  itemp->type = STKLOG;
  itemp->value.integer = TRUE_REP;

  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_true");
  return;
}                                                          /* end PUT_TRUE */
/***************************************************************************/



/***************************************************************************/
/* push_logical()  Push logical value onto runtime stack                   */

push_logical(item_value)
LOGICAL_REP item_value;
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_logical");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKLOG;
  itemp->value.integer = item_value;

  stack_access_debug("Pushed", tos);
  exit_debug("push_logical");
  return;
}                                                      /* end PUSH_LOGICAL */
/***************************************************************************/



/***************************************************************************/
/* put_logical()  Put logical value onto runtime stack                     */

put_logical(sptr, item_value)
STACK_ITEM_PTR sptr;
LOGICAL_REP item_value;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_logical");

  itemp->type = STKLOG;
  itemp->value.integer = item_value;

  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_logical");
  return;
}                                                       /* end PUT_LOGICAL */
/***************************************************************************/



/***************************************************************************/
/* get_logical(sptr)  Get a boolean/logical from the runtime stack         */

LOGICAL_REP get_logical(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  LOGICAL_REP item_value = UNKNOWN_REP;
  entry_debug("get_logical");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
  }

  if (itemp->type != STKLOG) {
    stack_warning(STKLOG, itemp->type);
  }
  else {
    item_value = itemp->value.integer;
  }

  exit_debug("get_logical");
  return(item_value);
}                                                       /* end GET_LOGICAL */
/***************************************************************************/



/***************************************************************************/
/* push_string(item_value)  Push a string onto the stack                   */

STRING push_string(item_value)
STRING item_value;
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_string");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKSTR;
  itemp->value.string = item_value;

  stack_access_debug("Pushed", tos);

  exit_debug("push_string");
  return;
}                                                       /* end PUSH_STRING */
/***************************************************************************/



/***************************************************************************/
/* put_string(sptr, item_value)  Put a string into the stack               */

STRING put_string(sptr, item_value)
STACK_ITEM_PTR sptr;
STRING item_value;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_string");

  itemp->type = STKSTR;
  itemp->value.string = item_value;

  stack_access_debug("Put", itemp);

  if (itemp > tos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }

  exit_debug("put_string");
  return;
}                                                        /* end PUT_STRING */
/***************************************************************************/



/***************************************************************************/
/* get_stacked_string(sptr)  Get a string from the stack                   */

STRING get_stacked_string(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  STRING item_value = "";
  entry_debug("get_stacked_string");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
  }

  if (itemp->type != STKSTR) {
    stack_warning(STKSTR, itemp->type);
  }
  else {
    item_value = itemp->value.string;
  }

  exit_debug("get_stacked_string");
  return(item_value);
}                                                /* end GET_STACKED_STRING */
/***************************************************************************/



/***************************************************************************/
/* push_undef()  Push undefined `?' onto runtime stack                     */

push_undef()
{
  STACK_ITEM_PTR itemp = ++tos;
  entry_debug("push_undef");

  maxtos = tos > maxtos ? tos : maxtos;

  if (itemp >= &stack[MAX_STACK_SIZE]) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
  }

  itemp->type = STKUDF;
  itemp->value.integer = '\?';
 
  stack_access_debug("Pushed", tos);
  exit_debug("push_undef");
  return;
}                                                        /* end PUSH_UNDEF */
/***************************************************************************/



/***************************************************************************/
/* put_undef(sptr)  Put undefined `?' into runtime stack                  */

put_undef(sptr)
STACK_ITEM_PTR sptr;
{
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("put_undef");


  itemp->type = STKUDF;
  itemp->value.integer = '\?';

  stack_access_debug("Put", itemp);
  if (itemp > maxtos) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  exit_debug("put_undef");
  return;
}                                                         /* end PUT_UNDEF */
/***************************************************************************/



/***************************************************************************/
/* get_undef()  Get undefined `?' from runtime stack                       */

char get_undef(sptr)
STACK_ITEM_PTR sptr;
{
  char item_value = ' ';
  STACK_ITEM_PTR itemp = sptr;
  entry_debug("get_undef");
  stack_access_debug("Got", itemp);

  if (itemp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
  }

  if (itemp->type != STKUDF) {
    item_value = ' ';
    runtime_warning(STKUDF, itemp->type);
  }
  else {
    item_value = itemp->value.integer;
  }

  exit_debug("get_undef");
  return(item_value);
}                                                         /* end GET_UNDEF */
/***************************************************************************/



/***************************************************************************/
/* is_value_undef(sptr)   TRUE iff value on stack at sptr is undef         */

BOOLEAN is_value_undef(sptr)
STACK_ITEM_PTR sptr;
{
  BOOLEAN result = FALSE;

  if (sptr == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
  }
  else {
    result = (sptr->type == STKUDF);
  }
  return(result);
}                                                    /* end IS_VALUE_UNDEF */
/***************************************************************************/



/***************************************************************************/
/* copy_value(to, from)   Copies stack value                               */

copy_value(top, fromp)
STACK_ITEM_PTR top;
STACK_ITEM_PTR fromp;
{
  STACK_TYPE type;
  entry_debug("copy_value (l2xixutl.c)");

  if (top == NULL || fromp == NULL) {
    runtime_warning(INVALID_STACK_ACCESS);
    exit_debug("copy_value");
    return;
  }

    stack_access_debug("Copy -- replacing: ", top);
    stack_access_debug("             with: ", fromp);
    type = fromp->type;
    switch (type) {
      case STKINT: {
        top->type = type;
        top->value.integer = fromp->value.integer;
        break;
      }
      case STKREA: {
        top->type = type;
        top->value.real = fromp->value.real;
        break;
      }
      case STKADD:
      case STKARY:
      case STKBAG:
      case STKLST:
      case STKSET:
      case STKENT: {
        top->type = type;
        top->value.address = fromp->value.address;
        break;
      }
      case STKUDF: {
        put_undef(top);
        break;
      }
      default: {
        break;
      }
    } /* end switch */
  
  exit_debug("copy_value");
  return;
}                                                        /* end COPY_VALUE */
/***************************************************************************/



/***************************************************************************/
/* create_copy_value(fromp)   Copies a stack value to a new value          */
/*    returns pointer to the new copied value                              */

STACK_ITEM_PTR create_copy_value(fromp)
STACK_ITEM_PTR fromp;
{
  STACK_TYPE type;
  STACK_ITEM_PTR top;
  entry_debug("create_copy_value (l2xixutl.c)");

  /* get the memory required */
  top = alloc_struct(STACK_ITEM);
  if (top == NULL) {
    runtime_error(RUNTIME_STACK_OVERFLOW);
    exit_debug("create_copy_value");
    return(NULL);
  }

    type = fromp->type;
    switch (type) {
      case STKINT: {
        top->type = type;
        top->value.integer = fromp->value.integer;
        break;
      }
      case STKREA: {
        top->type = type;
        top->value.real = fromp->value.real;
        break;
      }
      case STKADD:
      case STKARY:
      case STKBAG:
      case STKLST:
      case STKSET:
      case STKENT: {
        top->type = type;
        top->value.address = fromp->value.address;
        break;
      }
      case STKUDF: {
        put_undef(top);
        break;
      }
      default: {
        break;
      }
    } /* end switch */
  stack_access_debug("Created copy of: ", fromp);
  stack_access_debug("             as: ", top);

  exit_debug("create_copy_value");
  return(top);
}                                                 /* end CREATE_COPY_VALUE */
/***************************************************************************/



/***************************************************************************/
/* execute(rtn_idp)  Execute a routine's code segment                      */
/*                                                                         */

execute(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
  entry_debug("execute");

  routine_entry(rtn_idp);

  get_ctoken();
  exec_statement();

  routine_exit(rtn_idp);

  exit_debug("execute");
  return;
}                                                           /* end execute */
/***************************************************************************/


/***************************************************************************/
/* routine_entry(rtn_idp)  Point to the new routine's code segment         */
/*                         and allocate its locals                         */

routine_entry(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;         /* new routine's id */
{
  SYMTAB_NODE_PTR var_idp;       /* local variable id */
  entry_debug("routine_entry");
  stack_debug();

  trace_routine_entry(rtn_idp);

  /* switch to new code segment */
  code_segmentp = rtn_idp->defn.info.routine.code_segment;

  /* allocate local variables */
  for (var_idp = rtn_idp->defn.info.routine.locals;
       var_idp != NULL;
       var_idp = var_idp->next) {
    alloc_local(var_idp->typep);
  }

  stack_debug();
  exit_debug("routine_entry");
  return;
}                                                     /* end routine_entry */
/***************************************************************************/


/***************************************************************************/
/* routine_exit(rtn_idp)  Deallocate the routine's parameters and locals.  */
/*                        Cut off its stack frame and return to the        */
/*                        caller's code segment.                           */

routine_exit(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;         /* exiting routine's id */
{
  SYMTAB_NODE_PTR idp;        /* local variable or param id */
  STACK_FRAME_HEADER_PTR hp;  /* ptr to stack frame header */
  TYPE_STRUCT_PTR target_tp;  /* ptr to return type of routine */
  TYPE_STRUCT_PTR expr_tp;    /* ptr to type of RETURN expression */

  entry_debug("routine_exit");
  stack_debug();

  trace_routine_exit(rtn_idp);

  /* Treat a RETURN expression as an assignment to the routine's id */
  if (ctoken == LPAREN) {
    target_tp = rtn_idp->typep;
    expr_tp = exec_expression();
    exec_the_assign(stack_frame_basep, target_tp, expr_tp);
  }

  /* Deallocate parameters and local variables */
  for (idp = rtn_idp->defn.info.routine.parms;
       idp != NULL;
       idp = idp->next) {
    free_data(idp);
  }
  for (idp = rtn_idp->defn.info.routine.locals;
       idp != NULL;
       idp = idp->next) {
    free_data(idp);
  }

  /* pop off the stack frame and return to caller's code segmnent */
  entry_debug("routine_exit: pop the frame stack");
  stack_debug();
  hp = (STACK_FRAME_HEADER_PTR) stack_frame_basep;
  code_segmentp = get_return_address(hp);
  tos = (rtn_idp->defn.key == PROC_DEFN)
        ? stack_frame_basep - 1
        : stack_frame_basep;
  stack_frame_basep = (STACK_ITEM_PTR) get_dynamic_link(hp);

  exit_debug("routine_exit: pop the frame stack");
  stack_debug();
  exit_debug("routine_exit");
  return;
}                                                      /* end routine_exit */
/***************************************************************************/


/***************************************************************************/
/* push_stack_frame_header(old_level, new_level) Allocate the callee       */
/*                         routine's stack frame                           */

push_stack_frame_header(old_level, new_level)
int old_level;            /* level of caller */
int new_level;            /* level of callee */
{
  STACK_FRAME_HEADER_PTR hp;
  STACK_ITEM_PTR newbasep;     /* pointer to base of new frame */
  entry_debug("push_stack_frame_header");

  stack_debug();
/*  push_integer(0);                    return value */
  hp = (STACK_FRAME_HEADER_PTR) stack_frame_basep;
  newbasep = tos + 1;
  push_frame_data(0, NULL, NULL, NULL);

  /* static link */
  if (new_level == (old_level + 1)) {
    /* calling a routine nested in the caller */
    /* push pointer to caller's stack frame */
    put_static_link(newbasep, (ADDRESS) hp);
  }
  else if (new_level == old_level) {
    /* calling routine at the same level */
    /* push pointer to stack of common parent */
    put_static_link(newbasep, get_static_link(hp));
  }
  else {
    /* calling a routine at a lesser level (nested less deeply ) */
    /* push pointer to stack of nearest common ancestor */
    int delta = (old_level - new_level);
    
    while (delta-- >= 0) {
      hp = (STACK_FRAME_HEADER_PTR) get_static_link(hp);
    }
    put_static_link(newbasep, hp);
  }

  put_dynamic_link(newbasep, stack_frame_basep);

  stack_debug();
  exit_debug("push_stack_frame_header");
  return;
}                                           /* end push_stack_frame_header */
/***************************************************************************/


/***************************************************************************/
/* alloc_local(tp)   Allocate a local variable on the stack                */
/*                                                                         */

alloc_local(tp)
TYPE_STRUCT_PTR tp;           /* ptr to type of variable */
{
  LBS_PTR lbs;           /* pointer to dynamic agg */
  STACK_TYPE stktyp;
  entry_debug("alloc_local");

  if (tp == integer_typep) {
    push_integer(0);
  }
  else if (tp == real_typep) {
    push_real(0.0);
  }
  else if (tp == boolean_typep) {
    push_false();     /* FALSE */
  }
  else if (tp == string_typep || tp->form == STRING_FORM) {
    push_string(NULL);
  }
  else if (tp == logical_typep) {
    push_unknown();
  }
  else {
    switch (tp->form) {
      case ENUM_FORM: {
        push_integer(0);
        break;
      }
      case SUBRANGE_FORM: {
        alloc_local(tp->info.subrange.range_typep);
        break;
      }
      case ARRAY_FORM: {
        ADDRESS ptr = (ADDRESS) alloc_array(STACK_ITEM_PTR, tp->size);
        sprintf(dbuffer, "Allocated %d bytes for array at %d\n",
                          tp->size, ptr);
        debug_print(dbuffer);
        push_address((ADDRESS) ptr);
        break;
      }
      case ENTITY_FORM: {
        ADDRESS ptr = (ADDRESS) alloc_array(STACK_ITEM_PTR, tp->size);
        sprintf(dbuffer, "Allocated %d bytes for entity at %d\n",
                          tp->size, ptr);
        debug_print(dbuffer);
        push_address_type((ADDRESS) ptr, STKENT);
        break;
      }
      case BAG_FORM:
      case LIST_FORM:
      case SET_FORM: {
        lbs = lbs_init();
        push_address_type(lbs, form2stack[tp->form]);
        break;
      }
    } /* end switch */
  }

  exit_debug("alloc_local");
  return;
}                                                       /* end alloc_local */
/***************************************************************************/


/***************************************************************************/
/* free_data(idp)  Deallocate the data area of an array or record local    */
/*                 variable or value parameter                             */

free_data(idp)
SYMTAB_NODE_PTR idp;             /* parm or variable id */
{
  STACK_ITEM_PTR itemp;                 /* ptr to stack item */
  TYPE_STRUCT_PTR tp = idp->typep;      /* ptr to id's type */
  entry_debug("free_data");

  if (((tp->form == ARRAY_FORM) || (tp->form == ENTITY_FORM)) &&
      (idp->defn.key != VARPARM_DEFN)) {
    stack_frame_debug();
    itemp = stack_frame_basep + idp->defn.info.data.offset;
    stack_item_debug(itemp);
    free(get_address(itemp));
  }

  exit_debug("free_data");
  return;
}                                                         /* end free_data */
/***************************************************************************/


/***************************************************************************/
/* push_frame_data(int, add, add, add) Push frame data onto runtime stack  */

push_frame_data(ifrv, asl, adl, ara)
int ifrv;               /* function return value */
ADDRESS asl;            /* static link */
ADDRESS adl;            /* dynamic link */
ADDRESS ara;            /* return address */
{
  entry_debug("push_frame_data");
  stack_debug();

  push_integer(ifrv);
  push_address(asl);
  push_address(adl);
  push_address(ara);

  stack_debug();
  exit_debug("push_frame_data");  
  return;
}                                                   /* end push_frame_data */
/***************************************************************************/


/***************************************************************************/
/* put_static_link(framep, address)  Put static link data into frame       */

put_static_link(framep, address)
STACK_ITEM_PTR framep;            /* pointer to frame */
ADDRESS address;                  /* static link */
{
  entry_debug("put_static_link");
  put_address((framep+1), address);
  exit_debug("put_static_link");

}                                                   /* end put_static_link */
/***************************************************************************/



/***************************************************************************/
/* ADDRESS get_static_link(framep) Get static link data from frame         */

ADDRESS get_static_link(framep)
STACK_ITEM_PTR framep;            /* pointer to frame */
{
  ADDRESS result;
  entry_debug("get_static_link");

  result = get_address(framep + 1);

  exit_debug("get_static_link");
  return(result);
}                                                   /* end get_static_link */
/***************************************************************************/


/***************************************************************************/
/* put_dynamic_link(framep, address)  Put dynamic link data into frame     */

put_dynamic_link(framep, address)
STACK_ITEM_PTR framep;            /* pointer to frame */
ADDRESS address;                  /* dynamic link */
{
  entry_debug("put_dynamic_link");
  put_address((framep+2), address);
  exit_debug("put_dynamic_link");

}                                                  /* end put_dynamic_link */
/***************************************************************************/



/***************************************************************************/
/* ADDRESS get_dynamic_link(framep) Get dynamic link data from frame       */

ADDRESS get_dynamic_link(framep)
STACK_ITEM_PTR framep;                  /* pointer to base of frame */
{
  ADDRESS result;
  entry_debug("get_dynamic_link");

  result = get_address(framep + 2);

  exit_debug("get_dynamic_link");
  return(result);
}                                                  /* end get_dynamic_link */
/***************************************************************************/


/***************************************************************************/
/* put_return_address(framep, address)  Put return address data into frame */

put_return_address(framep, address)
STACK_ITEM_PTR framep;            /* pointer to frame */
ADDRESS address;                  /* return link */
{

  entry_debug("put_return_address");
  put_address((framep+3), address);
  exit_debug("put_return_address");

}                                                /* end put_return_address */
/***************************************************************************/


/***************************************************************************/
/* ADDRESS get_return_address(framep) Get return address data from frame   */

ADDRESS get_return_address(framep)
STACK_ITEM_PTR framep;            /* pointer to frame */
{
  ADDRESS result;
  entry_debug("get_return_address");
  
  result = get_address(framep + 3);

  exit_debug("get_return_address");
  return(result);
}                                                /* end get_return_address */
/***************************************************************************/



/***************************************************************************/
/* stack_value_equal(a, b)  Tests whether two stack items have the same    */
/*                          data value.                                    */
/*     returns:  UNKNOWN_REP if either arg is indeterminate                */
/*               otherwise TRUE_REP or FALSE_REP as appropriate            */

LOGICAL_REP stack_value_equal(a, b)
STACK_ITEM_PTR a;
STACK_ITEM_PTR b;
{
  STACK_TYPE atype, btype;
  int ans;
  XPRSAINT i1, i2;
  XPRSAREAL r1, r2;
  LOGICAL_REP b1, b2;
  LOGICAL_REP log = FALSE_REP;

  entry_debug("stack_value_equal (l2xixutl.c)");

  /* check for indeterminate values */
  atype = get_stackval_type(a);
  if (atype == STKUDF) log = UNKNOWN_REP;
  btype = get_stackval_type(b);
  if (btype == STKUDF) log = UNKNOWN_REP;
  if (log == UNKNOWN_REP) {
    exit_debug("stack_value_equal (indeterminate UNKNOWN_REP)");
    return(log);
  }
  

   /* check type equality */
  if (atype != btype) {
    exit_debug("stack_value_equal (different types FALSE_REP)");
    return(FALSE_REP);
  }

  switch (atype) {
    case STKINT: {
      i2 = get_integer(b);
      i1 = get_integer(a);
      sprintf(dbuffer, "Checking %d == %d\n", 
                        i1, i2);
      debug_print(dbuffer);
      ans = (i1 == i2);
      sprintf(dbuffer, "Checked %d == %d, with result = ", 
                        i1, i2);
      debug_print(dbuffer);
      if (ans) sprintf(dbuffer, "TRUE\n");
      else sprintf(dbuffer, "FALSE\n");
      debug_print(dbuffer);
      break;
    }
    case STKREA: {
      ans = (get_real(a) == get_real(b));
      break;
    }
    case STKLOG: {
      ans = (get_logical(a) == get_logical(b));
      break;
    }
    case STKSTR: {
      ans = strcmp(get_stacked_string(a), get_stacked_string(b));
      if (ans == 0) ans = TRUE;
      else ans = FALSE;
      break;
    }
    default: {               /* for now, only test on simple types */
      exit_debug("stack_value_equal (default UNKNOWN_REP)");
      return(UNKNOWN_REP);
      break;
    }
  } /* end switch */

  if (ans) {
    exit_debug("stack_value_equal (end switch TRUE_REP)");
    return(TRUE_REP);
  }
  else {
    exit_debug("stack_value_equal (end switch FALSE_REP)");
    return(FALSE_REP);
  }

}                                                 /* end STACK_VALUE_EQUAL */
/***************************************************************************/