/* $Id: rt_expr.c,v 1.11 2008/11/06 20:54:36 kiesling Exp $ */

/*
  This file is part of ctalk.
  Copyright  2005-2008  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 <string.h>
#include <stdlib.h>
#include <errno.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"
#include "typeof.h"
#include "rtinfo.h"

/*
 *  The expression parser stack.
 */

static int expr_parser_lvl = 0;

static EXPR_PARSER *expr_parsers[MAXARGS+1];
static int expr_parser_ptr = MAXARGS + 1;

int __get_expr_parser_ptr (void) {
  return expr_parser_ptr;
}

int __get_expr_parser_level (void) {
  return expr_parser_lvl;
}

void __push_expr_parser (EXPR_PARSER *p) {
  if (expr_parser_ptr == 0)
    _error ("__push_expr_parser: stack overflow.");
  expr_parsers[--expr_parser_ptr] = p;
}

EXPR_PARSER *__pop_expr_parser (void) {
  EXPR_PARSER *p;
  if (expr_parser_ptr > MAXARGS)
    _error ("__pop_expr_parser: stack underflow.");
  p = expr_parsers[expr_parser_ptr] ;
  expr_parsers[expr_parser_ptr++] = NULL;
  return p;
}

EXPR_PARSER *__current_expr_parser (void) {
  return expr_parsers[expr_parser_ptr];
}

EXPR_PARSER *__expr_parser_at (int i) {
  return expr_parsers[i];
}

EXPR_PARSER *__new_expr_parser (void) {

  EXPR_PARSER *p;

  if ((p = (EXPR_PARSER *)calloc (1, sizeof (EXPR_PARSER))) == NULL)
    _error ("__new_expr_parser: %s.", strerror (errno));

  p -> lvl = expr_parser_lvl;

  return p;
}

void __delete_expr_parser (EXPR_PARSER *p) {
  free (p);
}

/*
 *  Expression message stack.
 */

static MESSAGE *e_messages[P_MESSAGES+1]; /* Eval message stack.        */
static int e_message_ptr = P_MESSAGES;    /* Eval stack pointer.        */

int _get_e_message_ptr (void) {
  return e_message_ptr;
}

MESSAGE_STACK _get_e_messages (void) {
  return e_messages;
}

Boolean is_constant_rcvr;
Boolean is_arg_expr;

OBJECT *__ctalkEvalExpr (char *s) {
  OBJECT *__recv_obj, *__recv_class, *arg_object = NULL;
  int scope;
  I_EXCEPTION *ex;

  /*
   *   Find the receiver depending on whether it is a class
   *   object, a dictionary object, or a method parameter.
   *
   *   We have to push the receiver again to make the stack
   *   the same for _eval_expr () as when called from rt_arg ().
   *
   *   If there's no receiver, then the receiver class defaults
   *   to, "Integer."
   */

  __recv_obj = __ctalk_receiver_pop_deref ();
  __ctalk_receiver_push_ref (__recv_obj);
  __ctalk_receiver_push_ref (__recv_obj);

  if (__recv_obj) {
    if ((__recv_class = __recv_obj->__o_class) == NULL) {
      _error ("__ctalkEvalExpr: : Undefined receiver class %s.\n",
	      __recv_obj -> __o_classname);
    }
    scope = __recv_obj -> scope;
  } else {
    __recv_class = __ctalk_get_object (INTEGER_CLASSNAME,
				       INTEGER_SUPERCLASSNAME);
    scope = RECEIVER_VAR;
  }

  arg_object = eval_expr (s, __recv_class, NULL, scope);

  /*
   *  Critical errors from an expression should be handled by
   *  the caller of __ctalkEvalExpr ().
   */
  if (((ex = __ctalkTrapExceptionInternal (NULL)) != NULL) &&
      (ex -> is_critical == 0)) {
    __ctalkHandleRunTimeExceptionInternal ();

    if (!arg_object || !IS_OBJECT (arg_object)) {
      arg_object = __ctalkCreateObjectInit ("result", INTEGER_CLASSNAME,
					       INTEGER_SUPERCLASSNAME,
					       scope, "0");
    }
  }

  __ctalk_receiver_pop_deref ();

  delete_method_arg_cvars ();

  if (!strcmp (arg_object -> __o_name, NULLSTR)) {
    __ctalkDeleteObjectInternal (arg_object);
    arg_object = NULL;
  }

  return arg_object;
}

int e_message_push (MESSAGE *m) {
  if (e_message_ptr == 0) {
    _warning (_("e_message_push: stack overflow.\n"));
    return ERROR;
  }
#ifdef MACRO_STACK_TRACE
  debug (_("e_message_push %d. %s."), e_message_ptr, m -> name);
#endif
  e_messages[e_message_ptr--] = m;
  return e_message_ptr + 1;
}

MESSAGE *e_message_pop (void) {
  MESSAGE *m;
  if (e_message_ptr == P_MESSAGES) {
    _warning (_("e_message_pop: stack underflow.\n"));
    return (MESSAGE *)NULL;
  }
#ifdef MACRO_STACK_TRACE
  debug (_("e_message_pop %d. %s."), e_message_ptr, 
	 e_messages[e_message_ptr+1] -> name);
#endif
  if (e_messages[e_message_ptr + 1] && 
      IS_MESSAGE(e_messages[e_message_ptr + 1])) {
    m = e_messages[e_message_ptr + 1];
    e_messages[++e_message_ptr] = NULL;
    return m;
  } else {
    e_messages[++e_message_ptr] = NULL;
    return NULL;
  }
}

/*
 *  Return the method's params table entry for the next argument, 
 *  or NULL.
 */

PARAM *has_param_def (METHOD *m) {

  if (!m) return NULL;

  if (m -> n_args >= m -> n_params) {
#ifdef DEBUG_DYNAMIC_C_ARGS
    _warning ("has_param_def (): method %s has no further definitions.\n",
	      m -> name);
#endif
    return NULL;
  }

  return m -> params[m -> n_args];
}

/*
 *   Evaluate an expression. 
 *
 *   NOTE - unlike __ctalk_arg (), __ctalkEvalExpr () 
 *   and __ctalk_arg_expr () call this function 
 *   with METHOD == NULL.
 */

