/* l2xixxpr.c  LTX2X interpreter expression executor 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 <math.h>
#include "l2xicmon.h"
#include "l2xierr.h"
#include "l2xiscan.h"
#include "l2xisymt.h"
#include "l2xiprse.h"
#include "l2xiidbg.h"
#include "l2xiexec.h"

#include "listsetc.h"

/* EXTERNALS */

extern int level;
 
extern ICT *code_segmentp;           /* code segment ptr */ /* used? */
extern TOKEN_CODE ctoken;            /* token from code segment */

extern STACK_ITEM *stack;                  /* runtime stack */
extern STACK_ITEM_PTR tos;                 /* ptr to top of runtime stack */
extern STACK_ITEM_PTR stack_frame_basep;   /* ptr to stack fame base */

extern BOOLEAN is_value_undef();
extern STRING get_stacked_string();

extern STACK_TYPE form2stack[];      /* map form type to stack type */

/* FORWARDS */

TYPE_STRUCT_PTR exec_expression(), exec_simple_expression(),
                exec_term(), exec_factor(),
                exec_constant(), exec_variable(),
                exec_subscripts();
TYPE_STRUCT_PTR exec_simple_factor(), exec_attribute();

STRING concat_strings();

/* MACROS */

/* undef_types(tp1, tp2) TRUE if either type is undef, else FALSE */
#define undef_types(tp1, tp2) ((tp1 == any_typep) || (tp2 == any_typep))

/* undef_values(sp1, sp2) TRUE if either stack value is undef */
#define undef_values(sp1, sp2) (is_value_undef(sp1) || is_value_undef(sp2))

/* set_undef(tp1)    Sets tp1 to undef type */
#define set_undef(tp1) (tp1 = any_typep)

/* is_undef(tp1)  TRUE if tp1 is undef type, else FALSE */
#define is_undef(tp1) (tp1 == any_typep)

/* string_operands(tp1, tp2) TRUE iff tp1 and tp2 are string types */
#define string_operands(tp1, tp2) ((tp1)->form == STRING_FORM && (tp2)->form == STRING_FORM)

/***************************************************************************/
/* exec_expression()  Execute an expression                                */
/*                   <sexp> [ <relop> <sexp> ]                             */
/* return a pointer to the type structure.                                 */

TYPE_STRUCT_PTR exec_expression()
{
  STACK_ITEM_PTR operandp1, operandp2;       /* ptrs to operands */
  TYPE_STRUCT_PTR result_tp, tp1, tp2;       /* ptrs to types */
  TOKEN_CODE op;                             /* operator token */
  BOOLEAN result;
  LOGICAL_REP log;
  entry_debug("exec_expression");

  tp1 = exec_simple_expression();      /* first simple expression */
  result_tp = tp1;

  /* process relop sexp, if any */
  if ((ctoken == EQUAL) || (ctoken == LT) || (ctoken == GT) ||
      (ctoken == NE)    || (ctoken == LE) || (ctoken == GE) ||
      (ctoken == COLONEQUALCOLON) || (ctoken == COLONNEQCOLON) ||
      (ctoken == IN) || (ctoken == XLIKE) ) {
    op = ctoken;
    tp1 = base_type(tp1);
    result_tp = logical_typep;

    get_ctoken();
    tp2 = base_type(exec_simple_expression());  /* second simple expression */
    /* get operands */
    operandp1 = tos - 1;
    operandp2 = tos;

    if (undef_types(tp1, tp2) || undef_values(operandp1, operandp2)) {
      put_unknown(operandp1);
      pop();
      expression_type_debug(result_tp);
      exit_debug("exec_expression");
      return(result_tp);
    }

    log = do_relop(operandp1, tp1, op, operandp2, tp2);
    /* replace the two operands on the stack by the result */
    put_logical(operandp1, log);
    pop();
  } /* end if on relop */

  
  expression_type_debug(result_tp);
  exit_debug("exec_expression");
  return(result_tp);
}                                                   /* end exec_expression */
/***************************************************************************/


/***************************************************************************/
/* do_relop()  execute a relop expression                                  */

