/* $Id: method.c,v 1.18 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 program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program 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 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 <stdlib.h>
#include <string.h>
#include <errno.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"
#include "typeof.h"
#include "bufmthd.h"

extern int error_line,      /* Declared in errorloc.c.                  */
  error_column;

extern I_PASS interpreter_pass;    /* Declared in main.c.               */
extern int nolinemarker_opt; 

extern CTRLBLK                     /* Declared in control.c             */
*ctrlblks[MAXARGS + 1];
extern int ctrlblk_ptr;
extern int ctrlblk_pred, ctrlblk_blk, ctrlblk_else_blk;
extern int for_init, for_term, for_inc;

extern int do_predicate;           /* Declared in loop.c.               */

extern METHOD *new_methods[MAXARGS+1]; /* Declared in lib/rtnwmthd.c.   */
extern int new_method_ptr;

static MESSAGE *m_messages[N_MESSAGES+1];
static int m_message_ptr;

extern OBJECT *rcvr_class_obj;        /* Declared in lib/rtnwmthd.c and    */
                                      /* filled in by                      */
                                      /* new_instance|class_method ().     */

static char errbuf[MAXMSG];

typedef struct {
  char classname[MAXLABEL];
  char source_file[FILENAME_MAX];
  METHOD *method;
} IMPORTED_METHOD;

static IMPORTED_METHOD *imported_methods[MAXARGS+1]; /* Methods that are forward */
static int imported_method_ptr = MAXARGS;   /* declared if imported within a     */
                                     /* function.                         */

HASHTAB declared_method_names;       /* Initialized from init_method_stack*/
HASHTAB declared_method_selectors;   /* for now.                          */

MESSAGE_STACK method_message_stack (void) { return m_messages; }

int get_method_message_ptr (void) {return m_message_ptr; }

static void cvar_register_output (CVAR *c, char *name) {
  if (ctrlblk_pred) {
    ctrlblk_register_c_var (
      fmt_register_c_method_arg_call (c, name, FRAME_SCOPE),
      FRAME_START_IDX);
  } else {
    generate_register_c_method_arg_call (c, name, FRAME_SCOPE, 
					 FRAME_START_IDX);
  }
}

/*
 *   Register a C variable for the run time lib, and make sure 
 *   that the appropriate class library gets loaded.
 *
 *   If the variable is a member of a struct or array, then
 *   collate the tokens to get the name of the variable member.
 *
 *   Constant expressions get the same check in eval_arg ().
 *
 *   Register_c_var_method and register_c_var_receiver are here in 
 *   case the are needed by subscript_expr () in subscr.c, in the
 *   cases where a subscript contains an object and needs to be 
 *   evaluated.  In that case, the template that evaluates the 
 *   subscript is output here, and sets subscript_object_expr to
 *   TRUE, outputs the formatted template, and returns so that a 
 *   normal register_c_var call isn't output further down in the 
 *   function, and a __ctalk_arg () call isn't output after returning 
 *   to method_args ().
 */

METHOD *register_c_var_method;
static OBJECT *register_c_var_receiver;
extern int subscript_object_expr;

int arg_has_leading_unary;
int basename_idx;
static int leading_unary_idx;

int register_c_var (MESSAGE_STACK messages, int idx) {

  CVAR *v;
  int i, stack_top,
    n_parens,
    n_blocks,
    n_labels,
    n_derefs,
    subscript_begin,
    subscript_end;
  Boolean subscript_is_obj;
  OBJECT *subscript_obj;
  char basename[MAXLABEL], 
    varname[MAXLABEL],
    subscriptbuf[MAXMSG];
  MESSAGE *m;
	
  if (IS_C_UNARY_MATH_OP(M_TOK(messages[idx]))) {
    int _basename_lookahead;
    _basename_lookahead = nextlangmsg (messages, idx);
    strcpy (basename, M_NAME(messages[_basename_lookahead]));
  } else {
    strcpy (basename, M_NAME(messages[idx]));
  }

  stack_top = get_stack_top (messages);

  subscript_obj = NULL;

  for (i = idx, *varname = 0, n_derefs = 0, 
	 n_labels = 0, n_blocks = 0, n_parens = 0, subscript_is_obj = False,
	 subscript_begin = -1, subscript_end = -1; 
       i > stack_top; i--)
    {
      m = messages[i];
      switch (m -> tokentype)
	{
	case SEMICOLON:
	case ARGSEPARATOR:
	  goto done;
	  break;
	case OPENPAREN:
	  ++n_parens;
	  break;
	case CLOSEPAREN:
	  if (!n_parens)
	    goto done;
	  --n_parens;
	  break;
	case ARRAYOPEN:
	  /*
	   *  It should be okay to have whitespace here.
	   */
	  if (!n_blocks) subscript_begin = i - 1;
	  ++n_blocks;
	  break;
	case ARRAYCLOSE:
	  if (--n_blocks == 0) {
	    int _subscr_lookahead;
	    subscript_end = i + 1;
	    /*
	     *  Look ahead for another subscript; e.g., a[]<[>]
	     */
	    if (((_subscr_lookahead = nextlangmsg (messages, i)) == ERROR) ||
		(M_TOK(messages[_subscr_lookahead]) != ARRAYOPEN)) {
	      strcat (varname, messages[i] -> name);
	      goto done;
	    }
	  }
	  break;
	case LABEL:
	  /*
	   *  Check for the case where a method label follows a 
	   *  variable label.  That is, we already have a label, 
	   *  or a sequence of dereferenced labels, and another 
	   *  label follows it.  The loop should not need to 
	   *  check for math operators.
	   */
	  ++n_labels;
	  if ((n_labels > (n_derefs + 1)) && !n_blocks && !n_parens)
	    goto done;
	  /*
	   *  If within an array subscript, check whether the label refers
	   *  to an object, and record the subscript expression.
	   *
	   *  If register_c_var_method is NULL, the function is 
	   *  being called while parsing a run-time expression,
	   *  and we won't need a separate block later (from 
	   *  subscr_expr ()) to extract an array element as a method 
	   *  argument.
	   */
	  if (n_blocks && register_c_var_method) {
 	    if ((subscript_obj =
		 resolve_arg (register_c_var_method, messages, i)) != NULL) {
	    }
	  }
	  break;
	case DEREF:
	case PERIOD:
	  ++n_derefs;
	  break;
	case WHITESPACE:
	  break;
	case AMPERSAND:
	  break;
	default:
	  if (!n_parens && !n_blocks)
	    goto done;
	  break;
	}
      strcat (varname, messages[i] -> name);
    }

 done:

  if (((v = get_local_var (basename)) != NULL) ||
      ((v = get_global_var (basename)) != NULL)) {
    if (!validate_type_declarations (v))
      return ERROR;

    /*
     *  Check for a struct or union member.
     */
    if (!strcmp (v -> type, "struct") ||
	!strcmp (v -> type, "union") ||
	!strcmp (v -> qualifier, "struct") ||
	!strcmp (v -> qualifier, "union")) {
      /*
       *  If the cvar is a struct pointer, look for the
       *  struct definition.
       */
      if (v -> n_derefs && !v -> members) {
	CVAR *struct_defn;
	CVAR *mbr;
	if (((struct_defn = get_local_struct_defn (v -> type)) == NULL) &&
	    ((struct_defn = get_global_struct_defn (v -> type)) == NULL) && 
	    ((struct_defn = have_struct (v -> type)) == NULL)) {
	  __ctalkExceptionInternal (messages[idx],
				    undefined_type_x, v -> type);
	  return ERROR;
	}

	/*
	 *  Find the struct member.
	 */
	if (strstr (varname, ".") ||
	    strstr (varname, "->")) {
	  for (mbr = struct_defn -> members; mbr; mbr = mbr -> next) {
	    if (strstr (varname, mbr -> name))
	      cvar_register_output (mbr, varname);
	  }
	} else {
	  /*
	   *  The complete struct.
	   */
	  cvar_register_output (v, varname);
	}
      } else {
	/*
	 *  The cvar is a struct declaration, but not the definition.
	 */
	if (v -> members == NULL) {
	  CVAR *struct_defn;
	  CVAR *mbr;
	  if (((struct_defn = get_local_struct_defn (v -> type)) == NULL) &&
	      ((struct_defn = get_global_struct_defn (v -> type)) == NULL)) {
	    __ctalkExceptionInternal (messages[idx],
				      undefined_type_x, v -> type);
	    return ERROR;
	  }

	  /*
	   *  Struct member.
	   */
	  if (strstr (v -> name, ".") ||
	      strstr (v -> name, "->")) {
	    for (mbr = struct_defn -> members; mbr; mbr = mbr -> next) {
	      if (strstr (varname, mbr -> name))
		cvar_register_output (mbr, varname);
	    }
	  } else {
	    if (v -> attrs & CVAR_ATTR_STRUCT) {
	      /*
	       *  Variable derived from a declaration independent of 
	       *  the definition.
	       *  The function found struct_defn above.
	       */
	      if (strstr (varname, ".") ||
		  strstr (varname, "->")) {
		for (mbr = struct_defn -> members; mbr; mbr = mbr -> next) {
		  if (strstr (varname, mbr -> name))
		    cvar_register_output (mbr, varname);
		}
	      }
	    } else {
	      /*
	       *  Complete struct.
	       */
	      cvar_register_output (v, varname);
	    }
	  }
	} else {
	  CVAR *mbr;
	  for (mbr = v -> members; mbr; mbr = mbr -> next) {
	    if (strstr (varname, mbr -> name))
	      cvar_register_output (mbr, varname);
	  }
	}
      }
    } else {
      /*
       *  If varname refers to an array member, create a new 
       *  cvar for the member.  If the subscript is an object
       *  expression, generate a wrapper for it and paste
       *  into the varname buffer.  It would be much better
       *  to add another state somewhere so the interpreter can 
       *  treat arguments subscripts like in-line subscripts, but 
       *  it should work.
       */

      if (subscript_obj) {
	char tmpbuf[MAXMSG];
	subscript_object_expr = TRUE;
	strcpy (tmpbuf, 
		subscript_expr (m_messages, subscript_begin,
				subscript_end, register_c_var_receiver,
				register_c_var_method, basename, v));
	fileout (tmpbuf, 0, FRAME_START_IDX);
	return SUCCESS;
      } else {
	strcpy (subscriptbuf, varname);
      }

      if ((v -> n_derefs > 0) && index (varname, '[')) {
	CVAR *v_mbr;
	_new_cvar (CVARREF(v_mbr));
	strcpy (v_mbr -> name, varname);
	if (*v -> decl) strcpy (v_mbr -> decl, v -> decl);
	if (*v -> type) strcpy (v_mbr -> type, v -> type);
	if (*v -> qualifier) strcpy (v_mbr -> qualifier, v -> qualifier);
	if (*v -> qualifier2) strcpy (v_mbr -> qualifier2, v -> qualifier2);
	if (*v -> storage_class) 
	  strcpy (v_mbr -> storage_class, v -> storage_class);
	/*
	 *  For array elements, use one less level of dereferencing.
	 */
	if (v -> n_derefs) v_mbr -> n_derefs = v -> n_derefs - 1;
	v_mbr -> is_unsigned = v -> is_unsigned;
	v_mbr -> scope = v -> scope;
	if (!subscript_object_expr) /* Complete template output above. */
	  cvar_register_output (v_mbr, subscriptbuf);
	free (v_mbr);
      } else {
	cvar_register_output (v, varname);
      }
    }

    /*
     *  If we load libraries that correspond to C types here,
     *  we don't need to load and parse them at run time.
     */

    if (!strcmp (v -> type, "int")) {
      if (!strcmp (v -> qualifier, "long") &&
	  !strcmp (v -> qualifier2, "long")) {
	class_object_search ("LongInteger");
      } else {
	class_object_search ("Integer");
      }
    }
    if (!strcmp (v -> type, "double"))
      class_object_search ("Float");

    return SUCCESS;
  }
  return ERROR;
}

static ARG_CLASS arg_class;

/*
 *  Evaluate an argument.  Arguments can consist of:
 *  1. A constant token; e.g., 
 *     1, 2.5, 'c', "A string".
 *       -> argument object.
 *  2. An object token; e.g.,
 *     myInt, myString
 *       -> argument object.
 *  3. A C variable token;
 *       -> Expr object.
 *  4. An object expression; e.g.,
 *     myString length == 10;
 *       -> Expr object.
 *  5. A C function; e.g., 
 *     strlen ();
 *       -> a CFunction object.
 *  6. An expression with a C function; e.g., 
 *     strlen (s) + myInt
 *
 *  7. A method parameter.
 *
 *  The error_loc arg is the start of the argument which is necessary
 *  when the function tokenizes the argument.  The global
 *  line counters when entering this function are at the location
 *  of the method token, as long as method_args () does not call
 *  tokenize () before entering this function.
 */

extern int fn_arg_expression_call;

