/* $Id: rexpr.c,v 1.14 2009/02/08 16:43:04 kiesling Exp $ */

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

  This 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.
*/

/*
 *  Evaluate the right hand side of an equation, and translate
 *  if:
 *  1. The right-hand expression is a method argument;
 *  2. The right hand expression contains objects.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "ctrlblk.h"

extern I_PASS interpreter_pass;

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

extern CTRLBLK *ctrlblks[MAXARGS + 1];
extern int ctrlblk_ptr;
extern int ctrlblk_pred,              /* Global states. */
   ctrlblk_blk;
extern int for_init, 
  for_term,
  for_inc;

/*
 *  Return the context that an object appears in: as an lvalue,
 *  a receiver, an argument, or any C context.  
 */

OBJECT_CONTEXT object_context (MESSAGE_STACK messages, int obj_ptr) {

  int i, j,
    stack_start_ptr,
    stack_end_ptr;
  int prev_token_ptr,
    lookback,
    lookahead;
  MESSAGE *m;
  OBJECT_CONTEXT context;
  char *path;

  if (is_c_prefix_op (messages, obj_ptr))
    return c_context;

  stack_start_ptr = stack_start (messages);
  stack_end_ptr = get_stack_top (messages);

  /*
   *  Scan back to determine if we can find the context.
   */
  for (i = obj_ptr + 1, context = null_context; i <= stack_start_ptr; i++) {

    m = messages[i];

    switch (m -> tokentype)
      {
	/*
	 *  If the previous identifier is specifically a C type, 
	 *  or a prefix operarator, we're done.
	 */
      case CHAR:
      case AMPERSAND:
      case EXCLAM:
      case BIT_COMP:
      case CTYPE:
	return c_context;
	break;
	/*
	 *  If the previous identifier is a constant, we might check what 
	 *  it and our object are arguments to.  A context like this 
	 *  *should* normally be taken care of in the ARGSEPARATOR case, 
	 *  below.
	 */
      case LITERAL:
      case LITERAL_CHAR:
      case INTEGER:
      case FLOAT:
      case LONG:
      case LONGLONG:
	return c_context;
	break;
	/* 
	 *  If the previous identifier is a label that is a C 
	 *  function, variable, keyword, return c_context. If it's 
	 *  a method, return argument_context.
	 *
	 *  An exception is the, "return," keyword.  We will have to
	 *  match the type with the return value.  So far, we can only
	 *  make an exception for methods, where we can return
	 *  argument_context.  That should not need to change soon,
	 *  because all methods return, "OBJECT *."
	 *
	 *  Returning argument_context generates a, "__ctalk_method
	 *  ("self", <value_method>)," method call, and is probably
	 *  not worth making an exception for, although returning,
	 *  "receiver_context," would generate the more direct
	 *  "__ctalk_self_internal ()," call.
	 */
      case LABEL:
	if (!strcmp (m -> name, "return")) {
	  return argument_context;
	} else {
	  if (get_function (m -> name) ||
	      get_local_var (m -> name) ||
	      global_var_is_declared (m -> name) ||
	      get_typedef (m -> name) ||
	      is_c_keyword (m -> name)) {
	    return c_context;
	  } else {
	    if ((path = find_library_include (m -> name, FALSE)) != NULL)
	      return argument_context;
	  }
	}
	break;
	/*
	 *   If the previous non-whitespace token is a semicolon, 
	 *   or left curly brace, the object begins the statement, 
	 *   and it is the receiver, unless it is a leading operator.
	 */
      case SEMICOLON:
      case OPENBLOCK:
	if ((M_TOK(messages[obj_ptr]) == ASTERISK) ||
	    IS_C_UNARY_MATH_OP(M_TOK(messages[obj_ptr])))
	  return c_context;
	return receiver_context;
	break;
	/*
	 *  Determine if the preceding label token is a method
	 *  by looking up the method from the receiver object,
	 *  if any.  If a receiver or method is not found, the
	 *  label is either a C keyword or a C library function.
	 *  
	 *  Note that this criterion also applies to overloaded 
	 *  C operators.
	 */
      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 ASTERISK:
      case MULT_ASSIGN:
      case DIVIDE:
      case DIV_ASSIGN:
      case BOOLEAN_AND:
      case BIT_AND_ASSIGN:
      case INEQUALITY:
      case BIT_OR:
      case BOOLEAN_OR:
      case BIT_OR_ASSIGN:
      case BIT_XOR:
      case BIT_XOR_ASSIGN:
      case PERIOD:
      case ELLIPSIS:
      case LITERALIZE:
      case MACRO_CONCAT:
      case SIZEOF:
      case CONDITIONAL:
      case COLON:
      case CLOSEBLOCK:
      case MODULUS:
	/* If the object itself is not a label, then it 
	   can only be an overloaded operator, in which case the
	   previous token must be a label if it is used in an object
	   context.
	*/
	if (messages[obj_ptr] -> tokentype != LABEL) {
	  return c_context;
	} else {
	  /*
	   *  If the label before the operator does not have an object
	   *  associated with it, and the label is not the name of a 
	   *  variable of type OBJECT *, use C argument context.  If it 
	   *  has an object, use argument context.
	   */

	  if (((lookback = prevlangmsg (messages, i)) != ERROR) &&
	      (messages[lookback] -> tokentype == LABEL) &&
	      (messages[lookback] -> obj == NULL)) {
	    CVAR *c;
	    if (((c = get_local_var (messages[lookback] -> name)) != NULL) ||
		((c = get_global_var (messages[lookback] -> name)) != NULL)) {
	      if (!strcmp (c -> type, "OBJECT") && 
		  (c -> n_derefs)) {
		/*
		 * This case is treated the same as a normal method argument,
		 * because methods use OBJECT *.
		 */
		return argument_context;
	      } else {
		return c_argument_context;
	      }
	    } else {
	      if (is_struct_member (M_NAME(messages[lookback]))) {
		return c_argument_context;
	      } else {
		/*
		 *  This case should cover anything that the parser 
		 *  doesn't know about.
		 */
		return argument_context;
	      }
	    }
	  } else {
	    /*
	     *  If the lookback token is a C constant expression,
	     *  then the operator is a C operator and not a method.
	     */
	    if ((lookback != ERROR) && 
		((messages[lookback] -> tokentype == LITERAL) ||
		 (messages[lookback] -> tokentype == LITERAL_CHAR) ||
		 (messages[lookback] -> tokentype == INTEGER) ||
		 (messages[lookback] -> tokentype == FLOAT) ||
		 (messages[lookback] -> tokentype == DOUBLE) ||
		 (messages[lookback] -> tokentype == LONG) ||
		 (messages[lookback] -> tokentype == LONGLONG))) {
	      return c_argument_context;
	    } else {
	      /*
	       *  If the lookback token is a closing parenthesis, then
	       *  check whether the previous operand is a function 
	       *  call.
	       */
	      if ((lookback != ERROR) &&
		  (messages[lookback] -> tokentype == CLOSEPAREN)) {
		int lookback2, n_parens;
		for (lookback2 = lookback, n_parens = -1;
		     (lookback2 <= stack_start_ptr) && n_parens; 
		     lookback2++) {
		  if (M_TOK(messages[lookback2]) == CLOSEPAREN)
		    ++n_parens;
		  if (M_TOK(messages[lookback2]) == OPENPAREN)
		    --n_parens;
		}
		if ((lookback2 = prevlangmsg (messages, lookback2)) != ERROR){
		  if (M_TOK(messages[lookback2]) == LABEL) {
		    if (get_function(M_NAME(messages[lookback2]))) {
		      return c_argument_context;
		    } else {
		      return argument_context;
		    }
		  }
		}
	      } else {
		return argument_context;
	      }
	    }
	  }
	}
	break;
      case CLOSEPAREN:
	/*
	 *  Look for the case where the token is the first after
	 *  a control block predicate.
	 */
	if (ctrlblk_blk) {
	  if (i == ctrlblks[ctrlblk_ptr+1]->pred_end_ptr)
	    return receiver_context;
	}
	break;
      case ARGSEPARATOR:
	/*
	 *  Check for a printf format argument.
	 */
	if ((lookahead = nextlangmsg (messages, i)) != ERROR) {
	  if (is_fmt_arg(messages,lookahead,stack_start(messages),
			 get_stack_top(messages)))
	    return c_argument_context;
	}
	/*
	 *  If we're in an argument list, scan back toward
	 *  the beginning of the list, and try to determine
	 *  what we're an argument to.
	 */
	for (j = i + 1, context = c_context; j <= stack_start_ptr; j++) {
	  /*
	   *  If we reach the end of the previous statement or the
	   *  beginning of a block, we're in a statement.
	   */
	  if ((messages[j] -> tokentype == SEMICOLON) ||
	      (messages[j] -> tokentype == OPENBLOCK))
	    break;

	  if ((M_TOK(messages[j]) == ARRAYCLOSE) ||
	      (M_TOK(messages[j]) == DEREF) ||
	      (M_TOK(messages[j]) == PERIOD)) {
	    return c_argument_context;
	  }

	  switch (messages[j] -> tokentype)
	    {
	    case LABEL:
	      if ((prev_token_ptr = prevlangmsg (messages, j)) != ERROR) {
		/*
		 *  If we have two successive labels, try to determine
		 *  if they're a receiver and a method.
		 */
		if (messages[prev_token_ptr] -> tokentype == LABEL) {
		  OBJECT *rcvr;
		  METHOD *method;
		  if ((rcvr = 
		       get_object (messages[prev_token_ptr] -> name, NULL))
		      != NULL) {
		    if ((method = get_instance_method (messages[j],
						       rcvr, messages[j] -> name, FALSE))
			!= NULL)
		      context = argument_context;
		  }
		}
	      }
	      return context;
	      break;
	    case METHODMSGLABEL:
	      context = argument_context;
	      break;
	    case CLOSEPAREN:
	      j = match_paren_rev (messages, j, stack_start_ptr);
	      break;
	    }
	}
      default:
	break;
      }
  }
  return context;
}