LOGICAL_REP do_relop(operandp1, tp1, op, operandp2, tp2)
STACK_ITEM_PTR operandp1, operandp2;         /* the operands */
TYPE_STRUCT_PTR tp1, tp2;                    /* their types */
TOKEN_CODE op;                               /* the relop */
{
  int result;
  LOGICAL_REP log;
  
  entry_debug("do_relop (l2xixxpr.c)");

    if (((tp1 == integer_typep) && (tp2 == integer_typep)) ||
        (tp1->form == ENUM_FORM)) { 
       /* both operands are integer, bool or enum */
      switch (op) {
        case EQUAL:
        case COLONEQUALCOLON: {
          result = get_integer(operandp1) == get_integer(operandp2);
          break;
        }
        case LT: {
          result = get_integer(operandp1) < get_integer(operandp2);
          break;
        }
        case GT: {
          result = get_integer(operandp1) > get_integer(operandp2);
          break;
        }
        case NE:
        case COLONNEQCOLON: {
          result = get_integer(operandp1) != get_integer(operandp2);
          break;
        }
        case LE: {
          result = get_integer(operandp1) <= get_integer(operandp2);
          break;
        }
        case GE: {
          result = get_integer(operandp1) >= get_integer(operandp2);
          break;
        }
      } /* end switch on op */
    }  

    else if ((tp1 == real_typep) || (tp2 == real_typep)) {
             /* One operand real, t'other real or integer */
      promote_operands_to_real(operandp1, tp1, operandp2, tp2);

      switch (op) {
        case EQUAL:
        case COLONEQUALCOLON: {
          result = get_real(operandp1) == get_real(operandp2);
          break;
        }
        case LT: {
          result = get_real(operandp1) < get_real(operandp2);
          break;
        }
        case GT: {
          result = get_real(operandp1) > get_real(operandp2);
          break;
        }
        case NE:
        case COLONNEQCOLON: {
          result = get_real(operandp1) != get_real(operandp2);
          break;
        }
        case LE: {
          result = get_real(operandp1) <= get_real(operandp2);
          break;
        }
        case GE: {
          result = get_real(operandp1) >= get_real(operandp2);
          break;
        }
      } /* end switch */
    }

    else if (string_operands(tp1, tp2)) {    /* strings */
      if (op == XLIKE) {
        result = like_expr(get_stacked_string(operandp1), 
                           get_stacked_string(operandp2));
        if (result < 0) {               /* invalid pattern */
          runtime_error(INVALID_REGULAR_EXPRESSION);
          log = UNKNOWN_REP;
        }
        else if (result == 0) {
          log = FALSE_REP;
        }
        else {
          log = TRUE_REP;
        }
        exit_debug("do_relop (at LIKE)");
        return(log);
      }
      else {             /* general relational operator */
        int cmp = strncmp(get_stacked_string(operandp1), 
                          get_stacked_string(operandp2));

        result = (((cmp < 0) &&
                 ((op == NE) || (op == COLONNEQCOLON) || (op == LE) || (op == LT)))
               || ((cmp == 0) && 
                   ((op == EQUAL) || (op == COLONEQUALCOLON) || (op == LE) || (op == GE)))
               || ((cmp > 0) &&
                   ((op == NE) || (op == COLONNEQCOLON) || (op == GE) || (op == GT))));
      }
    }

    else if (is_dynagg(tp1) || is_dynagg(tp2)) {     /* dynamic agg */
      log = exec_dynagg_relop(tp1, operandp1, op, tp2, operandp2);
      exit_debug("do_relop (at dynagg)");
      return(log);
    }

  exit_debug("do_relop");
  if (result == TRUE) return(TRUE_REP);
  else return(FALSE_REP);

}                                                          /* end DO_RELOP */
/***************************************************************************/



/***************************************************************************/
/* exec_simple_expression()  Execute a simple expression                   */
/*             [ <unary-op> ] <term> <pmop> <term> { <pmop> <term> }       */
/* return a pointer to the type structure.                                 */

