/* $Id: typecast.c,v 1.5 2009/02/28 19:18:26 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.
*/

/*
 *  Type casts and typedefs.
 */

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

extern CVAR *typedefs;           /* Declared in rt_cvar.c. */
extern CVAR *typedefs_ptr;

extern I_PASS interpreter_pass;  /* Declared in rtinfo.c.  */

int __ctalkRegisterCTypedef (char *type, 
			     char *qualifier,
			     char *qualifier2,
			     char *qualifier3, 
			     char *qualifier4,
			     char *storage_class,
			     char *name,
			     int n_derefs,
			     int attrs,
			     int is_unsigned,
			     int scope) {
  CVAR *c;

  _new_cvar (CVARREF(c));

  if (type) strcpy (c -> type, type);
  if (qualifier) strcpy (c -> qualifier, qualifier);
  if (qualifier2) strcpy (c -> qualifier2, qualifier2);
  if (qualifier3) strcpy (c -> qualifier3, qualifier3);
  if (qualifier4) strcpy (c -> qualifier4, qualifier4);
  if (storage_class) strcpy (c -> storage_class, storage_class);
  if (name) strcpy (c -> name, name);
  c -> n_derefs = n_derefs;
  c -> attrs = attrs;
  c -> is_unsigned = is_unsigned;
  c -> scope = scope;

  if (!typedefs_ptr) {
    typedefs = typedefs_ptr = c;
  } else {
    typedefs_ptr -> next = c;
    c -> prev = typedefs_ptr;
    typedefs_ptr = c;
  }

  return SUCCESS;
}

int typecast_states[] = {
  OPENPAREN, LABEL,          0,
  LABEL,     LABEL,          0,
  LABEL,     ASTERISK,       0,
  LABEL,     CLOSEPAREN,     0,
  ASTERISK,  ASTERISK,       0,
  ASTERISK,  CLOSEPAREN,     0,
  OPENPAREN, CTYPE,          0,
  OPENPAREN, OPENPAREN,      0,
  CTYPE,     CTYPE,          0,
  CTYPE,     LABEL,          0,
  CLOSEPAREN,CHAR,           0,
  CLOSEPAREN,LABEL,          0,
  CLOSEPAREN,LITERAL,        0,
  CLOSEPAREN,LITERAL_CHAR,   0,
  CLOSEPAREN,INTEGER,        0,
  CLOSEPAREN,FLOAT,          0,
  CLOSEPAREN,LONG,           0,
  CLOSEPAREN,LONGLONG,       0,
  CLOSEPAREN,METHODMSGLABEL, 0,
  CLOSEPAREN,OPENPAREN,      0,
  CTYPE, CLOSEPAREN,         0,
  -1, 0, 0
};

#define TYPECAST_STATE_COLS 3

#define TYPECAST_STATE(x) (__check_state ((x), messages, \
              typecast_states, TYPECAST_STATE_COLS))

int __rt_is_typecast (MESSAGE_STACK messages, int start, int end) {

  int i,
    state,
    n_parens,
    lookahead,
    cast_expr_paren_lvl;
  MESSAGE *m_tok;

  for (i = start, n_parens = 0, cast_expr_paren_lvl = -1; i >= end; i--) {

    m_tok = messages[i];

    if (M_ISSPACE (m_tok)) continue;

    if ((state = TYPECAST_STATE (i)) == ERROR)
      return FALSE;

    switch (m_tok -> tokentype)
      {
      case LABEL:
      case CTYPE:
	if (!is_c_data_type (m_tok -> name) &&
	    !__get_rt_typedef (m_tok -> name))
	  return FALSE;
	break;
      case OPENPAREN:
	++n_parens;
	if ((lookahead = __ctalkNextLangMsg (messages, i, 
					     _get_e_message_ptr ()))
	    != ERROR) {
	  if ((M_TOK(messages[lookahead]) == CTYPE) ||
	      (M_TOK(messages[lookahead]) == LABEL)) {
	    if (is_c_data_type (M_NAME(messages[lookahead])) ||
		__get_rt_typedef (M_NAME(messages[lookahead])))
	      cast_expr_paren_lvl = n_parens;
	  }
	}
	break;
      case CLOSEPAREN:
	if (n_parens == cast_expr_paren_lvl) return TRUE;
	--n_parens;
	break;
      default:
	break;
      }
  }

  return FALSE;
}

int typecast_is_pointer (MESSAGE_STACK messages, int start, int end) {
  int i,
    n_derefs;
  
  for (i = start, n_derefs = 0; i >= end; i--) 
    if (messages[i] -> tokentype == ASTERISK)
      ++n_derefs;
  return n_derefs;
}

/*
 *  For now, it should be sufficient to cast all pointer expressions
 *  to a Symbol.  If the result of the expression is an Integer or
 *  LongInteger, use that as the symbol address.  If the result is a
 *  Character string or array, use the address of the first element.
 *  All other Classes that correspond to derived types should have a
 *  Symbol as the class of the value instance variable.
 */