/*
 *   Determine if an object does not have a method following
 *   it, and if so, use the, "value," method.  Otherwise, do
 *   nothing.
 */ 

int default_method (MESSAGE_STACK messages, int obj_ptr) {

  MESSAGE *m_obj,
    *m_next;
  int m_next_ptr;
  int tmp_end_ptr;
  int method_attrs;
  METHOD *m;
  OBJECT *class;
  OBJECT_CONTEXT context;
  char buf[MAXMSG];

  if (interpreter_pass == expr_check)
    return SUCCESS;

  m_obj = messages[obj_ptr];
  
  if ((m_next_ptr = nextlangmsg (messages, obj_ptr)) == ERROR) {
    /* Called by eval_arg or something similar that has its own
     * stack.
     */
    return ERROR;
  }

  m_next = messages[m_next_ptr];

  /*
   *  If a non-label token is a method, we don't need to 
   *  process it here.
   */

  if (!strcmp (m_obj -> obj -> __o_classname, "Class")) {
    class = m_obj -> obj;
  } else {
    if (!strcmp (m_obj -> obj -> __o_classname, EXPR_CLASSNAME)) {
      return SUCCESS;
    } else {
      if ((class = get_class_object (m_obj -> obj -> __o_classname)) == NULL)
	error (m_obj, "Undefined class in default_method ().");
    }
  }

  /*
   *  Cases like <any-receiver> <classvariable>
   */
  if (m_next -> attrs & OBJ_IS_CLASS_VAR)
    return ERROR;

  /*
   *  This case should not be needed if there is no 
   *  m_obj->obj->instancevars.
   */
  if ((m_obj->attrs & OBJ_IS_CLASS_VAR) && m_obj->obj->instancevars) {
    if (((m = get_instance_method (m_obj, m_obj->obj->instancevars,
			       M_NAME(m_next), FALSE)) != NULL) ||
 	((m = get_class_method (m_obj, m_obj->obj->instancevars,
				M_NAME(m_next), FALSE)) != NULL))
      return ERROR;
  }

  if ((!METHOD_ARG_TERM_MSG_TYPE (m_next)) &&
      (((m = get_instance_method (m_obj, m_obj->obj, m_next->name, FALSE))!=NULL) || 
       ((m = get_class_method (m_obj, m_obj->obj, m_next->name, FALSE))!=NULL) || 
       __ctalkGetInstanceVariable (m_obj -> obj, m_next -> name, FALSE) ||
       __ctalkGetClassVariable (m_obj -> obj, m_next -> name, FALSE) ||
       (compound_method (messages, m_next_ptr, &method_attrs)) != m_next_ptr))
    return ERROR;

  /* 
   * Look up the "value" method.  It should at least be defined in 
   * Object class, so there won't be a need for another library
   * search, and possible redefinition of the receiver's class.
   */

  if ((m = get_instance_method (m_obj, m_obj -> obj, "value", TRUE)) == NULL) {
    char errbuf[MAXMSG];
    sprintf (errbuf, "%s (Class %s)", "value", m_obj -> obj -> __o_classname);
    __ctalkExceptionInternal (m_obj, undefined_method_x, errbuf);
    return ERROR;
  }

  context = object_context (messages, obj_ptr);

  switch (context)
    {
    case c_context:
    case c_argument_context:
      /*
       *  Handle expression cases where an object is preceded
       *  by a C variable and a C operator.  In the case of 
       *  control structure predicates, we need to output the
       *  expression here, because the operator probably won't
       *  be recognized as a method.  Normally, loop expressions
       *  get analyzed and output in loop_block_start () in 
       *  loop.c.
       */
      if ((ctrlblk_ptr < MAXARGS) &&
	  ((ctrlblk_pred && (C_CTRL_BLK -> stmt_type == stmt_while)) ||
	   (ctrlblk_pred && (C_CTRL_BLK -> stmt_type == stmt_do)) ||
 	   (ctrlblk_pred && (C_CTRL_BLK -> stmt_type == stmt_if)) ||
	   (for_term))) {
	int pred_expr_start_idx, pred_expr_end_idx;
	switch (C_CTRL_BLK -> stmt_type)
	  {
	  case stmt_for:
	    pred_expr_start_idx = C_CTRL_BLK -> for_term_start;
	    pred_expr_end_idx = C_CTRL_BLK -> for_term_end;
	    break;
	  case stmt_while:
	  case stmt_do:
	  case stmt_if:
	    pred_expr_start_idx = C_CTRL_BLK -> pred_start_ptr - 1;
	    pred_expr_end_idx = C_CTRL_BLK -> pred_end_ptr + 1;
	    break;
	  default:
	    pred_expr_start_idx = C_CTRL_BLK -> pred_start_ptr - 1;
	    pred_expr_end_idx = C_CTRL_BLK -> pred_end_ptr + 1;
	    _warning ("Unimplemented loop type in default_method.\n");
	    break;
	  }
	tmp_end_ptr = obj_ptr;
	/*
	 *  For now, simply translate the object's class directly
	 *  to its C type, without worrying what a receiver or
	 *  lvalue might need.
	 */
	fileout 
	  (fmt_rt_return
	   (fmt_rt_expr 
	    (messages, obj_ptr, &tmp_end_ptr), 
	    M_OBJ(messages[obj_ptr])->__o_classname, TRUE),
	    FALSE, obj_ptr);
	C_CTRL_BLK -> pred_expr_evaled = TRUE;
      } else {
/* 	strcpy (buf,  */
/* 		obj_2_c_wrapper  */
/* 		(m_obj, m_obj -> obj, m,  */
/* 		 rt_library_method_call (m_obj -> obj, m, */
/* 					 messages, obj_ptr), TRUE)); */
	strcpy (buf, 
		obj_2_c_wrapper_trans 
		(messages, obj_ptr, m_obj, m_obj -> obj, m, 
		 rt_library_method_call (m_obj -> obj, m,
					 messages, obj_ptr), TRUE));
	output_buffer (buf, obj_ptr);
      } /* if (for_term) { */
      break;
    default:
      /*
       *  If we're parsing the arguments of a C function, then
       *  the context could be argument_context if the first 
       *  token of the sub-parser is an opening parenthesis.
       */
      if (interpreter_pass == c_fn_pass) {
	strcpy (buf, 
		obj_2_c_wrapper 
		(m_obj, m_obj -> obj, m, 
		 rt_library_method_call (m_obj -> obj, m,
					 messages, obj_ptr), TRUE));
	output_buffer (buf, obj_ptr);
      } else {
	generate_method_call (m_obj -> obj, m -> selector, obj_ptr);
      }
      break;
    }

  ++m_obj -> evaled;
  ++m_obj -> output;

  return SUCCESS;

}