OBJECT *eval_expr (char *s, OBJECT *recv_class, METHOD *method, int scope) {

  int i, j,
    stack_start,
    stack_top,
    aggregate_type_end,
    aggregate_type_start,
    prev_msg_ptr,
    next_msg_ptr,
    prev_msg_ptr_tmp,
    expr_lookahead_ptr,
    fn_tok_ptr,
    precedence = 0,
    matching_paren_ptr,
    c_rcvr_idx,           /* Start token of receiver of compound statements.*/
    x_msg_ptr;            /* Message that caused an exception. */
  MESSAGE *m = NULL,      /* These must be initialized.        */
    *m_prev_msg = NULL;
  OBJECT *result_obj, *value_obj, *arg_class, *val_class, *subexpr_result,
    *subexpr_rcvr, *param_class;
  OBJECT *op1_subexpr_result, *op2_subexpr_result;
  OBJECT *(*cfunc)();
  CVAR *c;
  char namebuf[MAXLABEL],
    x_text[MAXMSG];
  EXPR_PARSER *p;
  I_EXCEPTION *ex;
  char tmpbuf[MAXMSG];
  METHOD *e_method;

  if (!s || !strlen (s)) 
    return null_result_obj (method, scope);

  /*
   *  So eval_expr doesn't need to backtrack over leading whitespace.
   */
  strcpy (tmpbuf, trimstr(s));

  ++expr_parser_lvl;
  p = __new_expr_parser ();
  p -> rcvr_frame_top = __ctalkGetReceiverPtr ();
  p -> msg_frame_top = e_message_ptr;
  __push_expr_parser (p);

  result_obj = value_obj = arg_class = val_class = subexpr_result =
    subexpr_rcvr = param_class = NULL;
  op1_subexpr_result = op2_subexpr_result = NULL;

  aggregate_type_start = aggregate_type_end = c_rcvr_idx = -1;

  /*
   *  The code below still chokes on empty expressions, so
   *  just create an object and return.  
   */

  if (!strlen (tmpbuf))
    return null_result_obj (method, scope);

  x_msg_ptr = stack_start = e_message_ptr;

  stack_top = tokenize (e_message_push, tmpbuf);

  for (i = stack_start; i >= stack_top; i--) {

    m = e_messages[i];

    switch (m -> tokentype)
      {
      case LABEL:

	if ((prev_msg_ptr_tmp = __ctalkPrevLangMsg (e_messages, i, 
	    expr_parsers[expr_parser_ptr]->msg_frame_top)) != ERROR)
	  m_prev_msg = e_messages[prev_msg_ptr_tmp];
	/*
	 *  Handle an an array or struct member.  If we find 
	 *  the declaration end here, we don't need to 
	 *  count opening and closing blocks below.
	 *  However, we do need to check for a method 
	 *  message after a closing block, so we should do
	 *  it here.
	 *
	 *  If, "aggregate_type_start," and, "aggregate_type_end,"
	 *  are not -1, they are used below to set the message
	 *  object for the entire aggregate expression, and 
	 *  they are reset after the expression tokens' object
	 *  is set.
	 *
	 *  TO DO - Evaluate array element indexes that are
	 *  expressions.
	 */
	if ((aggregate_type_end = 
	     is_aggregate_type (e_messages, i, stack_top, namebuf)) != -1) {
	  aggregate_type_start = i;
	  i = aggregate_type_end;
	} else {
	  strcpy (namebuf, m -> name);
	}

	/*
	 *  Find out if the label refers to an object or a class.  If
	 *  not, create the object.  If the label refers to a C variable,
	 *  use that as the object's value.
	 *  
	 *  An argument object that represents a C variable has the
	 *  class, "Expr," and the "value," instance variable has 
	 *  the class of the receiver and the value of the C variable.
	 *
	 *  "Self," in an argument, should be the previous receiver pushed
	 *  on the stack, becuase we've pushed the receiver of the method
	 *  to be called. 
	 * 
	 *  In the case where the receiver is a constant expression,
	 *  then we have to use the most immediate receiver.  For the
	 *  moment, we'll simply use the _is_constant_rcvr state, 
	 *  which is set and cleared in __ctalk_arg ()
	 *
	 *  This is also the case where the receiver is a normal object.
	 *  To cope with it, __ctalk_arg () sets is_arg_expr to True.
	 *  This use is experimental until __ctalkEvalExpr () can 
	 *  figure out on its own whether self appears as an lvalue
	 *  or receiver for an entire statement, or as the value in an 
	 *  argument.  __ctalkEvalExpr (), and the functions that call it, 
	 *  should also be able to DTRT if we have an expression like,
	 *  "self subString 1, self length - 1," or,
	 *  "myString subString 1, self length - 1."
	 */
	if (!strcmp (namebuf, "self")) {
	  int rcvr_ptr;

	  rcvr_ptr = __ctalkGetReceiverPtr ();

	  if ((is_constant_rcvr == True) || (is_arg_expr == True))
	    m -> obj = self_expr (NULL);
	  else
	    m -> obj = __ctalk_receivers_at (rcvr_ptr + 2);

	  if (!m -> obj || !IS_OBJECT (m -> obj))
	    _warning ("eval_expr (): \"Self\" not found.\n");

	} else {

	  if (((m -> obj = __ctalk_get_object (namebuf, NULL)) == NULL) &&
	      ((m -> obj = __ctalkGetClass (namebuf)) == NULL)) {

 	    if (__ctalk_isMethod (namebuf, e_messages, i, stack_start, 
				  stack_top)) {
	      /*
	       *  Duplicate check here, in case this gets refactored
	       *  elsewhere later.  Non-label methods should be only
	       *  math ops, handled below.
	       */
	      if (m -> tokentype == LABEL)
		m -> tokentype = METHODMSGLABEL;
 	    } else {
 	      if ((m -> obj = __ctalkFindClassVariable (namebuf, FALSE))
 		  == NULL) {
		if (m_prev_msg && m_prev_msg->obj && 
		    IS_OBJECT(m_prev_msg->obj)&&
		    ((m -> obj = 
		       __ctalkGetInstanceVariable (m_prev_msg -> obj,
						   m -> name, FALSE)) 
		      != NULL)) {
		  m -> attrs = (OBJ_IS_INSTANCE_VAR|OBJ_IS_CLASS_VAR);
		  c_rcvr_idx = prev_msg_ptr_tmp;
		  m_prev_msg -> value_obj = m -> obj;
		  ++m_prev_msg -> evaled;
		  ++m -> evaled;
		} else {
		  if ((c = get_method_arg_cvars (namebuf)) != NULL) {
		    if ((aggregate_type_start != -1) && 
			(aggregate_type_end != -1)) {
		      if (!m -> obj || !IS_OBJECT (m -> obj)) {
			m -> obj = cvar_object (c);
			/*
			 *  TODO - This scope might also be
			 *  used for objects created from
			 *  scratch in cvar_object ().  Also
			 *  used below.
			 */
			if (m -> obj -> scope & CVAR_VAR_ALIAS_COPY)
			  m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
		      }
		      __set_aggregate_obj (e_messages, aggregate_type_start,
					   aggregate_type_end, m -> obj);
		      __resolve_aggregate_method (e_messages,
						  aggregate_type_end);
		      aggregate_type_start = aggregate_type_end = -1;
		    } else {
		      m -> obj = cvar_object (c);
		      if (m -> obj -> scope & CVAR_VAR_ALIAS_COPY)
			m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
		    }
		  } else {/*if ((c = get_method_arg_cvars (namebuf)) != NULL)*/
		    if (need_defined_receiver (e_messages, i, 
					       stack_start, stack_top,
					       recv_class)) {
 		      __ctalkExceptionInternal (NULL, undefined_receiver_x,
 						M_NAME(m));
		    }
		    m -> obj = create_param (namebuf, method,recv_class);
		    __ctalkSetObjectScope (m -> obj, CREATED_PARAM);
		  } /* if ((c = get_method_arg_cvars (namebuf)) != NULL) */
		} /*if (m_prev_msg -> obj && IS_OBJECT (m_prev_msg -> obj)&&*/
	      }
 	    } /* else */
	  } /* if (((m -> obj = __ctalk_get_object (namebuf, NULL)) == NULL) */
	}
	/*
	 * If the label is a class variable in an expression like,
	 *  <class> <classvariable>...  Then set the attributes
	 *  and receiver here.
	 */
	if (m_prev_msg && IS_MESSAGE(m_prev_msg) &&
	    M_OBJ(m_prev_msg) &&
	    IS_CLASS_OBJECT(m_prev_msg->obj) &&
	    M_OBJ(m) && 
	    (m->obj->__o_class == m_prev_msg->obj)) {
	  if (__ctalkIsClassVariableOf(m_prev_msg->obj,
				       m->obj->__o_name)) {
	    m->receiver_obj = m_prev_msg->obj;
	    m->attrs |= OBJ_IS_CLASS_VAR;
	  }
	}
	
	/*
	 *  The check for a data type should be sufficient to 
	 *  prevent warnings in casts.
	 */
	if (!m -> obj && (m -> tokentype != METHODMSGLABEL) &&
	    !is_c_data_type (m -> name)) {
	  __ctalkExceptionInternal (m, undefined_label_x, namebuf);
	  x_msg_ptr = i;
	  goto cleanup;
	}	  
	++m -> evaled;
	break;
      case LITERAL:
	if ((arg_class = __ctalkGetClass ("String")) == NULL) {
	  _warning ("%s: Class \"String\" undefined.\n",__argvFileName ());
	  return NULL;
	} else {
	  m -> obj = 
	    create_param (unescape_str_quotes (m -> name), method, arg_class);
 	  __ctalkSetObjectScope (m -> obj, scope);
	  m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
	}
	++m -> evaled;
	break;
      case LITERAL_CHAR:
	if ((arg_class = __ctalkGetClass ("Character")) == NULL) {
	  _warning ("%s: Class \"Character\" undefined.\n",__argvFileName ());
	  return NULL;
	} else {
	  m -> obj = create_param (m -> name, method, arg_class);
 	  __ctalkSetObjectScope (m -> obj, scope);
	  m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
	}
	++m -> evaled;
	break;
      case INTEGER:
      case LONG:
	if ((arg_class = __ctalkGetClass ("Integer")) == NULL) {
	  _warning ("%s: Class \"Integer\" undefined.\n",__argvFileName ());
	  return NULL;
	} else {
	  m -> obj = create_param (m -> name, method, arg_class);
 	  __ctalkSetObjectScope (m -> obj, scope);
	  m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
	}
	++m -> evaled;
	break;
      case LONGLONG:
	if ((arg_class = __ctalkGetClass ("LongInteger")) == NULL) {
_warning ("%s: Class \"LongInteger\" undefined.\n",__argvFileName ());
	  return NULL;
	} else {
	  m -> obj = create_param (m -> name, method, arg_class);
	  __ctalkSetObjectScope (m -> obj, scope);
	  m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
	}
	++m -> evaled;
	break;
      case FLOAT:
	if ((arg_class = __ctalkGetClass ("Float")) == NULL) {
	  _warning ("%s: Class \"Float\" undefined.\n",__argvFileName ());
	  return NULL;
	} else {
	  m -> obj = create_param (m -> name, method, arg_class);
	  __ctalkSetObjectScope (m -> obj, scope);
	  m -> attrs |= TOK_OBJ_IS_CREATED_PARAM;
	}
	++m -> evaled;
	break;
      case SEMICOLON:
      case NEWLINE:
      case CHAR:
      case WHITESPACE:
      case PREPROCESS_EVALED:
      case RESULT:
	/*
	 *  Characters that are not operators can be a character type.
	 *  We can fake our way through here if Character class isn't
	 *  loaded.
	 */
	m -> obj = __ctalkCreateObjectInit (m -> name, 
					       CHARACTER_CLASSNAME,
					       CHARACTER_SUPERCLASSNAME, 
					       scope, m -> name);
	++m -> evaled;
	break;
      default:
	break;
      }
  }

  re_eval:  for (i = stack_start; i >= stack_top; i--) {

    m = e_messages[i];

    if ((aggregate_type_end = 
	 is_aggregate_type (e_messages, i, stack_top, namebuf)) != -1) {
      int j;
      for (j = i; j >= aggregate_type_end; j--)
	++e_messages[j] -> evaled;
      i = aggregate_type_end;
    } else {
      strcpy (namebuf, m -> name);
    }

    switch (m -> tokentype)
      {
      case SEMICOLON:
	++m -> evaled;
	goto done;
	break;
      case CHAR:
      case LITERAL_CHAR:
      case LITERAL:
      case INTEGER:
      case LONG:
      case LONGLONG:
      case DOUBLE:
      case PREPROCESS_EVALED:
      case RESULT:
	break;
      case WHITESPACE:
      case NEWLINE:
	break;
      case METHODMSGLABEL:
	if (m -> evaled <= 1) {
	  if ((prev_msg_ptr = 
	       __ctalkPrevLangMsg (e_messages, i, stack_start)) != ERROR) {
	    /*
	     *  Scan back until we find the first token
	     *  with the receiver object.
	     */
	    while (1) {
	      if ((prev_msg_ptr + 1) > stack_start)
		break;
	      if (M_VALUE_OBJ (e_messages[prev_msg_ptr + 1])
		  == M_VALUE_OBJ (e_messages[prev_msg_ptr]))
		prev_msg_ptr++;
	      else
		break;
	    }
	    m_prev_msg = e_messages[prev_msg_ptr];
	    if (m_prev_msg && m_prev_msg -> obj && 
		IS_OBJECT (m_prev_msg -> obj)) {
	      int j, last_arg_ptr = -1;
	      OBJECT *e_class = NULL, /* These must be re-initialized. */
		*e_result, *m_prev_value_obj,
		*m_prev_super_obj;
	      OBJECT *n_result = NULL;
	      OBJECT *e_result_dup = NULL;
	      Boolean is_arg_value = False;
	      /*
	       *  We want to use the value object to get the
	       *  actual class of a receiver variable and
	       *  the method. Ugh.
	       */
	      if (M_OBJ_IS_VAR(m_prev_msg)) 
		m_prev_value_obj = M_VALUE_OBJ(m_prev_msg) -> instancevars;
	      else
		m_prev_value_obj = M_VALUE_OBJ(m_prev_msg);
	      m_prev_msg -> attrs |= TOK_VALUE_OBJ_IS_RECEIVER;
	      if ((e_result = 
		   __ctalkGetInstanceVariable (m_prev_value_obj, 
					       M_NAME(m),
					       FALSE)) != NULL) {
		/*
		 *  If the next message is a method, then 
		 *  create a complete object - hopefully 
		 *  only ARG_VAR scope is needed, with
		 *  no references from here.
		 */
		int __next_tok_idx;
		if ((__next_tok_idx = 
		     __ctalkNextLangMsg (e_messages, i,
					 stack_top)) != ERROR) {
		  if (__ctalk_isMethod(M_NAME(e_messages[__next_tok_idx]),
				       e_messages, __next_tok_idx,
				       stack_start, stack_top)) {
		    e_result = 
		      __ctalkCreateObjectInit 
		      (e_result -> __o_name,
		       e_result -> __o_classname,
		       e_result -> __o_superclassname,
		       ARG_VAR, e_result -> __o_value);
		  }
		}
		last_arg_ptr = i;
		goto set_method_expr_value;
	      }
	      if ((e_result = 
		   __ctalkGetClassVariable (m_prev_value_obj, 
					       M_NAME(m),
					       FALSE)) != NULL) {
		last_arg_ptr = i;
		goto set_method_expr_value;
	      }
	      if (M_VALUE_OBJ_IS_SUPERCLASS(m_prev_msg)) {
		OBJECT *__r;
		if ((__r = __ctalkRtGetMethod() -> rcvr_class_obj) != NULL)
		  m_prev_super_obj = __r -> __o_superclass;
	      }
	      if ((e_method = 
		   __ctalkFindMethodByName 
		   ((M_VALUE_OBJ_IS_SUPERCLASS(m_prev_msg) ? 
		    &m_prev_super_obj : &m_prev_value_obj),
		   m -> name, FALSE)) == NULL) {
		sprintf (x_text, 
			 "eval_expr: (): Object %s (Class %s) does not understand message %s.\n",
			 m_prev_msg -> obj -> __o_name,
			 m_prev_msg -> obj -> __o_classname,
			 m -> name);
		__ctalkExceptionInternal (m, undefined_method_x, x_text);
		x_msg_ptr = i;
		goto done;
	      }
	      if (!e_class) 
		e_class = m_prev_msg->obj->__o_class;
	      if (e_method) {
		m -> receiver_obj = M_VALUE_OBJ (m_prev_msg);
 		last_arg_ptr = __rt_method_args (e_method, e_messages,
					 stack_start, e_message_ptr, i);
		if (!IS_CLASS_OBJECT(m_prev_msg->obj)) {
		  if ((e_class = m_prev_msg -> obj -> __o_class) == NULL) 
		    _warning ("eval_expr: Class %s not found.\n",
			      m_prev_msg -> obj -> __o_classname);
		}
		   
		expr_parsers[expr_parser_ptr] -> e_method = e_method;
 		cfunc = e_method -> cfunc;

		__save_rt_info (m_prev_value_obj, e_class, NULL, 
				e_method, cfunc, False);

		if (M_VALUE_OBJ_IS_SUPERCLASS(m_prev_msg)) {
		  __ctalk_receiver_push_ref (m_prev_msg->receiver_obj);
		} else {
		  __ctalk_receiver_push_ref (m_prev_value_obj);
		}
 		e_result = (*cfunc)();
		/*
		 *  NULL result gets filled in below.
		 *  TODO - Make sure that IS_OBJECT 
		 *  does not segfault no matter what
		 *  segment the pointer is in.  Issue
		 *  with older systems/compilers.
		 */
		if (e_result && !IS_OBJECT(e_result)) 
		  e_result = NULL;

 		__ctalk_receiver_pop_deref ();

		__restore_rt_info ();

		expr_parsers[expr_parser_ptr] -> e_method = NULL;

		for (j = 0; j < e_method -> n_args; j++) {
		  OBJECT *__arg_object;
		  int i_1, __have_obj_ref = FALSE;
		  char __obj_ref_str[MAXLABEL];
		  __arg_object = __ctalk_arg_pop ();
		  __ctalk_arg_push (__arg_object);
		  if (__arg_object -> instancevars &&
		      OBJ_REF_STR(__arg_object->instancevars->__o_value)) {
		    __have_obj_ref = TRUE;
		    strcpy (__obj_ref_str, 
			    __arg_object->instancevars->__o_value);
		  } else {
		    __have_obj_ref = FALSE;
		  }
 		  if (!__ctalk_arg_cleanup ()) {
		    for (i_1 = stack_start; i_1 >= stack_top; i_1--) {
		      if (e_messages[i_1] -> obj == __arg_object) {
			if ((e_messages[i_1] -> attrs & 
			    TOK_OBJ_IS_CREATED_PARAM) &&
			    __have_obj_ref) {
			  cvar_ref_delete (__obj_ref_str);
/* 			  cvar_object_ref_cleanup(e_messages, i_1, */
/* 						  stack_start, */
/* 						  stack_top); */
			}
			e_messages[i_1] -> obj = NULL;
		      }
		      if (e_messages[i_1] -> value_obj == __arg_object) {
			if ((e_messages[i_1] -> attrs & 
			    TOK_OBJ_IS_CREATED_PARAM) &&
			    __have_obj_ref) {
			  cvar_ref_delete (__obj_ref_str);
/* 			  cvar_object_ref_cleanup(e_messages, i_1, */
/* 						  stack_start, */
/* 						  stack_top); */
			}
			e_messages[i_1] -> value_obj = NULL;
		      }
		    }
		  } else {
		    for (i_1 = stack_start; i_1 >= stack_top; i_1--) {
		      if (e_messages[i_1] -> obj == __arg_object) {
			if (e_messages[i_1] -> attrs & 
			    TOK_OBJ_IS_CREATED_PARAM) {
/* 			  cvar_object_ref_cleanup(e_messages, i_1, */
/* 						  stack_start, */
/* 						  stack_top); */
			}
			e_messages[i_1] -> obj = NULL;
		      }
		      if (e_messages[i_1] -> value_obj == __arg_object) {
			if (e_messages[i_1] -> attrs & 
			    TOK_OBJ_IS_CREATED_PARAM) {
/* 			  cvar_object_ref_cleanup(e_messages, i_1, */
/* 						  stack_start, */
/* 						  stack_top); */
			}
			e_messages[i_1] -> value_obj = NULL;
		      }
		    }
		  }
		  e_method -> args[j] = NULL;
		}
		e_method -> n_args = 0;

		/*
		 *  If the method returns NULL, create an
		 *  object that evaluates to False.
		 */

		if (!e_result)
		  e_result = null_result_obj (e_method, scope);

		/*
		 *  If a method returns an instance variable, 
		 *  then copy it to a new instance variable 
		 *  object.  Then create a new object, and
		 *  ad the instance variable.
		 *
		 *  We should not simply add a reference to the
		 *  existing instance variable.  We would need 
		 *  to make certain that the object is de-reffed 
		 *  in all cases of return from methods and 
		 *  expressions.
		 *
		 *  This might not be correct in all cases, but 
		 *  the result here should be consistent with
		 *  other result objects.
		 */
		/*
		 *  Try to find an actual object for the method
		 *  return value.
		 *
		 *  TO DO - This should be done in __ctalk_method
		 *  also.
		 */
		if (!e_result -> instancevars && (is_arg_value == False)) {
		  if (e_result == m_prev_value_obj -> instancevars) {
		    /* self */
		    e_result = m_prev_value_obj;
		  } else {
		    if (e_result -> __o_p_obj) {
		      /* Instance variable - return the complete object. */
		      e_result = e_result -> __o_p_obj;
		    } else {
		      if (e_result && IS_OBJECT (e_result)) {
			if (__ctalkIsInstanceVariableOf 
			    (e_result -> __o_class, 
			     e_result -> __o_name)) {
			  __ctalkCopyObject (OBJREF(e_result), 
					     OBJREF(e_result_dup));
			  n_result = 
			    __ctalkCreateObjectInit ("result", 
						   e_result -> __o_classname,
					   e_result -> __o_superclassname,
						   scope,
						   e_result -> __o_value);
			  __ctalkSetObjectValueVar (n_result, 
						  e_result -> __o_value);
			  __ctalkAddInstanceVariable (n_result, 
						    e_result_dup -> __o_name,
						    e_result_dup);
			/*
			 *  CtalkAddInstanceVariable copies a value
			 *  variable if one does not exist, and simply 
			 *  adds other variables. So we do not
			 *  need e_result_dup in that case.
			 *  That case should be simplified.
			 */
			  if (!strcmp (e_result_dup -> __o_name, "value")) 
			    __ctalkDeleteObject (e_result_dup);
		      
			  __objRefCntSet (OBJREF(n_result), e_result -> nrefs);
			  __ctalkDeleteObjectInternal (e_result);
			  e_result = n_result;
			}
		      } else {
			/*
			 *  TODO - this should be an exception.
			 */
			e_result = null_result_obj (e_method, scope);
		      }
		    }
		  }
		}

		/*
		 *  If the method message is part of a compound
		 *  statement, use the receiver index.  Otherwise, 
		 *  the receiver is the previous token.
		 */
	      set_method_expr_value:
		for (j = ((c_rcvr_idx != -1) ? c_rcvr_idx : prev_msg_ptr); 
		     j >= last_arg_ptr; j--) {
		  if (e_messages[j] -> value_obj && 
		      IS_OBJECT(e_messages[j] -> value_obj)) {
		    int j_1;
		    OBJECT *__s;
		    __s = e_messages[j]->value_obj;
		    _cleanup_temporary_objects(e_messages[j] -> value_obj,
					       e_result, NULL);
		    for (j_1 = j; j_1 >= last_arg_ptr; j_1--) {
		      if (e_messages[j_1] -> value_obj == __s) 
			e_messages[j_1] -> value_obj = NULL;
		      if (e_messages[j_1] -> obj == __s) 
			e_messages[j_1] -> obj = NULL;
		    }
		  }
 		  e_messages[j]->value_obj = e_result;
 		  ++e_messages[j]->evaled;
 		}
		if ((next_msg_ptr = 
		     __ctalkNextLangMsg (e_messages, last_arg_ptr, 
					 stack_top)) != ERROR) {
		  /*
		   *   Add the value object at the following paren
		   *   iff 
		   *   1. The token following the paren is an operator.
		   *   This is so the argument expression works as 
		   *   an operand when != is evaled.
		   *   
		   *     e.g.  (CFunction cScanf ("%c", c) != EOF)
		   *                                     ^
		   *   2. The paren is the end of the stack.
		   *
		   *   Should only be necessary in cases like these, at 
		   *   least so far.
		   */
		  if (M_TOK(e_messages[next_msg_ptr]) == CLOSEPAREN) {
		    if ((expr_lookahead_ptr = 
			 __ctalkNextLangMsg (e_messages, next_msg_ptr,
					     stack_top)) != ERROR) {
		      if (IS_C_OP_TOKEN_NOEVAL
			  (M_TOK(e_messages[expr_lookahead_ptr])))
			e_messages[next_msg_ptr] -> value_obj = e_result;
		    } else {
		      if (next_msg_ptr == stack_top) {
			e_messages[next_msg_ptr] -> value_obj = e_result;
		      }
		    }
		  }
		} /* if ((next_msg_ptr = ... */
	      }
	    }
	  }
	}
	++m -> evaled;
	break;
      case LABEL:
	/*
	 *  Check whether we've evaluated a receiver from the previous 
	 *  subexpressions.  If so, evaluate this token again.
	 */
	if (__ctalk_isMethod (M_NAME(m), e_messages, i, stack_start, 
				  stack_top)) {
	  m -> tokentype = METHODMSGLABEL;
	  ++i;
	  continue;
	} else {
	  OBJECT *var_object;
	  m_prev_msg = 
	    e_messages[__ctalkPrevLangMsg (e_messages, i, stack_start)];
	  if (m_prev_msg && IS_MESSAGE(m_prev_msg) && m_prev_msg -> obj &&
	      IS_OBJECT(m_prev_msg -> obj)) {
	    if (((var_object = 
		 __ctalkGetInstanceVariable (m_prev_msg -> obj, M_NAME(m),
					     FALSE)) != NULL) ||
		((var_object = 
		  __ctalkGetClassVariable (m_prev_msg -> obj, M_NAME(m), 
					   FALSE)) != NULL)) {
	      __refObj (OBJREF(m_prev_msg -> value_obj), 
			OBJREF(var_object));
	      __refObj (OBJREF(m -> value_obj), OBJREF(var_object));
	      if (c_rcvr_idx == -1)
		c_rcvr_idx = __ctalkPrevLangMsg (e_messages, i, stack_start);
	    }
	    if (!strcmp (M_NAME(m), "super")) {
	      __refObj (OBJREF(m->value_obj),
			OBJREF(m_prev_msg->obj->__o_superclass));
	      m -> attrs |= TOK_VALUE_OBJ_IS_SUPERCLASS;
	      m -> receiver_obj = M_VALUE_OBJ(m_prev_msg);
	      if (c_rcvr_idx == -1)
		c_rcvr_idx = __ctalkPrevLangMsg(e_messages, i, stack_start);
	    }
	  }
	  ++m -> evaled;
	}
	break;
      default:
	if (_rt_op_precedence (precedence, e_messages, i, 
			   FALSE, stack_top, stack_start)) {
	  switch (m -> tokentype)
	    {
	    case OPENPAREN:
	      /*
	       *  Check for a matching close parenthesis, then:
	       *  1. Typecast.
	       *  2. C function, evaluated by syntax.
	       *  3. Subexpression.
	       */
	      if ((matching_paren_ptr = 
		   __ctalkMatchParen (e_messages, i, stack_top)) == ERROR) {
		__ctalkExceptionInternal (m, mismatched_paren_x, "");
		x_msg_ptr = i;
		goto cleanup;
	      }
	      fn_tok_ptr = __ctalkPrevLangMsg (e_messages, i, stack_start);
	      if (__rt_is_typecast (e_messages, i, matching_paren_ptr)) {
		if (typecast_is_pointer (e_messages, i, matching_paren_ptr)) {
		  typecast_ptr_expr (e_messages, i, matching_paren_ptr);
		} else {
		  typecast_value_expr (e_messages, i, matching_paren_ptr);
		}
	      } else {
 		if ((fn_tok_ptr != ERROR) && 
		    is_c_fn_syntax (e_messages, fn_tok_ptr)) {
		  for (j = fn_tok_ptr; j >= matching_paren_ptr; j--)
		    ++e_messages[j] -> evaled;
 		} else {
		  if ((subexpr_result = eval_subexpr (e_messages, i)) == NULL){
		    /*
		     *  Empty set of parentheses.
		     */
		    int prev_val_ptr, i_sub;
		    if ((prev_val_ptr = 
			 __ctalkPrevLangMsg (e_messages, i, stack_start))
			!= ERROR) {
		      if (e_messages[prev_val_ptr] && 
			  IS_MESSAGE(e_messages[prev_val_ptr])) {
			_set_expr_value (e_messages, prev_val_ptr, 
					 matching_paren_ptr, i, 
				 M_VALUE_OBJ(e_messages[prev_val_ptr]));
			for (i_sub = i; i >= matching_paren_ptr; i--)
			  ++e_messages[i] -> evaled;
		      }
		    }
		    x_msg_ptr = i;
		  } else {
		    /*
		     *  subexpr_result needs at least 1 
		     *  reference count.
		     */
  		    if (!is_current_receiver(subexpr_result)) {
		      if (subexpr_result -> nrefs == 0)
			__ctalkSetObjectScope (subexpr_result, 
					       subexpr_result -> scope | 
					       SUBEXPR_CREATED_RESULT);
  		      __objRefCntInc(OBJREF(subexpr_result));
		    }
		  }
 		}
	      }
	      break;
	    case CLOSEPAREN:
	      ++m -> evaled;
	      break;
	      /* Binary numeric and boolean operators. */
	    case PLUS:
	    case MINUS:
	    case MULT:
	    case DIVIDE:
	    case ASL:
	    case ASR:
	    case BIT_AND:
	    case BIT_OR:
	    case BIT_XOR:
	    case BOOLEAN_EQ:
	    case GT:
	    case LT:
	    case GE:
	    case LE:
	    case INEQUALITY:
	    case BOOLEAN_AND:
	    case BOOLEAN_OR:
	    case MODULUS:
	      if (!m -> evaled) {
		if (__ctalk_isMethod (M_NAME(m), e_messages, i, stack_start, 
				      stack_top)) {
		  int __op1_ptr, __op2_ptr;
		  _rt_operands (e_messages, i, 
				&__op1_ptr, &__op2_ptr, 
				stack_top, stack_start);

		  if (e_messages[__op1_ptr] -> value_obj &&
		      IS_OBJECT(e_messages[__op1_ptr] -> value_obj) &&
		      e_messages[__op1_ptr] 
  		      -> value_obj -> scope & SUBEXPR_CREATED_RESULT) {
		    if (op1_subexpr_result && 
			IS_OBJECT (op1_subexpr_result)) {
		      __objRefCntDec(OBJREF(op1_subexpr_result));
		      __ctalkDeleteObjectInternal (op1_subexpr_result);
		    }
		    op1_subexpr_result = e_messages[__op1_ptr]->value_obj;
		  }

		  if (e_messages[__op2_ptr] -> value_obj &&
		      IS_OBJECT(e_messages[__op2_ptr] -> value_obj) &&
		      e_messages[__op2_ptr] 
  		      -> value_obj -> scope & SUBEXPR_CREATED_RESULT) {
		    if (op2_subexpr_result && 
			IS_OBJECT (op2_subexpr_result)) {
		      __objRefCntDec(OBJREF(op2_subexpr_result));
		      __ctalkDeleteObjectInternal (op2_subexpr_result);
		    }
		    op2_subexpr_result = e_messages[__op2_ptr]->value_obj;
		  }
		        
		  m -> tokentype = METHODMSGLABEL;
		  ++i;
		  continue;
		} else {
		  value_obj = _rt_math_op (e_messages, i, stack_start, 
					   stack_top);
		  if (__ctalkTrapExceptionInternal (NULL)) {
		    x_msg_ptr = i;
		    goto done;
		  }
		  _set_expr_value (e_messages, stack_start, stack_top, 
				   i, value_obj);
		  ++m -> evaled;
		}
	      }
	      break;
	      /* Unary math and boolean operators. */
	    case BIT_COMP:
	    case LOG_NEG:
	      if (!m -> evaled) {
		value_obj = _rt_unary_math_op (e_messages, i, stack_start, 
					       stack_top);
		if (__ctalkTrapExceptionInternal (NULL)) {
		  x_msg_ptr = i;
		  goto done;
		}
		_set_unary_expr_value (e_messages, stack_start, stack_top, 
				 i, value_obj);
		++m -> evaled;
	      }
	      break;
	    case CONDITIONAL:     /* No-op for now. */
	    case SIZEOF:
	      ++m -> evaled;
	      break;
	    case INCREMENT:
	    case DECREMENT:
	    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:
	      /*
	       *  FIXME!  Check for an overloaded operator here, too.
	       */
	      if (__ctalk_isMethod (M_NAME(m), e_messages, i, stack_start, 
				    stack_top)) {
		m -> tokentype = METHODMSGLABEL;
		++i;
		continue;
	      } else {
		if (!m -> evaled) {
		  value_obj = _rt_math_op (e_messages, i, stack_start, 
					   stack_top);
		  _set_expr_value (e_messages, stack_start, stack_top, i, 
				   value_obj);
		  ++m -> evaled;
		}
	      }
	      break;
	    default:
	      ++m -> evaled;
	      break;
	    }
	}
	break;
      }
  }

  for (; precedence < 13; ++precedence) {
    for (i = stack_start; i >= stack_top; i--) {
      if (_rt_op_precedence (precedence, e_messages, i, FALSE,
			 stack_top, stack_start) &&
	  (e_messages[i] -> evaled == 0))
	goto re_eval;
    }
  }

  done:

  /*
   *  If there is an exception, the result object should
   *  be a null object.  Otherwise, the value object or
   *  object of the expression is the result.
   *
   *  Check the critical flag, so the exception is handled 
   *  at the level of the __ctalkEvalExpr () call if necessary.
   */
  if (((ex = __ctalkTrapExceptionInternal (NULL)) != NULL) &&
      (ex -> is_critical == 0)) {
    OBJECT *__result_object_tmp, *__rcvr_object_tmp;
    /*
     *  TODO - Make this function more useful.
     */
/*      __result_object_tmp = exception_null_obj (e_messages, x_msg_ptr); */
    __result_object_tmp = __ctalkCreateObjectInit (NULLSTR, 
						   INTEGER_CLASSNAME,
						   INTEGER_SUPERCLASSNAME, 
						   scope, "0");
    __ctalkHandleRunTimeException ();
    if ((__rcvr_object_tmp = __ctalkRtReceiverObject ()) != NULL) {
      result_obj = __result_object_tmp;
      __objRefCntSet (OBJREF(result_obj), __rcvr_object_tmp -> nrefs);
      __ctalkSetObjectScope (result_obj, __rcvr_object_tmp -> scope);
    }
  } else {

    if (e_messages[stack_start] -> value_obj && 
	IS_OBJECT (e_messages[stack_start] -> value_obj)) {
	result_obj = e_messages[stack_start]->value_obj;
    } else {
      if (e_messages[stack_start] -> obj && 
	  IS_OBJECT (e_messages[stack_start] -> obj)) {
	result_obj = e_messages[stack_start]->obj;
      } else {
	result_obj = NULL;
      }
    }
  }

  /*
   *  NOTE - This will have to be factored out eventually.
   *
   *  Note that the routine must do a sweep for the deleted
   *  object (in the loops with j) in the messages preceding 
   *  the message of the deleted object, if _cleanup_temporary_objects () 
   *  decided it was deleteable.  Otherwise, we run into a 
   *  problem where a freed object's memory still looks like an 
   *  object, and the routine tries to delete it again.
   *
   *  This routine must take care that i matches e_message_ptr.
   */

 cleanup:

  if (op1_subexpr_result &&
      IS_OBJECT(op1_subexpr_result) &&
      (op1_subexpr_result -> scope & SUBEXPR_CREATED_RESULT)) {
    op1_subexpr_result -> scope ^ SUBEXPR_CREATED_RESULT;
    __objRefCntDec (OBJREF(op1_subexpr_result));
    __ctalkDeleteObjectInternal (op1_subexpr_result);
  }
  if (op2_subexpr_result &&
      IS_OBJECT(op2_subexpr_result) &&
      (op2_subexpr_result -> scope & SUBEXPR_CREATED_RESULT)) {
    op2_subexpr_result -> scope ^ SUBEXPR_CREATED_RESULT;
    __objRefCntDec (OBJREF(op2_subexpr_result));
    __ctalkDeleteObjectInternal (op2_subexpr_result);
  }

  for (i = stack_top; i <= stack_start; i++) {
    MESSAGE *n;
    n = e_message_pop ();
    if (!n || !IS_MESSAGE (n))
      continue;
    if (n -> obj && IS_OBJECT (n -> obj))
      if (!_cleanup_temporary_objects (n -> obj, result_obj, subexpr_result)) {
	for (j = i + 1; j <= stack_start; j++) {
	  if (e_messages[j] -> obj == n -> obj)
	    e_messages[j] -> obj = NULL;
	}
	n -> obj = NULL;
      }

    if (n -> value_obj && IS_OBJECT (n -> value_obj))
      if (!_cleanup_temporary_objects (n -> value_obj, result_obj, subexpr_result)) {
	for (j = i + 1; j <= stack_start; j++) {
	  if (e_messages[j] -> value_obj == n -> value_obj)
	    e_messages[j] -> value_obj = NULL;
	}
	n -> value_obj = NULL;
      }
    delete_message (n);
  }
  --expr_parser_lvl;
  p = __pop_expr_parser ();
  __delete_expr_parser (p);
  return result_obj;
}