OBJECT *eval_arg (METHOD *method, OBJECT *rcvr_class, char *argbuf,
		  int main_stack_idx) {
  int i, j,
    start_stack,
    stack_end,
    n_brackets,
    parent_error_line,
    parent_error_column;
  OBJECT *arg_obj,           /* Object from argument.                */
    *fn_arg_obj,             /* Object of C function arg expression. */
    *class_obj;
  MESSAGE *m_arg;
  CVAR *cvar;
  CVAR *struct_defn = NULL;  /* Avoid a warning.  Maybe struct member
				lookups should be refactored further. */

  if (!strlen (argbuf))
    return NULL;

  arg_class = arg_null;
  arg_obj = fn_arg_obj = class_obj = NULL;
  start_stack = m_message_ptr;

  stack_end = tokenize (method_message_push, argbuf);

  error_line = parent_error_line;
  error_column = parent_error_column;

  /*
   *  Single token arguments.
   */
  if (start_stack == (stack_end + 1)) {

    m_arg = m_messages[start_stack];

    switch (m_arg -> tokentype)
      {
      case LITERAL:
      case LITERAL_CHAR:
      case INTEGER:
      case LONG:
      case FLOAT:
      case LONGLONG:
	/*
	 *   Constant tokens.
	 */
	arg_obj = constant_token_object (m_arg);
	save_method_object (arg_obj);
	arg_class = arg_const_tok;
	break;
      case DEREF:
	break;
      case LABEL:
	/*
	 *  Labels can be:
	 *  1. A global argument.
	 *  2. An Object.
	 *  3. A C variable ==> Create an, "Expr," object.
	 *  4. A new object if necessary.
	 */
	if ((arg_obj = resolve_arg (method, m_messages, start_stack)) 
	    == NULL) {
	  if (((cvar = get_local_var (m_arg -> name)) != NULL) ||
	      ((cvar = get_global_var (m_arg -> name)) != NULL)) {
	    register_c_var (m_messages, start_stack);
	    arg_obj = __ctalkCreateObject (m_arg -> name,
					   EXPR_CLASSNAME,
					   NULL,
					   LOCAL_VAR);
 	    strcpy (arg_obj -> __o_superclassname, EXPR_SUPERCLASSNAME);
 	    arg_obj -> __o_superclass = get_class_object(EXPR_SUPERCLASSNAME);
	    arg_class = arg_c_var_tok;
	    save_method_object (arg_obj);
	  } else {
	    /*
	     * Undefined object and not a C variable.  If the method
	     * still requires an object, create an object if necessary
	     * as:
	     * 1. The parameter specification, if any.  (Primitive
	     *    methods do not have parameter specifications.)
	     * 2. The receiver's class.
	     */
	    if (method -> n_args < method -> n_params) {
	      if (method -> params[method -> n_args]) {
	      } else {
		arg_obj = 
		  create_object_init (rcvr_class -> __o_name, 
				      ((rcvr_class -> __o_superclassname) ?
				       rcvr_class -> __o_superclassname : NULL),
				      m_arg -> name, m_arg -> name);
		arg_obj -> __o_class = rcvr_class;
		arg_obj -> __o_superclass = rcvr_class -> __o_superclass;
		arg_obj -> instancevars -> __o_class = rcvr_class;
		arg_obj -> instancevars -> __o_superclass = rcvr_class -> __o_superclass;
	      }
	      arg_class = arg_obj_tok;
	    }
	  }
	} else {
	  arg_class = arg_obj_tok;
	}
      } /* switch */

    if (!arg_obj) { 
      arg_obj = arg_expr_object (argbuf);
      arg_class = arg_obj_expr;
    }

    goto arg_evaled;
  } /* if (start_stack == (stack_end + 1)) */

  /*
   *  Arguments with multiple tokens.
   */
  for (i = start_stack, n_brackets = 0; i > stack_end; i--) {

    if (arg_is_constant (m_messages, start_stack, stack_end)) {
      arg_obj = arg_expr_object (argbuf);
      arg_class = arg_const_expr;
      goto arg_evaled;
    }

    m_arg = m_messages[i];

    if ((m_arg -> tokentype == NEWLINE) ||
	(m_arg -> tokentype == WHITESPACE))
      continue;

    if (i == start_stack) {
      if (IS_C_UNARY_MATH_OP(M_TOK(m_arg))) {
 	arg_has_leading_unary = m_arg -> name[0];
	leading_unary_idx = i;
	/*  	continue; */
      } else {
 	arg_has_leading_unary = FALSE;
	leading_unary_idx = -1;
      }
    }
	
    /*
     *  C function.  
     *
     *  Search for functions in the following order.
     *
     *    1. A function in the source text.
     *    2. A cvar declaration that is a prototype, 
     *       typedef, or other declaration, generally
     *       from a header file.
     *    3. A C library function for which a CFunction
     *       class method exists.
     *
     *  If the function is part of a 
     *  complex expression, issue a warning (for now).
     */
    if (IS_DEFINED_FN (M_NAME (m_arg))) {
      char *t_fn_buf, t_fn_buf_2[MAXMSG];
      int fn_arg_start, fn_arg_end;

      fn_check_expr (m_messages, i, start_stack, main_stack_idx);

      fn_arg_obj = fn_arg_expression (rcvr_class, method, m_messages, i);

      if ((fn_arg_start = scanforward (m_messages, i, stack_end, 
				       OPENPAREN)) == ERROR)
	error (m_arg, "Parser error.");
      if ((fn_arg_end = match_paren (m_messages, fn_arg_start,
				     stack_end)) == ERROR)
	error (m_arg, "Parser error.");

      t_fn_buf = collect_tokens (m_messages, i, fn_arg_start);
      if (fn_arg_obj)
	sprintf (t_fn_buf_2, "%s%s)", t_fn_buf, fn_arg_obj -> __o_name);
      else
	sprintf (t_fn_buf_2, "%s)", t_fn_buf);
      delete_object (fn_arg_obj);
      fn_arg_obj = create_object (CFUNCTION_CLASSNAME, t_fn_buf_2);
      fn_arg_obj -> 
	__o_superclass = get_class_object (CFUNCTION_SUPERCLASSNAME);
      __ctalkSetObjectValue (fn_arg_obj, t_fn_buf_2);
      free (t_fn_buf);
      arg_class = arg_c_fn_expr;
      save_method_object (fn_arg_obj);
      goto arg_evaled;
    }
  
    /*
     *  An aggregate variable.
     */
    if (((cvar = get_global_var (m_arg -> name)) != NULL) ||
	((cvar = get_local_var (m_arg -> name)) != NULL)) {
      int lookahead;
      CVAR *t;
      /*
       *  If the variable label token is the final token 
       *  of the argument, then simply register it and break, 
       *  because the reference cannot be an aggregate type.
       */
      if ((lookahead = nextlangmsg (m_messages, i)) != ERROR) {
	switch (m_messages[lookahead] -> tokentype)
	  {
	  case PERIOD:
	    if (strcmp (cvar -> type, "struct") &&
		strcmp (cvar -> qualifier, "struct"))
	      __ctalkExceptionInternal (m_messages[lookahead], 
					parse_error_x, NULL);

	    if ((lookahead = nextlangmsg (m_messages, lookahead)) == ERROR)
	      __ctalkExceptionInternal (m_arg, parse_error_x, NULL);

	    /*
	     *  If the struct is a declaration but not a definition,
	     *  then find the definition.
	     */
	    if (cvar -> members == NULL) {
	      if (((struct_defn = 
		    get_global_struct_defn (cvar -> type)) == NULL) &&
		  ((struct_defn = 
		    get_local_struct_defn (cvar -> type)) == NULL)) {
		warning (m_arg, "Undefined struct %s.", cvar -> type);
	      } else {
		for (t = struct_defn -> members; t; t = t -> next) {
		  if (!strcmp (t -> name, m_messages[lookahead] -> name)) {
		    register_c_var (m_messages, i);
		    arg_obj = create_object (EXPR_CLASSNAME, argbuf);
		    strcpy (arg_obj -> __o_superclassname, 
			    EXPR_SUPERCLASSNAME);
		    arg_obj -> __o_superclass = 
		      get_class_object (EXPR_SUPERCLASSNAME);
		    __ctalkSetObjectValue (arg_obj, argbuf);
		    arg_obj -> scope = ARG_VAR;
		    if (arg_class != arg_null) arg_class = arg_c_var_expr;
 		    goto arg_evaled;
		  }
		}
	      }
	    } else {
	      for (t = cvar -> members; t; t = t -> next) {
		if (!strcmp (t -> name, m_messages[lookahead] -> name)) {
		  register_c_var (m_messages, i);
		  arg_obj = create_object (EXPR_CLASSNAME, argbuf);
		  strcpy (arg_obj -> __o_superclassname, EXPR_SUPERCLASSNAME);
		    arg_obj -> __o_superclass = 
		      get_class_object (EXPR_SUPERCLASSNAME);
		  __ctalkSetObjectValue (arg_obj, argbuf);
		  arg_obj -> scope = ARG_VAR;
		  if (arg_class != arg_null)  arg_class = arg_c_var_expr;
 		  goto arg_evaled;
		}
	      }
	    }
	    break;
	  case DEREF:
	    if (strcmp (cvar -> type, "struct") &&
		strcmp (cvar -> qualifier, "struct"))
	      warning (m_arg, "Syntax error.");
	    if ((lookahead = nextlangmsg (m_messages, lookahead)) == ERROR)
	      error (m_arg, "Parser error.");
	    /*
	     *  A pointer to a struct might not contain the
	     *  definitions of the member elements.  Look for 
	     *  the struct definition.  It should be the
	     *  "type" member of the struct pointer cvar.
	     */
	    if (!cvar -> members) {
	      struct_defn = NULL;
	      if (!strcmp (cvar -> qualifier, "struct")) {
		if (((struct_defn = 
		      get_global_struct_defn (cvar -> type)) == NULL) &&
		    ((struct_defn = 
		      get_local_struct_defn (cvar -> type)) == NULL)) {
		  warning (m_arg, "Undefined struct %s.", cvar -> type);
		}
	      }
	    }
	    if (struct_defn) {
	      for (t = struct_defn -> members; t; t = t -> next) {
		if (!strcmp (t -> name, m_messages[lookahead] -> name)) {
		  register_c_var (m_messages, i);
		}
	      }
	    }
	    break;
	  case ARRAYOPEN:
	    ++n_brackets;
	    register_c_var (m_messages, i);
	    break;
	  case ARRAYCLOSE:
	    --n_brackets;
	    break;
	  default:
	    if (!n_brackets)
	      register_c_var (m_messages, i);
	    break;
	  }
      } else {
	/*
	 *  No further tokens - register the var, but first make sure that
	 *  it isn't an aggregate expression that we've already registered.
	 *  Some systems can register the member names independently of 
	 *  the struct definition.
	 */
	int _p;
	if ((_p = prevlangmsg(m_messages, i)) != ERROR) {
	  if ((M_TOK(m_messages[_p]) != PERIOD) && (M_TOK(m_messages[_p]) != DEREF)) {
	    register_c_var (m_messages, i);
	    if (arg_class != arg_null) arg_class = arg_c_var_expr;
	  }
	}
      }
    } else { /*  if (((cvar = get_global_var (m_arg -> name)) != NULL) ... */
      if (m_arg -> tokentype == LABEL) {
	OBJECT *tmp_object;
	if ((tmp_object = get_object (M_NAME(m_arg), NULL)) != NULL) {
	  if (m_arg -> obj && IS_OBJECT (m_arg -> obj)) {
	    m_arg -> value_obj = tmp_object;
	  } else {
	    m_arg -> obj = tmp_object;
	  }
	} else {
	  /*
	   *  TO DO - At some point, we might need to start
	   *  backtracking here if there are a lot of unresolved
	   *  args from struct elements...
	   *  strstr () is here, but this should be refactored out
	   *  along with the struct member lookup code in register_c_var.
	   */
	  if (strstr (argbuf, "->") || strstr (argbuf, ".")) {
	    if (!is_struct_member (M_NAME(m_messages[i]))) {
	  /*
	   *  For now, if a label isn't defined anywhere, generate an 
	   *  exception.
	   */
	      if (!label_is_defined (m_messages, i)) {
	__ctalkExceptionInternal (m_arg, undefined_label_x, m_arg -> name);
	      }
	    }
	  } else {
	    /*
	     *  TODO - also check secondary, etc. method and argument
	     *  labels.
	     */
	    if (i == start_stack && 
		!label_is_defined (m_messages, i)) {
	      warning (message_stack_at (main_stack_idx),
		       "Undefined label, \"%s\".", 
		       M_NAME(m_messages[i]));
	    }
	  }
	}
      }
    }
  }
      
  /*
   *  A ctalk expression.  Create an, "Expr," object.
   */
  if (arg_has_leading_unary) {
    char __t[MAXLABEL];
    strcpy (__t, trim_leading_whitespace (&argbuf[1]));
    arg_obj = arg_expr_object (__t);
  } else {
    arg_obj = arg_expr_object (argbuf);
  }
  if (arg_class != arg_null)  arg_class = arg_obj_expr;

 arg_evaled:
  for (j = start_stack; j > stack_end; j--)
    delete_message (method_message_pop ());

  if (fn_arg_obj)
    return fn_arg_obj;
  else
    return arg_obj;
}

/*
 *  Like resolve (), but only has the code for method arguments.
 */