/*
 *  Determine if a statement requires evaluation at run time.
 *  1. Constant expressions that contain objects that are
 *     within if, while, and do..while predicates.
 *  2. Expressions that contain objects that are arguments to 
 *     C library functions.
 */

int need_rt_eval (MESSAGE_STACK messages, int rcvr_ptr) {

  int i, j,
    stack_end,
    have_object,
    have_c_var,
    have_c_fn,
    have_complex_expression,
    is_single_token_param = FALSE,
    last_obj_ptr = rcvr_ptr;    /* Avoid warnings. */
  METHOD *method = NULL;
  MESSAGE *m;
  OBJECT *obj = NULL;
  OBJECT_CONTEXT context;
  CVAR *c;
  CFUNC *fn;

  /*
   *  Run time expressions in for and while loops get handled in
   *  loop_blk_start.
   */

  if (ctrlblk_pred)
    if ((C_CTRL_BLK -> stmt_type == stmt_for) ||
	(C_CTRL_BLK -> stmt_type == stmt_while))
      return FALSE;

  stack_end = get_stack_top (messages);

  if (interpreter_pass == method_pass)
    method = new_methods[new_method_ptr + 1];

  for (i = rcvr_ptr, have_object = FALSE, 
	 have_c_var = FALSE, have_complex_expression = FALSE; 
       i > stack_end; i--) {

    m = messages[i];

    /*
     *   If parsing a method, determine if the label is the name of 
     *   a parameter.  Otherwise check global and local objects and
     *   c variables.
     */
    if (interpreter_pass == method_pass) {
      for (j = 0; j < method -> n_params; j++) {
	if (!strcmp (method -> params[j] -> name, m -> name)) {
	  ++have_object;
	  last_obj_ptr = i;
	  if (i == (stack_end + 1)) {
	    is_single_token_param = TRUE;
	  }
	}
      }
    }

    /*
     *  Check for global and local objects.
     */
    if (!have_object)
      if ((obj = get_object (m -> name, NULL)) != NULL) {
	++have_object;
	last_obj_ptr = i;
      }

    /*
     *  Check for, "self," and, "super," keywords.
     */

    if (!strcmp (m -> name, "self")) {
      int _next_idx;
      ++have_object;
      last_obj_ptr = i;
      if ((_next_idx = nextlangmsg (messages, rcvr_ptr)) != ERROR) {
	if (complex_self_method_statement (rcvr_class_obj, 
					   messages, _next_idx) ||
	    self_class_or_instance_variable_lookahead (messages, 
							rcvr_ptr) ||
	    is_method_proto (rcvr_class_obj -> __o_name,
			     M_NAME(messages[_next_idx]))) {
	  have_complex_expression = TRUE;
	}
      }
    }
    if (!strcmp (m -> name, "super")) {
      have_complex_expression = TRUE;
    }

    if (((c = get_local_var (m -> name)) != NULL) ||
	global_var_is_declared (m -> name)) {
      ++have_c_var;
    }

    if ((fn = get_function (m -> name)) != NULL) {
      ++have_c_fn;
    }

    if ((m -> tokentype == SEMICOLON) ||
	(m -> tokentype == CLOSEBLOCK) ||
	(m -> tokentype == ARRAYCLOSE))
      break;

    /*
     *  If the expression is part of an argument list, 
     *  then determine the context of the receiver.
     * 
     *  NOTE - this might not be needed.
     *  
     */

    if (m -> tokentype == ARGSEPARATOR) {
      context = object_context (messages, last_obj_ptr);
      switch (context)
	{
	case c_context:
	case c_argument_context:
	  break;
	case lvalue_context:
	case receiver_context:
	case argument_context:
	case null_context:
	  break;
	}
    }

  }

  /*
   *  Expressions with objects within if, while, and do... while
   *  predicates require run time evaluation.
   */

  if (is_single_token_param)
    return FALSE;

  if ((have_object && ctrlblk_pred) ||
      have_complex_expression)
    return TRUE;

  return FALSE;
}   