TYPE_STRUCT_PTR exec_simple_expression()
{
  STACK_ITEM_PTR operandp1, operandp2;       /* ptrs to operands */
  TYPE_STRUCT_PTR result_tp, tp2;            /* ptrs to types */
  TOKEN_CODE op;                             /* operator token */
  TOKEN_CODE unary_op = PLUS;                /* unary op token */
  XPRSAINT i1;
  LOGICAL_REP b1, b2, br;
  XPRSAREAL r1;
  STRING str;
  entry_debug("exec_simple_expression");

  /* remember unary op */
  if ((ctoken == PLUS) || (ctoken == MINUS)) {
    unary_op = ctoken;
    get_ctoken();
  }

  result_tp = exec_term();                /* first term */

  /* if there was a unary MINUS, negate the top of the stack */
  if (unary_op == MINUS) {
    if (!is_value_undef(tos)) {
      if (result_tp == integer_typep) put_integer(tos, -get_integer(tos));
      else                            put_real(tos, -get_real(tos));
    }
  }

  /* loop to process following terms (seperated by <op> ) */
  while ((ctoken == PLUS) || (ctoken == MINUS) || 
         (ctoken == OR) || (ctoken == XXOR) ) {
    op = ctoken;                        /* operator */
    result_tp = base_type(result_tp);

    get_ctoken();
    tp2 = base_type(exec_term());       /* term */

    operandp1 = tos - 1;
    operandp2 = tos;

    if (undef_values(operandp1, operandp2)) {
      put_undef(operandp1);
    }

    else if ((op == OR) || (op == XXOR)) {
      b1 = get_logical(operandp1);
      b2 = get_logical(operandp2);
      br = FALSE_REP;
      if (op == OR) {                                 /* term OR term */
        if (b1 == FALSE_REP && b2 == FALSE_REP) {
          br = FALSE_REP;
        }
        else if (b1 == UNKNOWN_REP && 
                 (b2 == UNKNOWN_REP || b2 == FALSE_REP)) {
          br = UNKNOWN_REP;
        }
        else if (b1 == FALSE_REP && b2 == UNKNOWN_REP) {
          br = UNKNOWN_REP;
        }
        else {
          br = TRUE_REP;
        }
      }
      else {                                           /* term XOR term */
        if (b1 == TRUE_REP && b2 == TRUE_REP) {
          br = FALSE_REP;
        }
        else if (b1 == TRUE_REP && b2 == FALSE_REP) {
          br = TRUE_REP;
        }
        else if (b1 == FALSE_REP && b2 == TRUE_REP) {
          br = TRUE_REP;
        }
        else if (b1 == FALSE_REP && b2 == FALSE_REP) {
          br = FALSE_REP;
        }
        else {
          br = UNKNOWN_REP;
        }
      }
      put_logical(operandp1, br);
      result_tp = logical_typep;
    }

        /* op is + or - */
    else if ((result_tp == integer_typep) &&
             (tp2 == integer_typep)) {
             /* both operands are integer */
      i1 = (op == PLUS)
                         ? get_integer(operandp1) + get_integer(operandp2)
                         : get_integer(operandp1) - get_integer(operandp2);
      put_integer(operandp1, i1);
      result_tp = integer_typep;
    }
    else if ((result_tp == string_typep || result_tp->form == STRING_FORM) &&
             (tp2 == string_typep || tp2->form == STRING_FORM)) {
           /* two strings, plus is only operator */
      if (op == PLUS) {
        str = concat_strings(operandp1, operandp2);
        free(get_stacked_string(operandp1));
        put_string(operandp1, str);
        result_tp = string_typep;
        result_tp->form == STRING_FORM;
      }
    }
    else {
       /* mix of real and integer */
      promote_operands_to_real(operandp1, result_tp, operandp2, tp2);
      r1 = (op == PLUS)
                         ? get_real(operandp1) + get_real(operandp2)
                         : get_real(operandp1) - get_real(operandp2);
      put_real(operandp1, r1);
      result_tp = real_typep;
    }

    /* pop off the second operand */
    pop();
  } /* end while over <op> <term> */

  exit_debug("exec_simple_expression");
  return(result_tp);
}                                            /* end exec_simple_expression */
/***************************************************************************/



/***************************************************************************/
/* exec_term()  Execute a term                                             */
/*              <factor> <multop> <factor> { <multop> <factor> }           */
/* return a pointer to the type structure.                                 */

TYPE_STRUCT_PTR exec_term()
{
  STACK_ITEM_PTR operandp1, operandp2;       /* ptrs to operands */
  TYPE_STRUCT_PTR result_tp, tp2;            /* ptrs to types */
  TOKEN_CODE op;                             /* operator token */
  XPRSAINT i1;
  XPRSAREAL r1;
  LOGICAL_REP b1, b2, br;
  entry_debug("exec_term");

  result_tp = exec_factor();           /* first factor */

  /* loop to process following <multop> <factor> pairs */
  while ((ctoken == STAR) || (ctoken == SLASH) || (ctoken == DIV) ||
         (ctoken == MOD) || (ctoken == AND) || (ctoken == BARBAR)) {
    op = ctoken;
    result_tp = base_type(result_tp);

    get_ctoken();
    tp2 = exec_factor();              /* next factor */

    operandp1 = tos - 1;
    operandp2 = tos;

    if (undef_values(operandp1, operandp2)) {
      put_undef(operandp1);
    }

    else if (op == AND) {
      b1 = get_logical(operandp1);
      b2 = get_logical(operandp2);
      if (b1 == TRUE_REP && b2 == TRUE_REP) {
        br = TRUE_REP;
      }
      else if (b1 == TRUE_REP && b2 == UNKNOWN_REP) {
        br = UNKNOWN_REP;
      }
      else if (b1 == UNKNOWN_REP && b2 == TRUE_REP) {
        br = UNKNOWN_REP;
      }
      else if (b1 == UNKNOWN_REP && b2 == UNKNOWN_REP) {
        br = UNKNOWN_REP;
      }
      else {
        br = FALSE_REP;
      }
      put_logical(operandp1, br);
      result_tp = logical_typep;
    }

    else if (op == BARBAR) {
      runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
/*      result_tp = &dummy_typep; */
    }

    else {
          /* *, /, DIV or MOD */
      switch (op) {
        case STAR: {
          if ((result_tp == integer_typep) && (tp2 == integer_typep)) {
            /* integer operands */
            i1 = get_integer(operandp1) * get_integer(operandp2);
            put_integer(operandp1, i1);
            result_tp = integer_typep;
          }
          else {
             /* at least one real */
            promote_operands_to_real(operandp1, result_tp, operandp2, tp2);
            r1 = get_real(operandp1) * get_real(operandp2);
            put_real(operandp1, r1);
            result_tp = real_typep;
          }
          break;
        }
        case SLASH: {
          promote_operands_to_real(operandp1, result_tp, operandp2, tp2);
          if (get_real(operandp2) == 0.0) {
            runtime_error(DIVISION_BY_ZERO);
          }
          else {
            r1 = get_real(operandp1) / get_real(operandp2);
            put_real(operandp1, r1);
          }
          result_tp = real_typep;
          break;
        }
        case DIV:
        case MOD: {
               /* both operands integer */
          if (get_integer(operandp2) == 0) {
            runtime_error(DIVISION_BY_ZERO);
          }
          else {
            i1 = (op == DIV)
                               ? get_integer(operandp1) / get_integer(operandp2)
                               : get_integer(operandp1) % get_integer(operandp2);
            put_integer(operandp1, i1);
          }
          result_tp = integer_typep;
          break;
        }
      } /* end switch */
    }

    /* pop off the second operand */
    pop();

  } /* end while over op/factor pairs */

  exit_debug("exec_term");
  return(result_tp);
}                                                         /* end exec_term */
/***************************************************************************/



