/* l2xiexpr.c  LTX2X interpreter parsing routines for expressions      */
/*  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 "l2xiprse.h" 
#include "l2xiidbg.h"
#ifndef l2xicpr_h
#include "l2xicpr.h"           /* extern token code lists */
#endif

/* EXTERNALS */

extern TOKEN_CODE token;
extern char token_string[];
extern char word_string[];
extern LITERAL literal;

extern SYMTAB_NODE_PTR symtab_display[]; 
extern int level; 

  /* built-in constants */
extern SYMTAB_NODE_PTR false_idp, true_idp, unknown_idp;
extern SYMTAB_NODE_PTR conste_idp, pi_idp, undef_idp;
extern SYMTAB_NODE_PTR day_idp, month_idp, year_idp;

/* FORWARDS */
TYPE_STRUCT_PTR expression(), simple_expression(), term(), factor(),
                function_call();
TYPE_STRUCT_PTR simple_factor();
TYPE_STRUCT_PTR index_list();


/* MACROS */

/* integer_operands(tp1, tp2) TRUE if both are integer, else FALSE         */
#define integer_operands(tp1, tp2) ((tp1 == integer_typep) && \
                                    (tp2 == integer_typep))

/* real_operands(tp1, tp2)  TRUE if one or both operands are real, and     */
/*                          the other is integer, else FALSE               */
#define real_operands(tp1, tp2) (((tp1 == real_typep) &&      \
                                  ((tp2 == real_typep) ||     \
                                   (tp2 == integer_typep)))   \
                                         ||                   \
                                  ((tp2 == real_typep) &&     \
                                    ((tp1 == real_typep) ||   \
                                     (tp1 == integer_typep))))

/* boolean_operands(tp1, tp2) TRUE if both are boolean, else FALSE         */
#define boolean_operands(tp1, tp2) ((tp1 == boolean_typep) &&  \
                                    (tp2 == boolean_typep))

/* logical_operands(tp1, tp2) TRUE if both are logical/boolean, else FALSE    */
#define logical_operands(tp1, tp2) ((tp1 == boolean_typep || tp1 == logical_typep) &&  \
                                    (tp2 == boolean_typep || tp2 == logical_typep))

/* string_operands(tp1, tp2) TRUE iff both are string */
#define string_operands(tp1, tp2) ((tp1 == string_typep ||         \
                                    tp1->form == STRING_FORM) &&   \
                                   (tp2 == string_typep ||         \
                                    tp2->form == STRING_FORM))

/* NEW undef_types(tp1, tp2) TRUE if either is undefined, else FALSE */
#define undef_types(tp1, tp2) ((tp1 == any_typep) || \
                               (tp2 == any_typep))

/* NEW is_undef(tp1) TRUE if undefined, else FALSE */
#define is_undef(tp1) (tp1 == any_typep)

/* NEW set_undef(tp1)  sets tp1 to be an undef */
#define set_undef(tp1) tp1 = any_typep