/*
 *  Use the parser to check a constant expression without writing
 *  any output. 
 */

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

  char buf[MAXMSG];
  int i,
    p_ctrlblk_pred;
  I_PASS p_pass;

  for (i = start, *buf = 0; i >= end; i--)
    strcat (buf, messages[i] -> name);

  p_pass = interpreter_pass;
  p_ctrlblk_pred = ctrlblk_pred;
  ctrlblk_pred = 0;
  interpreter_pass = expr_check;
  parse (buf, strlen (buf));
  /*
   *  "Self" needs to be determined for an interpreter pass,
   *  and probably a few other things before the interpreter
   *  can use the library call.
   */
  interpreter_pass = p_pass;
  ctrlblk_pred = p_ctrlblk_pred;
  return SUCCESS;
}

/*
 *  Determine if the C function is a library function
 *  that handles complete objects.  See the IS_OBJ_FN
 *  macro in object.h.  This should only be called in 
 *  a C context.
 *
 *  TO DO - This only works if there are no intervening
 *  opening parenthesis between the object token and 
 *  the start of the argument list.  Rewrite so 
 *  that the function can determine the start of a 
 *  complex argument list, including within a control
 *  block predicate.
 */

/* int obj_fn (MESSAGE_STACK messages, int msg_ptr) { */