/***************************************************************************/
/* exec_factor()   Execute an EXPRESS factor                               */
/*         <simple_factor> ** <simple_factor>                              */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR exec_factor()
{
  TYPE_STRUCT_PTR result_tp;               /* ptr to type */
  STACK_ITEM_PTR operand1, operand2;       /* ptrs to operands */
  TYPE_STRUCT_PTR tp2;
  XPRSAINT i1, i2, i;
  XPRSAREAL r1, r2, r;
  entry_debug("exec_factor");

  result_tp = exec_simple_factor();  /* first operand */

  if (ctoken == STARSTAR) {                 /* have an operator */
    result_tp = base_type(result_tp);

    get_ctoken();
    tp2 = base_type(exec_simple_factor());

    operand1 = tos - 1;
    operand2 = tos;

    if (undef_values(operand1, operand2)) {
      put_undef(operand1);
    }

    else if ((result_tp == integer_typep) && (tp2 == integer_typep)) {
            /* integer operands */
            i1 = get_integer(operand1);
            i2 = get_integer(operand2);
            if ((i1 == 0) && (i2 <= 0) ) {
              runtime_error(INVALID_FUNCTION_ARGUMENT);
            }
            else {
              i = (XPRSAINT) pow((double) i1, (double) i2);
              sprintf(dbuffer, "i1= %d, i2= %d, pow(i1, i2)= %d\n", i1, i2, i);
              debug_print(dbuffer);
              put_integer(operand1, i);
              result_tp = integer_typep;
            }
          }
    else {
             /* at least one real */
      if ((tp2 == integer_typep)) {  /* first real, second int */
        r1 = get_real(operand1);
        i2 = get_integer(operand2);
        if ((r1 == 0.0) && (i2 <= 0)) {
          runtime_error(INVALID_FUNCTION_ARGUMENT);
        }
        else {
          r = (XPRSAREAL) pow((double) r1, (double) i2);
          put_real(operand1, r);
          result_tp = real_typep;
        }
      }
      else if ((result_tp == real_typep) && (tp2 == real_typep)) {
        r1 = get_real(operand1);
        r2 = get_real(operand2);
        if (((r1 == 0.0) && (r2 <= 0.0)) || (r1 < 0.0)) {
          runtime_error(INVALID_FUNCTION_ARGUMENT);
        }
        else {
          r = (XPRSAREAL) pow((double) r1, (double) r2);
          put_real(operand1, r);
          result_tp = real_typep;
        }
      }
      else {           /* first int, second real */
        i1 = get_integer(operand1);
        r2 = get_real(operand2);
        if ((i1 == 0) && (r2 <= 0.0)) {
          runtime_error(INVALID_FUNCTION_ARGUMENT);
        }
        else {
          r = (XPRSAREAL) pow((double) i1, (double) r2);
          put_real(operand1, r);
          result_tp = real_typep;
        }
      }
    }

    pop();          /* pop off the second operand */
  }

  exit_debug("exec_factor");
  return(result_tp);
}                                                       /* end EXEC_FACTOR */
/***************************************************************************/



/***************************************************************************/
/* exec_simple_factor()  Execute a simple factor                           */
/*      <variable> | <number> | NOT <simple_factor> | ( <expression> )     */
/*    or an interval expression = {expr op expr op expr}                   */
/* return a pointer to the type structure.                                 */