/***************************************************************************/
/* expression() Process an expression consisting of a simple expression,   */
/*              optionally followed by a relational operator and a         */
/*              second simple expression.                                  */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR expression()
{
  TYPE_STRUCT_PTR result_tp, tp2;
  entry_debug("expression");

  /* first simple expression */
  result_tp = simple_expression();

  /* if operator, process following expression */
  if (token_in(rel_op_list)) {
    result_tp = base_type(result_tp);
    /* second expression */
    get_token();
    tp2 = base_type(simple_expression());
    check_rel_op_types(result_tp, tp2);
    result_tp = logical_typep;
  }

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



/***************************************************************************/
/* simple_expression() Process a simple expression                         */
/*                     consisting of terms seperated by +, -, OR, XXOR     */
/*                     operators. There may be an initial unary operator   */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR simple_expression()
{
  TOKEN_CODE op;                   /* operator token */
  TYPE_STRUCT_PTR result_tp, tp2;
  BOOLEAN saw_unary_op = FALSE;
  entry_debug("simple_expression");

  /* remember intial unary op */
  if ((token == PLUS) || (token == MINUS)) {
    saw_unary_op = TRUE;
    get_token();
  }

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

  /* if there was a unary operator, check its type for integer or real. */
  if (saw_unary_op && (base_type(result_tp) != integer_typep) &&
                      (result_tp != real_typep)) error(INCOMPATIBLE_TYPES);

  /* loop to process subsequent terms seperated by operators */
  while (token_in(add_op_list)) {
    op = token;
    result_tp = base_type(result_tp);

    get_token();
    tp2 = base_type(term());    /* next term */
    if (undef_types(result_tp, tp2)) {
      set_undef(result_tp);
    }
    else {
    switch (op) {
      case PLUS:  {
        /* integer op integer -> integer */
        if (integer_operands(result_tp, tp2))  result_tp = integer_typep;
        /* numbers -> real, */
        else if (real_operands(result_tp, tp2)) result_tp = real_typep;
        /* string concatenation */
        else if (string_operands(result_tp, tp2)) result_tp = string_typep;
        else {
          error(INCOMPATIBLE_TYPES);
          result_tp = &dummy_type;
        }
        break;
      }
      case MINUS: {  
        /* integer op integer -> integer */
        if (integer_operands(result_tp, tp2))  result_tp = integer_typep;
        /* otherwise numbers -> real, else error */
        else if (real_operands(result_tp, tp2)) result_tp = real_typep;
        else {
          error(INCOMPATIBLE_TYPES);
          result_tp = &dummy_type;
        }
        break;
      }
      case OR:
      case XXOR: {
        /* boolean OR boolean -> boolean */
        if (!logical_operands(result_tp, tp2)) {
          error(INCOMPATIBLE_TYPES);
          result_tp = &dummy_type;
          break;
        }
        result_tp = logical_typep;
        break;
      }
      case XLIKE: {
         /* string LIKE string -> boolean */
        if (!string_operands(result_tp, tp2)) {
          error(INCOMPATIBLE_TYPES);
          result_tp = &dummy_type;
          break;
        }
        result_tp = logical_typep;
        break;
      }
    } /* end switch */
  }
  } /* end while */

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



/***************************************************************************/
/* term() Process a term                                                   */
/*                     consisting of factors seperated by                  */
/*                     *, /, DIV, MOD, or AND                              */
/*                     operators.                                          */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR term()
{
  TOKEN_CODE op;                   /* operator token */
  TYPE_STRUCT_PTR result_tp, tp2;
  entry_debug("term");

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

  /* loop to process subsequent factors seperated by operators */
  while (token_in(mult_op_list)) {
    op = token;
    result_tp = base_type(result_tp);

    get_token();
    tp2 = base_type(factor());    /* next factor */
    if (undef_types(result_tp, tp2)) {
      set_undef(result_tp);
    }
    else {
    switch (op) {
      case STAR: {  
        /* integer op integer -> integer */
        if (integer_operands(result_tp, tp2))  result_tp = integer_typep;
        /* otherwise numbers -> real, else error */
        else if (real_operands(result_tp, tp2)) result_tp = real_typep;
        else {
          error(INCOMPATIBLE_TYPES);
          result_tp = &dummy_type;
        }
        break;
      }
      case SLASH: {  
        /* number op number -> real */
        if ((!real_operands(result_tp, tp2)) && 
            (!integer_operands(result_tp, tp2))) {
          error(INCOMPATIBLE_TYPES);
        }
        result_tp = real_typep;
        break;
      }
      case DIV:
      case MOD: {
        /* integer op integer -> integer */
        if (!integer_operands(result_tp, tp2)) error(INCOMPATIBLE_TYPES);
        result_tp = integer_typep;
        break;
      }
      case AND: {
        /* boolean op boolean -> boolean */
        if (!logical_operands(result_tp, tp2)) {
          error(INCOMPATIBLE_TYPES);
          result_tp = logical_typep;
          break;
        }
      }
    } /* end switch */
  }
  } /* end while */

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



/***************************************************************************/
/* factor()    Process an EXPRESS factor                                   */
/*             simple_factor [ ** simple_factor ]                          */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR factor()
{
  TOKEN_CODE op;                   /* operator token */
  TYPE_STRUCT_PTR result_tp, tp2;
  entry_debug("factor");

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

  op = token;
  if (op == STARSTAR) {
    result_tp = base_type(result_tp);
    get_token();
    tp2 = base_type(simple_factor());
    if (undef_types(result_tp, tp2)) {
      set_undef(result_tp);
    }
    else if (integer_operands(result_tp, tp2)) result_tp = integer_typep;
    else if (real_operands(result_tp, tp2)) result_tp = real_typep;
    else {
      error(INCOMPATIBLE_TYPES);
      result_tp = &dummy_type;
    }
  }

  exit_debug("factor");
  return(result_tp);
}                                                            /* end FACTOR */
/***************************************************************************/



/***************************************************************************/
/* simple_factor() Process a simple factor                                 */
/*                     a variable, a number, NOT factor, a                 */
/*                     parenthesized expression, or an interval expression */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR simple_factor()
{
  TYPE_STRUCT_PTR tp;
  TYPE_STRUCT_PTR tp1, tp2;
  TOKEN_CODE op;
  entry_debug("simple_factor");

  if (token_in(constant_list)) {             /* language defined constant */
    switch (token) {
      case XFALSE : {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(false_idp);
        tp = logical_typep;
        break;
      }
      case XTRUE : {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(true_idp);
        tp = logical_typep;
        break;
      }
      case XUNKNOWN : {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(unknown_idp);
        tp = logical_typep;
        break;
      }
      case XCONST_E : {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(conste_idp);
        tp = real_typep;
        break;
      }
      case XPI : {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(pi_idp);
        tp = real_typep;
        break;
      }
      case QUERY_CHAR : {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(undef_idp);
        set_undef(tp);
        break;
      }
      case THE_DAY: {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(day_idp);
        tp = integer_typep;
        break;
      }
      case THE_MONTH: {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(month_idp);
        tp = integer_typep;
        break;
      }
      case THE_YEAR: {
        change_crunched_token(IDENTIFIER);
        crunch_symtab_node_ptr(year_idp);
        tp = integer_typep;
        break;
      }
      default : {
        error(UNIMPLEMENTED_CONSTANT);
        tp = &dummy_type;
        break;
      }
    }  /* end switch */
    get_token();
    exit_debug("factor at defined constants");
    return(tp);
  }  /* end of language defined constants */

  switch (token) {
    case IDENTIFIER: {  
      SYMTAB_NODE_PTR idp;

      search_and_find_all_symtab(idp);
      switch (idp->defn.key) {
        case FUNC_DEFN: {
          crunch_symtab_node_ptr(idp);
          get_token();
          tp = routine_call(idp,TRUE);
          break;
        }
        case PROC_DEFN: {
          error(INVALID_IDENTIFIER_USAGE);
          get_token();
          actual_parm_list(idp,FALSE);
          tp = &dummy_type;
          break;
        }
        case CONST_DEFN: {
          crunch_symtab_node_ptr(idp);
          get_token();
          tp = idp->typep;
          break;
        }
        default: {
          tp = variable(idp, EXPR_USE);
          break;
        }
      } /* end switch */
      break;
    }
    case NUMBER_LITERAL: {
      SYMTAB_NODE_PTR np;

      np = search_symtab(token_string, symtab_display[1]);
      if (np == NULL) np = enter_symtab(token_string, symtab_display[1]);

      if (literal.type == INTEGER_LIT ) {
        tp = np->typep = integer_typep;
        np->defn.info.constant.value.integer = literal.value.integer;
      }
      else {         /* a real literal */
        tp = np->typep = real_typep;
        np->defn.info.constant.value.real = literal.value.real;
      }
      crunch_symtab_node_ptr(np);
      get_token();
      break;
    }
    case STRING_LITERAL: {
      SYMTAB_NODE_PTR np;
      int length = strlen(literal.value.string);

      np = search_symtab(token_string, symtab_display[1]);
      if (np == NULL) np = enter_symtab(token_string, symtab_display[1]);
      np->typep = tp = make_string_typep(length);
      np->info = alloc_bytes(length + 1);
      strcpy(np->info, literal.value.string);
      crunch_symtab_node_ptr(np);
      get_token();
      break;
    }
    case NOT: {
      get_token();
      tp = simple_factor();
      break;
    }
    case LPAREN: {
      get_token();
      tp = expression();

      if_token_get_else_error(RPAREN, MISSING_RPAREN);
      break;
    }
    case LBRACE: {   /* interval expression {expr op var op expr} */
      get_token();
      tp1 = simple_expression();
      op = token;
      if (op != LT && op != LE) {
        error(EXPECTED_INTERVAL_OP);
      }
      get_token();
      tp = simple_expression();
      check_rel_op_types(tp1, tp);
      op = token;
      if (op != LT && op != LE) {
        error(EXPECTED_INTERVAL_OP);
      }
      get_token();
      tp2 = simple_expression();
      check_rel_op_types(tp, tp2);
      if_token_get_else_error(RBRACE, MISSING_RBRACE);
      tp = logical_typep;
      break;
    }
    default: {
      error(INVALID_EXPRESSION);
      tp = &dummy_type;
      break;
    }
  } /* end switch */

  exit_debug("simple_factor");
  return(tp);

}                                                     /* end SIMPLE_FACTOR */
/***************************************************************************/



/***************************************************************************/
/* variable(var_idp, use) Process a variable                               */
/*                     consisting of                                       */
/*                     a simple id, an array id with subscripts,           */
/*                     or an entity id with attributes                     */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR variable(var_idp, use)
SYMTAB_NODE_PTR var_idp;                  /* var id */
USE use;                                  /* how variable is used */
{
  TYPE_STRUCT_PTR tp = var_idp->typep;
  DEFN_KEY defn_key = var_idp->defn.key;
  TYPE_STRUCT_PTR array_subscript_list();
  TYPE_STRUCT_PTR entity_attr();
  entry_debug("variable");

  crunch_symtab_node_ptr(var_idp);

  /* check the definition of the variable */
  switch (defn_key) {
    case VAR_DEFN:
    case VALPARM_DEFN:
    case VARPARM_DEFN:
    case FUNC_DEFN:
    case UNDEFINED: {
      break;
    }
    default: {
      tp = &dummy_type;
      error(INVALID_IDENTIFIER_USAGE);
      break;
    }
  } /* end switch */

  get_token();

  /* there must not be a parameter list, but parse for one anyway */
  if (token == LPAREN) {
    error(UNEXPECTED_TOKEN);
    actual_parm_list(var_idp, FALSE);
    exit_debug("variable (unexpected parm list)");
    return(tp);
  }

  /* subscripts or fields? */
  while ((token == LBRACKET) || (token == PERIOD)) {
    if (token == PERIOD) {
      tp = entity_attr(tp);
    }
    else {
      if (var_idp->typep == string_typep || 
          var_idp->typep->form == STRING_FORM) { /* substring op */
        tp = index_list(tp);
      }
      else {                                     /* aggregate index */
        tp = array_subscript_list(tp); 
      }
    }
  }

  exit_debug("variable");
  return(tp);

}                                                          /* end variable */
/***************************************************************************/



/***************************************************************************/
/* index_list(tp) Process a (pair of) subscript v                          */
/*                     '[' <int_expr> [ ':' <int_expr> ] ']'               */
/* return a pointer to the type structure                                  */
/*  at entry: token is opening [                                           */
/*  at exit: token is after closing ]                                      */

TYPE_STRUCT_PTR index_list(tp)
TYPE_STRUCT_PTR tp;                 /* type of var just before opening [ */
{
  TYPE_STRUCT_PTR ss1_tp, ss2_tp;
  entry_debug("index_list (l2xiexpr.c)");

            /* check on var type */
  if (tp == string_typep || tp->form == STRING_FORM) {  /* OK */
    ;
  }
  else {
    error(UNEXPECTED_TOKEN);
  }

     /* do first expression */
  get_token();
  ss1_tp = expression();
  if (ss1_tp != integer_typep) error(INCOMPATIBLE_TYPES);

  if (token == COLON) {           /* do second expression */
    get_token();
    ss2_tp = expression();
    if (ss2_tp != integer_typep) error(INCOMPATIBLE_TYPES);
  }

  if_token_get_else_error(RBRACKET, MISSING_RBRACKET);
  exit_debug("index_list");
  return(tp);

}                                                        /* end INDEX_LIST */
/***************************************************************************/


/***************************************************************************/
/* array_subscript_list(tp) Process a list of subscripts                   */
/*                     [ <expr>, <expr>, ... ]                             */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR array_subscript_list(tp)
TYPE_STRUCT_PTR tp;
{
  TYPE_STRUCT_PTR index_tp, elmt_tp, ss_tp;

  /* loop to process the list */
  do {
    if (tp->form == ARRAY_FORM) {
      index_tp = tp->info.array.index_typep;
      elmt_tp = tp->info.array.elmt_typep;
      get_token();
      ss_tp = expression();
     
      /* check assignment compatibility */
      if (!is_assign_type_compatible(index_tp, ss_tp)) error(INCOMPATIBLE_TYPES);
      tp = elmt_tp;
    }
    else if (tp->form == BAG_FORM ||
             tp->form == LIST_FORM ||
             tp->form == SET_FORM) {
      index_tp = tp->info.dynagg.index_typep;
      elmt_tp = tp->info.dynagg.elmt_typep;
      get_token();
      ss_tp = expression();
     
      /* check assignment compatibility */
      if (!is_assign_type_compatible(index_tp, ss_tp)) error(INCOMPATIBLE_TYPES);
      tp = elmt_tp;
    }
    else {
      error(TOO_MANY_SUBSCRIPTS);
      while ((token != RBRACKET) && (!token_in(statement_end_list))) {
        get_token();
      }
    }
  } while (token == COMMA); /* end do */

  if_token_get_else_error(RBRACKET, MISSING_RBRACKET);
  return(tp);

}                                              /* end array_subscript_list */
/***************************************************************************/


/***************************************************************************/
/* entity_attr(tp) Process an entity attribute                             */
/*                     .  <attr-variable>                                  */
/* return a pointer to the type structure                                  */

TYPE_STRUCT_PTR entity_attr(tp)
TYPE_STRUCT_PTR tp;
{
  SYMTAB_NODE_PTR attr_idp;

  get_token();

  if ((token == IDENTIFIER) && (tp->form == ENTITY_FORM)) {
    search_this_symtab(attr_idp, tp->info.entity.attribute_symtab);

    crunch_symtab_node_ptr(attr_idp);
    get_token();
    if (attr_idp != NULL) return(attr_idp->typep);
    else {
      error(INVALID_ATTRIBUTE);
      return(&dummy_type);
    }
  }
  else {
    get_token();
    error(INVALID_ATTRIBUTE);
    return(&dummy_type);
  }

}                                                       /* end entity_attr */
/***************************************************************************/




/* TYPE COMPATIBILITY */


/***************************************************************************/
/* check_rel_op_types(tp1, tp2) Check operand types of a relational         */
/*                             operator                                    */

check_rel_op_types(tp1, tp2)
TYPE_STRUCT_PTR tp1;
TYPE_STRUCT_PTR tp2;
{

  /* identical scalar or enumeration types */
  if ((tp1 == tp2) && 
      ((tp1->form == SCALAR_FORM) || (tp1->form == ENUM_FORM))) {
    return;
  }

  /* one integer and one real */
  if (((tp1 == integer_typep) && (tp2 == real_typep)) ||
      ((tp2 == integer_typep) && (tp1 == real_typep))) {
    return;
  }

  /* two arbitrary strings */
  if (string_operands(tp1, tp2)) {
    return;
  }

  /* for the IN operator */
  /* tp2 is a dynamic aggregate, tp1 is the elmt type */
  if (is_dynagg(tp2)) {
    if (tp1 == tp2->info.dynagg.elmt_typep) {
      return;
    }
  }

  error(INCOMPATIBLE_TYPES);
}                                                /* end check_rel_op_types */
/***************************************************************************/


/***************************************************************************/
/* is_assign_type_compatible(tp1, tp2) Check if a value of type tp2        */
/*                               can be assigned to a variable of type tp1 */
/*                            (i.e. tp1 := tp2)                            */
/* return TRUE if types assignment compatible, else FALSE                  */

BOOLEAN is_assign_type_compatible(tp1, tp2)
TYPE_STRUCT_PTR tp1;
TYPE_STRUCT_PTR tp2;
{

  tp1 = base_type(tp1);
  tp2 = base_type(tp2);

  if (tp1 == tp2) return(TRUE);
  if (is_undef(tp2)) {
    compile_warning(ASSIGN_TO_UNDEF);
    return(TRUE);
  }

  /* real := integer */
  if ((tp1 == real_typep) && (tp2 == integer_typep)) return(TRUE);

  if (string_operands(tp1, tp2)) return(TRUE);

  /* incompatible */
  return(FALSE);

}                                         /* end is_assign_type_compatible */
/***************************************************************************/


/***************************************************************************/
/* base_type(tp)  Return the range type of a subrange type                 */

TYPE_STRUCT_PTR base_type(tp)
TYPE_STRUCT_PTR tp;
{
  return((tp->form == SUBRANGE_FORM)
                   ? tp->info.subrange.range_typep
                   : tp);
}                                                         /* end base_type */
/***************************************************************************/