OBJECT *resolve_arg (METHOD *rcvr_method, MESSAGE_STACK messages, 
		     int message_ptr) {

  OBJECT *result_object = NULL;
  OBJECT *class_object; 
  OBJECT *instancevar_object;
  MESSAGE *m;      
  int msg_frame_top,
    ctrlblk_start;
  int prev_label_ptr = ERROR;  /* Stack pointers to messages with      */
  int prev_method_ptr = ERROR; /*  resolved objects.                   */
  int prev_tok_ptr = ERROR;
  MESSAGE *m_prev_label = NULL;    /* Messages that refer to previously  */
  MESSAGE *m_prev_method = NULL;   /* resolved objects. Initialized here */
				   /* to avoid warnings.                 */
  int next_label_ptr;          /* Forward message references to check  */
  MESSAGE *m_next_label;       /*  method arguments, if necessary.     */
  METHOD *method;

  m = messages[message_ptr];

  if (!m || !IS_MESSAGE(m))
    _error ("Invalid message in resolve_arg.\n");

  if (m -> evaled)
    return NULL;

  /* Look for a class object. */

  msg_frame_top = message_frame_top_n (parser_frame_ptr ());

  if ((result_object = class_object_search (m -> name)) != NULL) {
    m -> obj = result_object;
    /*
     *  Add a value method only if necessary.
     */
    if (!ctrlblk_pred)
      default_method (m_messages, message_ptr);
    return result_object;
  }

  /*
   *   Check for a method parameter reference and create an
   *   object if necessary.
   */
  if (interpreter_pass == method_pass) {
    int i, n;
    METHOD *method;
    OBJECT_CONTEXT param_context;
    method = new_methods[new_method_ptr + 1];

    if (method && IS_METHOD (method)) {
      for (i = 0; i < method -> n_params; i++) {
	if (!strcmp (m -> name, method -> params[i] -> name) &&
	    !(method->params[i]->attrs & PARAM_C_PARAM)) {

	  param_context = object_context (m_messages, message_ptr);

	  switch (param_context) 
	    {
	    case c_context:
	      for (n = 0; n < method -> n_params; n++) {
		if (!strcmp (m -> name, method -> params[n] -> name)) {
		  if (need_rt_eval (m_messages, message_ptr)) {
 		    ctrlblk_pred_rt_expr (m_messages, message_ptr);
		  } else {
		    method_arg_accessor_fn (messages, message_ptr, 
					    method -> n_params - n - 1);
		  }
		}
	      }
	      break;
	    case argument_context:
 	      for (n = 0; n < method -> n_params; n++) {
 		if (!strcmp (m -> name, method -> params[n] -> name))
 		  method_arg_accessor_fn (messages, message_ptr,
 					  method -> n_params - n - 1);
 	      }
	      break;
	    default:
	      if (!m -> obj || !IS_OBJECT (m -> obj)) {
		if ((class_object =
		     class_object_search (method -> params[i] -> class)) 
		    == NULL) 
		  error (m, "Resolve: Undefined class %s.",
			 method -> params[i] -> class);
		
		result_object =
		  create_object_init (
				      ((method -> params[i] -> class) ?
				       method -> params[i] -> class : "Object"),
				      "Object",
				      method -> params[i] -> name,
				      NULLSTR);
		result_object -> __o_class = 
		  get_class_object(method -> params[i] -> class);
		result_object -> __o_superclass = 
		  get_class_object(method->params[i]->class)->__o_superclass;
		result_object -> instancevars -> __o_class = 
		  get_class_object(method -> params[i] -> class);
		result_object -> instancevars -> __o_superclass = 
		  get_class_object(method->params[i]->class)->__o_superclass;
		m -> obj = result_object;
		save_method_object (result_object);
		return result_object;
	      }
	      break;
	    }
	}
      }
    }
  }

  /* 
   *  Look for a global or local object.  
   */

  if ((result_object = get_object (m -> name, NULL)) != NULL) {
    m -> obj = result_object;
    /*
     *  The default method should be handled by resolve (),
     *  if there is no method message, so we shouldn't need
     *  to check for it here.
     */
    return result_object;
  }

  /* 
   *  Check for, "self," "super," "return," and 
   *  "returnObjectClass." If they don't translate 
   *  into run time code, they get elided here also.
   */

  if (!strcmp (m -> name, "self") && 
      need_rt_eval (messages, message_ptr)) {
    ctrlblk_pred_rt_expr (messages, message_ptr);
  } else {
    if ((result_object = 
	 self_object (messages, message_ptr)) != NULL) {
      m -> obj = result_object;
      default_method (m_messages, message_ptr);
      return result_object;
    }
  }

  if (fn_return (messages, message_ptr) == 0)
    return NULL;

  if (interpreter_pass == method_pass) {
    int i, next_label_ptr;
    if (!strcmp (m -> name, "returnObjectClass")) {
      if ((next_label_ptr = scanforward (message_stack (), message_ptr - 1,
			 message_frame_top_n (parser_frame_ptr () - 1), 
					 LABEL)) == ERROR) {
	warning (m, "Syntax error");
      }
      strcpy (new_methods[new_method_ptr + 1] -> returnclass,
	      message_stack_at(next_label_ptr) -> name);
      if ((next_label_ptr = scanforward (message_stack (), message_ptr + 1,
			 message_frame_top_n (parser_frame_ptr () - 1), 
					 SEMICOLON)) == ERROR) {
	warning (m, "Syntax error");
      }
      for (i = message_ptr; i >= next_label_ptr; i--) {
	++(message_stack_at (i)) -> evaled;
	++(message_stack_at (i)) -> output;
      }
      return NULL;
    }

    if (!strcmp (M_NAME(m), "noMethodInit")) {
      new_methods[new_method_ptr + 1] -> no_init = TRUE;
      ++(message_stack_at (message_ptr)) -> evaled;
      ++(message_stack_at (message_ptr)) -> output;
      if ((i = nextlangmsg (message_stack (), message_ptr)) == ERROR)
	warning (m, "Syntax error");
      if (message_stack_at (i) -> tokentype != SEMICOLON)
	warning (m, "Syntax error");
      ++(message_stack_at (i)) -> evaled;
      ++(message_stack_at (i)) -> output;
      return NULL;
    }
  }

  /* Attempt to resolve methods and arguments. */

  /*
   *  Here we need to check for control blocks that are not enclosed
   *  by braces and occur in the same frame as the predicate.
   */
  if (ctrlblk_blk)
    ctrlblk_start = C_CTRL_BLK -> blk_start_ptr;
  if (ctrlblk_pred)
    ctrlblk_start = C_CTRL_BLK -> pred_start_ptr;
  /*
   *  Here we have to account for, "for," loop predicates.  Because
   *  they all occur within the same frame, we use the CTRLBLK structure
   *  indexes instead of the frame top.
   */
#define STACK_MSG_FRAME(__idx_exp) ((messages == m_messages) ? \
      N_MESSAGES : \
     ((messages == tmpl_message_stack ()) ? \
      N_MESSAGES : \
     ((messages == fn_message_stack ()) ? \
      P_MESSAGES : \
     ((messages == method_buf_message_stack ()) ? \
      P_MESSAGES : \
     ((messages == c_message_stack ()) ? \
      N_MESSAGES : \
     ((messages == var_message_stack ()) ? \
      N_MESSAGES : \
     (__idx_exp)))))))

  if (ctrlblk_pred) {
    if (C_CTRL_BLK -> stmt_type == stmt_for) {
      if (for_init)
	msg_frame_top = STACK_MSG_FRAME(C_CTRL_BLK -> for_init_start);
      if (for_term)
	msg_frame_top = STACK_MSG_FRAME(C_CTRL_BLK -> for_term_start);
      if (for_inc)
	msg_frame_top = STACK_MSG_FRAME(C_CTRL_BLK -> for_inc_start);
    } else {
      /*
       *  NOTE - It may be necessary to add conditions for other
       *  special loop cases here.
       */
      msg_frame_top = 
	STACK_MSG_FRAME(message_frame_top_n (parser_frame_ptr ()));
    }
  } else {
    if (ctrlblk_blk) {
      /*
       *  If we have a code block without braces, then set 
       *  msg_frame_top to blk_start_ptr.  If the control block
       *  is delimited by braces, we can simply use the frame 
       *  top.
       */
      if (C_CTRL_BLK -> braces == 0) {
	msg_frame_top = STACK_MSG_FRAME(C_CTRL_BLK -> blk_start_ptr);
      } else {
	msg_frame_top = 
	  STACK_MSG_FRAME(message_frame_top_n (parser_frame_ptr ()));
      }
    } else {
      msg_frame_top = 
	STACK_MSG_FRAME(message_frame_top_n (parser_frame_ptr ()));
    }
  }

  /*
   *  The prev_*_ptr's are initialized at the start
   *  of the function.  Unless the function is using
   *  message_stack () (which it isn't currently), 
   *  these aren't needed.
   */
  if (message_ptr < N_MESSAGES) {
    prev_label_ptr = scanback (messages, message_ptr + 1,
			       msg_frame_top, LABEL);
    prev_method_ptr = scanback (messages, message_ptr + 1,
				msg_frame_top, METHODMSGLABEL);
    prev_tok_ptr = prevlangmsg (messages, message_ptr);
  }

  /*  If the label isn't resolved as an object above, it could be
   *  interpreted as an argument or method of an object in the
   *  predicate.
   */

  if (ctrlblk_blk) {
    if ((message_ptr <= C_CTRL_BLK -> blk_start_ptr) &&
	(message_ptr >= C_CTRL_BLK -> blk_end_ptr) &&
	(prev_label_ptr <= C_CTRL_BLK -> pred_start_ptr) &&
	(prev_label_ptr >= C_CTRL_BLK -> pred_end_ptr))
      return NULL;
  }

  if (prev_label_ptr != ERROR)
    m_prev_label = m_messages[prev_label_ptr];
  if (prev_method_ptr != ERROR)
    m_prev_method = m_messages[prev_method_ptr];

  if (prev_label_ptr == ERROR) {
    if ((message_ptr > N_MESSAGES) && 
	((prev_label_ptr = scanback (m_messages, message_ptr + 1,
 				    msg_frame_top, C_KEYWORD)) != -1)) {
      return NULL;
    } else {
      OBJECT_CONTEXT c;
      /* 
       *  Issue a warning only if we can determine that the 
       *  token appears in an object context.
       */
      if (is_c_identifier (m -> name) || is_c_keyword (m -> name))
	return NULL;
      c = object_context (m_messages, message_ptr);
      if ((c == argument_context) || (c == receiver_context))
	warning (m, "\"%s\" has unknown receiver.\n", m -> name);
      return NULL;
    }
  }

  /* The most recent reference is an object. */

  if (prev_label_ptr != ERROR && 
      ((prev_method_ptr == ERROR) || 
       (prev_label_ptr < prev_method_ptr))) {

    if (m_prev_label && m_prev_label -> obj && 
	IS_OBJECT (m_prev_label -> obj)) {

      /* The previous reference is a class object. */
      if (!strcmp (m_prev_label -> obj -> __o_classname, "Class")) {
	if (((method = get_instance_method 
	      (m_prev_label, m_prev_label -> obj, m -> name, FALSE)) != NULL) ||
	    ((method = get_class_method
	      (m_prev_label, m_prev_label -> obj, m -> name, FALSE)) != NULL)) {
	  m -> receiver_msg = m_prev_label;
	  m -> receiver_obj = m_prev_label -> obj;
	  m -> tokentype = METHODMSGLABEL;
	} else {
	  sprintf (errbuf, METHOD_ERR_FMT, M_NAME(m), 
		   m_prev_label -> obj -> __o_classname);
	  __ctalkExceptionInternal (m, undefined_method_x, errbuf);
	  return NULL;
	}
      } else {
	
	/* 
	 *   The previous label refers to an object instance. 
	 *   Determine if this label refers to a method or 
	 *   an instance variable.  Give precedence to instance
	 *   variables.
	 */

	if ((class_object = 
	     get_class_object (m_prev_label -> obj -> __o_classname))
	    == NULL)
 	  error (m_prev_label, "Unknown class \"%s.\"", 
		 m_prev_label ->obj -> __o_classname);
	/*
	 *   We still have to make sure that the method (at message_ptr)
	 *   immediately follows the receiver (at prev_label_ptr).
	 */
	if (prev_tok_ptr == prev_label_ptr) {
	  if (((instancevar_object = 
		__ctalkGetInstanceVariable (m_prev_label -> obj, m -> name,
					     FALSE)) != NULL) ||
	      ((method = get_instance_method (m, 
					      class_object, 
					      m -> name, TRUE)) != NULL)) {
	    m -> receiver_msg = m_prev_label;
	    m -> receiver_obj = m_prev_label -> obj;
	    m -> tokentype = METHODMSGLABEL;
	  } else {
	    sprintf (errbuf, METHOD_ERR_FMT, M_NAME(m),
		     m_prev_label -> obj -> __o_classname);
	    __ctalkExceptionInternal (m, undefined_method_x, errbuf);
	    return NULL;
	  }
	}
      }
    }

    /* If there are no arguments, the method call code must be output
	immediately, because it also marks the ctalk statement not to
	be output.  Otherwise, the parser waits for the next pass, and
	the ctalk statement is output, which is non-parsable by GCC.
    */
    if ((next_label_ptr = nextlangmsg (message_stack (), message_ptr))
	!= ERROR) {
      m_next_label = m_messages[next_label_ptr];

      if (m -> tokentype == METHODMSGLABEL)
	method_call (message_ptr);
      return M_VALUE_OBJ (m_prev_label);
    }

    return m_prev_label -> obj;
  }
  
  /* The most recent reference is a method.  If not evaled, set the
     method args and call the method.
  */

  m -> obj = result_object;
  m -> receiver_obj = m_prev_label -> obj;

  if (prev_method_ptr < prev_label_ptr) {
    if ((method = get_instance_method (m_prev_label, m_prev_label -> obj, 
      m_prev_method -> name, TRUE)) != NULL) {

      if (!m -> evaled && !m_prev_label -> evaled) 
	method_args (method, message_ptr);
    }    
  }  

  /* ERROR here means that the statement has already been evaluated. */

  if (!m_prev_method -> evaled) {
    if (method_call (prev_method_ptr) == ERROR)
      return NULL;
  }

  return result_object;

}

extern METHOD *new_methods[MAXARGS+1];  /* Declared in primitives.c. */
extern int new_method_ptr;

/* 
 * Eval_receiver_token is like eval_arg (), but it also checks for
 * parameters and constants that are receivers.
 */

OBJECT *eval_receiver_token (MESSAGE_STACK messages, int idx, OBJECT *rcvr_class) {

  OBJECT *token_object = NULL;  /* Avoid a warning. */
  char buf[MAXLABEL];
  int r;
  MESSAGE *m;

  m = messages[idx];

  switch (m -> tokentype)
    {
    case LABEL:  /* Determine if the argument refers to an object,
		    a parameter, or a C variable.  If an object doesn't 
		    exist, create an object of the appropriate class. */
      if ((r = register_c_var (messages, idx)) == SUCCESS) {
 	if ((token_object = get_object (m -> name,
 					rcvr_class -> __o_name)) == NULL) {
 	  token_object = create_object (rcvr_class -> __o_name, m -> name);
 	  strcpy (token_object -> __o_superclassname,
 		  rcvr_class -> __o_superclassname);
 	  token_object -> scope = ARG_VAR;
 	  strcpy (token_object -> __o_value, token_object -> __o_name);
 	}
      } else {  /* The message refers to an object. */
	/*
	 *  The order of precedence for resolving objects should be:
	 *  1. Local objects.
	 *  2. Method parameters, if parsing a method. Note that 
	 *     the new method is at the top of the new_methods[]
         *     stack in primitives.c, and not the method that 
	 *     gets passed to method_args (), which should be 
	 *     a superclass method.
	 *  3. Global objects.
	 *
	 *  TO DO - We should also check for parameter shadowing 
	 *  here.
	 */
 	if ((token_object =
 	     get_local_object (m -> name, rcvr_class -> __o_name))
	    == NULL) {
	  
 	  if (interpreter_pass == method_pass)
	    token_object = method_arg_object (m);

	  /*
	   *  If the object isn't global either, then create a new
	   *  object.
	   */
 	  if (!token_object) {
 	    if ((token_object =
 		 get_global_object (m -> name, rcvr_class -> __o_name))
 		== NULL) {
 	      token_object = 
 		create_object_init (rcvr_class -> __o_name,
 				    rcvr_class -> __o_superclassname,
 				    m -> name, m -> name);
 	      __ctalkSetObjectValue (token_object, m -> name);
 	      token_object -> scope = ARG_VAR;
	      save_method_object (token_object);
 	    }
 	  }
 	}
      }
      break;
      /* 
       *  Translate a C constant into an object of the appropriate
       *  type.  We shouldn't need to load a library for them.
       */
    case LITERAL:
    case INTEGER:
    case LONG:
    case DOUBLE:
    case LONGLONG:
      token_object = constant_token_object (m);
      __ctalkSetObjectValue (token_object, m -> name);
      token_object -> scope = ARG_VAR;
      break;
    case LITERAL_CHAR:
      token_object = constant_token_object (m);
      if (*m -> name == '\'')
  	strcpy (buf, m -> name);
      else
  	sprintf (buf, "\'%s\'", m -> name);
      __ctalkSetObjectValue (token_object, m -> name);
      token_object -> scope = ARG_VAR;
      break;
    }
  return token_object;
}