TYPE_STRUCT_PTR exec_simple_factor()
{
  TYPE_STRUCT_PTR result_tp;            /* ptr to type */
  TYPE_STRUCT_PTR tp1, tp2, tp3;
  LOGICAL_REP b1, br;
  TOKEN_CODE op1, op2;
  STACK_ITEM_PTR operandp1, operandp2, operandp3;
  STACK_TYPE t1, t2, t3;
  entry_debug("exec_simple_factor");

  switch (ctoken) {
    case IDENTIFIER: {
      SYMTAB_NODE_PTR idp = get_symtab_cptr();

      if (idp->defn.key == FUNC_DEFN) { 
        result_tp = exec_routine_call(idp);
      }
      else if (idp->defn.key == CONST_DEFN) { 
        result_tp = exec_constant(idp);
      }
      else {
        result_tp = exec_variable(idp, EXPR_USE);
      }
      break;
    }
    case NUMBER_LITERAL: {
      SYMTAB_NODE_PTR np = get_symtab_cptr();

      /* get the number from the symbol table and push it on the stack */
      if (np->typep == integer_typep) {
        push_integer(np->defn.info.constant.value.integer);
        result_tp = integer_typep;
      }
      else {
        push_real(np->defn.info.constant.value.real);
        result_tp = real_typep;
      }
      
      get_ctoken();
      break;
    }

    case STRING_LITERAL: {
      SYMTAB_NODE_PTR np = get_symtab_cptr();
      int length = strlen(np->name);
      push_string((STRING) np->info);
      result_tp = np->typep;
      get_ctoken();
      break;
    }

    case NOT: {
      get_ctoken();
      result_tp = exec_simple_factor();
      if (is_undef(result_tp) || is_value_undef(tos)) {
        put_undef(tos);
      }
      else {
        b1 = get_logical(tos);
        if (b1 == TRUE_REP) {
          br = FALSE_REP;
        }
        else if (b1 == FALSE_REP) {
          br = TRUE_REP;
        }
        else {
          br = UNKNOWN_REP;
        }
        put_logical(tos, br);  /* TRUE -> FALSE, FALSE -> TRUE */
      }
      break;
    }

    case LPAREN: {
      get_ctoken();
      result_tp = exec_expression();
      get_ctoken();                    /* the token after the ) */
      break;
    }

    case LBRACE: {            /* interval expression */
      result_tp = logical_typep;
      get_ctoken();
      tp1 = exec_simple_expression();
      op1 = ctoken;
      get_ctoken();
      tp2 = exec_simple_expression();
      op2 = ctoken;
      get_ctoken();
      tp3 = exec_simple_expression();
      get_ctoken();                 /* the token after the } */
      operandp1 = tos - 2;
      operandp2 = tos - 1;
      operandp3 = tos;
      pop();
      pop();
          /* check if anything is indeterminate */
      t1 = get_stackval_type(operandp1);
      if (t1 == STKUDF) {
        put_unknown(operandp1);
        break;
      }
      t2 = get_stackval_type(operandp2);
      if (t2 == STKUDF) {
        put_unknown(operandp1);
        break;
      }
      t3 = get_stackval_type(operandp3);
      if (t3 == STKUDF) {
        put_unknown(operandp1);
        break;
      }
         /* check first condition */
      b1 = do_relop(operandp1, tp1, op1, operandp2, tp2);
      if (b1 == FALSE_REP) {
        put_false(operandp1);
        break;
      }
         /* and the second */
      br = do_relop(operandp2, tp2, op2, operandp3, tp3);
      if (br == FALSE_REP) {
        put_false(operandp1);
        break;
      }
      if (b1 == TRUE_REP && br == TRUE_REP) {
        put_true(operandp1);
      }
      else {
        put_unknown(operandp1);
      }
      break;
    }

  } /* end switch */

  expression_type_debug(result_tp);
  exit_debug("exec_simple_factor");
  return(result_tp);
}                                                /* end exec_simple_factor */
/***************************************************************************/



/***************************************************************************/
/* exec_constant(idp)  Push the value of a non-string constant id,         */
/*               or the address of a string constant id onto the stack     */
/* return a pointer to the type structure.                                 */

TYPE_STRUCT_PTR exec_constant(idp)
SYMTAB_NODE_PTR idp;                       /* constant id */
{
  TYPE_STRUCT_PTR tp = idp->typep;            /* ptrs to types */
  entry_debug("exec_constant");

  if (base_type(tp) == logical_typep) {
    push_logical(idp->defn.info.constant.value.integer);
  }
  else if ((base_type(tp) == integer_typep) || (tp->form == ENUM_FORM)) {
    push_integer(idp->defn.info.constant.value.integer);
  }
  else if (tp == real_typep) {
    push_real(idp->defn.info.constant.value.real);
  }
  else if (tp->form == ARRAY_FORM) {
    push_address((ADDRESS) idp->defn.info.constant.value.stringp);
  }
  else if (tp->form == STRING_FORM) {
    push_string((STRING) idp->defn.info.constant.value.stringp);
  }
  else if (is_undef(tp)) {
    push_undef();
  }

  trace_data_fetch(idp, tp, tos);
  get_ctoken();
 
  exit_debug("exec_constant");
  return(tp);
}                                                     /* end exec_constant */
/***************************************************************************/



/***************************************************************************/
/* exec_variable(idp, use)  Push either the variable's address or its      */
/*                          value onto the stack                           */
/* return a pointer to the type structure.                                 */