/*   int arg_start_ptr, */
/*     fn_label_ptr, */
/*     stack_begin; */

/*   stack_begin = stack_start (messages); */

/*   if ((arg_start_ptr = scanback (messages, msg_ptr + 1, stack_begin, */
/* 				 OPENPAREN)) == ERROR) */
/*     error (messages[msg_ptr], "Parser error."); */

/*   if ((fn_label_ptr = scanback (messages, arg_start_ptr + 1, stack_begin, */
/* 				 LABEL)) == ERROR) */
/*     error (messages[msg_ptr], "Parser error."); */

/*   if (IS_OBJ_FN (messages[fn_label_ptr] -> name)) */
/*     return TRUE; */

/*   return FALSE; */

/* } */



/*
 *  Called by method_args (), so the expression always
 *  has an object to cope with.
 *
 *  If we can use a __ctalk_arg () call, then call the
 *  function from within the __ctalk_arg () call, with
 *  an object wrapper.  If the function requires
 *  a run-time evaluation, then register the function
 *  result.  We also have to do this for multiple
 *  argument methods, below.
 *
 *  This function should be called for functions that are 
 *  arguments.  That means method_msg_ptr points to the
 *  method, and arg_start and arg_end point to the 
 *  first token of the function and the closing paren,
 *  respectively.  However, we check here for a valid function
 *  name in several places.
 *
 *  If within a control block predicate, wait until we 
 *  format the expression.
 */