int typecast_ptr_expr (MESSAGE_STACK messages, int cast_expr_start, 
		       int cast_expr_end) {
  char expr[MAXMSG],
    valbuf[MAXMSG];
  int i, 
    expr_end,
    cast_start,
    cast_end,
    lookahead,
    scope;
  OBJECT *e_result, 
    *e_result_val, 
    *recv_object,
    *recv_class_object,
    *ptr_result = NULL;       /* Avoid a warning. */

  /*
   *  Find the parentheses of the cast.
   */
  for (i = cast_expr_start; i >= cast_expr_end; i--) {
    if (M_TOK(messages[i]) == OPENPAREN) {
      if ((lookahead = __ctalkNextLangMsg (messages, i, 
					   __rt_get_stack_end (messages)))
	  == ERROR) {
	_warning ("End of stack in typecast_ptr_expr.");
      }
      if ((M_TOK(messages[lookahead]) == LABEL) ||
	  (M_TOK(messages[lookahead]) == CTYPE)) {
	if (is_c_data_type (M_NAME(messages[lookahead])) ||
	    __get_rt_typedef (M_NAME(messages[lookahead]))) {
	  cast_start = i;
	  cast_end = __ctalkMatchParen (messages, cast_start,
					__rt_get_stack_end (messages));
	}
      }
    }
  }

  strcpy (expr, 
	  collect_typecast_expr (messages, 
				 __ctalkNextLangMsg (messages, cast_end,
				     __rt_get_stack_end (messages)),
				 &expr_end));

  recv_object = __ctalk_receiver_pop_deref ();
  __ctalk_receiver_push_ref (recv_object);
  if (recv_object) {
    recv_class_object = recv_object -> __o_class;
    scope = recv_object -> scope;
  } else {
    recv_class_object = __ctalk_get_object (INTEGER_CLASSNAME,
				       INTEGER_SUPERCLASSNAME);
    scope = RECEIVER_VAR;
  }
  if ((e_result = eval_expr (expr, recv_class_object, NULL, NULL, scope, 
			     FALSE)) != NULL) {
    if ((e_result_val = __ctalkGetInstanceVariable (e_result, "value", TRUE))
	!= NULL) {
      switch (_lextype (e_result_val -> __o_value))
	{
	case INTEGER_T:
	case LONG_T:
	case LONGLONG_T:
	case LITERAL_CHAR_T:
	  strcpy (valbuf, hex_from_numeric_val (e_result_val -> __o_value));
	  ptr_result = __ctalkCreateObjectInit ("result",
						   SYMBOL_CLASSNAME,
						   SYMBOL_SUPERCLASSNAME,
						   LOCAL_VAR,
						   valbuf);
	  break;
	case LITERAL_T:
	  break;
	case PTR_T:
	  break;
	case DOUBLE_T:
	default:
	  __ctalkExceptionInternal (NULL, ptr_conversion_x, "");
	  ptr_result = __ctalkCreateObjectInit ("result", 
					  SYMBOL_CLASSNAME,
					  SYMBOL_SUPERCLASSNAME, 
					   LOCAL_VAR, "0x0");
	  break;
	}
    }
  }

  for (i = cast_expr_start; i >= expr_end; i--) {
    if (messages[i] && IS_MESSAGE(messages[i])) {
      ++messages[i] -> evaled;
      if (messages[i]->obj)
	messages[i]->value_obj = ptr_result;
      else
	messages[i]->obj = ptr_result;
    }
  }

  return SUCCESS;
}

/*
 *  TODO - Most scalar typecasts can be elided, until we have a 
 *  case where the value alone doesn't translate directly to an
 *  object.
 */
int typecast_value_expr (MESSAGE_STACK messages, int cast_start, 
		       int cast_end) {
  int i;
  for (i = cast_start; i >= cast_end; i--) {
    messages[i] -> attrs |= TOK_IS_TYPECAST_EXPR;
    ++(messages[i] -> evaled);
    ++(messages[i] -> output);
  }
  return FALSE;
}

char *collect_typecast_expr (MESSAGE_STACK messages, int expr_start,
			     int *expr_end) {
  static char buf[MAXMSG];
  int stack_end,
    lookahead,
    n_parens,
    n_blocks;

  stack_end = __rt_get_stack_end (messages);

  for (*expr_end = expr_start, *buf = 0, n_blocks = 0, n_parens = 0; 
       *expr_end > stack_end; --(*expr_end)) {

    strcat (buf, messages[*expr_end] -> name);

    if ((lookahead = __ctalkNextLangMsg (messages, *expr_end, stack_end))
	== ERROR)
      return buf;

    switch (messages[lookahead] -> tokentype)
      {
      case OPENPAREN:
	++n_parens;
	break;
      case CLOSEPAREN:
	--n_parens;
	if (n_parens < 0) return buf;
	break;
      case ARRAYOPEN:
	++n_blocks;
	break;
      case ARRAYCLOSE:
	--n_blocks;
	if (n_blocks < 0) return buf;
	break;
      case ARGSEPARATOR:
      case SEMICOLON:
	return buf;
	break;
      default:
	break;
      }
  }
  return buf;
}

CVAR *__ctalkGetTypedef (char *name) {
  CVAR *__c;
  for (__c = typedefs; __c; __c = __c -> next) {
    if (!strcmp (__c -> name, name))
      return __c;
  }
  return NULL;
}