TYPE_STRUCT_PTR exec_variable(idp, use)
SYMTAB_NODE_PTR idp;                         /* variable id */
USE use;                                     /* how variable is used */
{
  int delta;                                 /* difference in levels */
  TYPE_STRUCT_PTR tp = idp->typep;           /* ptrs to types */
  TYPE_STRUCT_PTR base_tp;                   /* ptrs to types */
  STACK_ITEM_PTR datap;                      /* ptr to data area */
  STACK_ITEM_PTR hp;
  STACK_TYPE stype;
  entry_debug("exec_variable (l2xixxpr.c)");

  /* point to the variable's stack item. If the variable's level */
  /* is less than the current level, follow the static links to the */
  /* appropriate stack frame base */
  hp = (STACK_ITEM_PTR) stack_frame_basep;
  delta = level - idp->level;
  while (delta-- > 0) {
    hp = (STACK_ITEM_PTR) get_static_link(hp);
  }
  datap = hp + idp->defn.info.data.offset;

  /* If a scalar or enumeration VAR parm, that item points to the */
  /* actual item */
  if ((idp->defn.key == VARPARM_DEFN) &&
      (tp->form != ARRAY_FORM) &&
      (tp->form != ENTITY_FORM) &&
      (tp->form != BAG_FORM) &&
      (tp->form != LIST_FORM) &&
      (tp->form != SET_FORM)) { 
    datap = (STACK_ITEM_PTR) get_address(datap);
  }

  /* push the address of the variables data area */
  if ((tp->form == BAG_FORM) ||
      (tp->form == LIST_FORM) ||
      (tp->form == SET_FORM)) {
    stype = form2stack[tp->form];
    push_address_type(get_address_type(datap, stype), stype);
  }      
  else if ((tp->form == ARRAY_FORM) ||
           (tp->form == ENTITY_FORM)) {
    push_address((ADDRESS) get_address(datap));
  }
  else {
      push_address((ADDRESS) datap);
  }      

  get_ctoken();

  /* for a string, may be dealing with a substring only */
  if (tp->form == STRING_FORM) {
    if (ctoken == LBRACKET) {
      exec_substring(use);
      if (use != TARGET_USE && use != VARPARM_USE) {
        exit_debug("exec_variable");
        return(tp);
      }
    }
  }
  else {
    /* if there are any subscripts or attribute designators, */
    /* modify the address to point to the array element record field */
    while ((ctoken == LBRACKET) || (ctoken == PERIOD)) {
      if (ctoken == LBRACKET) tp = exec_subscripts(tp);
      else if (ctoken == PERIOD) tp = exec_attribute();
    }
  }

  base_tp = base_type(tp);

  /* leave the modified address on top of the stack if it:  */
  /*    is an assignment target */
  /*    represents a parameter passed by reference */
  /*    is the address of an array or entity */
  /* Otherwise, replace the address with the value it points to */

  if ((use != TARGET_USE) && (use != VARPARM_USE) &&
      (tp->form != ARRAY_FORM) && 
      (tp->form != ENTITY_FORM) &&
      (tp->form != BAG_FORM) &&
      (tp->form != LIST_FORM) &&
      (tp->form != SET_FORM)) {
    if (is_value_undef(get_address(tos))) {
      put_undef(tos);
    }
    else if (base_tp == logical_typep) {
      put_logical(tos, get_logical(get_address(tos)));
    }
    else if ((base_tp == integer_typep) || (tp->form == ENUM_FORM)) {
      put_integer(tos, get_integer(get_address(tos)));
    }
    else if (tp->form == STRING_FORM) {
      put_string(tos, get_stacked_string(get_address(tos)));
    }
    else if (tp->form == BAG_FORM ||
             tp->form == LIST_FORM ||
             tp->form == SET_FORM) {
      stype = get_stackval_type(tos);
      put_address_type(tos, get_address_type(tos, stype), stype);
    }
    else {
      put_real(tos, get_real(get_address(tos)));
    }
  }

  if ((use != TARGET_USE) && (use != VARPARM_USE)) {
    stype = get_stackval_type(tos);
    if ((tp->form == ARRAY_FORM) || 
        (tp->form == ENTITY_FORM) ||
        (tp->form == BAG_FORM) ||
        (tp->form == LIST_FORM) ||
        (tp->form == SET_FORM)) {
      trace_data_fetch(idp, tp, get_address_type(tos, stype));
    }
    else {
      trace_data_fetch(idp, tp, tos);
    }
  }

  expression_type_debug(tp);
  exit_debug("exec_variable");
  return(tp);
}                                                     /* end exec_variable */
/***************************************************************************/



/***************************************************************************/
/* exec_substring()  Execute subscripts to modify the string on top        */
/*                      of the stack                                       */
/*   at entry: ctoken is the opening [                                     */
/*   at exit:  ctoken is after the closing ]                               */