/*
 *  Clean up temporary objects used in expressions.
 *
 *  Return SUCCESS (0) if the object is deleted, ERROR
 *  (-1) otherwise.
 *
 *  Make sure that the reference count of 
 *  objects used in subexpressions is 0 for both the
 *  object and the instance variable.
 *
 *  Also delete the value instance variable explicitly
 *  if there is a case of a repeate term.  Kludgy, but
 *  it should be sufficient for now.
 */
int _cleanup_temporary_objects (OBJECT *obj, OBJECT *result_obj, OBJECT *subexpr_obj) {

  if (!obj || !IS_OBJECT(obj))
    return ERROR;
  if (object_is_deletable(obj, result_obj, subexpr_obj)) {
    __ctalkDeleteObject (obj);
    return SUCCESS;
  }
  if (obj->__o_p_obj && is_receiver(obj->__o_p_obj))
    return ERROR;

  if (object_is_decrementable(obj, result_obj, subexpr_obj)) {
    __ctalkDeleteObjectInternal (obj);
    return SUCCESS;
  }
  return ERROR;
}

/*
 *   These operators are all binary, so the function does not need to
 *   check their context.
 */

static METHOD *op_method;
OBJECT *_rt_math_op (MESSAGE_STACK messages, int op_ptr, int stack_start, 
		int stack_end) {

  int op1_ptr, op2_ptr;
  OBJECT *(*cfunc)(), *result_object, *rcvr_class_object;
  METHOD *__caller_op_method;
  char errmsg[MAXMSG];

  _rt_operands (messages, op_ptr, &op1_ptr, &op2_ptr, stack_end, stack_start);
  if ((op1_ptr == ERROR) || (op2_ptr == ERROR)) {
    _warning ("rt_math_op (): Invalid operand.\n");
    return NULL;
  }

  /*
   *  If the class of the message's object is, "Expr,", try to get the
   *  class from the, "value," instance variable.
   */

  if (!strcmp (M_VALUE_OBJ (messages[op1_ptr]) -> __o_classname, "Expr")) {
    OBJECT *value_var;
    if ((value_var = 
	 __ctalkGetInstanceVariable (M_VALUE_OBJ (messages[op1_ptr]), 
				      "value", FALSE)) == NULL) {
      _warning ("rt_math_op: %s undefined value.\n", 
		M_VALUE_OBJ (messages[op1_ptr]) -> __o_name);
      return NULL;
    }
    
    if ((rcvr_class_object = __ctalkGetClass (value_var -> __o_classname))
	== NULL) {
      _warning ("rt_math_op: Undefined class %s.\n", value_var -> __o_classname);
      return NULL;
    }

  } else {
    if ((rcvr_class_object = 
	 __ctalkGetClass (M_VALUE_OBJ (messages[op1_ptr]) -> __o_classname))
	== NULL) {
      _warning ("rt_math_op: Undefined class %s.\n", 
		M_VALUE_OBJ (messages[op1_ptr]) -> __o_classname);
      return NULL;
    }
  }

  if ((op_method = 
       __ctalkGetInstanceMethodByName (M_VALUE_OBJ (messages[op1_ptr]), 
				messages[op_ptr] -> name, TRUE)) 
      == NULL) {
    sprintf (errmsg, 
     "rt_math_op (): Object %s (Class %s) does not understand message %s.\n",
	     M_VALUE_OBJ(messages[op1_ptr]) -> __o_name, 
	     M_VALUE_OBJ(messages[op1_ptr]) -> __o_classname, 
	     (M_VALUE_OBJ(messages[op_ptr]) ? 
	      M_VALUE_OBJ(messages[op_ptr]) -> __o_name :
	      messages[op_ptr] -> name));
    __ctalkExceptionInternal (messages[op_ptr], undefined_method_x, errmsg);
    return NULL;
  }
  
  __ctalk_receiver_push (M_VALUE_OBJ (messages[op1_ptr]));
  __ctalk_arg_push (M_VALUE_OBJ (messages[op2_ptr]));
  op_method -> args[0] = M_VALUE_OBJ (messages[op2_ptr]);
  __objRefCntInc (OBJREF (op_method -> args[0]));

  __caller_op_method = op_method;
      
  __save_rt_info (M_VALUE_OBJ(messages[op1_ptr]), rcvr_class_object, NULL, 
		  op_method, op_method->cfunc, False);

  cfunc = op_method -> cfunc;
  result_object = (*cfunc) ();

  /*
   *  If a method has a return statement like, "return self,"
   *  then we are likely to get back the, "value," instance variable
   *  of the receiver.  Check if the result is an instance variable
   *  of the receiver, and if so, make the receiver the result
   *  object.
   */

  if (! result_object -> instancevars)
    result_object = __ctalkInstanceVariableReceiver (result_object);

  op_method = __caller_op_method;

  __ctalk_arg_pop ();
  __objRefCntDec (OBJREF (op_method -> args[0]));
  op_method -> args[0] = NULL;
  __ctalk_receiver_pop ();

  __restore_rt_info ();

  op_method = NULL;

  ++e_messages[op_ptr] -> evaled;

  return result_object;
}

