/* l2xixstd.c  LTX2X interpreter standard procedure/function 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 <stdlib.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"


#define DEFAULT_NUMERIC_FIELD_WIDTH 10
#define DEFAULT_PRECISION 4
  /* added for ltx2x */
#define MAX_LTX2X_BUFFER 2000

/* EXTERNALS */


extern int level;
extern int exec_line_number;         /* no. of line executed */

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 STACK_ITEM_PTR stack_display[];     /*  ????????? */


extern BOOLEAN is_value_undef();

extern STRING get_stacked_string();

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

extern STACK_ITEM_PTR create_copy_value();

/* FORWARDS */

TYPE_STRUCT_PTR exec_eof_eoln(), exec_abs_sqr(),
                exec_arctan_cos_exp_ln_sin_sqrt(),
                exec_odd(), exec_round_trunc();
TYPE_STRUCT_PTR exec_atan(), exec_exists_etc(), exec_nvl_etc();
TYPE_STRUCT_PTR exec_rexpr_etc(), exec_hibound_etc(), exec_length_etc();

/* GLOBALS */

BOOLEAN eof_flag = FALSE;
char acbuffer[MAX_LTX2X_BUFFER];        /* added for ltx2x */



/************************************************************************/
/* exec_standard_routine_call(rtn_idp)  Execute a call to a standard    */
/*                                      procedure or function           */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_standard_routine_call(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  entry_debug("exec_standard_routine_call");

  switch (rtn_idp->defn.info.routine.key) {
    case READ:
    case READLN: {
      exec_read_readln(rtn_idp);
      exit_debug("exec_standard_routine_call");
      return(NULL);
    }
    case WRITE: 
    case WRITELN: {
      exec_write_writeln(rtn_idp);
      exit_debug("exec_standard_routine_call");
      return(NULL);
    }
    case EOFF: 
    case EOLN: {
      exit_debug("exec_standard_routine_call");
      return(exec_eof_eoln(rtn_idp));
    }
    case ABS:        /* real or int -> real or int */
              {
      exit_debug("exec_standard_routine_call");
      return(exec_abs_sqr(rtn_idp));
    }
    case COS:        /* real or int -> real */
    case EXP:
    case SIN:
    case SQRT:
    case XACOS:  
    case XASIN:
    case XLOG:
    case XLOG2:
    case XLOG10:
    case XTAN: {
      exit_debug("exec_standard_routine_call");
      return(exec_arctan_cos_exp_ln_sin_sqrt(rtn_idp));
    }
    case XATAN: {             /* extra for EXPRESS */
      exit_debug("exec_standard_routine_call");
      return(exec_atan(rtn_idp));
    }
    case ODD: {      /* int -> boolean */
      exit_debug("exec_standard_routine_call");
      return(exec_odd());
    }
    case ROUND:      /* real -> int */
    case TRUNC: {
      exit_debug("exec_standard_routine_call");
      return(exec_round_trunc(rtn_idp));
    }
    case L2XPRINT: 
    case L2XPRINTLN: {                         /* added for ltx2x */
      exec_print_println(rtn_idp);
      exit_debug("exec_standard_routine_call");
      return(NULL);
    }
    case L2XSYSTEM: {                          /* added for ltx2x */
      exec_system_etc(rtn_idp);
      exit_debug("exec_standard_routine_call");
      return(NULL);
    }
    case L2XREXPR: {                           /* added for ltx2x */
      exit_debug("exec_standard_routine_call");
      return(exec_rexpr_etc(rtn_idp));
    }
    case XHIBOUND:
    case XHIINDEX:
    case XLOBOUND:
    case XLOINDEX:
    case XSIZEOF: {
      exit_debug("exec_standard_routine_call");
      return(exec_hibound_etc(rtn_idp));
    }
    case XLENGTH: {
      exit_debug("exec_standard_routine_call");
      return(exec_length_etc(rtn_idp));
    }
    case XEXISTS: {
      exit_debug("exec_standard_routine_call");
      return(exec_exists_etc(rtn_idp));
    }
    case XNVL: {
      exit_debug("exec_standard_routine_call");
      return(exec_nvl_etc(rtn_idp));
    }
    case XINSERT:
    case XREMOVE: {
      exec_insert_etc(rtn_idp);
      exit_debug("exec_standard_routine_call");
      return(NULL);
    }
    case XBLENGTH:
    case XFORMAT:
    case XROLESOF:
    case XTYPEOF:
    case XUSEDIN:
    case XVALUE:
    case XVALUE_IN:
    case XVALUE_UNIQUE:  {               /* unimplemented EXPRESS stuff */
      runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
      exit_debug("exec_standard_routine_call");
      return(NULL);
    }
    default: {
      runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
      break;
    }

  } /* end switch */

  exit_debug("exec_standard_routine_call");
  return(NULL);
}                                     /* end exec_standard_routine_call */
/************************************************************************/