struct _argstr {
  char *arg;
  int start_idx,
    end_idx;
} argstrs[MAXARGS];
int argstrptr;

int arg_error_loc (int level, int line, int column, 
		   ERROR_LOCATION *error_loc) {
   error_loc -> error_line = line;
   error_loc -> error_column = column;
   error_loc -> parser_lvl = level;
   strcpy (error_loc -> fn, "");
   return SUCCESS;
}

/*
 *  Split the argument list in separate arguments, then 
 *  evaluate each argument.
 *  
 *  To split the argument lists correctly, check first
 *  if the method requires any arguments.  If not, 
 *  simply return.  
 *
 *  If the method requires one argument, then simply
 *  evaluate the tokens until the next METHOD_ARG_TERM_MSG_TYPE;
 *  
 *  If the method requires more than one argument, 
 *  then split the argument by commas, and evaluate each
 *  separately.  NOTE - If there are subexpressions within 
 *  the argument, make sure that only the outer expression
 *  gets split.
 *
 *  Technically, an argument expression may be one of the following:
 *
 *    LABEL
 *    LITERAL
 *    LITERAL_CHAR
 *    INTEGER
 *    FLOAT
 *    LONG
 *    LONGLONG
 *    (<expr>)
 *
 *  NOTE = However, if we encounter a complex argument, or a sequence of
 *  methods that requires backtracking, we simply record all
 *  the tokens following the method, register the variables, and 
 *  evaluate the entire sequence of tokens at run time, when we
 *  know the values of the objects.
 *
 *  FIXME! - If we encounter an expression like:
 *    myArray size == a
 *  it is evaluated as a method with one argument, "== a,"
 *  instead of a method with no arguments followed by a method
 *  that requires one argument.
 */

int method_args (METHOD *method, int method_msg_ptr) {

  int arglist_start,
    arglist_end,
    i,
    j,
    r,
    open_paren_ptr,
    close_paren_ptr;
  Boolean paren_delim;
  char argbuf[MAXMSG],
    rcvr_class[MAXLABEL];
  OBJECT *rcvr_class_obj,
    *arg_obj;
  MESSAGE *m_arg, 
    *m_method;
  ERROR_LOCATION error_loc;

  if ((arglist_start = nextlangmsg (message_stack(), method_msg_ptr)) == ERROR)
    error (message_stack_at (method_msg_ptr), "Parser error.");

  if ((arglist_end = method_arglist_limit (message_stack (), arglist_start, 
					   method -> n_params,
					   method -> varargs)) == ERROR) {
    __ctalkExceptionInternal (message_stack_at (method_msg_ptr),
			     parse_error_x, NULL);
    return ERROR;
  }

  if (!global_constructor_arg (method, method_msg_ptr))
    return SUCCESS;

  /*
   *  Handle empty argument lists directly.
   */
  if ((arglist_start == arglist_end) && 
      _is_empty_arglist (message_stack (), arglist_start,
			 stack_start (message_stack ()))) {
    if (method -> n_args) {
      warning (message_stack_at (arglist_start), 
	       "Method %s: Wrong number of arguments\n.", method -> name);
    }
    return SUCCESS; 
  }

  /*
   *  Adjust the start and end indexes to exclude parentheses.
   * 
   *  However, we also have to check that the parentheses
   *  enclose the entire expression, so we don't also 
   *  exclude parentheses in expressions like (1 + 1) - (2 + 2).
   */

  if ((message_stack_at (arglist_start) -> tokentype == OPENPAREN) &&
      (message_stack_at (arglist_end) -> tokentype == CLOSEPAREN) &&
      _external_parens (message_stack (), arglist_start, arglist_end)) {
    paren_delim = True;
    ++message_stack_at (arglist_start) -> evaled;
    ++message_stack_at (arglist_start) -> output;
    ++message_stack_at (arglist_end) -> evaled;
    ++message_stack_at (arglist_end) -> output;
    open_paren_ptr = arglist_start;
    close_paren_ptr = arglist_end;
    arglist_start = nextlangmsg (message_stack (), arglist_start);
    arglist_end = prevlangmsg (message_stack (), arglist_end);
  } else {
    paren_delim = False;
    open_paren_ptr = close_paren_ptr = -1;
  }

  argstrs[0].start_idx = arglist_start;

  /*
   *  If the method requires only one argument, and does not
   *  accept a variable number of arguments, then collect
   *  the tokens of the first thing that looks like an argument.
   */

  if (!method -> varargs && (method -> n_params == 1)) {

    m_method = message_stack_at (method_msg_ptr);
    if (!strcmp (m_method -> receiver_obj -> __o_classname, "Class"))
      strcpy (rcvr_class, m_method -> receiver_obj -> __o_name);
    else
      strcpy (rcvr_class, m_method -> receiver_obj -> __o_classname);

    rcvr_class_obj = get_class_object (rcvr_class);

    /*
     *  If we're looking for only one argument and we encounter
     *  a parenthesis, then the argument is the entire expression
     *  in the parenthesis.  Otherwise, it should only be 
     *  the first token after the method.
     */

    argstrs[0].arg = collect_tokens (message_stack (), arglist_start,
				     arglist_end);
    argstrs[0].end_idx = arglist_end;

    arg_error_loc (current_parser () -> level,
		   message_stack_at (argstrs[0].start_idx) -> error_line,
		   message_stack_at (argstrs[0].start_idx) -> error_column,
		   &error_loc);

/*      if ((arg_obj = eval_arg (method, rcvr_class_obj, argstrs[0].arg,  */
/*  			     error_loc)) */
/*  	!= NULL) { */
    if ((arg_obj = eval_arg (method, rcvr_class_obj, argstrs[0].arg, 
			     method_msg_ptr))
	!= NULL) {
      if (IS_COLLECTION_SUBCLASS_OBJ(rcvr_class_obj)) {
	/* Collection argument processing should go here. */
	/* The expression should be formatted as a run time expression,
	   from method_call. */
	if (subscript_object_expr)
	  subscript_object_expr = FALSE;
      } else {
	/*
	 *  TO DO - eval_arg () can set arg_obj to a function that is
	 *  itself an argument, so a temporary fix here is to output
	 *  the entire argument.  This needs to be fixed in eval_arg ()
	 *  if the parser is to interpret mixed function/object
	 *  argument lists correctly.
	 */
	if (!strcmp (arg_obj -> __o_classname, "CFunction")) {
  	  if ((r = fn_output_context (message_stack (), method_msg_ptr,
		      arg_obj, method, arglist_start, arglist_end))
  	      == FN_IS_ARG_RETURN_VAL) {
  	    generate_c_expr_store_arg_call (m_method -> receiver_obj,
  					    method, arg_obj, 
  			    M_NAME(message_stack_at (argstrs[0].start_idx)),
  			    FRAME_START_IDX);
  	  }
	} else {
	  if (ctrlblk_pred) {
	    ctrlblk_push_args (m_method -> receiver_obj, method, arg_obj);
	  } else {
	    if (!method -> cfunc) {
	      if (subscript_object_expr) {
		/* The template with the argument call is generated in 
		   register_c_method_arg () */
		subscript_object_expr = FALSE;
	      } else {
		if (!collection_needs_rt_eval 
		    (get_class_object (m_method -> receiver_obj -> __o_classname), 
		     method))
		  generate_store_arg_call (m_method -> receiver_obj,
					   method, arg_obj, FRAME_START_IDX);
	      }
	    }
	  }
	}
      }
      store_arg_object (method, arg_obj);
    }
    for (i = arglist_start; i >= arglist_end; i--) {
      message_stack_at (i) -> obj = arg_obj;
      ++message_stack_at (i) -> evaled;
      ++message_stack_at (i) -> output;
    }
    free (argstrs[0].arg);
    argstrs[0].arg = NULL;
    argstrs[0].start_idx = argstrs[0].end_idx = 0;
    return SUCCESS;
  }

  for (i = arglist_start, argstrptr = 0, *argbuf = 0; i >= arglist_end; i--) {

    m_arg = message_stack_at (i);

    switch (m_arg -> tokentype)
      {
      case SEMICOLON:
      case ARGSEPARATOR:
	argstrs[argstrptr].arg = strdup (trim_leading_whitespace (argbuf));
	argstrs[argstrptr++].end_idx = prevlangmsg (message_stack (), i);
	/*
	 *  Set the start index of the next argument here.
	 */
	argstrs[argstrptr].start_idx = nextlangmsg (message_stack (), i) ;
	*argbuf = 0;
	++m_arg -> evaled;
	++m_arg -> output;
	break;
      default:
	strcat (argbuf, m_arg -> name);
	break;
      }

    if (i == arglist_end) {
      argstrs[argstrptr].arg = 
	strdup (trim_leading_whitespace (argbuf));
      argstrs[argstrptr++].end_idx = arglist_end;
    }
  }

  m_method = message_stack_at (method_msg_ptr);
  if (!strcmp (m_method -> receiver_obj -> __o_classname, "Class"))
    strcpy (rcvr_class, m_method -> receiver_obj -> __o_name);
  else
    strcpy (rcvr_class, m_method -> receiver_obj -> __o_classname);

  rcvr_class_obj = get_class_object (rcvr_class);

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

    arg_error_loc (current_parser () -> level, 
		   message_stack_at (argstrs[i].start_idx) -> error_line,
 		   message_stack_at (argstrs[i].start_idx) -> error_column,
 		   &error_loc);

/*      if ((arg_obj = eval_arg (method, rcvr_class_obj, argstrs[i].arg,  */
/*  			     error_loc)) != NULL) { */
    if ((arg_obj = eval_arg (method, rcvr_class_obj, argstrs[i].arg, 
			     method_msg_ptr)) != NULL) {
      if (!strcmp (arg_obj -> __o_classname, "CFunction")) {
	int r_fn;
	if ((r_fn = fn_output_context (message_stack (), method_msg_ptr, 
			   arg_obj, method, 
			   argstrs[i].start_idx, 
				       argstrs[i].end_idx)) 
	    == FN_IS_ARG_RETURN_VAL) {
	  /*
	   *  Look for a template and generate a store arg call.  
	   *  Otherwise, print a warning.
	   */
	  char *r_expr;
 	  OBJECT *r_expr_object;
	  if ((r_expr = clib_fn_rt_expr (message_stack(), 
					 argstrs[i].start_idx)) != NULL) {
 	    r_expr_object = create_object (EXPR_CLASSNAME, r_expr);
 	    r_expr_object -> __o_superclass = 
	      get_class_object (EXPR_SUPERCLASSNAME); 
	    generate_store_arg_call (m_method -> receiver_obj,
				     method, 
				     r_expr_object,
	     frame_at (current_parser () -> frame) -> message_frame_top);
	  } else {
	    warning (message_stack_at(argstrs[i].start_idx),
		     "Function %s used as method argument without template.",
		     M_NAME(message_stack_at(argstrs[i].start_idx)));
	  }
	} else {
	  CFUNC *__fn;
	  int __idx, __start_idx, __end_idx;
	  char __rtrncls[MAXLABEL], __objrtrnbf[MAXMSG], __buf[MAXMSG];
	  __start_idx = m_message_ptr;
	  __end_idx = tokenize (method_message_push, arg_obj -> __o_name);
	  
	  if ((__fn = get_function (M_NAME(m_messages[__start_idx])))
	      == NULL) {
	    if (interpreter_pass == method_pass)
	      warning (message_stack_at(method_msg_ptr), "In method %s:",
		       new_methods[new_method_ptr+1] -> name);
	    warning (message_stack_at(method_msg_ptr), 
		     "Undefined or unprototyped function %s.  Return class defaulting to Integer.",
		     M_NAME(m_messages[__start_idx]));
	    strcpy (__rtrncls, INTEGER_CLASSNAME);
	  } else {
	    if (__fn) {
	      strcpy (__rtrncls, cfunc_return_class (__fn));
	    }
	  }

	  strcpy (__objrtrnbf, 
	  fmt_c_to_obj_call (m_method -> receiver_obj, method, arg_obj));

	  sprintf (__buf, "__ctalk_arg (\"%s\", \"%s\", (void *)%s);\n",
		   m_method -> receiver_obj->__o_name, 
		   method->name, __objrtrnbf);

	  fileout (__buf, 0,
		   frame_at (current_parser () -> frame) -> message_frame_top);

	  for (__idx = __start_idx; __idx > __end_idx; __idx--)
	    delete_message (method_message_pop ());
	} 
      } else {
	if (ctrlblk_pred) {
	  ctrlblk_push_args (m_method -> receiver_obj, method, arg_obj);
	} else {
	  /*
	   *  If the interpreter method has a cfunc, it is a primitive
	   *  method and is called by method_call (), below.
	   */
	  if (!method -> cfunc) {
	    if (!collection_needs_rt_eval 
		(get_class_object(m_method->receiver_obj->__o_classname), method))
	      generate_store_arg_call (m_method -> receiver_obj,
				       method, arg_obj,
	       frame_at (current_parser () -> frame) -> message_frame_top);
	  }
	}
      }
      store_arg_object (method, arg_obj);
      for (j = argstrs[i].start_idx; j >= argstrs[i].end_idx; j--) {
	message_stack_at (j) -> obj = arg_obj;
	++message_stack_at (j) -> evaled;
	++message_stack_at (j) -> output;
      }
    }
  }

  while (--argstrptr >= 0) {
    free (argstrs[argstrptr].arg);
    argstrs[argstrptr].arg = NULL;
    argstrs[argstrptr].start_idx = 
      argstrs[argstrptr].end_idx = 0;
  }

  return SUCCESS;
}

METHOD *get_super_instance_method (OBJECT *o, char *name, int warn) {

  if (o -> __o_superclass) 
    return get_instance_method (NULL, o -> __o_superclass, name, warn);
  else
    return NULL;
}