int fn_output_context (MESSAGE_STACK messages, int method_msg_ptr,
		       OBJECT *arg_object, METHOD *method, 
		       int arg_start, int arg_end) {

  int rcvr_ptr, fn_ptr, method_context_ptr;
  MESSAGE *m_method;

  if (ctrlblk_pred) return SUCCESS;

  m_method = messages[method_msg_ptr];

  if ((rcvr_ptr = prevlangmsg (message_stack (), method_msg_ptr))
      == ERROR) {
    warning (message_stack_at (method_msg_ptr),
	     "Unknown receiver in method_args ().");
    return ERROR;
  }

  /*
   *  The function name should be at the start of the argument,
   *  but if it isn't, search for it within the expression.
   */
  fn_ptr = arg_start;

  if ((method_context_ptr = prevlangmsg (messages, arg_start)) == ERROR) {
    warning (message_stack_at (method_msg_ptr),
	     "Unknown function in method_args ().");
    return ERROR;
  }

  if (!strcmp (M_NAME(messages[method_context_ptr]), method -> name)) {
    return FN_IS_ARG_RETURN_VAL;
  } else {
    OBJECT_CONTEXT __c;
    __c = object_context (messages, fn_ptr);
    switch (__c)
      {
      case c_argument_context:
	return FN_IS_ARG_RETURN_VAL;
	break;
      case argument_context:
	return FN_IS_METHOD_ARG;
	break;
      default:
	generate_c_to_obj_call (m_method -> receiver_obj, method, arg_object);
	break;
      }
  }

  return SUCCESS;
}