/*
 *  Ctalk does not handle unary, prefix math ops, so this 
 *  is here in case we encounter a math expression.  In that case,
 *  we evaluate the argument using C.  At this time only 
 *  logical negation (!), and bit comp (~) need to get handled in 
 *  rt_op.c.
 */

OBJECT *_rt_unary_math_op (MESSAGE_STACK messages, int op_ptr, 
			   int stack_start, int stack_end) {

  int op1_ptr;
  OBJECT *result_object;
  OBJECT *value_var;

  _rt_unary_operand (messages, op_ptr, &op1_ptr, stack_end, stack_start);

  if (op1_ptr == ERROR) {
    _warning ("_rt_unary_math_op (): Invalid operand.\n");
    return NULL;
  }

  if ((value_var = 
       __ctalkGetInstanceVariable (M_VALUE_OBJ (messages[op1_ptr]), 
				   "value", FALSE)) == NULL) {
    _warning ("_rt_unary_math_op: %s undefined value.\n", 
	      M_VALUE_OBJ (messages[op1_ptr]) -> __o_name);
    return NULL;
  }
    
  result_object = _rt_unary_math (messages, op_ptr, value_var);

  ++e_messages[op_ptr] -> evaled;

  return result_object;
}

/*
 *  Set the value of an expression.  Check the values of the 
 *  operand token(s) objects and value objects to determine
 *  the limits of the operands.  If the operands have a value
 *  object already, then clean it up if it is temporary.
 */