/* 
 *   Retrieve an instance method for an object by its class object or 
 *   superclass objects.  If not found, search the class libraries
 *   for the method.
 */

METHOD *get_instance_method (MESSAGE *m_org, OBJECT *o, char *name, int warn) {

  OBJECT *class;
  METHOD *m, *requested_method = NULL;

  if (strcmp (o -> __o_classname, "Class")) 
    class = class_object_search (o -> __o_classname);
  else 
    class = o;

  if (!class || !IS_OBJECT(class))
    return NULL;

  for (m = class -> instance_methods; m; m = m -> next) {
    if (!strcmp (m -> name, name)) {
      requested_method = m;
      queue_method_for_output (class -> __o_name, m);
    }
    if (!m -> next)
      break;
  }
  
  if (requested_method)
    return requested_method;

  if (o->__o_superclass) {
    if ((requested_method = 
	 get_instance_method (((m_org) ? m_org : NULL), 
			      o->__o_superclass, name, warn)) != NULL) 
      return requested_method;
  } else {
    OBJECT *__t;
    if ((__t = 
	 class_object_search (class -> __o_superclassname)) != NULL) {
      if (IS_OBJECT(__t)) {
	if ((requested_method = 
	     get_instance_method (((m_org) ? m_org : NULL), 
				  __t, name, warn)) != NULL) 
	  return requested_method;
      }
    }
  }

  /* If the method still isn't found, try to find a prototype, then
     look in the the class library once more. */
  if (requested_method) {
    return requested_method;
  } else {
    library_search (class -> __o_name);
    for (m = class -> instance_methods; m; m = m -> next) {
      if (!strcmp (m -> name, name)) {
	requested_method = m;
	queue_method_for_output (class -> __o_name, m);
      }
      if (!m -> next)
	break;
    }
  }

  if (requested_method) {
    return requested_method;
  } else {
    if (warn)
      _warning ("Method %s (Class %s) not found.\n", name, class -> __o_name);
  }

  return NULL;

}

/* 
 *   Retrieve a class method for an object by its class object or 
 *   superclass objects.  If not found, search the class libraries
 *   for the method.
 */

METHOD *get_class_method (MESSAGE *m_org, OBJECT *o, char *name, int warn) {

  OBJECT *class;
  METHOD *m, *requested_method = NULL;

  if (strcmp (o -> __o_classname, "Class")) 
    class = class_object_search (o -> __o_classname);
  else 
    class = o;

  for (m = class -> class_methods; m; m = m -> next) {
    if (!strcmp (m -> name, name)) {
      requested_method = m;
      queue_method_for_output (class -> __o_name, m);
    }
    if (!m -> next)
      break;
  }
  
  if (requested_method)
    return requested_method;

  if (o->__o_superclass) {
    if ((requested_method = 
	 get_class_method (m_org, o -> __o_superclass, name, warn)) != NULL) 
      return requested_method;
  } else {
    if ((o->__o_superclass = 
	 class_object_search (class -> __o_superclassname)) != NULL) {
      if ((requested_method = 
	   get_class_method (m_org, o -> __o_superclass, name, warn)) != NULL) 
	return requested_method;
    }
  }

  /* If the method still isn't found, try to find it in the class
     library once more. */
  if (requested_method) {
    return requested_method;
  } else {
    library_search (class -> __o_name);
    for (m = class -> class_methods; m; m = m -> next) {
      if (!strcmp (m -> name, name)) {
	requested_method = m;
	queue_method_for_output (class -> __o_name, m);
      }
      if (!m -> next)
	break;
    }
  }

  if (requested_method) {
    return requested_method;
  } else {
    if (warn)
      _warning ("Method %s (Class %s) not found.\n", name, class -> __o_name);
  }

  return NULL;

}

/* 
 *   Define a method for a class object's internal 
 *   dictionary.
 */

int define_method  (char *classname, char *name, 
		    int src_msg, int n_params) {
  METHOD *m, *tm;
  OBJECT *class;
  int func_end_ptr;
  char src[MAXMSG];
  char newsrc[MAXMSG];   /* DJGPP's sprintf () seems not to be
			    able to sprintf to the argument buf. */
  char buf[MAXMSG];
  int i;
  
  if ((class = get_class_object (classname)) == NULL)
    error (message_stack_at (src_msg), "Undefined class %s", classname);

  if ((m = (METHOD *)calloc (1, sizeof (struct _method))) == NULL)
    error (message_stack_at (src_msg),
	   "define_method: %s.", strerror (errno));

  strcpy (m -> sig, "METHOD");
  strcpy (m -> name, name);
  m -> n_params = n_params;
  m -> n_args = 0;
  func_end_ptr = find_function_close (message_stack (), src_msg - 1);
  *src = *buf = 0;
  for (i = src_msg; i >= func_end_ptr; i--)
    strcat (src, message_stack_at (i) -> name);

  /* Construct the method selector for the generated source code. */
  /* TO DO - Factor this out for instance methods, and make sure
     that we don't also need to change the method name. */
  trim_whitespace (newsrc, src);
  sprintf (buf, "%s%s%s", classname, INSTANCE_SELECTOR, newsrc);

  m -> src = strdup (buf);

  m -> error_line = message_stack_at (src_msg) -> error_line;
  m -> error_column = message_stack_at (src_msg) -> error_column;

  if (class -> instance_methods == NULL) {
    class -> instance_methods = m;
  } else {
    for (tm = class -> instance_methods; tm -> next; tm = tm -> next)
      ;
    tm -> next = m;
    m -> prev = tm;
  }

  /* All methods are marked as imported until we get the method
     buffering straightened out. */
  m -> imported = True;
  m -> queued = False;
  queue_method_for_output (class -> __o_name, m);

  return SUCCESS;
}

/*
 *    Define a primitive method (one that calls a C language function
 *    internally).  Because this is part of the initialization code,
 *    we'll construct a dummy method with error line 0 for the 
 *    error messages.
 */

int define_primitive_method (char *classname, char *name, 
			     OBJECT *(*cfunc)(int), int n_params, 
			     int varargs) {
  METHOD *m, *tm;
  MESSAGE *orig;
  OBJECT *class;
  
  orig = new_message ();

  if ((class = get_class_object (classname)) == NULL)
    error (orig, "Undefined class %s", classname);

  if ((m = (METHOD *)calloc (1, sizeof (struct _method))) == NULL)
    _error ("define_primitive_method: %s.", strerror (errno));

  strcpy (m -> sig, "METHOD");
  strcpy (m -> name, name);
  m -> n_params = n_params;
  m -> varargs = varargs;
  m -> n_args = 0;
  m -> cfunc = cfunc;

  if (class -> instance_methods == NULL) {
    class -> instance_methods = m;
  } else {
    for (tm = class -> instance_methods; tm -> next; tm = tm -> next)
      ;
    tm -> next = m;
    m -> prev = tm;
  }
  delete_message (orig);
  return SUCCESS;
}

/*
 *  New_method (), and other functions, call method_declaration_msg ()
 *  when the input has been tokenized and semantically analyzed by
 *  resolve, so it also uses the METHODMSGLABEL token, unlike 
 *  method_declaration_info (), evaluates the C translation
 *  of the method declaration.
 *
 *  Unlike method_declaration_info, this function does not parse
 *  the parameter declarations.
 *
 *  This function should be able to parse from the start of 
 *  a stack frame, to the beginning of the parameter declarations,
 *  and also parse a method declaration that ends without a
 *  parameter declaration and function body.
 *  Handle a method declaration that has either one or two 
 *  arguments after the method. 
 *
 *  A method declaration can have the form of:
 *
 *       <Class> method <function> (<param_declarations>) {<body>}
 *
 *  or 
 *       <Class> method <alias> <function> (<param_declarations>) {<body>}
 *
 *  New_method translates the method declaration into standard C, and 
 *  stores the declaration with the method body in the class object's
 *  method dictionary.
 *
 *  <param_declarations> in an untranslated method get parsed by 
 *  function_param_declarations ().
 *  The <function> element must be a valid C function name, although
 *  it gets changed into a class method selector.
 */

static int method_decl_msg_states[] = {
  LABEL,             METHODMSGLABEL,  /* 0. */
  LABEL,             OPENPAREN,       /* 1. */
  LABEL,             LABEL,           /* 2. */
  LABEL,             SEMICOLON,       /* 3. */
  METHODMSGLABEL,    LABEL,           /* 4. */
  METHODMSGLABEL,    CHAR,            /* 5. Overloaded C operators.  */
  METHODMSGLABEL,    EQ,              /* 6. */
  METHODMSGLABEL,    BOOLEAN_EQ,      /* 7. */
  METHODMSGLABEL,    GT,              /* 8. */
  METHODMSGLABEL,    GE,              /* 9. */
  METHODMSGLABEL,    ASR,             /* 10. */
  METHODMSGLABEL,    ASR_ASSIGN,      /* 11. */
  METHODMSGLABEL,    LT,              /* 12. */
  METHODMSGLABEL,    LE,              /* 13. */
  METHODMSGLABEL,    ASL,             /* 14. */
  METHODMSGLABEL,    ASL_ASSIGN,      /* 15. */
  METHODMSGLABEL,    PLUS,            /* 16. */
  METHODMSGLABEL,    PLUS_ASSIGN,     /* 17. */
  METHODMSGLABEL,    INCREMENT,       /* 18. */
  METHODMSGLABEL,    MINUS,           /* 19. */
  METHODMSGLABEL,    MINUS_ASSIGN,    /* 20. */
  METHODMSGLABEL,    DECREMENT,       /* 21. */
  METHODMSGLABEL,    DEREF,           /* 22. */
  METHODMSGLABEL,    MULT,            /* 23. */
  METHODMSGLABEL,    MULT_ASSIGN,     /* 24. */
  METHODMSGLABEL,    DIVIDE,          /* 25. */
  METHODMSGLABEL,    DIV_ASSIGN,      /* 26. */
  METHODMSGLABEL,    BIT_AND,         /* 27. */
  METHODMSGLABEL,    BOOLEAN_AND,     /* 28. */
  METHODMSGLABEL,    BIT_AND_ASSIGN,  /* 29. */
  METHODMSGLABEL,    BIT_COMP,        /* 30. */
  METHODMSGLABEL,    LOG_NEG,         /* 31. */
  METHODMSGLABEL,    INEQUALITY,      /* 32. */
  METHODMSGLABEL,    BIT_OR,          /* 33. */
  METHODMSGLABEL,    BIT_XOR,         /* 34. */
  METHODMSGLABEL,    BIT_OR_ASSIGN,   /* 35. */
  METHODMSGLABEL,    BIT_XOR_ASSIGN,  /* 36. */
  METHODMSGLABEL,    PERIOD,          /* 37. */
  METHODMSGLABEL,    ELLIPSIS,        /* 38. */
  METHODMSGLABEL,    LITERALIZE,      /* 39. */
  METHODMSGLABEL,    MACRO_CONCAT,    /* 40. */
  METHODMSGLABEL,    CONDITIONAL,     /* 41. */
  METHODMSGLABEL,    COLON,           /* 42. */
  METHODMSGLABEL,    BOOLEAN_AND,     /* 43. */
  METHODMSGLABEL,    BOOLEAN_OR,      /* 44. */
  METHODMSGLABEL,    MODULUS,         /* 45. */
  CHAR,              LABEL,           /* 46. */
  EQ,                LABEL,           /* 47. Overloaded operators. */
  BOOLEAN_EQ,     LABEL,           /* 48. */
  GT,             LABEL,           /* 49. */
  GE,             LABEL,           /* 50. */
  ASR,            LABEL,           /* 51. */
  ASR_ASSIGN,     LABEL,           /* 52. */
  LT,             LABEL,           /* 53. */
  LE,             LABEL,           /* 55. */
  ASL,            LABEL,           /* 56. */
  ASL_ASSIGN,     LABEL,           /* 57. */
  PLUS,           LABEL,           /* 58. */
  PLUS_ASSIGN,    LABEL,           /* 59. */
  INCREMENT,      LABEL,           /* 60. */
  MINUS,          LABEL,           /* 61. */
  MINUS_ASSIGN,   LABEL,           /* 62. */
  DECREMENT,      LABEL,           /* 63. */
  DEREF,          LABEL,           /* 64. */
  MULT,           LABEL,           /* 65. */
  MULT_ASSIGN,    LABEL,           /* 66. */
  DIVIDE,         LABEL,           /* 67. */
  DIV_ASSIGN,     LABEL,           /* 68. */
  BIT_AND,        LABEL,           /* 70. */
  BOOLEAN_AND,    LABEL,           /* 71. */
  BIT_AND_ASSIGN, LABEL,           /* 72. */
  BIT_COMP,       LABEL,           /* 73. */
  LOG_NEG,        LABEL,           /* 74. */
  INEQUALITY,     LABEL,           /* 75. */
  BIT_OR,         LABEL,           /* 76. */
  BIT_OR_ASSIGN,  LABEL,           /* 77. */
  BIT_XOR,        LABEL,           /* 78. */
  BIT_XOR_ASSIGN, LABEL,           /* 79. */
  PERIOD,         LABEL,           /* 80. */
  ELLIPSIS,       LABEL,           /* 81. */
  LITERALIZE,     LABEL,           /* 82. */
  MACRO_CONCAT,   LABEL,           /* 83. */
  CONDITIONAL,    LABEL,           /* 84. */
  COLON,          LABEL,           /* 85. */
  BOOLEAN_AND,    LABEL,           /* 86. */
  BOOLEAN_OR,     LABEL,           /* 87. */ 
  MODULUS,        LABEL,           /* 88. */ 
  -1,                           0 
  };

#define METHOD_MSG_STATE_COLS 2

#define METHOD_DECL_MSG_STATE(x) (check_state ((x), message_stack (), \
              method_decl_msg_states, METHOD_MSG_STATE_COLS))

