/* $Id: rt_op.c,v 1.2 2009/01/04 15:42:15 kiesling Exp $ */

/*
  This file is part of Ctalk.
  Copyright © 2005-2009  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"
#include "typeof.h"

/*
 *  Operators have a binary context if they are preceded 
 *  by non-whitespace tokens that are not operators, unary
 *  otherwise.
 */

OP_CONTEXT _rt_op_context (MESSAGE_STACK messages, int op_ptr, 
			   int preprocessing,
			   int stack_top, int stack_start) {

  MESSAGE *m_op;
  int lookahead,
    lookback,
    op_res;
  OP_CONTEXT context;

  m_op = messages[op_ptr];

  if (!IS_C_OP (m_op -> tokentype))
    return op_not_an_op;

  /* Just return unary here - checking for mismatched
     parentheses is done elsewhere. */
  if ((m_op -> tokentype == OPENPAREN) ||
      (m_op -> tokentype == CLOSEPAREN)) {
    context = op_unary_context;
    return context;
  }

  if ((op_res = _rt_operands (messages, op_ptr, &lookback, &lookahead,
			      stack_top, stack_start))
      == ERROR)
    return op_not_an_op;

  context = op_binary_context;

  if (lookahead == ERROR) {
    return op_null_context;
  }

  /* Typecast. */
  if (messages[lookahead] -> name[0] == ')')
    return op_cast_context;

  /* Evaluating how an operator binds is determined by whether the 
     context is unary or binary, and then evaluating in order of 
     operator precedence. */
  if (lookback == ERROR) {
    context = op_unary_context;
  } else {
    if (IS_C_OP_TOKEN_NOEVAL (messages[lookback] -> tokentype) || 
	is_c_keyword (messages[lookback] -> name) ||
	is_macro_keyword (messages[lookback] -> name))
      context = op_unary_context;
  }
  return context;
}


/*
 *  Return true if an operator's expression should be evaluated at
 *  this precedence level.
 *
 *  Here is the order of C language operator precedence.
 *
 *   0. Postfix (), [], ++ -- 
 *   1. unary +, -, !; ~ ++, (cast), & (address of), sizeof ()
 *   2. *, /, % (multiplicative)
 *   3. +, - (additive)
 *   4. <<, >>
 *   5. <, <=, >, >=
 *   6. ==, !=
 *   7. & (bitwise and)
 *   8. ^ (bitwise xor), | (bitwise or)
 *   9. && (logical and)
 *   10. || (logical or)
 *   11. ? : (conditional)
 *   12. Assignment ops.
 *   13. , (comma)
 */