/************************************************************************/
/* exec_read_readln(rtn_idp)  Execute a call to READ or READLN          */

exec_read_readln(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  SYMTAB_NODE_PTR parm_idp;            /* param id */
  TYPE_STRUCT_PTR parm_tp;             /* param type */
  STACK_ITEM_PTR targetp;              /* ptr to read target */
  XPRSAINT i1;
  XPRSAREAL r1;
  int len;
  char ch;
  char tbuff[MAX_LTX2X_BUFFER];
  STRING lhs;

  entry_debug("exec_read_readln");

  /* params are optional for readln */
  get_ctoken();
  if (ctoken == LPAREN) {           /* id list */
    do {
      get_ctoken();
      parm_idp = get_symtab_cptr();
      parm_tp = base_type(exec_variable(parm_idp, VARPARM_USE));
      targetp = (STACK_ITEM_PTR) get_address(tos);
      pop();             /* pop off address */

      if (parm_tp == integer_typep) {
        scanf("%d", &i1); 
        put_integer(targetp, i1);
      }
      else if (parm_tp == real_typep) {
        scanf("%g", &r1); 
        put_real(targetp, r1);
      }
      else {             /* a string or a logical */
        scanf("%sMAX_LTX2X_BUFFER", tbuff);
        len = strlen(tbuff);
        sprintf(dbuffer, "strlen(str) = %d, str = %s\n", len, tbuff);
        debug_print(dbuffer);
        if (parm_tp == logical_typep) {   /* check which one */
          if (len == 4 && (tbuff[0] == 't' || tbuff[0] == 'T')) { /* TRUE */
            put_true(targetp);
          }
          else if (len == 5 && (tbuff[0] == 'f' || tbuff[0] == 'F')) {  /* FALSE */
            put_false(targetp);
          }
          else if (len == 7 && (tbuff[0] == 'u' || tbuff[0] == 'U')) { /* UNKNOWN */
            put_unknown(targetp);
          }
          else {   /* an error */
            runtime_error(INVALID_FUNCTION_ARGUMENT);
            put_unknown(targetp);
          }
	}
        else {             /* a string */
          free(targetp->value.string);
          lhs = alloc_bytes(len+1);
          sprintf(dbuffer, "lhs = %d", lhs);
          debug_print(dbuffer);
          strcpy(lhs, tbuff);
          sprintf(dbuffer, ", str = %s\n", lhs);
          debug_print(dbuffer);
          put_string(targetp, lhs);
        }
      }

      trace_data_store(parm_idp, parm_idp->typep, targetp, parm_tp);
    } while (ctoken == COMMA); /* end do */
    get_ctoken();           /* token after RPAREN */
  }

  if (rtn_idp->defn.info.routine.key == READLN) {
    do {
      ch = getchar();
    } while(!eof_flag && (ch != '\n'));
  }

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



/************************************************************************/
/* exec_write_writeln(rtn_idp)  Execute a call to WRITE or WRITELN      */
/*        Each actual parameter can be: <expr>                          */
/*                                  or  <expr> : <expr>                 */
/*                                  or  <expr> : <expr> : <expr>        */

exec_write_writeln(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;               /* parameter type */
  STACK_TYPE stype;
  XPRSAINT field_width;
  XPRSAINT precision;
  entry_debug("exec_write_writeln");

  /* parameters are optional for writeln */
  get_ctoken();
  if (ctoken == LPAREN) {
    do {
      /* push value */
      get_ctoken();
      parm_tp = base_type(exec_expression());

      /* check if dynamic agg */
      if (is_dynagg(parm_tp)) parm_tp = parm_tp->info.dynagg.elmt_typep;

      field_width = DEFAULT_NUMERIC_FIELD_WIDTH;
      precision = DEFAULT_PRECISION;

      /* optional field width expresion */
      if (ctoken == COLON) {
        get_ctoken();
        exec_expression();
        if (!is_value_undef(tos)) {
          field_width = get_integer(tos);
        }
        pop();                      /* field width */

        /* optional decimal places expresion */
        if (ctoken == COLON) {
          get_ctoken();
          exec_expression();
          if (!is_value_undef(tos)) {
            precision = get_integer(tos);
          }
          pop();                      /* precision */
        }
      }

      if (parm_tp->form == ARRAY_FORM) {  /* array, address on top of stack */
        if (get_stackval_type(tos) == STKADD) {
          copy_value(tos, get_address(tos));
        }
      }

     stype = get_stackval_type(tos);

      /* write value */
      if (is_value_undef(tos)) {
        printf("%*c", field_width, get_undef(tos));
      }
      else if (stype == STKINT) {
        printf("%*d", field_width, get_integer(tos));
      }
      else if (stype == STKREA) {
        printf("%*.*g", field_width, precision, get_real(tos));
      }
      else if (stype == STKLOG) {
        field_width = 0;
        switch (get_logical(tos)) {
          case TRUE_REP: {
            printf("%*s", -field_width, "TRUE");
            break;
	  }
          case FALSE_REP: {
            printf("%*s", -field_width, "FALSE");
            break;
	  }
          case UNKNOWN_REP: {
            printf("%*s", -field_width, "UNKNOWN");
            break;
          }
          default: {
            printf("%*s", -field_width, "??UNKNOWN??");
            break;
	  }
        } /* end switch */
      }
      else if (parm_tp == string_typep || parm_tp->form == STRING_FORM) {
        field_width = 0;
        printf("%*s", -field_width, get_stacked_string(tos) );
      }

      pop();     /* value */
    } while (ctoken == COMMA);  /* end do */

    get_ctoken();      /* token after RPAREN */
  } /* end of if over parameters */

  if (rtn_idp->defn.info.routine.key == WRITELN) putchar('\n');

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



/************************************************************************/
/* exec_print_println(rtn_idp)  Execute a call to PRINT or PRINTLN      */
/*        Each actual parameter can be: <expr>                          */
/*                                  or  <expr> : <expr>                 */
/*                                  or  <expr> : <expr> : <expr>        */
/*  Identical to exec_write_writeln, except output is to ltx2x myprint  */

exec_print_println(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;               /* parameter type */
  STACK_TYPE stype;
  XPRSAINT field_width;
  XPRSAINT precision;
  entry_debug("exec_print_println");

  /* parameters are optional for println */
  get_ctoken();
  if (ctoken == LPAREN) {
    do {
      /* push value */
      get_ctoken();
      parm_tp = base_type(exec_expression());

      /* check if dynamic agg */
      if (is_dynagg(parm_tp)) {
         parm_tp = parm_tp->info.dynagg.elmt_typep;
       }

      field_width = DEFAULT_NUMERIC_FIELD_WIDTH;
      precision = DEFAULT_PRECISION;


      /* optional field width expresion */
      if (ctoken == COLON) {
        get_ctoken();
        exec_expression();
        if (!is_value_undef(tos)) {
          field_width = get_integer(tos);
        }
        pop();                      /* field width */

        /* optional decimal places expresion */
        if (ctoken == COLON) {
          get_ctoken();
          exec_expression();
          if (!is_value_undef(tos)) {
            precision = get_integer(tos);
          }
          pop();                      /* precision */
        }
      }

      if (parm_tp->form == ARRAY_FORM) {   /* array, address on top of stack */
        if (get_stackval_type(tos) == STKADD) {
          copy_value(tos, get_address(tos));
        }
      }
      
      stype = get_stackval_type(tos);
      /* write value */
      if (is_value_undef(tos)) {
        sprintf(acbuffer, "%*c", field_width, get_undef(tos));
      }
      else if (stype == STKINT) {
        sprintf(acbuffer, "%*d", field_width, get_integer(tos));
      }
      else if (stype == STKREA) {
        sprintf(acbuffer, "%*.*g", field_width, precision, get_real(tos));
      }
      else if (stype == STKLOG) {
        field_width = 0;
        switch (get_logical(tos)) {
          case TRUE_REP: {
            sprintf(acbuffer, "%*s", -field_width, "TRUE");
            break;
	  }
          case FALSE_REP: {
            sprintf(acbuffer, "%*s", -field_width, "FALSE");
            break;
	  }
          case UNKNOWN_REP: {
            sprintf(acbuffer, "%*s", -field_width, "UNKNOWN");
            break;
	  }
          default: {
            sprintf(acbuffer, "%*s", -field_width, "??UNKNOWN??");
            break;
	  }
        } /* end switch */
      }
      else if (parm_tp == string_typep || parm_tp->form == STRING_FORM) {
        field_width = 0;
        sprintf(acbuffer, "%*s", -field_width, get_stacked_string(tos) );
      }
      myprint(acbuffer);
      pop();     /* value */
    } while (ctoken == COMMA);  /* end do */

    get_ctoken();      /* token after RPAREN */
  } /* end of if over parameters */

  if (rtn_idp->defn.info.routine.key == L2XPRINTLN) myprint("\n");

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



/************************************************************************/
/* exec_insert_etc(rtn_idp)  Execute a call to procedure INSERT, etc    */
/*        INSERT(<list>, <item>, <posn>)                                */
/*        REMOVE(<list>, <posn>)                                        */
/*  at entry: ctoken is `proc'                                          */
/*  at exit:  ctoken is the token after the closing )                   */

exec_insert_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;               /* parameter type */
  LBS_PTR list;
  LBS_NODE_PTR nod;
  STACK_ITEM_PTR pitem;
  XPRSAINT pos;
  int code = rtn_idp->defn.info.routine.key;

  entry_debug("exec_insert_etc (l2xixstd.c)");

  /* first parameter */
  get_ctoken();       /* should be ( */
  get_ctoken();       /* should be param 1 */
  parm_tp = base_type(exec_expression());
  if (parm_tp->form != LIST_FORM) {
    runtime_error(INVALID_FUNCTION_ARGUMENT);
  }
  list = (LBS_PTR) get_address_type(tos, STKLST);
  sprintf(dbuffer, "list = %d\n", list);
  debug_print(dbuffer);
  pop();  /* first parm */
  get_ctoken();    /* start of next parameter */

  if (code == XINSERT) {             /* do INSERT second param */
    exec_expression();
    pitem = create_copy_value(tos);
    sprintf(dbuffer, "pitem = %d\n", pitem);
    debug_print(dbuffer);
    get_ctoken();   /* start of next parameter */
  }

     /* final parameter */
  parm_tp = base_type(exec_expression());
  pos = get_integer(tos);
  pop();  /* last parm */
  get_ctoken();     /* token after closing ) */

  switch (code) {
    case XINSERT: {
      nod = lbs_insert(list, (genptr) pitem, pos);
      sprintf(dbuffer, "inserted node = %d, with data = %d, at pos = %d, into list = %d\n", 
                        nod, pitem, pos, list);
      debug_print(dbuffer);
      pop(); /* middle parm */
      break;
    }
    case XREMOVE: {
      nod = lbs_remove(list, pos);
      sprintf(dbuffer, "removed node = %d\n", nod);
      debug_print(dbuffer);
      break;
    }
  } /* end switch */

  exit_debug("exec_insert_etc");
  return;
}                                               /* end EXEC_INSERT_ETC  */
/************************************************************************/



/************************************************************************/
/* exec_eof_eoln(rtn_idp)  Execute a call to EOF or EOLN                */
/*                         No parameters => boolean result              */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_eof_eoln(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  char ch = getchar();
  entry_debug("exec_eof_eoln");
  
  switch (rtn_idp->defn.info.routine.key) {
    case EOFF: {
      if (eof_flag || feof(stdin)) {
        eof_flag = TRUE;
        push_true();
      }
      else {
        push_false();
        ungetc(ch, stdin);
      }
      break;
    }
    case EOLN: {
      if (eof_flag || feof(stdin)) {
        eof_flag = TRUE;
        push_true();
      }
      else {
        push_logical(ch == '\n' ? TRUE_REP : FALSE_REP);
        ungetc(ch, stdin);
      }
      break;
    }
  } /* end switch */

  get_ctoken();             /* token after function name */

  exit_debug("exec_eof_eoln");
  return(logical_typep);
}                                                 /* end exec_eof_eoln  */
/************************************************************************/



/************************************************************************/
/* exec_system_etc(rtn_idp)    Execute a call to system, etc            */
/*                         fun('string')                                */
/*                         String parameter, no result                  */
/*    at entry, ctoken is `fun'                                         */
/*    at exit, ctoken is token after closing )                          */

exec_system_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                      /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;                    /* actual param type */
  entry_debug("exec_system_etc");

  get_ctoken();             /* should be ( */
  get_ctoken();             /* start of param */
  parm_tp = base_type(exec_expression());
  if (parm_tp->form != STRING_FORM) {
    runtime_error(INVALID_FUNCTION_ARGUMENT);
  }
  else {
    switch (rtn_idp->defn.info.routine.key) {
      case L2XSYSTEM : {
        system(get_stacked_string(tos));
        break;
      }
      default : {
        runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
        break; 
      }
    } /* end switch */
  }

  get_ctoken();         /* token after closing ) */
  exit_debug("exec_system_etc");
  return;
}                                                /* end EXEC_SYSTEM_ETC */
/************************************************************************/



/************************************************************************/
/* exec_rexpr_etc(rtn_idp)  Execute a call to REXPR, etc                */
/*           In general, any function fun(p1, p2) that:                 */
/*           p1 and p2 are strings --> boolean result                   */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_rexpr_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm1_tp, parm2_tp;       /* actual param types */
  TYPE_STRUCT_PTR result_tp = logical_typep;
  STRING parm1, parm2;              /* parameters */
  BOOLEAN undef_parm = FALSE;
  int code = rtn_idp->defn.info.routine.key;
  int result;
  entry_debug("exec_rexpr_etc (l2xixstd.c)");

  get_ctoken();    /* LPAREN */
  get_ctoken();    /* start of first parameter */
  parm1_tp = base_type(exec_expression());
  if (is_value_undef(tos)) {
    undef_parm = TRUE;
  }
  else {
    parm1 = get_stacked_string(tos);
  }
/*  get_ctoken();    COMMA */
  get_ctoken();    /* start of second parameter */
  parm2_tp = base_type(exec_expression());
  if (is_value_undef(tos)) {
    undef_parm = TRUE;
  }
  else {
    parm2 = get_stacked_string(tos);
  }
  pop();

  if (code == L2XREXPR) {  /* parm1 = string, parm2 = pattern */
    if (undef_parm) {
      put_undef(tos);
    }
    else { 
      result = rexpr(parm1, parm2);
      if (result < 0) {
        runtime_error(INVALID_REGULAR_EXPRESSION);
        put_undef(tos);
      }
      else if (result == 0) {
        put_false(tos);
      }
      else {
        put_true(tos);
      }
    }
  }

  get_ctoken();   /* token after RPAREN */

  exit_debug("exec_rexpr_etc");
  return(result_tp);
}                                                 /* end EXEC_REXPR_ETC */
/************************************************************************/