int is_expr_obj (MESSAGE *m) {
  if (m && m -> obj && !strcmp (M_VALUE_OBJ(m) -> __o_classname, EXPR_CLASSNAME))
    return TRUE;
  else
    return FALSE;
}

int expr_has_objects (MESSAGE_STACK messages, int start_idx, int end_idx) {

  int i;
  MESSAGE *m;

  for (i = start_idx; i >= end_idx; i--) {
    m = messages[i];
    if (m && m -> obj && !strcmp (M_NAME(m), m -> obj -> __o_name))
      return TRUE;
  }
  return FALSE;
}

/*
 * Expression like <classname> <classvar>.
 */
int class_variable_expression (MESSAGE_STACK messages, int idx) {
  int prev_label_ptr, next_label_ptr, i;
  int fn_idx, arg_idx;
  char *exprbuf;
  OBJECT_CONTEXT context;
  OBJECT *classvariable_object, *classvariable_value_object;
  METHOD *method;
  char arg_classname[MAXLABEL];

  prev_label_ptr = prevlangmsg (messages, idx);
  if (prev_label_ptr == ERROR)
    return ERROR;

  messages[idx]->receiver_obj = messages[prev_label_ptr]->obj;
  context = object_context (message_stack (), prev_label_ptr);

  if (complex_method_statement (messages[prev_label_ptr]->obj, 
				messages, idx)) {
    int _end_idx;
    switch (context)
      {
      case c_context:
      case c_argument_context:
	if ((next_label_ptr = nextlangmsg (messages, idx)) != ERROR) {
	  if ((classvariable_object = 
	       get_class_variable (M_NAME(messages[idx]), 
				   messages[idx]->receiver_obj->__o_name,
				   FALSE)) != NULL) {
	    classvariable_value_object = 
	      ((classvariable_object -> instancevars != NULL) ?
	      classvariable_object -> instancevars :
	       classvariable_object);
	    if (((method = 
		  get_instance_method (messages[idx], 
				       classvariable_value_object,
				       M_NAME(messages[next_label_ptr]),
				       FALSE)) != NULL) ||
		((method = 
		  get_instance_method (messages[idx], 
				       classvariable_value_object,
				       M_NAME(messages[next_label_ptr]),
				       FALSE)) != NULL)) {
	      fileout (array_obj_2_c_wrapper
		       (messages, 
			idx, 
			messages[idx]->receiver_obj, 
			method,
			fmt_rt_expr (messages, prev_label_ptr, &_end_idx)),
		       FALSE, prev_label_ptr);
	      for (i = prev_label_ptr; i >= _end_idx; i--) {
		++messages[i]->evaled;
		++messages[i]->output;
	      }
	    } else {
	      /*
	       *  Method not found - don't translate with 
	       *  array_obj_2_c_wrapper.
	       */
	      rt_expr (messages, prev_label_ptr, &_end_idx);
	    }
	  } else {
	    /*
	     *  Class variable object not found.
	     */
	    rt_expr (messages, prev_label_ptr, &_end_idx);
	  }
	} else {
	  /*
	   *  Terminal class variable message - 
	   *  should not be reached.
	   */
	  rt_expr (messages, prev_label_ptr, &_end_idx);
	}
	break;
      default:
	rt_expr (messages, prev_label_ptr, &_end_idx);
	break;
      }
  } else {
    /*
     *  Warn if there's a nonterminal next message.
     */
    int _next_idx;
    OBJECT *_class_variable_object;
    char _variable_value_class_name[MAXLABEL];
    if (((_next_idx = nextlangmsg (messages, idx)) != ERROR) &&
	!METHOD_ARG_TERM_MSG_TYPE(messages[_next_idx])) {
      exprbuf = collect_tokens (message_stack (), prev_label_ptr, _next_idx);
      warning (messages[idx], "Unknown statement, \"%s.\"",exprbuf);
    }
    exprbuf = collect_tokens (message_stack (), prev_label_ptr, idx);
    switch (context)
      {
      case c_context:
      case c_argument_context:
	if ((_class_variable_object = 
	     get_class_variable (M_NAME(messages[idx]), 
				 messages[idx]->receiver_obj->__o_name,
				 FALSE)) == NULL) {
	  warning(
		  messages[idx], 
		  "Class variable expression: Unknown receiver class %s, defaulting to Integer.", messages[idx]->receiver_obj->__o_name);
	  strcpy (_variable_value_class_name, INTEGER_CLASSNAME);
	} else {
	  if (_class_variable_object -> instancevars) {
	    strcpy (_variable_value_class_name, 
		    _class_variable_object->instancevars->__o_classname);
	  } else {
	    warning( messages[idx], 
		  "Class variable expression: Unknown variable %s value, class defaulting to Integer.", _class_variable_object -> __o_name);
	  }
	}
	if ((arg_idx = 
	    obj_expr_is_arg (message_stack (), prev_label_ptr,
			     stack_start (message_stack ()),
			     get_stack_top (message_stack ()),
			     &fn_idx)) != ERROR) {
	  
	  CFUNC *cfn;
	  if ((cfn = get_function (M_NAME(messages[fn_idx]))) != NULL) {
	    int __n_th_arg;
	    CVAR *__arg;
	    for (__n_th_arg = 0, __arg = cfn -> params; 
		 __n_th_arg < arg_idx; __n_th_arg++) 
	      __arg = __arg -> next;
	    strcpy (arg_classname, 
		    basic_class_from_cvar (__arg, 0));
	    fileout 
	      (fmt_rt_return 
	       (fmt_eval_expr (exprbuf), arg_classname, TRUE),
	       FALSE, idx);
	  } else {
	    _warning ("class variable expression: could not find function %s.\n", M_NAME(messages[fn_idx]));
	  }
	} else {
	  fileout 
	    (fmt_rt_return 
	     (fmt_eval_expr (exprbuf), _variable_value_class_name, TRUE),
	     FALSE, idx);
	}
	break;
      default:
	fileout ( fmt_eval_expr (exprbuf), FALSE, idx);
	break;
      }
    free (exprbuf);
    for (i = prev_label_ptr; i >= idx; i--) {
      ++messages[i] -> evaled;
      ++messages[i] -> output;
    }
  }
  return SUCCESS;
}