int _set_expr_value (MESSAGE_STACK messages, int stack_start, int stack_end, 
		     int op_ptr, OBJECT *value_object) {
  int i, op1_ptr, op2_ptr;
  int op1_start = stack_start,   /* Avoid warnings. */
    op2_end = stack_end;

  _rt_operands (messages, op_ptr, &op1_ptr, &op2_ptr, stack_end, stack_start);
  
  if ((op1_ptr == ERROR) || (op2_ptr == ERROR)) {
    _warning ("_set_expr_value (): Invalid operand.\n");
    return ERROR;
  }

  if (messages[op1_ptr] -> value_obj && 
      IS_OBJECT (messages[op1_ptr] -> value_obj)) {
    for (i = op1_ptr; i <= stack_start; i++) {
      if (messages[i] -> value_obj == messages[op1_ptr] -> value_obj)
	op1_start = i;
    }
    _cleanup_temporary_objects (messages[op1_ptr] -> value_obj, value_object, NULL);
  } else {
    op1_start = op1_ptr;
  }

  if (messages[op2_ptr] -> value_obj && 
      IS_OBJECT (messages[op2_ptr] -> value_obj)) {
    for (i = op2_ptr; i >= stack_end; i--) {
      if (messages[i] -> value_obj == messages[op2_ptr] -> value_obj)
	op2_end = i;
    }
    _cleanup_temporary_objects (messages[op2_ptr] -> value_obj, value_object, NULL);
  } else {
    op2_end = op2_ptr;
  }

  for (i = op1_start; i >= op2_end; i--)
    messages[i] -> value_obj = value_object;

  return SUCCESS;
}