/************************************************************************/
/* exec_hibound_etc(rtn_idp)  Execute a call to HIBOUND, etc            */
/*                   agg type -> integer                                */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_hibound_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;                       /* actual param type */
  TYPE_STRUCT_PTR result_tp = integer_typep;           /* result type */
  XPRSAINT result = 0;
  STACK_TYPE stype;
  TYPE_FORM ftype;
  int code = rtn_idp->defn.info.routine.key;
  entry_debug("exec_hibound_etc");

  get_ctoken();     /* LPAREN */
  get_ctoken();
  parm_tp = base_type(exec_expression());
  if (is_value_undef(tos)) {
    put_undef(tos);
    get_ctoken();   /* token after RPAREN */
    exit_debug("exec_hibound_etc");
    return(result_tp);
  }
  ftype = parm_tp->form;
  if ((ftype != ARRAY_FORM) &&
      (ftype != BAG_FORM) &&
      (ftype != LIST_FORM) &&
      (ftype != SET_FORM) ) {
    runtime_error(INVALID_FUNCTION_ARGUMENT);
    put_undef(tos);
    get_ctoken();   /* token after RPAREN */
    exit_debug("exec_hibound_etc");
    return(result_tp);
  }

  stype = get_stackval_type(tos);
  if (stype != form2stack[ftype] &&
      stype != STKADD) {
    stack_warning(form2stack[ftype], stype);
  }

  switch (code) {
    case XHIBOUND: {                         /* declared upper bound */
      if (parm_tp->form == ARRAY_FORM) {
        result = parm_tp->info.array.max_index;
      }
      else {
        result = parm_tp->info.dynagg.max_index;
      }
      break;
    }
    case XHIINDEX: {           /* declared array upper bound, or # of elements */
      if (parm_tp->form == ARRAY_FORM) {
        result = parm_tp->info.array.max_index;
      }
      else {
        result = NELS((LBS_PTR) get_address_type(tos, stype));
      }
      break;
    }
    case XLOBOUND: {                         /* declared lower bound */
      if (parm_tp->form == ARRAY_FORM) {
        result = parm_tp->info.array.min_index;
      }
      else {
        result = parm_tp->info.dynagg.min_index;
      }
      break;
    }
    case XLOINDEX: {                       /* declared array lower bound, or 1 */
      if (parm_tp->form == ARRAY_FORM) {
        result = parm_tp->info.array.min_index;
      }
      else {
        result = 1;
      }
      break;
    }
    case XSIZEOF: {                         /* # of actual elements */
      if (parm_tp->form == ARRAY_FORM) {
        result = parm_tp->info.array.max_index - parm_tp->info.array.min_index + 1;
      }
      else {
        result = NELS((LBS_PTR) get_address_type(tos, stype));
      }
      break;
    }
   
    default: {           /* should not be here */
      runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
      get_ctoken();
      put_undef(tos);
      exit_debug("exec_hibound_etc");
      return(result_tp);
    }
  } /* end switch */

  get_ctoken();   /* token after RPAREN */

  put_integer(tos, result);
  exit_debug("exec_hibound_etc");
  return(result_tp);
}                                               /* end EXEC_HIBOUND_ETC */
/************************************************************************/