int _rt_op_precedence (int precedence, MESSAGE_STACK messages, int op_ptr,
		   int preprocessing, int stack_top, int stack_start) {

  int retval = FALSE;
  MESSAGE *m_op;
  OP_CONTEXT context;

  m_op = messages[op_ptr];

  if (!IS_C_OP (m_op -> tokentype) ||
      ((context = _rt_op_context (messages, op_ptr, preprocessing, 
			      stack_top, stack_start)) == 
       op_not_an_op))
    return FALSE;

  switch (precedence)
    {
    case 0:
      switch (m_op -> tokentype)
	{
	case OPENPAREN:
	case CLOSEPAREN:
	case ARRAYOPEN:
	case ARRAYCLOSE:
	case INCREMENT:
	case DECREMENT:
	case DEREF:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 1:
      switch (m_op -> tokentype) 
	{
	case SIZEOF:
	case EXCLAM:
	  retval = TRUE;
	  break;
	case PLUS:      
	case MINUS:     
	case ASTERISK:  
	case AMPERSAND:
	  if (context == op_unary_context)
	    retval = TRUE;
	default: 
	  break;
	}
      break;
    case 2:
      switch (m_op -> tokentype)
	{
	case DIVIDE:
	case MODULUS:
	  retval = TRUE;
	  break;
	case ASTERISK:
	  if (context == op_binary_context)
	    retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 3:
      switch (m_op -> tokentype)
	{
	case PLUS:
	case MINUS:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 4:
      switch (m_op -> tokentype)
	{
	case ASL:
	case ASR:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 5:
      switch (m_op -> tokentype)
	{
	case LT:
	case LE:
	case GT:
	case GE:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 6:
      switch (m_op -> tokentype)
	{
	case BOOLEAN_EQ:
	case INEQUALITY:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 7:
      if (m_op -> tokentype == BIT_AND)
	retval = TRUE;
      break;
    case 8:
      switch (m_op -> tokentype)
	{
	case BIT_OR:
	case BIT_XOR:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 9:
      if (m_op -> tokentype == BOOLEAN_AND)
	retval = TRUE;
      break;
    case 10:
      if (m_op -> tokentype == BOOLEAN_OR)
	retval = TRUE;
      break;
    case 11:
      switch (m_op -> tokentype)
	{
	case CONDITIONAL:
	case COLON:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
    case 12:
      switch (m_op -> tokentype)
	{
	case EQ:
	case ASR_ASSIGN:
	case ASL_ASSIGN:
	case PLUS_ASSIGN:
	case MINUS_ASSIGN:
	case MULT_ASSIGN:
	case DIV_ASSIGN:
	case BIT_AND_ASSIGN:
	case BIT_OR_ASSIGN:
	case BIT_XOR_ASSIGN:
	  retval = TRUE;
	  break;
	default:
	  break;
	}
      break;
    case 13:
      if (m_op -> tokentype == ARGSEPARATOR)
	retval = TRUE;
      break;
    default:
      break;
    }

  return retval;
}

/*
 *  Perform syntax checking here.  Returns error if the 
 *  message is not an operator or an operand is invalid
 *  for the operator.
 */

int _rt_operands (MESSAGE_STACK messages, int op_ptr, int *op1_ptr, 
		  int *op2_ptr, int stack_top, int stack_start) {

  int i;
  MESSAGE *m, *m_op;

  m_op = messages[op_ptr];

  if (!IS_C_OP (m_op -> tokentype))
    return ERROR;

  for (i = op_ptr + 1, *op1_ptr = ERROR; 
       (i <= stack_start) && (*op1_ptr == ERROR); i++) {

    m = messages[i];

    if (m -> tokentype == WHITESPACE)
      continue;

    if ((IS_C_OP_TOKEN_NOEVAL (m -> tokentype)) &&
	!IS_C_UNARY_MATH_OP (m_op -> tokentype)) {
      return ERROR;
    }

    if (m -> tokentype == NEWLINE) continue;

    *op1_ptr = i;
  }

  for (i = op_ptr - 1, *op2_ptr = ERROR; 
       (i >= stack_top) && (*op2_ptr == ERROR); i--) {

    m = messages[i];

    if (m -> tokentype == WHITESPACE)
      continue;

    *op2_ptr = i;
  }

  return FALSE;
}

int _rt_unary_operand (MESSAGE_STACK messages, int op_ptr, int *op1_ptr,
		       int stack_top, int stack_start) {
  int i;
  MESSAGE *m;

  for (i = op_ptr - 1, *op1_ptr = ERROR; 
       (i <= stack_start) && (*op1_ptr == ERROR); i--) {

    m = messages[i];

    if (m -> tokentype == WHITESPACE)
      continue;

    if (m -> tokentype == NEWLINE) continue;

    *op1_ptr = i;
  }

  if (*op1_ptr == ERROR)
    return ERROR;

  return SUCCESS;
}

/*
 *  Operations for logical negation (!) and bit comp (~) operators.
 *  If we encounter a pointer, treat it as an integer until we
 *  need to get this to work on 64-bit systems.
 */

OBJECT *_rt_unary_math (MESSAGE_STACK messages, int op_ptr, 
			OBJECT *operand_value) {
  VAL value;
  OBJECT *result_object = NULL;    /* Avoid a warning. */
  int int_r, ptr_r;
  long long int longlong_r;
  double double_r;
  long double longdouble_r;
  char buf[MAXMSG];

  numeric_value (operand_value -> __o_value, &value);

  switch (messages[op_ptr] -> tokentype) 
    {
    case LOG_NEG:
      switch (value.type)
	{
	case INTEGER_T:
	  int_r = ! value.value.i;
	  sprintf (buf, "%d", int_r);
	  result_object = __ctalkCreateObjectInit ("result",
						      INTEGER_CLASSNAME,
						      INTEGER_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	case LONG_T:
	  int_r = ! value.value.l;
	  sprintf (buf, "%d", int_r);
	  result_object = __ctalkCreateObjectInit ("result",
						      INTEGER_CLASSNAME,
						      INTEGER_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	case LONGLONG_T:
	  longlong_r = ! value.value.ll;
	  sprintf (buf, "%lld", longlong_r);
	  result_object = 
	    __ctalkCreateObjectInit ("result",
					LONGINTEGER_CLASSNAME,
					LONGINTEGER_SUPERCLASSNAME,
					LOCAL_VAR, buf);
	  break;
	case DOUBLE_T:
	case FLOAT_T:
	  double_r = ! value.value.d;
# if defined (__sparc__) && defined (__svr4__)
	  sprintf (buf, "%f", double_r);
#else
	  sprintf (buf, "%lf", double_r);
#endif
	  result_object = __ctalkCreateObjectInit ("result",
						      FLOAT_CLASSNAME,
						      FLOAT_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	case LONGDOUBLE_T:
	  longdouble_r = ! value.value.ld;
	  sprintf (buf, "%Lf", longdouble_r);
	  result_object = __ctalkCreateObjectInit ("result",
						      FLOAT_CLASSNAME,
						      FLOAT_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	case PTR_T:
	  ptr_r = ! value.value.ptr;
	  sprintf (buf, "%d", ptr_r);
	  result_object = __ctalkCreateObjectInit ("result",
						      INTEGER_CLASSNAME,
						      INTEGER_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	default:
	  _warning ("Invalid operand in _rt_unary_math ().\n");
	  break;
	}
      break;
    case BIT_COMP:
      switch (value.type)
	{
	case INTEGER_T:
	  int_r = ~ value.value.i;
	  sprintf (buf, "%d", int_r);
	  result_object = __ctalkCreateObjectInit ("result",
						      INTEGER_CLASSNAME,
						      INTEGER_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	case LONG_T:
	  int_r = ~ value.value.l;
	  sprintf (buf, "%d", int_r);
	  result_object = __ctalkCreateObjectInit ("result",
						      INTEGER_CLASSNAME,
						      INTEGER_SUPERCLASSNAME,
						      LOCAL_VAR, buf);
	  break;
	case LONGLONG_T:
	  longlong_r = ~ value.value.ll;
	  sprintf (buf, "%lld", longlong_r);
	  result_object = 
	    __ctalkCreateObjectInit ("result",
				     LONGINTEGER_CLASSNAME,
				     LONGINTEGER_SUPERCLASSNAME,
				     LOCAL_VAR, buf);
	  break;
	default:
	  _warning ("Invalid operand in _rt_unary_math ().\n");
	  break;
	}
      break;
    default:
      _warning ("Undefined operator in _rt_unary_math ().\n");
      result_object = NULL;
      break;
    } /* switch (messages[op_ptr] -> tokentype) */

  return result_object;
}