int method_declaration_msg (int start_ptr, int end_ptr, char *name, 
			    int *name_ptr, char *alias, 
			    int *alias_ptr) {
  int j;
  MESSAGE *m_tok;
  int state,
    last_state;

  for (j = start_ptr, *name = 0, *alias = 0,
	 *name_ptr = ERROR, *alias_ptr = ERROR; 
       j > end_ptr; j--) {

    m_tok = message_stack_at (j);
    if ((m_tok -> tokentype == NEWLINE) ||
	(m_tok -> tokentype == WHITESPACE))
      continue;

    if ((state = METHOD_DECL_MSG_STATE (j)) == ERROR) {
      char *s, buf[MAXMSG];
      s = collect_tokens (message_stack (), start_ptr, 
			  nextlangmsg (message_stack (), j));
      strcpy (buf, s);
      __xfree (s);
      error (m_tok, "Parse error: %s ... .", buf);
    }

    switch (m_tok -> tokentype)
      {
      case LABEL:
	if (state == 2) {
	  strcpy (alias, m_tok -> name);
	  *alias_ptr = j;
	}
	if (state == 1 || state == 3) {
	  strcpy (name, m_tok -> name);
	  *name_ptr = j;
	}
	break;
      case CHAR:
      case EQ:
      case BOOLEAN_EQ:
      case GT:
      case GE:
      case ASR:
      case ASR_ASSIGN:
      case LT:
      case LE:
      case ASL:
      case ASL_ASSIGN:
      case PLUS:
      case PLUS_ASSIGN:
      case INCREMENT:
      case MINUS:
      case MINUS_ASSIGN:
      case DECREMENT:
      case DEREF:
      case MULT:
      case MULT_ASSIGN:
      case DIVIDE:
      case DIV_ASSIGN:
      case BIT_AND:
      case BOOLEAN_AND:
      case BOOLEAN_OR:
      case BIT_AND_ASSIGN:
      case BIT_COMP:
      case LOG_NEG:
      case INEQUALITY:
      case BIT_OR:
      case BIT_OR_ASSIGN:
      case BIT_XOR:
      case BIT_XOR_ASSIGN:
      case MODULUS:
      case PERIOD:
      case ELLIPSIS:
      case LITERALIZE:
      case MACRO_CONCAT:
      case CONDITIONAL:
      case COLON:
	if (state >= 46 && state <= 88) {
	  strcpy (alias, m_tok -> name);
	  *alias_ptr = j;
	}
      default:
	break;
      }

    last_state = state;
  }

  return SUCCESS;

}

/*
 *    Call a method, given its token's message stack index.
 *    Resolve () should already have identified its receiver.
 */

int method_call (int method_message_ptr) {

  OBJECT *receiver = NULL;   /* Avoid a warning.  If receiver is not 
				set further down, then there needs
				to be another way to determine the
				receiver. */
  OBJECT *class_object;
  OBJECT *value_object;
  OBJECT *instancevar_object;
  MESSAGE *m,
    *m_super;
  MESSAGE *rcvr_msg;
  METHOD *method;
  int i,
    rcvr_ptr = 0,             /* Avoid a warning */
    stmt_end_ptr,
    m_super_idx;
  OBJECT *(*cfunc)(),         /* C function that performs method.      */
    *resultObject;

  m = message_stack_at (method_message_ptr);

  value_object = NULL;
  method = NULL;
  m_super_idx = -1;
  m_super = NULL;

  if (IS_METHOD_MSG (m)) {

    /* Get the receiver's class object. */
    receiver = m -> receiver_obj;
    if (!strcmp (receiver -> __o_classname, "Class"))
      class_object = receiver;
    else
      class_object = class_object_search (receiver -> __o_classname);
	
    if ((rcvr_ptr = scanback (message_stack (), method_message_ptr,
			      stack_start (message_stack ()), LABEL)) 
	== ERROR)
      error (m, "method_call: unknown receiver.");

    /* 
     *  If the message refers to an instance variable, then simply
     *  output the run time library call to retrieve the instance
     *  variable.
     *
     *  Note that the lvalue must be an OBJECT *, though in some
     *  future version the parser will do a translation to C, 
     *  as with the functions in lib/objtoc.c.
     *
     *  Generate_rt_instancevar_call () does not output a semicolon,
     *  so we only need to mark the instance variable label as evaled
     *  and output.
     *
     *  The message, m, is the message of the method or variable 
     *  name, which has the stack index, message_ptr.  rcvr_ptr 
     *  is set below to the stack index of the receiver.
     *
     *  Eval_receiver_token () determines here if the receiver is a 
     *  parameter name.
     */

    if ((value_object = 
	 __ctalkGetInstanceVariable (receiver, m -> name, FALSE))
	!= NULL) {
      OBJECT *rcvr_class, *token_object;
      OBJECT_CONTEXT rcvr_context;
      char output_buf[MAXMSG];

      rcvr_msg = message_stack_at (rcvr_ptr);

      if (strcmp (receiver -> __o_classname, "Class"))
	rcvr_class = get_class_object (receiver -> __o_classname);
      else
	rcvr_class = receiver;

      rcvr_context = object_context (message_stack (), rcvr_ptr);

      token_object = eval_receiver_token (message_stack (), rcvr_ptr, rcvr_class);

      if (complex_method_statement 
	  (token_object, message_stack (), method_message_ptr)) {
	int _expr_end, _idx;
	char _expr_class_buf[MAXLABEL], _expr_buf[MAXMSG];
	switch (rcvr_context)
	  {
	  case c_context:
	    strcpy (_expr_class_buf, 
		    complex_expr_class (message_stack (), rcvr_ptr));
	    strcpy (_expr_buf, 
		    fmt_rt_return 
		    (fmt_rt_expr (message_stack (), rcvr_ptr, &_expr_end),
		     _expr_class_buf, FALSE));
	    fileout (_expr_buf, 0, rcvr_ptr);
	    for (_idx = rcvr_ptr; _idx >= _expr_end; _idx--) {
	      ++(message_stack_at (_idx) -> evaled);
	      ++(message_stack_at (_idx) -> output);
	    }
	    break;
	  default:
	    rt_expr (message_stack (), rcvr_ptr, &_expr_end);
	    break;
	  }
	return SUCCESS;
      }

      switch (rcvr_context) 
	{
	case c_context:
	case c_argument_context:
	  strcpy (output_buf, 
		  fmt_rt_instancevar_call (token_object, m -> name));
	  if (((instancevar_object = 
	       __ctalkGetInstanceVariable (receiver, M_NAME(m), FALSE)) 
	       == NULL) &&
	      ((method = 
		get_instance_method (m, class_object, m->name, FALSE)) == NULL) &&
	      ((method = 
		get_class_method (m, class_object, m -> name, FALSE)) == NULL)) {
	    sprintf (errbuf, METHOD_ERR_FMT, M_NAME(m), 
		     class_object->__o_classname);
	    if (is_method_proto (CLASSNAME(receiver), M_NAME(m))) {
	      __ctalkExceptionInternal (m, method_used_before_define_x, errbuf);
	    } else {
	      __ctalkExceptionInternal (m, undefined_method_x, errbuf);
	    }
	    return ERROR;
	  }
	  if (is_global_frame ())
	    fn_init (obj_2_c_wrapper 
		     (m, token_object, method, output_buf, TRUE), FALSE);
	  else
	    fileout 
	      (obj_2_c_wrapper_trans (message_stack (), rcvr_ptr, 
				      m, token_object, method, 
				      output_buf, TRUE), 
	       0, method_message_ptr);
	  break;
	default:
	  generate_rt_instancevar_call (token_object,
					m -> name, method_message_ptr);
	  break;
	}

      ++m -> evaled; 
      ++m -> output;

      if (rcvr_msg -> obj && IS_OBJECT (rcvr_msg -> obj)) {
 	if (strcmp (rcvr_msg -> obj -> __o_name, receiver -> __o_name))
 	  error (m, "Method_call: receiver mismatch.");
      } else {
 	if (strcmp (rcvr_msg -> name, receiver -> __o_name))
 	  error (m, "Method_call: receiver mismatch.");
      }

      ++rcvr_msg -> evaled;
      ++rcvr_msg -> output;

      return SUCCESS;
    }

    /*
     *  If the message didn't refer to an instance variable, 
     *  above, and we're still here, then look for a method.
     *  Check for, "super," here.
     */

    if (!strcmp (M_NAME(m), "super")) {
      m_super_idx = nextlangmsg (message_stack (), method_message_ptr);
      m_super = message_stack_at (m_super_idx);
      if (((method = get_instance_method (m_super, receiver -> __o_superclass, 
					  M_NAME(m_super), FALSE))==NULL) ||
	  ((method = get_class_method (m_super, receiver -> __o_superclass, 
				       M_NAME(m_super), FALSE))==NULL)) {
	sprintf (errbuf, METHOD_ERR_FMT, M_NAME(m_super),
		 receiver -> __o_superclass -> __o_classname);
	__ctalkExceptionInternal (m_super, undefined_method_x, errbuf);
      }
    } else {
      /*
       *  Receivers that are class variables, both with and 
       *  without receiver value objects.
       */
      if (m->receiver_msg && (m->receiver_msg->attrs & OBJ_IS_CLASS_VAR)) {
	if (m->receiver_msg->obj->instancevars) {
	  if (((method = 
		get_instance_method (m, 
    get_class_object(m->receiver_msg->obj->instancevars->__o_classname), 
				     m -> name, FALSE))==NULL) &&
	    ((method = 
	      get_class_method (m, 
    get_class_object (m->receiver_msg->obj->instancevars->__o_classname), 
				m -> name, FALSE))==NULL)) {
	    sprintf (errbuf, 
		     METHOD_ERR_FMT, 
		     M_NAME(m), 
		     class_object->__o_classname);
	    __ctalkExceptionInternal (m, undefined_method_x, m -> name);
	    return ERROR;
	  }
	} else {
	  if (((method = 
		get_instance_method (m, 
		     get_class_object(m->receiver_msg->obj->__o_classname), 
				     m -> name, FALSE))==NULL) &&
	    ((method = 
	      get_class_method (m, 
    get_class_object (m->receiver_msg->obj->__o_classname), 
				m -> name, FALSE))==NULL)) {
	    sprintf (errbuf, 
		     METHOD_ERR_FMT, 
		     M_NAME(m), 
		     class_object->__o_classname);
	    __ctalkExceptionInternal (m, undefined_method_x, m -> name);
	    return ERROR;
	  }
	}
      } else {
	if (((method = 
	      get_instance_method (m, class_object, m -> name, FALSE))==NULL) &&
	    ((method = 
	      get_class_method (m, class_object, m -> name, FALSE))==NULL)) {
	  if (is_method_proto (class_object->__o_name, m -> name)) {
	    __ctalkExceptionInternal (m, method_used_before_define_x, 
				      M_NAME(m));
	    return ERROR;
	  } else {
	    sprintf (errbuf, METHOD_ERR_FMT, M_NAME(m), class_object->__o_classname);
	    __ctalkExceptionInternal (m, undefined_method_x, m -> name);
	    return ERROR;
	  }
	}
      }
    }

    /*
     *  If the method replaces a primitive method in the
     *  interpreter, use the primitive method instead.
     */
    method = method_replaces_primitive (method, class_object);

    /*
     *  The define_instance_method () and define_class_method () 
     *  primitives evaluate an entire method declaration and body 
     *  separately in a separate parser pass, so we shouldn't 
     *  duplicate the process here.
     *
     *  Register_c_var_method and register_c_var_receiver are here 
     *  in case register_c_var () above, needs to call resolve_arg (),
     *  or format an array subscript expression template.
     */
    /*
     *  NOTE: IS_PRIMITIVE_METHOD does not match, "new," which
     *  uses method_args () for its argument.
     */
    if (!IS_PRIMITIVE_METHOD(method)) {
      register_c_var_method = method;
      register_c_var_receiver = receiver;
      if (method_args (method, 
		       (m_super_idx != -1) ? m_super_idx : method_message_ptr)
	  != 0) {
  	return ERROR;
      }
      register_c_var_method = NULL;
    }
  }  /* if (IS_METHOD_MSG(m)) */

  /* Primitive methods mark the messages as evaluated and
     output. 
  */
  if (method -> cfunc) {
    /* Check_args () gets called by the primitive functions,
       or method_args () in the case of define_method ().
    */
    cfunc = method -> cfunc;
    resultObject = (cfunc) (method_message_ptr);
    cleanup_args (method, message_stack_at(method_message_ptr)->receiver_obj,
		  (frame_at (current_parser () -> frame - 1)) ->
		   message_frame_top + 1);
  } else {
    char output_buf[MAXMSG];
    OBJECT_CONTEXT context;

    /*
     * TO DO -
     *  Here the function may need a better scan back for 
     *  rcvr_ptr - until 
     *
     *  messages[method_message_ptr] -> receiver_obj == 
     *    messages[method_message_ptr+_n_] -> obj.
     *  
     *  For now, the scan to the start of the frame is 
     *  sufficient.
     */

    context = object_context (message_stack (), rcvr_ptr);

    /*
     *  If evaluating an argument didn't require finding a
     *  receiver class object, then find one now.  Note that
     *  rcvr_class_obj is also filled in by 
     *  new_instance|class_method.
     */
    if (!rcvr_class_obj || !IS_OBJECT(rcvr_class_obj))
      rcvr_class_obj = get_class_object (receiver -> __o_classname);

    /*
     *  Collection class methods without arguments don't
     *  need rt_expressions.
     */
    if (collection_needs_rt_eval 
	(get_class_object(m->receiver_obj->__o_classname), method)) {
      collection_rt_expr (method, message_stack (), method_message_ptr, 
			  context);
    } else {
      switch (context)
	{
	case c_context:
	case c_argument_context:
	  strcpy (output_buf, 
		  rt_library_method_call (receiver, method,
					  message_stack (), 
					  method_message_ptr));
	  if (ctrlblk_pred) {
	    ctrlblk_method_call (receiver, method, method_message_ptr);
	  } else {
	    strcpy (output_buf, 
		    rt_library_method_call (receiver, method, 
					    message_stack (), 
					    method_message_ptr));
	    if (is_global_frame ())
	      fn_init 
		(obj_2_c_wrapper (m, receiver, method, output_buf, TRUE), 0);
	    else 
	      fileout 
		(obj_2_c_wrapper (m, receiver, method, output_buf,
				  ((context == c_context) ? FALSE : TRUE)), 0,
		       method_message_ptr);
	  }
	  break;
	default:
	  /*
	   *  Don't depend on context if evaluating a do predicate in
	   *  its own frame with a sub-parser.  The same with for_term
	   *  and for_inc methods.  The latter two have receiver
	   *  context due to the preceding semicolon, but they are 
	   *  actually in C context.
	   *
	   *  TO DO - These cases should probably be handled in 
	   *  object_context (), and there probably will be 
	   *  unforseen cases where one of these would receive c_context
	   *  or c_argument_context; e.g. while (c = getchar ()). {...
	   *
	   *  Class-specific constructors get added to the function
	   *  initialization.
	   */
	  if (ctrlblk_pred && (do_predicate || for_term || for_inc)) {
	    ctrlblk_method_call (receiver, method, method_message_ptr);
	  } else {
	    if (!strcmp (method -> name, "new")) {
	      char fmt_buf[MAXLABEL];
	      strcpy (fmt_buf, fmt_method_call (receiver, method -> selector));
	      strcat (fmt_buf, ";\n");
	      queue_init_statement (fmt_buf, FALSE, 0);
	      /*
	       *  This is kludgy, but it will have to work until
	       *  there is a way to duplicate a non-primitive, "new,"
	       *  method.
	       */
	      if (is_global_frame ())
		generate_set_global_variable_call (method -> args[0] -> __o_name,
						   receiver -> __o_name);
	      else
		generate_set_local_variable_call (method -> args[0] -> __o_name,
						  receiver -> __o_name);
	    } else {
	      generate_method_call (receiver, method -> selector,
				    method_message_ptr);
	    }
	  }
	  break;
	} /* switch */
    }

    if (ctrlblk_pred) {
      stmt_end_ptr = C_CTRL_BLK -> blk_end_ptr - 1;
      ctrlblk_cleanup_args (method, stmt_end_ptr);
    } else {
      /*
       *  Expr_check can use only one frame, so check
       *  here before before trying to find the next
       *  frame.
       */
      if (interpreter_pass != expr_check)
	cleanup_args (method, m -> receiver_obj,
		      (frame_at (current_parser () -> frame - 1) ->
		       message_frame_top) + 1);
    }

    for (i = rcvr_ptr; i >= method_message_ptr; i--) {
      ++(message_stack_at (i) -> evaled);
      ++(message_stack_at (i) -> output);
    }

  }

  return SUCCESS;
}