/************************************************************************/
/* exec_length_etc(rtn_idp)    Execute a call to LENGTH, etc            */
/*                         fun('string')                                */
/*                         String parameter, integer result             */
/*    at entry, ctoken is `fun'                                         */
/*    at exit, ctoken is token after closing )                          */

TYPE_STRUCT_PTR exec_length_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                      /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;                    /* actual param type */
  TYPE_STRUCT_PTR result_tp;                  /* returned type */
  XPRSAINT result = 0;
  entry_debug("exec_length_etc (l2xixstd.c)");

  get_ctoken();             /* should be ( */
  get_ctoken();             /* start of param */
  parm_tp = base_type(exec_expression());
  if (parm_tp->form != STRING_FORM) {
    runtime_error(INVALID_FUNCTION_ARGUMENT);
  }
  else {
    switch (rtn_idp->defn.info.routine.key) {
      case XLENGTH : {                             /* # of chars in a string */
        result = (XPRSAINT) strlen(get_stacked_string(tos));
        break;
      }
      default : {
        runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
        break; 
      }
    } /* end switch */
  }

  get_ctoken();         /* token after closing ) */
  put_integer(tos, result);
  exit_debug("exec_length_etc");
  return(result_tp);
}                                                /* end EXEC_LENGTH_ETC */
/************************************************************************/