int _set_unary_expr_value (MESSAGE_STACK messages, int stack_start, 
			   int stack_end, int op_ptr, OBJECT *value_object) {
  int i, operand_ptr;
  int operand_end = stack_end;   /* Avoid a warning. */

  _rt_unary_operand (messages, op_ptr, &operand_ptr, stack_end, stack_start);
  
  if (messages[operand_ptr] -> value_obj && 
      IS_OBJECT (messages[operand_ptr] -> value_obj)) {
    for (i = operand_ptr; i >= stack_end; i--) {
      if (messages[i] -> value_obj == messages[operand_ptr] -> value_obj)
	operand_end = i;
    }
    _cleanup_temporary_objects (messages[operand_ptr] -> value_obj, value_object, NULL);
  } else {
    operand_end = operand_ptr;
  }

  for (i = op_ptr; i >= operand_end; i--)
    messages[i] -> value_obj = value_object;

  return SUCCESS;
}

int __set_left_operand_value (MESSAGE_STACK messages, int ptr, 
			      OBJECT *val_obj) {
  int i, stack_start;
  OBJECT *prev_val_obj;

  stack_start = __rt_get_stack_top (messages);

  prev_val_obj = messages[ptr] -> value_obj;

  if (prev_val_obj && IS_OBJECT (prev_val_obj)) {
    val_obj -> scope = prev_val_obj -> scope;
    val_obj -> nrefs = prev_val_obj -> nrefs;
  }

  _cleanup_temporary_objects (prev_val_obj, val_obj, NULL);

  for (i = ptr; i <= stack_start; i++) {
    if (messages[i] -> value_obj != prev_val_obj) {
      prev_val_obj = messages[i] -> value_obj;
      _cleanup_temporary_objects (messages[i] -> value_obj, val_obj, NULL);
    }
    messages[i] -> value_obj = val_obj;
  }

  return SUCCESS;
}

int __set_right_operand_value (MESSAGE_STACK messages, int ptr,
			       OBJECT *val_obj) {
  int i, stack_end, old_val_deleted;
  OBJECT *old_val_obj;

  stack_end = __rt_get_stack_end (messages);

  old_val_obj = messages[ptr] -> value_obj;

  if (old_val_obj && IS_OBJECT (old_val_obj)) {
    val_obj -> scope = old_val_obj -> scope;
    val_obj -> nrefs = old_val_obj -> nrefs;
  }

  for (i = ptr, old_val_deleted = FALSE; i > stack_end; i--) {
    if (messages[i] -> value_obj == old_val_obj) {
      if (!old_val_deleted) {
	if (!_cleanup_temporary_objects 
	    (messages[i] -> value_obj, val_obj, NULL)) {
	  old_val_deleted = TRUE;
	}
      }
      messages[i] -> value_obj = val_obj;
    }
  }

  return SUCCESS;
}

int __set_aggregate_obj (MESSAGE_STACK messages, int start, int end,
			       OBJECT *obj) {
  int i;

  for (i = start; i >= end; i--) {
    if (messages[i] -> obj != obj)
      messages[i] -> obj = obj;
    messages[i] -> attrs |= CVAR_AGGREGATE_TOK;
  }

  return SUCCESS;
}

METHOD *_math_op_method (void) {
  return op_method;
}

int __ctalkMatchParen (MESSAGE **messages, int this_message, 
			 int end_ptr) {

  int i, parens = 0;
  MESSAGE *m;

  for (i = this_message; i >= end_ptr; i--) {
    m = messages[i];
    if (m && IS_MESSAGE (m)) {
      switch (m -> tokentype)
	{
	case OPENPAREN:
	  ++parens;
	  break;
	case CLOSEPAREN:
	  --parens;
	  break;
	default:
	  break;
	}

      if (!parens)
	return i;
    }
  }
  _warning ("Mismatched parentheses.");
  return -1;
}

/*
 *  Return the stack index of the next non-whitespace and 
 *  non-newline message.
 */
int __ctalkNextLangMsg (MESSAGE **messages, int this_msg, int stack_end) { 

  int i;

  for (i = this_msg - 1; i >= stack_end; i--) {
    if (!messages[i] || !IS_MESSAGE (messages[i]))
      return ERROR;
    if ((messages[i] -> tokentype != NEWLINE) &&
	(messages[i] -> tokentype != WHITESPACE))
      return i;
  }
  return ERROR;
}