/* 
 *
 *   Functions to manage the method parser stack.
 *
 */

int init_method_stack (void) {
  m_message_ptr = N_MESSAGES;
  _new_hash (&declared_method_names);
  _new_hash (&declared_method_selectors);
  return SUCCESS;
}

int method_message_push (MESSAGE *m) {
  if (m_message_ptr == 0) {
    warning (m, "Method_message_push: stack_overflow.");
    return ERROR;
  }

  if (!m) {
    _warning ("Method_message_push: null pointer, messageptr = %d.", 
	     m_message_ptr);
    return ERROR;
  }

  m_messages[m_message_ptr] = m;

#ifdef STACK_TRACE
  warning (m_messages[m_message_ptr], "Method_message_push %d. %s.", 
	   m_message_ptr, m_messages[m_message_ptr] -> name); 
#endif

  --m_message_ptr;

  return m_message_ptr;
}

MESSAGE *method_message_pop (void) {

  MESSAGE *m;

  if (m_message_ptr == N_MESSAGES)
    return NULL;

  if (m_message_ptr > N_MESSAGES) {
    _warning ("Method_message_pop: Stack overflow, m_message_ptr = %d.", 
	    m_message_ptr);
    return NULL;
  }

  m = m_messages[++m_message_ptr];

#ifdef STACK_TRACE
  warning (m, "Method_message_pop %d. %s.", m_message_ptr, m -> name); 
#endif

  return m;
}

int store_arg_object (METHOD *m, OBJECT *arg) {

  if (interpreter_pass == expr_check) {
    if (arg -> scope == ARG_VAR) 
      delete_object (arg);
    return SUCCESS;
  }

  m -> args[m -> n_args++] = arg;

  return SUCCESS;
}

/*
 *  Clear the method arguments and generate a __ctalk_arg_cleanup () 
 *  call in the output.  Also delete temporary arg objects, and
 *  remove the message pointer from the argument stack.
 *
 *  If we're in a loop, ctrlblk_cleanup_args () in control.c performs
 *  this task after it queues the __ctalk_arg_cleanup () calls for the
 *  block predicates.
 *
 *  Whether to use stmt_end_ptr - 1 is questionable, because in
 *  a single-line control statement block, the semicolon also
 *  anchors the closing brace of an, "if," clause without an,
 *  "else," or the closing brace of an, "else," clause.
 */

void cleanup_args (METHOD *m, OBJECT *receiver_object, int stmt_end_ptr) {
  int i;
  int collection_rt_eval = FALSE;

  if (interpreter_pass == expr_check) return;

  if (!strcmp (receiver_object -> __o_classname, "Class")) {
    if (collection_needs_rt_eval (receiver_object, m))
      collection_rt_eval = TRUE;
  } else {
    if (collection_needs_rt_eval 
	(get_class_object(receiver_object->__o_classname), m))
      collection_rt_eval = TRUE;
  }

  for (i = m -> n_args - 1; i >= 0; i--) {
    if (!collection_rt_eval) {
      if (!m -> cfunc)
	generate_method_pop_arg_call (stmt_end_ptr - 1);
    }
    if (m -> args[i] -> scope == ARG_VAR) delete_object (m -> args[i]);
    m -> args[i] = NULL;
    m -> n_args = i;
  }
}

void init_imported_method_queue (void) {
  imported_method_ptr = 0;
}

void queue_method_for_output (char *classname, METHOD *m) {

  IMPORTED_METHOD *i;

  if (m -> queued == False) {

    if ((i = (IMPORTED_METHOD *)calloc (1, sizeof (IMPORTED_METHOD))) == NULL)
      _error ("queue_method_for_output: %s\n", strerror (errno));

    strcpy (i -> classname, classname);
    if (get_lib_includes_ptr () < MAXARGS) {
      strcpy (i -> source_file, 
	      lib_include_at (get_lib_includes_ptr () + 1) -> path);
    } else {
      strcpy (i -> source_file, __source_filename ());
    }
    i -> method = m;

    imported_methods[imported_method_ptr++] = i;
  }

}

extern MESSAGE *p_messages[P_MESSAGES+1]; /* Declared in preprocess.c. */

/* 
 *  Output imported methods.  The methods are preprocessed when
 *  retrieved by a library search, and added to the interpreter
 *  object's dictionary.  
 */

void output_imported_methods (void) {

  int i;
  OBJECT *methodClass;
  METHOD *m;
  char line_info_buf[MAXMSG];

  for (i = 0; i < imported_method_ptr; i++) {
    if (imported_methods[i] -> method -> imported == True) {

      imported_methods[i] -> method -> imported = False;
      imported_methods[i] -> method -> queued = False;

      if ((methodClass = get_class_object (imported_methods[i] -> classname))
	  == NULL)
	_error ("output_imported_methods: class %s not defined.",
		imported_methods[i] -> classname);

      m = NULL;
      for (m = methodClass -> instance_methods; m; m = m -> next)
	if (!strcmp (m -> name, imported_methods[i] -> method -> name))
	  break;
      if (!m)
	for (m = methodClass -> class_methods; m; m = m -> next)
	  if (!strcmp (m -> name, imported_methods[i] -> method -> name))
	    break;

      if (!m)
	_error ("output_imported_methods: Undefined %s method %s.",
		imported_methods[i] -> classname, 
		imported_methods[i] -> method -> name);

      /* The method initialization code is inserted in the 
	 source by new_instance|class_method ()'s call to parse (). */
      if (!nolinemarker_opt) {
	fmt_line_info (line_info_buf,
		       imported_methods[i] -> method -> error_line,
		       imported_methods[i] -> source_file,
		       0,
		       TRUE);
	__fileout (line_info_buf);
      }
      __fileout (imported_methods[i] -> method -> src);
      __fileout ("\n\n");

    }
    free (imported_methods[i]);
    imported_methods[i] = NULL;
  }

  imported_method_ptr = 0;

}

int is_method_name (const char *name) {
  if (_hash_get (declared_method_names, name)) 
    return TRUE;
  else
    return FALSE;
}

int is_method_selector (const char *selector) {
  if (_hash_get (declared_method_selectors, selector)) 
    return TRUE;
  else
    return FALSE;
}

/*
 * save_method_local_objects () and save_method_local_cvars ()
 * are sort of a kludge, because at the moment it is not possible
 * to output buffered methods from within the parser, where 
 * the variables are stored.  So we save them in the method
 * for use by format_method (), after the sub-parser returns.
 * 
 * If the method buffering gets fixed in a future release, 
 * then these functions, as well as the local_object and 
 * local_cvar members of the method structs, should go away.
 */

void save_method_local_objects (void) {
  PARSER *p;
  p = current_parser ();
  new_methods[new_method_ptr+1] -> local_objects.objs = p -> vars;
  p -> vars = NULL;
}

void save_method_local_cvars (void) {
  PARSER *p;
  CVAR *c, 
    *c_ptr = NULL,   /* Avoid a warning. */
    *t;
  METHOD *new_method;

  p = current_parser ();

  new_method = new_methods[new_method_ptr + 1];

  for (t = p -> cvars; t; t = t -> next) {
    c = __ctalkCopyCVariable (t);
    if (!new_method -> local_cvars) {
      new_method -> local_cvars = c_ptr = c;
    } else {
      c_ptr -> next = c;
      c -> prev = c_ptr;
      c_ptr = c_ptr -> next;
    }
  }
}

OBJECT *get_instance_method_local_object (char *s) {
  METHOD *m;
  OBJECT *o;

  if (new_method_ptr >= MAXARGS)
    return NULL;

  m = new_methods[new_method_ptr + 1];

  for (o = m -> local_objects.objs; o; o = o -> next) {
    if (!strcmp (o -> __o_name, s))
      return o;
  }
  return NULL;
}

CVAR *get_instance_method_local_cvar (char *s) {
  METHOD *m;
  CVAR *c;

  if (new_method_ptr >= MAXARGS)
    return NULL;

  m = new_methods[new_method_ptr + 1];

  for (c = m -> local_cvars; c; c = c -> next) {
    if (!strcmp (c -> name, s))
      return c;
  }
  return NULL;
}

/*
 *  Determine if a label is an instance method.  
 *  TO DO - Only works for a limited number of 
 *  cases right now.
 */

Boolean is_instance_method (MESSAGE_STACK messages, int msg_ptr) {

  METHOD *method;
  OBJECT *possible_rcvr;
  int prev_label_ptr;

  if (messages[msg_ptr] -> tokentype == METHODMSGLABEL)
    return True;

  switch (interpreter_pass)
    {
    case method_pass:
      /*
       *  TO DO - This case is too limited.
       */
      if ((method = get_instance_method (messages[msg_ptr],
					 rcvr_class_obj, messages[msg_ptr] -> name, 
					 FALSE)) != NULL) {
	return True;
      } else {
	if (messages[msg_ptr] -> attrs & METHOD_PROTOTYPE_MESSAGE) {
	  return True;
	}
      }
      break;
    case parsing_pass:
    case expr_check:
      if (is_c_constant_instance_method (messages, msg_ptr))
	return True;
      if (!rcvr_class_obj) {
	if ((prev_label_ptr = prevlangmsg (messages, msg_ptr)) != ERROR) {
	  if ((possible_rcvr = 
	       get_object (messages[prev_label_ptr] -> name, NULL)) != NULL) {
	    if ((method = get_instance_method (messages[msg_ptr], 
					       possible_rcvr, 
					       messages[msg_ptr] -> name, FALSE)) 
		!= NULL) {
	      return True;
	    } else {
	      if (messages[msg_ptr] -> attrs & METHOD_PROTOTYPE_MESSAGE) {
		return True;
	      }
	    }
	  }
	}
      }
      break;
    default:
      break;
    }
  
  return False;
}

Boolean is_class_method (MESSAGE_STACK messages, int msg_ptr) {

  METHOD *method;
  OBJECT *possible_rcvr;
  int prev_label_ptr;

  if (messages[msg_ptr] -> tokentype == METHODMSGLABEL)
    return True;

  switch (interpreter_pass)
    {
    case method_pass:
      /*
       *  TO DO - This case is too limited.
       */
      if ((method = get_class_method (messages[msg_ptr],
				      rcvr_class_obj, messages[msg_ptr] -> name, 
				FALSE)) != NULL)
	return True;
      break;
    case parsing_pass:
    case expr_check:
      /*
       *  Should not be needed for class objects.
       */
      if (!rcvr_class_obj) {
	if ((prev_label_ptr = prevlangmsg (messages, msg_ptr)) != ERROR) {
	  if ((possible_rcvr = 
	       get_object (messages[prev_label_ptr] -> name, NULL)) != NULL) {
	    if ((method = get_class_method (messages[msg_ptr], possible_rcvr, 
				      messages[msg_ptr] -> name, FALSE)) 
		!= NULL) {
	      return True;
	    }
	  }
	}
      }
      break;
    default:
      break;
    }
  
  return False;
}

/*
 *  Look back to the start of a frame, and find the previous
 *  identifier.  If the receiver is a C constant expression, 
 *  look for a method in the corresponding class.  Look back further 
 *  if the receiver is a C struct or array member.
 *
 *  NOTE - This function also returns False if a class library is
 *  not loaded yet.
 */