/************************************************************************/
/* exec_exists_etc(rtn_idp)  Execute a call to EXISTS, etc              */
/*                   any type -> boolean                                */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_exists_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;             /* actual param type */
  TYPE_STRUCT_PTR result_tp;           /* result type */
  int code = rtn_idp->defn.info.routine.key;
  entry_debug("exec_exists_etc");

  get_ctoken();     /* LPAREN */
  get_ctoken();
  parm_tp = base_type(exec_expression());

  if (code == XEXISTS) {
    if (is_value_undef(tos)) {
      put_true(tos);
    }
    else {
      put_false(tos);
    }
  }

  get_ctoken();   /* token after RPAREN */

  exit_debug("exec_exists_etc");
  return(logical_typep);
}                                                /* end EXEC_EXISTS_ETC */
/************************************************************************/



/************************************************************************/
/* exec_nvl_etc(rtn_idp)  Execute a call to NVL, etc                    */
/*           In general, any function fun(p1, p2) that:                 */
/*           any compatible params --> compatible result                */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_nvl_etc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm1_tp, parm2_tp;       /* actual param types */
  TYPE_STRUCT_PTR result_tp;
  STACK_ITEM_PTR parm1, parm2;              /* parameters */
  int code = rtn_idp->defn.info.routine.key;
  entry_debug("exec_nvl_etc");

  get_ctoken();    /* LPAREN */
  get_ctoken();    /* start of first parameter */
  parm1_tp = base_type(exec_expression());
  parm1 = tos;
  get_ctoken();    /* COMMA */
  get_ctoken();    /* start of second parameter */
  parm2_tp = base_type(exec_expression());
  parm2 = tos;


  if (code == XNVL) {
    if (is_value_undef(parm1)) {
      copy_value(parm1, parm2);
      result_tp = parm2_tp;
    }
    else {
      result_tp = parm1_tp;
    }
  }

  get_ctoken();   /* token after RPAREN */

  exit_debug("exec_nvl_etc");
  return(result_tp);
}                                                   /* end EXEC_NVL_ETC */
/************************************************************************/