int __ctalkPrevLangMsg (MESSAGE **messages, int this_msg, int stack_start) {

  int i;

  for (i = this_msg + 1; i <= stack_start; i++) {
    if ((messages[i] -> tokentype != NEWLINE) &&
	(messages[i] -> tokentype != WHITESPACE))
      return i;
  }
  return ERROR;
}

/*
 *  If the argument token matches a parameter, use the corresponding
 *  argument.  Otherwise, if the argument token has a class that
 *  matches a lexical type, use that.  Otherwise, use the class of the
 *  method parameter.
 */

OBJECT *create_param (char *name, METHOD *method, OBJECT *arg_class) {

  PARAM *p;
  OBJECT *param_object, *param_class,
    *rcvr, *rcvr_class, *(*methd_fn)();
  int i;

  param_object = NULL;

  if (method == NULL) {  /* Expression from __ctalkEvalExpr (). */
    if ((methd_fn = __ctalkRtGetMethodFn ()) != NULL) {
      rcvr = __ctalk_receivers_at (__ctalkGetReceiverPtr () + 1);
      rcvr_class = rcvr->__o_class;
      if (rcvr_class == rcvr) {
	if ((method = 
	     __ctalkFindClassMethodByFn (&rcvr, methd_fn, TRUE)) 
	    != NULL) {
	  for (i = 0; i < method -> n_params; i++) {
	    if (!strcmp (method -> params[i] -> name, name)) {
	      return method -> args[i];
	    }
	  }
	}
      } else {
	if ((method = 
	     __ctalkFindInstanceMethodByFn (&rcvr, methd_fn, FALSE)) 
	    != NULL) {
	  for (i = 0; i < method -> n_params; i++) {
	    if (!strcmp (method -> params[i] -> name, name)) {
	      return method -> args[i];
	    }
	  }
	}
      }
    }
  }

  if (arg_class) {
     param_object =
       __ctalkCreateObjectInit (name, arg_class -> __o_name,
				arg_class -> __o_superclassname, 
				CREATED_PARAM,
				name);
  } else {
    if ((p = has_param_def (method)) != NULL) {
      if ((param_class = __ctalkGetClass (p -> class)) != NULL) {
#ifdef DEBUG_DYNAMIC_C_ARGS
	_warning ("create_param (): method %s (param class %s) not found.\n",
		  m -> name, p -> class);
#endif
 	param_object =
 	  __ctalkCreateObjectInit (name, param_class -> __o_name,
				   param_class -> __o_superclassname, 
				   CREATED_PARAM,
				   name);
      }
    }
  }
  return param_object;
}

/*
 *  An aggregate type for the purposes of expression parsing is
 *  an array member, a struct or union member, or a function
 *  call.
 * 
 *  The function returns the index of the last token of the
 *  aggregate variable, or -1.
 */

int is_aggregate_type (MESSAGE_STACK messages, int token_start, int stack_end,
		       char *namebuf) {

  int i,
    n_brackets,
    n_parens,
    lookahead;
  Boolean have_brackets,
    have_deref,
    have_parens;

  if (messages[token_start] -> tokentype != LABEL)
    return -1;

  *namebuf = 0;
  n_brackets = n_parens = 0;
  have_brackets = have_deref = have_parens = False;

  for (i = token_start; i >= stack_end; i--) {
    
    switch (messages[i] -> tokentype) 
      {
      case LABEL:
	strcat (namebuf, messages[i] -> name);
	  lookahead = __ctalkNextLangMsg (messages, i, stack_end);
	  if (lookahead == ERROR) {
	    if (!n_brackets && !n_parens) {
	      if (have_deref == True) {
		if (i == stack_end) {
		  return i;
		} else {
		  return -1;
		}
	      } else {
		return -1;
	      }
	    }
	  }
	  if ((messages[lookahead] -> tokentype != PERIOD) &&
	      (messages[lookahead] -> tokentype != DEREF) &&
	      (messages[lookahead] -> tokentype != ARRAYOPEN) &&
	      (messages[lookahead] -> tokentype != OPENPAREN)) {

	    if (!n_brackets && !n_parens) {
	      if (have_deref == True) {
		return i;
	      } else {
		return -1;
	      }
	    }
	  }
	  /*
	   *  Don't keep going for a method or function call.
	   */
	  if ((i == token_start) &&
	      (messages[lookahead] -> tokentype == OPENPAREN))
	    return -1;
	break;
      case PERIOD:
      case DEREF:
	have_deref = True;
	strcat (namebuf, messages[i] -> name);
	break;
      case ARRAYOPEN:
	have_brackets = True;
	++n_brackets;
	strcat (namebuf, messages[i] -> name);
	break;
      case ARRAYCLOSE:
	--n_brackets;
	strcat (namebuf, messages[i] -> name);
	if ((have_brackets == True) && !n_brackets && !n_parens)
	  return i;
	break;
      case OPENPAREN:
	++n_parens;
	have_parens = True;
	strcat (namebuf, messages[i] -> name);
	break;
      case CLOSEPAREN:
	--n_parens;
	strcat (namebuf, messages[i] -> name);
	if ((have_parens == True) && !n_brackets && !n_parens)
	  return i;
	break;
      default:
	strcat (namebuf, messages[i] -> name);
	break;
      }

  }

  return -1;
}

int __ctalk_isMethod (char *name, MESSAGE_STACK messages, int msg_ptr, 
	       int stack_top, int stack_end) {
  int prev_tok_ptr;
  MESSAGE *m_prev_tok;
  METHOD *m;
  OBJECT *m_prev_tok_obj;

  if ((prev_tok_ptr = __ctalkPrevLangMsg (messages, msg_ptr, stack_top))
      != ERROR) {
    m_prev_tok = messages[prev_tok_ptr];
    if (M_OBJ_IS_VAR(m_prev_tok)) {
      if (m_prev_tok && m_prev_tok -> obj && 
	  m_prev_tok -> obj -> instancevars && 
	  IS_OBJECT (m_prev_tok -> obj -> instancevars)) {
	m_prev_tok_obj = m_prev_tok -> obj -> instancevars;
	if ((m = 
	     __ctalkFindMethodByName (&m_prev_tok_obj, 
				      name, FALSE)) != NULL)
	  return TRUE;
      }
    } else {
      m_prev_tok_obj = m_prev_tok -> obj;
      if (m_prev_tok && m_prev_tok -> obj && IS_OBJECT (m_prev_tok -> obj)) {
	if ((m = __ctalkFindMethodByName (&(m_prev_tok_obj), name, FALSE))
	    != NULL)
	  return TRUE;
      }
    }
  }
  return FALSE;
}

/*
 *  Check the exception and the message token type and create
 *  a NULL object of the appropriate class.  Default to an
 *  Integer object.
 */

OBJECT *exception_null_obj (MESSAGE_STACK messages, int x_msg_ptr) {

  OBJECT *o;
  I_EXCEPTION *x_list;
  int prev_msg_ptr;

  if ((x_list = __get_x_list ()) == NULL)
    return NULL;

  o = NULL;
  

  switch (x_list -> _exception)
    {
    case no_x:
    case file_is_directory_x:
    case mismatched_paren_x:
    case undefined_param_class_x:
    case parse_error_x:
    case undefined_class_x:
    case self_without_receiver_x:
    case undefined_label_x:
    case undefined_type_x:
    case undefined_receiver_x:
      break;
    case undefined_method_x:
      /*
       *  Should occur on a method message, so scan back to 
       *  the previous message.  Create an object of the 
       *  receiver class that evaluates to False.  If there's 
       *  no receiver, create an Integer object that evaluates 
       *  to False.
       */
      if ((prev_msg_ptr = 
	   __ctalkPrevLangMsg (e_messages,
			       x_msg_ptr,
			       expr_parsers[expr_parser_ptr]-> msg_frame_top)) 
	  == ERROR) {
	o = __ctalkCreateObjectInit ("result", 
					INTEGER_CLASSNAME,
					INTEGER_SUPERCLASSNAME, 
					LOCAL_VAR, "0");
      } else {
	if (messages[prev_msg_ptr] -> obj && 
	    IS_OBJECT (messages[prev_msg_ptr] -> obj)) {
	  o = __ctalkCreateObjectInit ("result", 
			  messages[prev_msg_ptr] -> obj -> __o_classname,
			  messages[prev_msg_ptr] -> obj -> __o_superclassname,
			  LOCAL_VAR, "0");
	} else {
	  o = __ctalkCreateObjectInit ("result", 
					  INTEGER_CLASSNAME,
					  INTEGER_SUPERCLASSNAME, 
					  LOCAL_VAR, "0");
	}
      }
      break;
    default:
      break;
    }
  return o;
}

/*
 *  If the method returns NULL, create an
 *  object that evaluates to False.
 */
OBJECT *null_result_obj (METHOD *m, int scope) {

  OBJECT *o, *return_class;

  if (!m || !strcmp (m -> returnclass, "Any")) {
    o = __ctalkCreateObjectInit (NULLSTR, 
				    INTEGER_CLASSNAME,
				    INTEGER_SUPERCLASSNAME, 
				    scope, "0");
  } else {
    if ((return_class = __ctalkGetClass (m -> returnclass)) == NULL) {
      _warning ("Undefined return class %s.\n",
		m -> returnclass);
      o = __ctalkCreateObjectInit (NULLSTR, 
				      INTEGER_CLASSNAME,
				      INTEGER_SUPERCLASSNAME, 
				      scope, "0");
    } else {
      o = __ctalkCreateObjectInit (NULLSTR, return_class -> __o_name,
				   return_class -> __o_superclassname,
				   scope, "0");
    }
  }

  return o;
}