Boolean is_c_constant_instance_method (MESSAGE_STACK messages, int msg_ptr) {

  int lookback,
    stack_begin,
    n_blocks;
  Boolean is_array_member,
    is_struct_member;
  OBJECT *rcvr_obj;
  MESSAGE *m;
  METHOD *method;
  CVAR *c;

  if ((interpreter_pass != parsing_pass) &&
      (interpreter_pass != method_pass))
    return False;

  stack_begin = stack_start (messages);
  is_array_member = is_struct_member = False;
  rcvr_obj = NULL;

  for (lookback = msg_ptr + 1; lookback <= stack_begin; lookback++) {

    m = messages[lookback];

    if ((m -> tokentype == NEWLINE) || (m -> tokentype == WHITESPACE))
      continue;
	
    switch (m -> tokentype)
      {
      case ARRAYCLOSE:
	n_blocks = 1;
	for (++lookback; (lookback <= stack_begin) && n_blocks; lookback++) {
	  switch (messages[lookback] -> tokentype)
	    {
	    case ARRAYCLOSE:
	      ++n_blocks;
	      break;
	    case ARRAYOPEN:
	      --n_blocks;
	      break;
	    }
	}
	/*
	 *  GNUC leaves lookback at one ahead of the last block.
	 *  Decrement lookback so it is set correctly for the 
	 *  next iteration.
	 */
	--lookback;
	is_array_member = True;
	break;
      case LITERAL:
	rcvr_obj = get_class_object (STRING_CLASSNAME);
	if (rcvr_obj && IS_OBJECT (rcvr_obj)) {
	  if ((method = 
	       get_instance_method (messages[msg_ptr],
				    rcvr_obj, messages[msg_ptr] -> name, FALSE))
	      != NULL)
	    return True;
	  else
	    return False;
	} else {
	  return False;
	}
	break;
      case LITERAL_CHAR:
	rcvr_obj = get_class_object (CHARACTER_CLASSNAME);
	if (rcvr_obj && IS_OBJECT (rcvr_obj)) {
	  if ((method = 
	       get_instance_method (messages[msg_ptr],
				    rcvr_obj, messages[msg_ptr] -> name, FALSE))
	      != NULL)
	    return True;
	  else
	    return False;
	} else {
	  return False;
	}
	break;
      case INTEGER:
      case LONG:
	rcvr_obj = get_class_object (INTEGER_CLASSNAME);
	if (rcvr_obj && IS_OBJECT (rcvr_obj)) {
	  if ((method = 
	       get_instance_method (messages[msg_ptr],
				    rcvr_obj, messages[msg_ptr] -> name, FALSE))
	      != NULL)
	    return True;
	  else
	    return False;
	} else {
	  return False;
	}
	break;
      case LONGLONG:
	rcvr_obj = get_class_object (LONGINTEGER_CLASSNAME);
	if (rcvr_obj && IS_OBJECT (rcvr_obj)) {
	  if ((method = 
	       get_instance_method (messages[msg_ptr],
				    rcvr_obj, messages[msg_ptr] -> name, FALSE))
	      != NULL)
	    return True;
	  else
	    return False;
	} else {
	  return False;
	}
	break;
      case DOUBLE:
	rcvr_obj = get_class_object (FLOAT_CLASSNAME);
	if (rcvr_obj && IS_OBJECT (rcvr_obj)) {
	  if ((method = 
	       get_instance_method (messages[msg_ptr],
				    rcvr_obj, messages[msg_ptr] -> name, FALSE))
	      != NULL)
	    return True;
	  else
	    return False;
	} else {
	  return False;
	}
	break;
      case LABEL:
	if (is_array_member) {
	  if (((c = get_local_var (m -> name)) != NULL) ||
	      ((c = get_global_var (m -> name)) != NULL)) {
	    switch (c -> n_derefs - 1)
	      {
	      case 0:
		if (!strcmp (c -> type, "char"))
		  rcvr_obj = get_class_object (CHARACTER_CLASSNAME);
		if (!strcmp (c -> type, "int")) {
		  if (!strcmp (c -> qualifier2, "long") &&
		      !strcmp (c -> qualifier, "long")) {
		    rcvr_obj = get_class_object (LONGINTEGER_CLASSNAME);
		  } else {
		    rcvr_obj = get_class_object (INTEGER_CLASSNAME);
		  }
		}
		if (!strcmp (c -> type, "double") ||
		    !strcmp (c -> type, "float"))
		  rcvr_obj = get_class_object (FLOAT_CLASSNAME);
		break;
	      case 1:
		if (!strcmp (c -> type, "char"))
		  rcvr_obj = get_class_object (STRING_CLASSNAME);
		if (!strcmp (c -> type, "int") ||
		    !strcmp (c -> type, "double") ||
		    !strcmp (c -> type, "float"))
		  rcvr_obj = get_class_object (ARRAY_CLASSNAME);
		break;
	      case 2:
		rcvr_obj = get_class_object (ARRAY_CLASSNAME);
		break;
	      case -1:
		break;
	      } /* switch (c -> n_derefs) */
	    if (rcvr_obj && IS_OBJECT (rcvr_obj)) {
	      if ((method = 
		   get_instance_method (messages[msg_ptr],
					rcvr_obj, messages[msg_ptr] -> name, FALSE))
		  != NULL)
		return True;
	      else
		return False;
	    } else {
	      /*
	       *  Note that this case can mean that a class is not loaded
	       *  yet.
	       */
	      return False;
	    }
	  } /* 	  if (((c = get_local_var (m -> name)) != NULL) ||
		      ((c = get_global_var (m -> name)) != NULL)) { */
	} /* 	if (is_array_member) */

	if (is_struct_member) {
	}

	if (!is_array_member && !is_struct_member) {
	}
	break;
      }
  }
  return False;
}

/*
 *  Replace a method parameter with its argument accessor function.
 *  TO DO - Note that at this time, we cannot simply give the 
 *  message an, "Expr," object, if the class hasn't been loaded
 *  yet.  So try to make, "Expr," a default class.
 *
 *  At the moment, we should simply be able to set the message's
 *  token type to, "RESULT," and also the messages following until
 *  the end of the argument.
 */

int method_arg_accessor_fn (MESSAGE_STACK messages, int idx, int arg_n) {

  if (is_char_ptr_fmt_arg (messages, 
			   idx, 
			   stack_start (messages), 
			   get_stack_top (messages))) {
    sprintf (messages[idx] -> name, "__ctalk_to_c_char_ptr (%s (%d))", 
	     METHOD_ARG_ACCESSOR_FN, arg_n);
  } else {
    sprintf (messages[idx] -> name, "%s (%d)", METHOD_ARG_ACCESSOR_FN, arg_n);
  }

  messages[idx] -> obj = 
    create_object_init (CFUNCTION_CLASSNAME, 
			CFUNCTION_SUPERCLASSNAME,
			messages[idx] -> name,
			messages[idx] -> name);
  save_method_object (messages[idx]->obj);
  
  return SUCCESS;
}

/*
 *  Called normally when we have a method call for
 *  that we might not yet have a class for; e.g., 
 *  method parameter receivers in the argument 
 *  of another method or C expression..
 */

int method_arg_rt_expr (MESSAGE_STACK messages, int idx) {

  int i, expr_end, stack_end;
  int n_subs, n_parens;
  char *tokenbuf, exprbuf[MAXMSG];

  stack_end = get_stack_top (messages);

  for (i = idx, n_subs = 0, n_parens = 0, expr_end = idx; 
       i >= stack_end; i--) {

    if (METHOD_ARG_TERM_MSG_TYPE (messages[i]) && !n_subs && !n_parens) {
      expr_end = i + 1;
      goto e_e;
    }
    switch (messages[i] -> tokentype)
      {
      case ARRAYOPEN:
	++n_subs;
	break;
      case ARRAYCLOSE:
	--n_subs;
	break;
      case OPENPAREN:
	++n_parens;
	break;
      case CLOSEPAREN:
	--n_parens;
	break;
      }

  }

 e_e:

  tokenbuf = collect_tokens (messages, idx, expr_end);
  strcpy (exprbuf, fmt_eval_expr (tokenbuf));
  free (tokenbuf);

  strcpy (messages[idx] -> name, exprbuf);
  messages[idx] -> tokentype = RESULT;

  for (i = idx - 1; i >= expr_end; i--) {
    strcpy (messages[i] -> name, " ");
    messages[i] -> tokentype = RESULT;
  }

  return SUCCESS;
}

OBJECT *method_arg_object ( MESSAGE *m) {

  int j;
  METHOD *new_method;
  char buf[MAXMSG];
  OBJECT *arg_expr_object = NULL;
	    
  new_method = new_methods[new_method_ptr + 1];

  if (!new_method || !IS_METHOD (new_method))
    error (m, "Invalid new method in method_arg_object ().");
	    
  for (j = 0; j < new_method -> n_params; j++) {
    if (!strcmp (m -> name, new_method -> params[j] -> name)) {
      sprintf (buf, "__ctalk_arg_internal (%d)", 
	       new_method -> n_params - j - 1);
      arg_expr_object = create_object_init (EXPR_CLASSNAME, 
					 EXPR_SUPERCLASSNAME,
					 buf, buf);
      __ctalkSetObjectValue (arg_expr_object, buf);
      arg_expr_object -> scope = ARG_VAR;
    }
  }
  return arg_expr_object;
}

/*
 *  The only method modifier so far is, "super."  
 *
 *  There are two semantics for, "super," covered here.
 *    1. "super" modifies a method, but it is preceded by
 *        a receiver.  
 *    2. "super" represents the receiver.  Then the function
 *       creates an object for super of the method's receiver
 *       superclass.
 */

int compound_method (MESSAGE_STACK messages, int method_idx, int *attrs) {

  int idx_next_tok,
    idx_prev_tok;
  MESSAGE *m_prev,
    *m_next,
    *m_orig;
  OBJECT *rcvr_obj;
  METHOD *method;

  m_orig = messages[method_idx];

  if (!strcmp (M_NAME(m_orig), "super")) {
    if (((idx_next_tok = nextlangmsg (messages, method_idx)) == ERROR) ||
	((idx_prev_tok = prevlangmsg (messages, method_idx)) == ERROR)) {
      __ctalkExceptionInternal (m_orig, parse_error_x, M_NAME(m_orig));
      return method_idx;
    }

    m_prev = messages[idx_prev_tok];
    m_next = messages[idx_next_tok];
    if (!m_prev -> obj) {
      /*
       *  Super keyword is acting as a receiver.
       */
      if (interpreter_pass == method_pass) {
	idx_prev_tok = method_idx;
	m_prev = messages[method_idx];
	m_prev -> obj = 
	  create_object_init (rcvr_class_obj -> __o_superclass -> __o_classname,
			      rcvr_class_obj -> __o_superclass -> __o_superclassname,
			      "result", NULLSTR);
      } else {
	__ctalkExceptionInternal (m_orig, undefined_receiver_x,
				  M_NAME(m_prev));
	return method_idx;
      }
    }
    rcvr_obj = M_VALUE_OBJ (m_prev);
    if ((method = get_super_instance_method (rcvr_obj, M_NAME(m_next), TRUE))
	== NULL) {
      sprintf (errbuf, METHOD_ERR_FMT, M_NAME(m_next), rcvr_obj -> __o_classname);
      __ctalkExceptionInternal (m_next, undefined_method_x, errbuf);
      return method_idx;
    }

    if (idx_prev_tok > method_idx) {
      /*
       *  Super is preceded by a receiver object.
       */
      m_orig ->  tokentype = 
	m_next -> tokentype = 
	METHODMSGLABEL;
      m_orig -> receiver_obj = 
	m_next -> receiver_obj =
	rcvr_obj;
      m_orig -> receiver_msg = 
	m_next -> receiver_msg =
	m_prev;

      ++m_orig -> evaled;
      ++m_orig -> output;

    } else {
      /*
       *  Super represents the receiver.
       */
      m_next -> receiver_obj = rcvr_obj;
      m_next -> receiver_msg = m_prev;
    }

    *attrs |= METHOD_SUPER_ATTR;

    return idx_next_tok;
  }

  return method_idx;
}

char *rt_library_method_call (OBJECT *rcvr, METHOD *method, 
			      MESSAGE_STACK messages, int method_idx) {
  return fmt_method_call (rcvr, method -> selector);
}

/*
 *  If the method replaces a primitive method in the interpreter
 *  use the primitive method instead.
 *  
 *  Note that method_replaces_primitive () must also be called in 
 *  a primitive that looks up the method; e.g., in new_object ().
 */

METHOD *method_replaces_primitive (METHOD *method, OBJECT *class_object) {

  OBJECT *superclass_object;
  METHOD *super_method;

  if (method -> cfunc)
    return method;

  for (superclass_object = class_object -> __o_superclass,
	 super_method = method;
       superclass_object && *superclass_object -> __o_superclassname;
       superclass_object = superclass_object -> __o_superclass) {
    if (((super_method = get_instance_method (NULL, superclass_object, 
					      method -> name, 
					      FALSE)) != NULL) &&
	super_method -> cfunc) {
      return super_method;
    }
  }
    
  if (class_object -> __o_superclass) {
    if (!strcmp (class_object -> __o_superclass -> __o_name, 
		 OBJECT_CLASSNAME)){
      if (((super_method = get_instance_method (NULL, superclass_object, 
					      method -> name, 
					      FALSE)) != NULL) &&
	  super_method -> cfunc) {
	return super_method;
      }
    }
  }

  return method;
}

METHOD *method_replaces_primitive_orig (METHOD *method, OBJECT *class_object) {

  OBJECT *superclass_object;
  METHOD *super_method;

  if (method -> cfunc)
    return method;

  for (superclass_object = 
	 get_class_object (class_object -> __o_superclassname),
	 super_method = method;
       superclass_object && *superclass_object -> __o_superclassname;
       superclass_object = 
	 get_class_object (superclass_object -> __o_superclassname)) {
    if (((super_method = get_instance_method (NULL, superclass_object, 
					      method -> name, 
					      FALSE)) != NULL) &&
	super_method -> cfunc) {
      return super_method;
    }
    
  }
    
  if (((superclass_object = get_class_object (class_object -> __o_superclassname))
      != NULL) &&
      (!strcmp (superclass_object -> __o_name, OBJECT_CLASSNAME))) {
    if (((super_method = get_instance_method (NULL, superclass_object, 
					      method -> name, 
					      FALSE)) != NULL) &&
	super_method -> cfunc) {
      return super_method;
    }
  }

  return method;
}

int is_method_parameter (MESSAGE_STACK messages, int idx) {

  METHOD *n_method;
  int n_th_param;

  if (interpreter_pass != method_pass) 
    return FALSE;

  n_method = new_methods[new_method_ptr + 1];

  for (n_th_param = 0; n_th_param < n_method -> n_params; n_th_param++) {
    if (!strcmp (M_NAME(messages[idx]), n_method -> params[n_th_param]->name))
      return TRUE;
  }

  return FALSE;
}

/*
 *  Save miscellaneous method objects so they can be found
 *  quickly later.
 */
void save_method_object (OBJECT *o) {

  OBJECT *p_obj;
  PARSER *p;

  p = current_parser ();

  if (p -> methodobjects == NULL) {
    p -> methodobjects = o;
    return;
  }

  for ( p_obj = p -> methodobjects ;p_obj && p_obj -> next; p_obj = p_obj -> next)
    ;
  p_obj -> next = o;
  o -> prev = p_obj;
  o -> next = NULL;
}

int saved_method_object (OBJECT *o) {

  OBJECT *p;

  for (p = current_parser () -> methodobjects; p; p = p -> next)
    if (p == o)
      return TRUE;

  return FALSE;
}