/************************************************************************/
/* exec_abs_sqr(rtn_idp)  Execute a call to ABS or SQR                  */
/*                         Integer --> integer result                   */
/*                         real --> real result                         */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_abs_sqr(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;             /* actual param type */
  TYPE_STRUCT_PTR result_tp;           /* result type */
  XPRSAINT i1;
  XPRSAREAL r1;
  int code = rtn_idp->defn.info.routine.key;
  entry_debug("exec_abs_sqr");

  get_ctoken();     /* LPAREN */
  get_ctoken();
  parm_tp = base_type(exec_expression());

  if (is_value_undef(tos)) {
    ;
  }
  if (code == ABS) {
    if (parm_tp == integer_typep) {
       i1 = get_integer(tos);
       if (i1 >= 0) {
         put_integer(tos, i1);
       }
       else {
         put_integer(tos, -i1);
       }
    }
    else {
      r1 = (XPRSAREAL) fabs((double) get_real(tos));
      put_real(tos, r1);
    }
  }

  get_ctoken();   /* token after RPAREN */

  exit_debug("exec_abs_sqr");
  return(parm_tp);
}                                                  /* end exec_abs_sqr  */
/************************************************************************/



/************************************************************************/
/* exec_arctan_cos_exp_ln_sin_sqrt(rtn_idp)  Execute a call to ARCTAN,  */
/*                  COS, EXP, LN, SIN or SQRT                           */
/*           In general, any function fun(p1) that:                     */
/*           integer or real param --> real result                      */
/* return a pointer to the type stucture of the call                    */
/* NOTE calling C library routines acos() and asin() give wierd interp error */