/*
 *  Expressions like self <classvariable>,
 *  or self <instancevariable>, but not
 *  "self value" which is handled by
 *  default_method ().
 */

int self_class_or_instance_variable_lookahead (MESSAGE_STACK messages, 
						int idx) {
  int next_label_ptr;
  OBJECT *var_object;

  if ((next_label_ptr = nextlangmsg (messages, idx)) == ERROR) {
    return FALSE;
  } else {
    if (METHOD_ARG_TERM_MSG_TYPE(messages[next_label_ptr])) {
      return FALSE;
    } else {
      if (M_TOK(messages[next_label_ptr]) != LABEL) { 
	return FALSE;
      } else {
 	if (!strcmp (M_NAME(messages[next_label_ptr]), "value"))
 	  return FALSE;
      }
    } 
  }

  for (var_object = rcvr_class_obj -> classvars; 
       var_object; var_object = var_object -> next) {
    if (!strcmp (M_NAME(messages[next_label_ptr]), 
		 var_object -> __o_name))
      return TRUE;
  }

  for (var_object = rcvr_class_obj -> instancevars; 
       var_object; var_object = var_object -> next) {
    if (!strcmp (M_NAME(messages[next_label_ptr]), 
		 var_object -> __o_name))
      return TRUE;
  }

  return FALSE;

}