exec_substring(usage)
USE usage;                     /* how the var is used */
{
  XPRSAINT subscript1_value, subscript2_value;
  STRING strorig;
  STRING strnew;
  int num, i, j;
  entry_debug("exec_substring (l2xixxpr.c)");
 
       /* save the current string */
  strorig = get_stacked_string(get_address(tos));

      /* do first expression */
  get_ctoken();
  exec_expression();
  subscript1_value = get_integer(tos);
  pop();
      /* check value in range */
  if ((subscript1_value < 1) ||
      (subscript1_value > MAX_EXPRESS_STRING)) {
    runtime_error(VALUE_OUT_OF_RANGE);
  }
  subscript2_value = subscript1_value;

  if (ctoken == COLON) {  /* do next expression */
    get_ctoken();
    exec_expression();
    subscript2_value = get_integer(tos);
    pop();
      /* check value in range */
    if ((subscript2_value < subscript1_value) ||
        (subscript2_value > MAX_EXPRESS_STRING)) {
      runtime_error(VALUE_OUT_OF_RANGE);
    }
  }

  get_ctoken();  /* token after closing ] */

    /* now do the substring stuff */
  num = (subscript2_value - subscript1_value + 1); /* no of chars */
  strnew = alloc_bytes(num+1);
  j = 0;
  for (i = subscript1_value - 1; i < subscript2_value; i++) {
    strnew[j] = strorig[i];
    j++;
  }
  strnew[j] = '\0';
    /* replace strorig in the stack with strnew, unless a lhs */
  if (usage != TARGET_USE && usage != VARPARM_USE) {
    put_string(tos, strnew);
  }

  exit_debug("exec_substring");
  return;
}                                                    /* end EXEC_SUBSTRING */
/***************************************************************************/



/***************************************************************************/
/* exec_subscripts(tp)  Execute subscripts to modify the array data area   */
/*                      address on the top of the stack                    */
/* return a pointer to the type of the array element                       */

TYPE_STRUCT_PTR exec_subscripts(tp)
TYPE_STRUCT_PTR tp;                      /* ptr to type structure */
{
  XPRSAINT subscript_value;
  STACK_ITEM_PTR adr, dat;
  STACK_TYPE stype;
  LBS_PTR lbs;
  LBS_NODE_PTR node;
  entry_debug("exec_subscripts");
 
  /* loop to execute bracketed subscripts */
  if (tp->form == ARRAY_FORM) {
    while (ctoken == LBRACKET) {
      /* loop to execute a subscript list */
      do {
        get_ctoken();
        exec_expression();

        subscript_value = get_integer(tos);
        pop();

        /* range check */
        if ((subscript_value < tp->info.array.min_index) ||
            (subscript_value > tp->info.array.max_index)) {
          runtime_error(VALUE_OUT_OF_RANGE);
        }

        /* modify the data area address */
        adr = (STACK_ITEM_PTR) get_address(tos);
        adr = adr +
                      ((subscript_value - tp->info.array.min_index) * 
                       (tp->info.array.elmt_typep->size))/sizeof(STACK_ITEM);
        put_address(tos, adr); 

        if (ctoken == COMMA) tp = tp->info.array.elmt_typep;
      } while (ctoken == COMMA); /* end do */

      get_ctoken();
      if (ctoken == LBRACKET) tp = tp->info.array.elmt_typep;
    } /* end while */
  }    /* end of array processing */
  
  else if (tp->form == BAG_FORM ||
           tp->form == LIST_FORM ||
           tp->form == SET_FORM) {           /* dynamic aggregate */
    stype = form2stack[tp->form];
    while (ctoken == LBRACKET) {
      get_ctoken();
      exec_expression();

      subscript_value = get_integer(tos);
      pop();

        /* range check */
      if ((subscript_value < tp->info.dynagg.min_index) ||
          (subscript_value > tp->info.dynagg.max_index)) {
        runtime_error(VALUE_OUT_OF_RANGE);
      }

        /* get the element from the aggregate */
      lbs = (LBS_PTR) get_address_type(tos, stype);
        /* outside element count? */
      sprintf(dbuffer, "lbs = %d, el count = %d, subscript = %d\n", 
                        lbs, NELS(lbs), subscript_value);
      debug_print(dbuffer);
      if (subscript_value > NELS(lbs)) runtime_error(VALUE_OUT_OF_RANGE);
      node = lbs_get_nth(lbs, subscript_value);
      sprintf(dbuffer, "node = %d\n", node);
      debug_print(dbuffer);
        /* put the element data on top of the stack */
      dat = (STACK_ITEM_PTR) DATA(node);
      sprintf(dbuffer, "data = %d\n", dat);
      debug_print(dbuffer);
      copy_value(tos, dat);

      get_ctoken();
      if (ctoken == LBRACKET) tp = tp->info.dynagg.elmt_typep;
    } /* end while */
  }  /* end of dynamic aggregate processing */

  exit_debug("exec_subscripts");
  return(tp);
}                                                   /* end exec_subscripts */
/***************************************************************************/




/***************************************************************************/
/* exec_attribute()  Execute an attribute designator to modify the         */
/*               entity data                                               */
/*               address area on the top of the stack                      */
/* return a pointer to the type of the attribute                           */

TYPE_STRUCT_PTR exec_attribute()
{
  SYMTAB_NODE_PTR attr_idp;
  ADDRESS adr;
  entry_debug("exec_attribute (l2xixxpr.c)");

  get_ctoken();
  attr_idp = get_symtab_cptr();

  adr = get_address(tos);
  adr += attr_idp->defn.info.data.offset;
  put_address(tos, adr);

  get_ctoken();

  exit_debug("exec_attribute");
  return(attr_idp->typep); 
}                                                    /* end EXEC_ATTRIBUTE */
/***************************************************************************/