TYPE_STRUCT_PTR exec_arctan_cos_exp_ln_sin_sqrt(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm_tp;       /* actual param type */
  int code = rtn_idp->defn.info.routine.key;
  XPRSAREAL r1, r2;
  entry_debug("exec_arctan_cos_exp_ln_sin_sqrt");

  get_ctoken();    /* LPAREN */
  get_ctoken();
  parm_tp = base_type(exec_expression());

  if (is_value_undef(tos)) {
    get_ctoken();   /* token after RPAREN */
    exit_debug("exec_arctan_cos_exp_ln_sin_sqrt");
    return(real_typep);
  }
  
  if (parm_tp == integer_typep) {
    put_real(tos, (XPRSAREAL) get_integer(tos));
  }

  r1 = (double) get_real(tos);

         /* check input value */
  if (((code == SQRT) && (r1 < 0.0)) ||
      ((code == XACOS) && (r1 < -1.0 || r1 > 1.0)) ||
      ((code == XASIN) && (r1 < -1.0 || r1 > 1.0)) ||
      ((code == XLOG) && (r1 <= 0.0)) ||
      ((code == XLOG2) && (r1 <= 0.0)) ||
      ((code == XLOG10) && (r1 <= 0.0)) ) {
    runtime_error(INVALID_FUNCTION_ARGUMENT);
    exit_debug("exec_arctan_cos_exp_ln_sin_sqrt");
  }
  else {
    switch (rtn_idp->defn.info.routine.key) {
      case COS: {
        put_real(tos, (XPRSAREAL) cos(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (COS)");
        break;
      }
      case EXP: {
        put_real(tos, (XPRSAREAL) exp(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (EXP)");
        break;
      }
      case SIN: {
        put_real(tos, (XPRSAREAL) sin(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (SIN)");
        break;
      }
      case SQRT: {
        put_real(tos, (XPRSAREAL) sqrt(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (SQRT)");
        break;
      }
      case XACOS: {           
        put_real(tos, (XPRSAREAL) acos(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (ACOS)");
        break;
      }
      case XASIN: {
        put_real(tos, (XPRSAREAL) asin(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (ASIN)");
        break;
      }
      case XLOG: {
        put_real(tos, (XPRSAREAL) log(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (LOG)");
        break;
      }
      case XLOG2: {    /* log_a(x) = ln(x)/ln(a) : ln(2) = 0.6931 47180 55994 */
        put_real(tos, (1.442695 * ((XPRSAREAL) log(r1))));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (LOG2)");
        break;
      }
      case XLOG10: {
        put_real(tos, (XPRSAREAL) log10(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (LOG10)");
        break;
      }
      case XTAN: {
        put_real(tos, (XPRSAREAL) tan(r1));
  exit_debug("exec_arctan_cos_exp_ln_sin_sqrt (TAN)");
        break;
      }
    } /* end switch */
  }

  get_ctoken();   /* token after RPAREN */

  return(real_typep);
}                               /* end exec_arctan_cos_exp_ln_sin_sqrt  */
/************************************************************************/



/************************************************************************/
/* exec_atan(rtn_idp)  Execute a call to ATAN,                          */
/*           In general, any function fun(p1, p2) that:                 */
/*           integer or real param --> real result                      */
/* return a pointer to the type stucture of the call                    */
/*   NOTE: Calling C library function atan2() gives wierd interp. error */

TYPE_STRUCT_PTR exec_atan(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  TYPE_STRUCT_PTR parm1_tp, parm2_tp;       /* actual param type */
  TYPE_STRUCT_PTR result_tp;
  STACK_ITEM_PTR parm1, parm2;
  int code = rtn_idp->defn.info.routine.key;
  XPRSAREAL r1;
  XPRSAREAL r2;
  entry_debug("exec_atan");

  get_ctoken();    /* LPAREN */
  get_ctoken();    /* start of first parameter */
  parm1_tp = base_type(exec_expression());
  parm1 = tos;

  get_ctoken();    /* COMMA */
  get_ctoken();    /* start of second parameter */
  parm2_tp = base_type(exec_expression());
  parm2 = tos;

  if (code == XATAN) {
    if (is_value_undef(parm1) || is_value_undef(parm2)) {
      put_undef(parm1);
    }
    else {
      if (parm1_tp == integer_typep) {
        put_real(parm1, (XPRSAREAL) get_integer(parm1));
      }
      r1 = get_real(parm1);
      if (parm2_tp == integer_typep) {
        put_real(parm2, (XPRSAREAL) get_integer(parm2));
      }
      r2 = get_real(parm2);
      if (r1 == 0.0 && r2 == 0.0) {
        runtime_error(INVALID_FUNCTION_ARGUMENT);
      }
      else {
        r1 = (double) r1;
        r2 = (double) r2;
        put_real(parm1, (XPRSAREAL) atan2(r1, r2));
      }
    }
  }

  pop();
  get_ctoken();   /* token after RPAREN */

  exit_debug("exec_atan");
  return(real_typep);
}                                                     /* end EXEC_ATAN  */
/************************************************************************/



/************************************************************************/
/* exec_odd()  Execute a call to ODD                                    */
/*                    integer param --> boolean result                  */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_odd()
{
  XPRSAINT i1;
  entry_debug("exec_odd");
  get_ctoken();  /* LPAREN */
  get_ctoken();
  exec_expression();

  if (!is_value_undef(tos)) {
    i1 = get_integer(tos);
    i1 &= 1;
    if (i1 == 0) {
      put_false(tos);
    }
    else {
      put_true(tos);
    }
  }

  get_ctoken();  /* after RPAREN */

  exit_debug("exec_odd");
  return(logical_typep);
}                                                      /* end exec_odd  */
/************************************************************************/



/************************************************************************/
/* exec_round_trunc(rtn_idp)  Execute a call to ROUND or TRUNC          */
/*                            real param --> integer result             */
/* return a pointer to the type stucture of the call                    */

TYPE_STRUCT_PTR exec_round_trunc(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;                  /* routine id */
{
  XPRSAREAL r1;
  XPRSAINT i1;

  entry_debug("exec_round_trunc");
  get_ctoken();  /* LPAREN */
  get_ctoken();
  exec_expression();

  if (!is_value_undef(tos)) {
    r1 = get_real(tos);
    if (rtn_idp->defn.info.routine.key == ROUND) {
      i1 = r1 > 0.0
                   ? (XPRSAINT) (r1 + 0.5)
	           : (XPRSAINT) (r1 - 0.5);
    }
    else {
      i1 = (XPRSAINT) r1;
    }
    put_integer(tos, i1);
  }

  get_ctoken();  /* after RPAREN */

  exit_debug("exec_round_trunc");
  return(integer_typep);
}                                              /* end exec_round_trunc  */
/************************************************************************/