/*
 *  When called by eval_expr (), we can be reasonably certain
 *  of the context that, "self," appears in.  If it appears
 *  in a method, then we can return the rtinfo () receiver
 *  object.  If, "self," appears in a function, then it
 *  should be as an argument to a method, and refers to 
 *  the receiver.  So a case like,
 *
 *    subDir = path substring 1, self length - 1;
 *
 *  should be evaluated as:
 *
 *    subDir =        path substring 1, self length - 1;
 *    ^      ^        ^
 *    rcvr   method   arg
 *
 *                    path substring 1, self length - 1;
 *                    ^    ^
 *                    rcvr args
 *    rcvr_ptr
 *    + 1
 *
 *  A case like,
 *
 *    subDir =    self substring 1, self length - 1;
 *    ^           ^
 *    rcvr_ptr    rcvr_ptr  
 *    + 1         + 2 (the method's receiver,
 *                     and also rtinfo.rcvr_obj.)
 */

OBJECT *self_expr (METHOD *method) {

  int rcvr_ptr;
  OBJECT *rt_rcvr;

  if (!method) {
    rcvr_ptr = __ctalkGetReceiverPtr ();

    if (is_constant_rcvr == True)
      return __ctalk_receivers_at (rcvr_ptr + 1);

    if (is_arg_expr == True) {
      if ((rt_rcvr = __ctalkRtReceiverObject ()) != NULL)
	return rt_rcvr;
      else
	return __ctalk_receivers_at (rcvr_ptr + 1);
    }
  }
  return NULL;
}

/*
 *   Classes for basic C types should be parsed by
 *   the time the expression is evaluated.  If not,
 *   issue a warning and continue anyway.
 *
 *   TO DO - Decide if we need a mechanism to load
 *   basic C type classes on the fly.  For now, 
 *   only issue warnings if debugging is enabled -
 *   DEBUG_DYNAMIC_C_ARGS, like all other debugging
 *   #defines, is in include/ctalk.h.
 *
 *   The function assumes that m -> obj contains the 
 *   basic object with the value printed in m -> value.  
 *   This function does not handle PTR_T value types.
 */

OBJECT *__set_obj_from_msg_val (MESSAGE *m,
			    char *classname, char *superclassname, int scope) {

  OBJECT *val_class, *value_obj;
  strcpy (m -> obj -> __o_classname, classname);

  if ((val_class = __ctalkGetClass (classname)) == NULL) {
#ifdef DEBUG_DYNAMIC_C_ARGS
    _warning ("%s: Class \"%s\" undefined.\n", classname, __argvFileName ());
#endif
    value_obj = 
      __ctalkCreateObject ("value", classname, superclassname, scope);
  } else {
    value_obj = 
      __ctalkCreateObject ("value", val_class -> __o_name,
			     val_class -> __o_superclassname,  scope);
  }
  strcpy (m -> obj -> __o_classname, value_obj -> __o_classname);
  strcpy (m -> obj -> __o_superclassname, 
	  value_obj -> __o_superclassname);
  __ctalkSetObjectValue (value_obj, m -> value);
  __ctalkAddInstanceVariable (m -> obj, "value", value_obj);
  return m -> obj;
}

/*
 *  Call with idx pointing to the opening parenthesis of the
 *  expression.
 */

OBJECT *eval_subexpr (MESSAGE_STACK messages, int idx) {

  int i = idx,                   /* Avoid warnings. */
    subexpr_scope = LOCAL_VAR,
    stack_top,
    subexpr_start,
    subexpr_end,
    matching_paren_ptr;
  char subexpr_buf[MAXMSG];
  char *token_buf;
  OBJECT *subexpr_rcvr,
    *subexpr_result;
  METHOD *method = NULL;        /* Avoid a warning. */

  stack_top = e_message_ptr;

  if (messages[idx] -> evaled && M_VALUE_OBJ (messages[idx]))
    return M_VALUE_OBJ (messages[idx]);

  if ((matching_paren_ptr = __ctalkMatchParen (e_messages, idx, stack_top)) 
      == ERROR) {
    __ctalkExceptionInternal (messages[i], mismatched_paren_x,
			      messages[i] -> name);
    return exception_null_obj (e_messages, idx);
  }

  if ((idx - matching_paren_ptr) == 1)
    return NULL;

  subexpr_start = __ctalkNextLangMsg (e_messages, idx, stack_top);
  subexpr_end = __ctalkPrevLangMsg (e_messages, matching_paren_ptr, idx);
  token_buf = collect_tokens (messages, subexpr_start, subexpr_end);
  strcpy (subexpr_buf, token_buf);
  free (token_buf);

  /*
   *  The subexpression receiver class is the class of the 
   *  first constant or label (i.e., not an operator or
   *  whitespace) in the subexpression.
   */

  for (i = subexpr_start, subexpr_rcvr = NULL; 
       (i >= subexpr_end) && ! subexpr_rcvr; i--) {
    if (!IS_C_OP (messages[i] -> tokentype) && 
	(messages[i] -> tokentype != NEWLINE) &&
	(messages[i] -> tokentype != WHITESPACE) &&
	messages[i] -> obj) {

      /* 
       *  If the receiver's class is, "Expr," then
       *  try to get the class name from the, "value,"
       *  instance variable.
       */

      if (!strcmp (messages[i] -> obj -> __o_classname, "Expr")) {
	OBJECT *value_var;
	if ((value_var = __ctalkGetInstanceVariable 
	     (messages[i] -> obj, "value", FALSE)) == NULL)
	  _warning ("eval_expr: %s: undefined value.\n",messages[i] -> name);
	else
	  subexpr_rcvr = 
	    __ctalkGetClass ((IS_CLASS_OBJECT(value_var) ?
			      value_var -> __o_name :
			      value_var -> __o_classname));
	subexpr_scope = value_var -> scope;
      } else {
	subexpr_rcvr = 
	  __ctalkGetClass ((IS_CLASS_OBJECT(messages[i]->obj) ? 
			    messages[i] -> obj -> __o_name : 
			    messages[i] -> obj -> __o_classname));
	subexpr_scope = messages[i] -> obj -> scope;
      }
    }
  }
  if (subexpr_rcvr) {
    subexpr_result = eval_expr (subexpr_buf, subexpr_rcvr, 
				method, subexpr_scope);
  } else {
    __ctalkExceptionInternal (messages[i], undefined_receiver_x,
			      messages[i] -> name);
    return NULL;
  }

  /*
   *  Set the value_obj of the subexpression tokens,
   *  and the obj if there is no object for the token.
   */
  for (i = idx; i >= matching_paren_ptr; i--) {
    if (!messages[i] -> obj || !IS_OBJECT (messages[i] -> obj))
      messages[i] -> obj = subexpr_result;
    messages[i] -> value_obj = subexpr_result;
    ++messages[i] -> evaled;
  }

  return subexpr_result;
}

#define A_IDX_(a,b,ncols) (((a)*(ncols))+(b))

int __check_state (int stack_idx, MESSAGE **messages, int *states, 
		   int cols) {
  int i, j;
  int token;
  int next_idx = stack_idx;

  token = messages[stack_idx] -> tokentype;

  for (i = 0; ; i++) {

  nextrans:
     if (states[A_IDX_(i,0,cols)] == ERROR)
       return ERROR;

     if (states[A_IDX_(i,0,cols)] == token) {

       for (j = 1, next_idx = stack_idx - 1; ; j++, next_idx--) {
	
	 while (states[A_IDX_(i,j,cols)]) {
	   if (!messages[next_idx] || !IS_MESSAGE (messages[next_idx]))
	     return ERROR;
	   while ((messages[next_idx]->tokentype == WHITESPACE) ||
		  (messages[next_idx]->tokentype == NEWLINE)) {
	     next_idx--;
	     /* TO DO - 
		Find a way to notify the calling function whether 
		the message is the end of the stack, or determine
		the end of the stack by the calling function.
	     */
	     if (!messages[next_idx] || !IS_MESSAGE (messages[next_idx]))
	       return ERROR;
	   }
	   if (states[A_IDX_(i,j,cols)] != messages[next_idx]->tokentype) {
	     ++i;
	     goto nextrans;
	   }
	   else
	     ++j;
	 }
	 return i;
       }
     }
  }
}

/*
 *  For now, rely on the token type of a function label as
 *  LABEL.  Later on, it may be a good idea to add a check for
 *  a method based on the receiver class.  This depends on
 *  whether the function is within a subexpression, however,
 *  to determine the order in which the tokens are evaluated.
 */

int is_c_fn_syntax (MESSAGE_STACK messages, int fn_label_ptr) {

  if ((messages[fn_label_ptr] -> tokentype == LABEL) &&
      (messages[__ctalkNextLangMsg (messages, 
				    fn_label_ptr,
				    __rt_get_stack_end (messages))] 
       -> tokentype == OPENPAREN))
    return TRUE;

  return FALSE;
}

/*
 *  These are API-level wrappers for __get_expr_parser_ptr () and
 *  __expr_parser_at ().
 */
int __ctalkGetExprParserPtr (void) {
  return __get_expr_parser_ptr ();
}

EXPR_PARSER *__ctalkGetExprParserAt (int ptr) {
  return __expr_parser_at (ptr);
}

char *expr_text (char *s) {
  s = collect_tokens (e_messages, P_MESSAGES, e_message_ptr + 1);
  return s;
}