/***************************************************************************/
/* promote_operands_to_real(operandp1, tp1, operandp2, tp2) If either      */
/*              operand is integer, convert it to real                     */

promote_operands_to_real(operandp1, tp1, operandp2, tp2)
STACK_ITEM_PTR operandp1, operandp2;             /* ptrs to operands */
TYPE_STRUCT_PTR tp1, tp2;                        /* ptrs to types */
{
  XPRSAINT i1;
  entry_debug("promote_operands_to_real");

  if (tp1 == integer_typep) {
    if (!is_value_undef(operandp1)) {
      i1 = get_integer(operandp1);
      put_real(operandp1, (XPRSAREAL) i1);
    }
  }
  if (tp2 == integer_typep) {
    if (!is_value_undef(operandp2)) {
      i1 = get_integer(operandp2);
      put_real(operandp2, (XPRSAREAL) i1);
    }
  }


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



/***************************************************************************/
/* concat_strings()  Concatenate two strings                               */

STRING concat_strings(op1, op2)
STACK_ITEM_PTR op1;                 /* pos of first string in the stack */
STACK_ITEM_PTR op2;                 /* pos of second string in the stack */
{
  int n1 = strlen(get_stacked_string(op1));
  int n2 = strlen(get_stacked_string(op2));
  int tot, i, j;
  STRING str = NULL;
  STRING two;
  entry_debug("concat_strings (l2xixxpr.c)");

  tot = n1 + n2;
  if (tot <= MAX_EXPRESS_STRING) {
    str = alloc_bytes(n1 + n2 + 1);
    strcpy(str, get_stacked_string(op1));
    strcat(str, get_stacked_string(op2));
  }
  else {
    runtime_error(RUNTIME_STRING_TOO_LONG);
    tot = MAX_EXPRESS_STRING;
    str = alloc_bytes(tot + 1);
    strcpy(str, get_stacked_string(op1));
    two = get_stacked_string(op2);
    j = n1;
    for (i = 0; j <= tot; i++) {
      str[j++] = two[i];
    }
    str[j] = '\0';
  }

  exit_debug("concat_strings");
  return(str);
}                                                    /* end CONCAT_STRINGS */
/***************************************************************************/



/***************************************************************************/
/* exec_dynagg_relop(t1, p1, op, t2, p2)   Execute a relop on dynamic      */
/*  aggregates                                                             */
/*                            p1 op p2                                     */
/*  returns a logical result                                               */

LOGICAL_REP exec_dynagg_relop(t1, p1, op, t2, p2)
TYPE_STRUCT_PTR t1;                  /* type of p1 */
STACK_ITEM_PTR p1;                   /* value of p1 */
TOKEN_CODE op;                       /* the operator */
TYPE_STRUCT_PTR t2;                  /* type of p2 */
STACK_ITEM_PTR p2;                   /* value of p2 */
{
  LOGICAL_REP result;
  STACK_ITEM_PTR agg;
  LBS_NODE_PTR nod, nextnod;
  STACK_TYPE agtp = get_stackval_type(p2);
  LBS_PTR head;

  entry_debug("exec_dynagg_relop (l2xixxpr.c)");

  sprintf(dbuffer, "t1 = %d, p1 = %d, t2 = %d, p2 = %d\n", t1, p1, t2, p2);
  debug_print(dbuffer);

  if (op == IN) {    /* element IN agg */
    if (t1 != t2->info.dynagg.elmt_typep) {   /* not an element */
      exit_debug("exec_dynagg_relop");
      return(FALSE_REP);
    }
    /* get first node */
    head = (LBS_PTR) get_address_type(p2, agtp);
    debug_print("Getting first node\n");
    nod = lbs_get_next_el(head, NULL);
    sprintf(dbuffer, "nod = %d\n", nod);
    debug_print(dbuffer);

    while (nod != NULL) {   /* loop over all nodes */
      debug_print("Testing for value equality\n");
      sprintf(dbuffer, "data = %d\n", DATA(nod));
      debug_print(dbuffer);
      result = stack_value_equal(p1, DATA(nod));
      if (result == UNKNOWN_REP || result == TRUE_REP) {
        exit_debug("exec_dynagg_relop (p1 IN p2 not FALSE)");
        return(result);
      }
      debug_print("Getting next node\n");
      nod = lbs_get_next_el(head, nod);
      sprintf(dbuffer, "nod = %d\n", nod);
      debug_print(dbuffer);
    }
    exit_debug("exec_dynagg_relop (p1 IN p2 is FALSE");
    return(FALSE_REP);
  }
  
  else {
    runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
    exit_debug("exec_dynagg_relop");
    return(UNKNOWN_REP);
  }


}                                                 /* end EXEC_DYNAGG_RELOP */
/***************************************************************************/