/* $Id: control.c,v 1.13 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.
*/

/*
 *  Functions to parse control structures.
 */

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

extern int do_predicate;     /* Declared in loop.c.  True if parsing a 
				do predicate in its own frame.         */

extern int nolinemarker_opt; /* Declared in main.c.                    */
extern I_PASS interpreter_pass;
extern char input_source_file[FILENAME_MAX];

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

CTRLBLK *ctrlblks[MAXARGS + 1];
int ctrlblk_ptr;

int ctrlblk_pred,            /* Global states.                         */
  ctrlblk_blk,
  ctrlblk_else_blk;

int for_init,                /* States of for loop predicates.         */
  for_term,
  for_inc;

static int loop_ext;                    /* Unique extent for loop labels. */

RT_EXPR_CLASS rt_expr_class = rt_expr_null;

int get_ctrlblk_ptr (void) {
  return ctrlblk_ptr;
}

void init_control_blocks (void) {
  ctrlblk_ptr = MAXARGS;
  ctrlblk_pred = ctrlblk_blk = FALSE;
  for_init = for_term = for_inc = FALSE;
  loop_ext = 0;
}

void ctrlblk_push (CTRLBLK *c) {
  ctrlblks[ctrlblk_ptr--] = c;
}

CTRLBLK *ctrlblk_pop (void) {
  static CTRLBLK *c;
  c = ctrlblks[++ctrlblk_ptr];
  ctrlblks[ctrlblk_ptr] = NULL;

  return c;
}

/*
 *  Delete the predicate lists for do and while loops, and
 *  the control block struct.
 *
 *  TO DO - 
 *  Delete the predicate lists of for loops when they
 *  are finished.
 */

void delete_control_block (CTRLBLK *c) {
  delete_list (&c -> pred_args);
  delete_list (&c -> for_init_args);
  delete_list (&c -> for_init_methods);
  delete_list (&c -> for_init_cleanup);
  delete_list (&c -> for_term_args);
  delete_list (&c -> for_term_methods);
  delete_list (&c -> for_term_cleanup);
  delete_list (&c -> for_inc_args);
  delete_list (&c -> for_inc_methods);
  delete_list (&c -> for_inc_cleanup);
  free (c);
}

CTRLBLK *new_ctrl_blk (void) {
  static CTRLBLK *c;

  if ((c = (CTRLBLK *) calloc (1, sizeof (CTRLBLK))) == NULL)
    return NULL;
  return c;
}

/* 
 *  If the message stack pointer is past the end of the 
 *  innermost control block, also make sure that we haven't
 *  recursed within a control block, by checking the parser
 *  level, before deleting the control block.
 *
 *  If the control block is an if statement, also check 
 *  whether the parser is past the else clause, if any.
 *
 *  NOTE - The control block states should be completely
 *  reset by ctrl_block_state ().  If this turns out 
 *  not to be the case, then remove_ctrl_block () should
 *  return whether it has deleted a block, and then
 *  all of the states should be completely reset.
 */

int remove_ctrl_block (int ptr) {
 
  if (C_CTRL_BLK -> stmt_type == stmt_if) {
    if (C_CTRL_BLK -> else_start_ptr) {
      if ((ptr < C_CTRL_BLK -> else_end_ptr) &&
	  (C_CTRL_BLK -> level >= current_parser () -> level)) {
 	ctrlblk_blk = ctrlblk_pred = ctrlblk_else_blk = FALSE;
	delete_control_block (ctrlblk_pop ());
      }
    } else {
      if ((ptr < C_CTRL_BLK -> blk_end_ptr) &&
	  (C_CTRL_BLK -> level >= current_parser () -> level)) {
 	ctrlblk_blk = ctrlblk_pred = ctrlblk_else_blk = FALSE;
	delete_control_block (ctrlblk_pop ());
      }
    }
  } else {
    if (C_CTRL_BLK -> stmt_type == stmt_do) {
      if ((ptr < C_CTRL_BLK -> pred_end_ptr) &&
	  (C_CTRL_BLK -> level >= current_parser () -> level)) {
	ctrlblk_blk = ctrlblk_pred = FALSE;
	delete_control_block (ctrlblk_pop ());
      }
    } else {
      if ((ptr < C_CTRL_BLK -> blk_end_ptr) &&
	  (C_CTRL_BLK -> level >= current_parser () -> level)) {
	ctrlblk_blk = ctrlblk_pred = FALSE;
	delete_control_block (ctrlblk_pop ());
      }
    }
  }
  return TRUE;
}      

int ctrl_block_state (int ptr) {

  if (ctrlblk_ptr == MAXARGS)
    return FALSE;

  if (do_predicate || (interpreter_pass == expr_check))
    return FALSE;

  remove_ctrl_block (ptr);
      
  if (ctrlblk_ptr == MAXARGS)
    return FALSE;

  if ((ptr <= C_CTRL_BLK -> pred_start_ptr) &&
      (ptr >= C_CTRL_BLK -> pred_end_ptr))
    ctrlblk_pred = TRUE;
  else
    ctrlblk_pred = FALSE;
      
  if ((ptr <= C_CTRL_BLK -> blk_start_ptr) &&
      (ptr >= C_CTRL_BLK -> blk_end_ptr))
    ctrlblk_blk = TRUE;
  else 
    ctrlblk_blk = FALSE;
  
  if (C_CTRL_BLK -> stmt_type == stmt_if) {
  if ((ptr <= C_CTRL_BLK -> else_start_ptr) &&
      (ptr >= C_CTRL_BLK -> else_end_ptr))
    ctrlblk_else_blk = TRUE;
  else 
    ctrlblk_else_blk = FALSE;
  }

  if (C_CTRL_BLK -> stmt_type == stmt_for) {
    if ((ptr <= C_CTRL_BLK -> for_init_start) &&
	(ptr >= C_CTRL_BLK -> for_init_end)) {
      for_init = TRUE;
      for_term = for_inc = FALSE;
    }
    if ((ptr <= C_CTRL_BLK -> for_term_start) &&
	(ptr >= C_CTRL_BLK -> for_term_end)) {
      for_term = TRUE;
      for_init = for_inc = FALSE;
    }
    if ((ptr <= C_CTRL_BLK -> for_inc_start) &&
	(ptr >= C_CTRL_BLK -> for_inc_end)) {
      for_inc = TRUE;
      for_init = for_term = FALSE;
    }
  } else {
    for_init = for_term = for_inc = FALSE;
  }

  if (ptr == C_CTRL_BLK -> pred_start_ptr)
    return PRED_START;
  if (ptr == C_CTRL_BLK -> pred_end_ptr)
    return PRED_END;
  if (ptr == C_CTRL_BLK -> blk_start_ptr)
    return BLK_START;
  if (ptr == C_CTRL_BLK -> blk_end_ptr)
    return BLK_END;
  if (ptr == C_CTRL_BLK -> else_start_ptr)
    return ELSE_START;
  if (ptr == C_CTRL_BLK -> else_end_ptr)
    return ELSE_END;
  if (ctrlblk_pred)
    return CTRLBLK_PRED;
  if (ctrlblk_blk)
    return CTRLBLK_BLK;
  if (ctrlblk_else_blk)
    return ELSE_BLK;

  remove_ctrl_block (ptr);
      
  return FALSE;

}

int control_structure (MESSAGE_STACK messages, int ptr) {

  /*
   *  Case statements use integer constants as their arguments,
   *  so their usefulness with objects is limited.  The case
   *  statement call is here if it is needed later.
   */
  if (!strcmp (messages[ptr] -> name, "case"))
    return case_stmt (messages, ptr);
  
  if (!strcmp (messages[ptr] -> name, "do"))
    return do_stmt (messages, ptr);
  
  if (!strcmp (messages[ptr] -> name, "for"))
    return for_stmt (messages, ptr);

  if (!strcmp (messages[ptr] -> name, "if"))
    return if_stmt (messages, ptr);

  if (!strcmp (messages[ptr] -> name, "switch"))
    return switch_stmt (messages, ptr);
  
  /*
   *  Make sure a, "while," is not part of a, "do .. while."
   */
  if (!strcmp (messages[ptr] -> name, "while") &&
      ! messages[ptr] -> evaled && ! messages[ptr] -> output)
      return while_stmt (messages, ptr);

  return ERROR;
}

int case_stmt (MESSAGE_STACK messages, int ptr) {
  return SUCCESS;
}

int for_predicates (CTRLBLK *c, MESSAGE_STACK messages, int ptr) {

  int i,
    lasttoken,
    lastmsgptr = ptr,        /* Avoid a warning. */
    nexttoken;
  MESSAGE *m, *m_next;
  enum {init, term, inc, null} for_state;
  
  for_state = null;

  for (i = c -> pred_start_ptr, 
	 c -> for_init_start = ERROR, 
	 c -> for_init_end = ERROR,
	 c -> for_term_start = ERROR,
	 c -> for_term_end = ERROR,
	 c -> for_inc_start = ERROR,
	 c -> for_inc_end = ERROR,
	 lasttoken = ERROR;
       i >= c -> pred_end_ptr; i--) {

    m = messages[i];
    if ((nexttoken = nextlangmsg (messages, i)) != ERROR)
      m_next = messages[nextlangmsg (messages, i)];
    else
      m_next = NULL;

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

    switch (lasttoken)
      {
      case OPENPAREN:
	if (lastmsgptr == c -> pred_start_ptr) {
	  for_state = init;
	  c -> for_init_start = i;
	}
	/*
	 *  Handle emtpy predicates.
	 */
	if (m -> tokentype == SEMICOLON)
	  c -> for_init_end = i;
	break;
      case SEMICOLON:
	switch (for_state) 
	  {
	  case init:
	    for_state = term;
	    c -> for_term_start = i;
	    /*  Handle emtpy predicates. */
	    if (m -> tokentype == SEMICOLON)
	      c -> for_term_end = i;
	    break;
	  case term:
	    for_state = inc;
	    c -> for_inc_start = i;
	    /* Empty predicate. */
	    if (m -> tokentype == CLOSEPAREN)
	      c -> for_inc_start = c -> for_inc_end = lastmsgptr;
	    break;
	  case inc:
	  case null:
	    break;
	  }
	break;
      case ERROR:
	break;
      default:
	if (m_next && IS_MESSAGE (m_next) &&
	    (m_next -> tokentype == SEMICOLON)) {
	  switch (for_state)
	    {
	    case init:
	      c -> for_init_end = i;
	      break;
	    case term:
	      c -> for_term_end = i;
	      break;
	    case inc:
	    case null:
	      break;
	    }
	}
      }

    if ((m -> tokentype == CLOSEPAREN) && (i == c -> pred_end_ptr)) {
      c -> for_inc_end = lastmsgptr;
      goto done;
    }


    lasttoken = m -> tokentype;
    lastmsgptr = i;
  }

 done:
  return SUCCESS;

}

int for_stmt (MESSAGE_STACK messages, int ptr) {

  CTRLBLK *c;
  int i, j, k,
    stack_end,
    p_start,
    p_end,
    lookahead,
    blk_level,
    last_frame_ptr,
    start_frame_ptr;
  FRAME *this_frame,
    *next_frame;
  MESSAGE *m;
  int n_pred_objects;
  OBJECT *pred_objects[MAXARGS];  /* Arrays of object references allow */
                                  /* the code to keep track of objects */
                                  /* without duplicating them.         */

  loop_linemarker (messages, ptr);

  if ((c = new_ctrl_blk ()) == NULL)
    error (messages[ptr], "if_stmt: %s.", strerror (errno));

  ctrlblk_push (c);

  c -> stmt_type = stmt_for;
  c -> id = loop_ext++;
  c -> level = current_parser () -> level;
  c -> keyword_ptr = ptr;

  stack_end = get_stack_top (messages);

  start_frame_ptr = current_parser () -> frame;
  last_frame_ptr = get_frame_pointer ();

  for (i = ptr, p_start = ERROR; i > stack_end; i--) {

    m = messages[i];

    switch (m -> tokentype)
      {
      case LABEL:
	break;
      case OPENPAREN:
	if (p_start == ERROR) {
	  p_start = c -> pred_start_ptr = i;
	  if ((p_end = match_paren (messages, i, stack_end)) == ERROR) {
	    warning (messages[i], "Mismatched parentheses.");
	    return ERROR;
	  }
	  c -> pred_end_ptr = p_end;
	  for_predicates (c, messages, i);
	}
	/*
	 *  Find the start and end of the block.
	 */

	/*
	 *  Look for the next message in the frame.
	 */
	if ((lookahead = nextlangmsg (messages, p_end)) == ERROR) {
	  /*
	   *  If at the end of the frame the block is a single 
	   *  statement and should be contained in the next
	   *  frame.
	   */
	  c -> braces = FALSE;
	  c -> blk_start_ptr = p_end - 1;
	  c -> blk_end_ptr = -1;

 	  for (j = start_frame_ptr - 1; 
 	       ((j >= last_frame_ptr) &&
 		(c -> blk_end_ptr == -1)); 
 	       j--) {
 	    this_frame = frame_at (j);
 	    next_frame = frame_at (j - 1);
 	    for (k = this_frame -> message_frame_top;
 		 ((k > next_frame -> message_frame_top) &&
 		    (c -> blk_end_ptr == -1));
 		 k--) {
 	      if ((messages[k] -> tokentype == NEWLINE) ||
 		  (messages[k] -> tokentype == WHITESPACE))
 		continue;
 	      if (messages[k] -> tokentype == SEMICOLON)
 		c -> blk_end_ptr = k;
 	    }
	  }


/* 	  c -> braces = FALSE; */
/* 	  c -> blk_start_ptr = c -> blk_end_ptr = -1; */
/* 	  for (j = start_frame_ptr - 1;  */
/* 	       ((j >= last_frame_ptr) && */
/* 		((c -> blk_start_ptr == -1) || (c -> blk_end_ptr == -1)));  */
/* 	       j--) { */
/* 	    this_frame = frame_at (j); */
/* 	    next_frame = frame_at (j - 1); */
	    
/* 	    for (k = this_frame -> message_frame_top; */
/* 		 ((k > next_frame -> message_frame_top) && */
/* 		   ((c -> blk_start_ptr == -1) || */
/* 		    (c -> blk_end_ptr == -1))); */
/* 		 k--) { */
/* 	      if ((messages[k] -> tokentype == NEWLINE) || */
/* 		  (messages[k] -> tokentype == WHITESPACE)) */
/* 		continue; */
/* 	      if ((messages[k] -> tokentype == LABEL) && */
/* 		  (c -> blk_start_ptr == -1)) */
/* 		c -> blk_start_ptr = k; */
/* 	      if (messages[k] -> tokentype == SEMICOLON) */
/* 		c -> blk_end_ptr = k; */
/* 	    } */
/* 	  } */
	} else {
	  /*
	   *  Empty block terminated by a semicolon.
	   */
	  if (messages[lookahead] -> tokentype == SEMICOLON) {
	    c -> blk_start_ptr = c -> blk_end_ptr = lookahead;
	    c -> braces = FALSE;

	    /*
	     *  Check for nested loops without braces.
	     */
	    for (k = ctrlblk_ptr + 2; k <= MAXARGS; k++) {
	      if (ctrlblks[k] -> level < current_parser () -> level)
		break;
	      if (ctrlblks[k] -> braces == FALSE) {
		ctrlblks[k] -> blk_end_ptr = c -> blk_end_ptr;
	      }
	    }
	  } else {
	    if (messages[lookahead] -> tokentype == OPENBLOCK) {
	      c -> braces = TRUE;
	      c -> blk_start_ptr = lookahead;
	      for (j = lookahead, blk_level = 0; j > stack_end; j--) {
		if (messages[j] -> tokentype == OPENBLOCK)
		  ++blk_level;
		if (messages[j] -> tokentype == CLOSEBLOCK) {
		  if (--blk_level == 0) {
		    c -> blk_end_ptr = j;
		    goto parsed;
		  }
		}
	      }
	    }
	  }
	}
	goto done;
	break;
      }
  }

 parsed:
  /* 
   *   Find the objects in the predicate.
   */

  for (i = c -> pred_start_ptr, n_pred_objects = 0; 
       i >= c -> pred_end_ptr; i--) {
    m = messages[i];
    if ((pred_objects[n_pred_objects] = get_object (m -> name, NULL))
	!= NULL)
      ++n_pred_objects;
  }

  /*
   *  Find the objects in the block.  Because methods may independently
   *  change the value of objects, any reference to a predicate object
   *  causes the function to set c -> pred_objs_d.  Only one reference
   *  is needed to set c -> pred_objs_d, so the iteration doesn't 
   *  need to look any further.
   */

  for (i = c -> blk_start_ptr; i >= c -> blk_end_ptr; i--) {
    for (j = 0; j < n_pred_objects; j++) {
      if (!strcmp (messages[i] -> name, pred_objects[j] -> __o_name)) {
	c -> pred_args_d = TRUE;
	goto done;
      }
    }
  }

 done:

  return SUCCESS;
}

int if_stmt (MESSAGE_STACK messages, int ptr) {

  CTRLBLK *c;
  int i, j, 
    k = 0,             /* Avoid a warning. */
    stack_end,
    frame_offset,
    start_frame,
    last_frame,
    n_parens,
    n_blocks = 0;      /* Avoid another warning. */
  FRAME *this_frame, 
    *next_frame;
  enum {
    if_null,
    if_keyword,
    if_pred_start,
    if_pred_end,
    if_blk_start,
    if_blk_end,
    if_else,
    if_else_start,
    if_else_end
  } if_state;

  loop_linemarker (messages, ptr);

  frame_offset = 0;
  if_state = if_null;

  last_frame = get_frame_pointer ();

  stack_end = get_stack_top (messages);

  this_frame = frame_at (current_parser () -> frame + frame_offset);
  start_frame = current_parser () -> frame;

  if ((c = new_ctrl_blk ()) == NULL)
    error (messages[ptr], "if_stmt: %s.", strerror (errno));

  ctrlblk_push (c);

  c -> stmt_type = stmt_if;
  c -> id = loop_ext++;
  c -> level = current_parser () -> level;
  c -> keyword_ptr = ptr;

  if_state = if_keyword;

  for ( ; start_frame + frame_offset >= last_frame; --frame_offset) {

    this_frame = frame_at (current_parser () -> frame + frame_offset);
    next_frame = frame_at (current_parser () -> frame + frame_offset - 1);

    switch (if_state)
      {
      case if_keyword:
	for (i = ptr, n_parens = 0, n_blocks = 0; 
	     (i > next_frame -> message_frame_top) && 
	       (if_state == if_keyword);
	     i--) {
	  if (messages[i] -> tokentype == OPENPAREN) {
	    c -> pred_start_ptr = i;
	    for (j = i; j > stack_end; j--) {
	      if (messages[j] -> tokentype == OPENPAREN) {
		++n_parens;
		if (n_parens == 1) {  /* Don't count nested parentheses. */
		  j = leading_unary_op (messages, j);
		  c -> pred_start_ptr = j;
		  if ((c -> pred_end_ptr = 
		       match_paren (messages, j, stack_end)) == ERROR) {
		    warning (messages[j], "Mismatched parentheses.");
		    delete_control_block (ctrlblk_pop ());
		    return ERROR;
		  }
		}
	      }
	      if (messages[j] -> tokentype == CLOSEPAREN) {
		--n_parens;
		if (n_parens == 0) {
		  c -> pred_end_ptr = j;
		  if_state = if_pred_end;
		  /*
		   *  If the next message is an open block, then
		   *  the control block has braces.  If 
		   *  We get to the end of the frame, then 
		   *  look through the next frames for 
		   *  a label.
		   */
		  for (k = j - 1; 
		       (k > stack_end) && (if_state == if_pred_end); 
		       k--) {
		    if (messages[k] -> tokentype == OPENBLOCK) {
		      c -> braces = TRUE;
		      c -> blk_start_ptr = k;
		      if_state = if_blk_start;
		      n_blocks = 1;
		      goto pred_frame_done;
		    }
		    if (messages[k] -> tokentype == LABEL) {
		      c -> braces = FALSE;
		      c -> blk_start_ptr = k;
		      if_state = if_blk_start;
		      goto pred_frame_done;
		    }
		  }
		}
	      }
	    }
	  }
	}
      pred_frame_done:
	/*
	 *  Find out which frame we ended on.  If the control block
	 *  has braces, go on to the next frame.  If there are no
	 *  braces, move back up one frame so the next loop will
	 *  also be on this frame.
	 */
	while (TRUE) {
	  if ((k <= this_frame -> message_frame_top) &&
	      (k > next_frame -> message_frame_top))
	    break;
	  --frame_offset;
	  this_frame = frame_at (current_parser () -> frame + 
				 frame_offset);
	  next_frame = frame_at (current_parser () -> frame + 
				 frame_offset - 1);
	}
	if (c -> braces == FALSE)
	  ++frame_offset;
	break;
      case if_pred_start:
      case if_pred_end:
	/*
	 *  The predicate start and end occur in the same frame as 
	 *  the keyword, so the function looks for it in the
	 *  case above.
	 */
	break;
      case if_blk_start:
	for (i = this_frame -> message_frame_top; 
	     (i > next_frame -> message_frame_top) && 
	       (if_state == if_blk_start);
	     i--) {
	  if (c -> braces == FALSE) {
	    if (!messages[i] || !IS_MESSAGE(messages[i])) {
	      if_state = if_blk_end;
	      c -> blk_end_ptr = i;
	    } else {
	      if (messages[i] -> tokentype == SEMICOLON) {
		if_state = if_blk_end;
		c -> blk_end_ptr = i;
	      }
	    }
	  } else {
	    if (messages[i] -> tokentype == OPENBLOCK)
	      ++n_blocks;
	    if (messages[i] -> tokentype == CLOSEBLOCK) {
	      --n_blocks;
	      if (n_blocks == 0) {
		if_state = if_blk_end;
		c -> blk_end_ptr = i;
	      }
	    }
	  }
	}
	break;
      case if_blk_end:
	/*
	 *  Look for an else clause.  We also have to 
	 *  back the frame offset up one so the next iteration
	 *  stays on this frame.  The opening brace of an
	 *  else clause is in the same frame as the keyword.
	 */
	for (i = this_frame -> message_frame_top; 
	     (i > next_frame -> message_frame_top) && 
	       (if_state == if_blk_end);
	     i--) {
	  if ((messages[i] -> tokentype == WHITESPACE) ||
	      (messages[i] -> tokentype == NEWLINE))
	    continue;
	  if (messages[i] -> tokentype == LABEL) {
	    if (!strcmp (messages[i] -> name, "else")) {
	      if_state = if_else;
	      ++frame_offset;
	    } else {
	      if_state = if_null;
	    }
	  } else {
	    if_state = if_null;
	  }
	}
	break;
      case if_else:
	/*
	 *  The next non-whitespace token after the, "else,"
	 *  keyword is the start of the else block.
	 */
	for (i = this_frame -> message_frame_top; 
	     (i > next_frame -> message_frame_top) && 
	       (if_state == if_else);
	     i--) {
	  if ((messages[i] -> tokentype == WHITESPACE) ||
	      (messages[i] -> tokentype == NEWLINE))
	    continue;
	  if ((messages[i] -> tokentype == LABEL) &&
	      (!strcmp (messages[i] -> name, "else"))) {
	    c -> keyword2_ptr = i;
	    continue;
	  }
 	  c -> else_start_ptr = i;
	  if_state = if_else_start;
	  if (messages[i] -> tokentype == OPENBLOCK)
	    ++n_blocks;
	  /*
	   *  If there are no braces, the end of the block
	   *  is in the same frame.  Look through this
	   *  frame again on the next iteration.
	   */
 	  if (c -> braces == FALSE)
 	    ++frame_offset;
	}
	break;
      case if_else_start:
	/*
	 *  n_blocks should be 1 here if the control structure
	 *  has blocks.
	 */
	for (i = this_frame -> message_frame_top; 
	     (i > next_frame -> message_frame_top) && 
	       (if_state == if_else_start);
	     i--) {
	  if (c -> braces == FALSE) {
	    if (messages[i] -> tokentype == SEMICOLON) {
	      if_state = if_else_end;
	      c -> else_end_ptr = i;
	    }
	  } else {
	    if (messages[i] -> tokentype == OPENBLOCK)
	      ++n_blocks;
	    if (messages[i] -> tokentype == CLOSEBLOCK) {
	      --n_blocks;
	      if (n_blocks == 0) {
		if_state = if_else_end;
		c -> else_end_ptr = i;
	      }
	    }
	  }
	}
	break;
      case if_else_end:
	break;
      case if_null:
	break;
      }
  }

  return SUCCESS;
}

#define LAST_FRAME ((current_parser()->frame + frame_offset) == \
  get_frame_pointer())

int while_stmt (MESSAGE_STACK messages, int ptr) {

  CTRLBLK *c;
  int i, j, k,
    stack_end;
  int frame_offset,
    start_frame,
    last_frame,
    n_parens,
    n_blocks = 0;         /* Avoid a warning. */
  FRAME *this_frame, 
    *next_frame;
  enum {
    while_null,
    while_keyword,
    while_pred_start,
    while_pred_end,
    while_blk_start,
    while_blk_end,
  } while_state;

  /*
   *  Make sure the keyword isn't part of a do... while loop.
   */
  if (ctrlblk_ptr < MAXARGS)
    if (C_CTRL_BLK -> stmt_type == stmt_do)
      return SUCCESS;

  loop_linemarker (messages, ptr);

  frame_offset = 0;
  while_state = while_null;
  last_frame = get_frame_pointer ();
  stack_end = get_stack_top (messages);

  this_frame = frame_at (current_parser () -> frame + frame_offset);
  start_frame = current_parser () -> frame;

  if ((c = new_ctrl_blk ()) == NULL)
    error (messages[ptr], "if_stmt: %s.", strerror (errno));

  ctrlblk_push (c);

  c -> stmt_type = stmt_while;
  c -> id = loop_ext++;
  c -> level = current_parser () -> level;
  c -> keyword_ptr = ptr;

  while_state = while_keyword;

  for ( ; (start_frame + frame_offset >= last_frame) &&
	  (while_state != while_blk_end); 
	--frame_offset) {

    this_frame = frame_at (current_parser () -> frame + frame_offset);
    next_frame = frame_at (current_parser () -> frame + frame_offset - 1);

    switch (while_state)
      {
      case while_keyword:
	for (i = this_frame -> message_frame_top, n_parens = 0, n_blocks = 0; 
	     (i > next_frame -> message_frame_top) && 
	       (while_state == while_keyword);
	     i--) {
	  if (messages[i] -> tokentype == OPENPAREN) {
	    if (!n_parens) { 
	      i = leading_unary_op (messages, i);
	      c -> pred_start_ptr = i;
	    }
	    for (j = this_frame -> message_frame_top; 
		 (j > stack_end) && 
		   (while_state == while_keyword);
		 j--) {
	      if (messages[j] -> tokentype == OPENPAREN) {
		++n_parens;
		/* This condition is here if we run into a
                   strange construct like an attribute. */
		if (!n_parens) {
		  j = leading_unary_op (messages, j);
		  c -> pred_start_ptr = j;
		  if ((c -> pred_end_ptr = 
		       match_paren (messages, j, stack_end)) == ERROR) {
		    warning (messages[j], "Mismatched parentheses.");
		    delete_control_block (ctrlblk_pop ());
		    return ERROR;
		  }
		}
	      }
	      if (messages[j] -> tokentype == CLOSEPAREN) {
		--n_parens;
		if (n_parens == 0) {
		  c -> pred_end_ptr = j;
		  while_state = while_pred_end;
		  /*
		   *  If the next message is an open block, then
		   *  the control block has braces.  If 
		   *  We get to the end of the frame, then 
		   *  look through the next frames for 
		   *  a label.
		   */
		  for (k = j - 1; 
		       (k > stack_end) && (while_state == while_pred_end); 
		       k--) {
		    if (!M_ISSPACE(messages[k])) {
		      c -> blk_start_ptr = k;
		      while_state = while_blk_start;
		      if (M_TOK(messages[k]) == OPENBLOCK) {
			c -> braces = TRUE;
			n_blocks = 1;
		      } else {
			c -> braces = FALSE;
			if (M_TOK(messages[k]) == SEMICOLON) {
			  c -> blk_end_ptr = k;
			  while_state = while_blk_end;
			}
			/*
			 *  I.e.; Empty loop at the end of a function.
			 */
			if (LAST_FRAME)
			  while_state == while_blk_end;
		      }
		    }
		  }
		}
	      }
	    }
	  }
	}	
	break;
      case while_pred_start:
      case while_pred_end:
	/*
	 *  The predicate start and end, and the block start,
	 *  all occur within the same frame, if the while
	 *  loop has braces, so they are all handled in the
	 *  case above.
	 */
	break;
      case while_blk_start:
	for (k = this_frame -> message_frame_top; 
	     (k > next_frame -> message_frame_top) && 
	       (while_state == while_blk_start);
	     k--) {
	  if (c -> braces == FALSE) {
	    if (LAST_FRAME) {
	      /*
	       *  I.e.; Empty loop at the end of a function.
	       */
	      c -> blk_end_ptr = c -> blk_start_ptr;
	      while_state = while_blk_end;
	    } else {
	      if (messages[k] -> tokentype == SEMICOLON) {
		while_state = while_blk_end;
		c -> blk_end_ptr = k;
	      }
	    }
	  } else {
	    if (messages[k] -> tokentype == OPENBLOCK)
	      ++n_blocks;
	    if (messages[k] -> tokentype == CLOSEBLOCK) {
	      --n_blocks;
	      if (n_blocks == 0) {
		while_state = while_blk_end;
		c -> blk_end_ptr = k;
	      }
	    }
	  }
	}
	break;
      case while_blk_end:
      case while_null:
	break;
      }
  }

  return SUCCESS;
}

int do_stmt (MESSAGE_STACK messages, int ptr) {

  CTRLBLK *c;
  int i, j,
    stack_end;
  int frame_offset,
    start_frame,
    last_frame,
    n_parens,
    n_blocks = 0;         /* Avoid a warning. */
  FRAME *this_frame, 
    *next_frame;
  enum {
    do_null,
    do_keyword,
    do_keyword2,
    do_pred_start,
    do_pred_end,
    do_blk_start,
    do_blk_end,
  } do_state;

  /*
   *  Make sure the keyword isn't part of a do... while loop.
   */
  if (ctrlblk_ptr < MAXARGS)
    if (C_CTRL_BLK -> stmt_type == stmt_do)
      return SUCCESS;

  loop_linemarker (messages, ptr);

  frame_offset = 0;
  do_state = do_null;
  last_frame = get_frame_pointer ();
  stack_end = get_stack_top (messages);

  this_frame = frame_at (current_parser () -> frame + frame_offset);
  start_frame = current_parser () -> frame;

  if ((c = new_ctrl_blk ()) == NULL)
    error (messages[ptr], "if_stmt: %s.", strerror (errno));

  ctrlblk_push (c);

  c -> stmt_type = stmt_do;
  c -> id = loop_ext++;
  c -> level = current_parser () -> level;
  c -> keyword_ptr = ptr;

  do_state = do_keyword;

  for ( ; start_frame + frame_offset >= last_frame; --frame_offset) {

    this_frame = frame_at (current_parser () -> frame + frame_offset);
    next_frame = frame_at (current_parser () -> frame + frame_offset - 1);

    switch (do_state)
      {
      case do_keyword:
	for (i = this_frame -> message_frame_top, n_parens = 0, n_blocks = 0; 
	     (i > next_frame -> message_frame_top) && 
	       (do_state == do_keyword);
	     i--) {
	  if (messages[i] -> tokentype == OPENBLOCK) {
	    c -> braces = TRUE;
	    c -> blk_start_ptr = i;
	    do_state = do_blk_start;
	    n_blocks = 1;
	  }
	  if (messages[i] -> tokentype == LABEL) {
	    if (strcmp (messages[i] -> name, "do")) {
	      c -> braces = FALSE;
	      c -> blk_start_ptr = i;
	      do_state = do_blk_start;
	    }
	  }
	}
	break;
      case do_keyword2:
	for (i = this_frame -> message_frame_top, n_parens = 0, n_blocks = 0; 
	     (i > next_frame -> message_frame_top) && 
	       (do_state == do_keyword2);
	     i--) {
	  if (messages[i] -> tokentype == OPENPAREN) {
	    c -> pred_start_ptr = i;
	    for (j = this_frame -> message_frame_top; 
		 j > next_frame -> message_frame_top;
		 j--) {
	      if (messages[j] -> tokentype == OPENPAREN) {
		++n_parens;
		j = leading_unary_op (messages, j);
		if (n_parens == 1)
		  c -> pred_start_ptr = j;
	      }
	      if (messages[j] -> tokentype == CLOSEPAREN) {
		--n_parens;
		if (n_parens == 0) {
		  c -> pred_end_ptr = j;
		  do_state = do_pred_end;
		}
	      }
	    }
	    i = c -> pred_end_ptr;
	  }
	}	
	break;
      case do_pred_start:
      case do_pred_end:
	/*
	 *  The predicate start and end, and the block start,
	 *  all occur within the same frame, if the while
	 *  loop has braces, so they are all handled in the
	 *  case above.
	 */
	break;
      case do_blk_start:
	for (i = this_frame -> message_frame_top; 
	     (i > next_frame -> message_frame_top) && 
	       (do_state == do_blk_start);
	     i--) {
	  if (c -> braces == FALSE) {
	    if (messages[i] -> tokentype == SEMICOLON) {
	      do_state = do_blk_end;
	      c -> blk_end_ptr = i;
	      /*
	       *  Back up a frame in case we're in the 
	       *  predicate frame.
	       */
	      ++frame_offset;
	    }
	  } else {
	    if (messages[i] -> tokentype == OPENBLOCK)
	      ++n_blocks;
	    if (messages[i] -> tokentype == CLOSEBLOCK) {
	      --n_blocks;
	      if (n_blocks == 0) {
		do_state = do_blk_end;
		c -> blk_end_ptr = i;
	      }
	    }
	  }
	}
	break;
      case do_blk_end:
	for (i = this_frame -> message_frame_top; 
	     (i > next_frame -> message_frame_top) && 
	       (do_state == do_blk_end);
	     i--) {
	  if ((messages[i] -> tokentype == LABEL) &&
	      (!strcmp (messages[i] -> name, "while"))) {
	    do_state = do_keyword2;
	    c -> keyword2_ptr = i;
	    /* The predicate should occur in the same frame
	     * as the, "while," keyword, so don't increment
	     * the frame offset in the next iteration.
	     */
	    ++frame_offset;
	  }
	}
	break;
      case do_null:
	break;
      }
  }

  return SUCCESS;
}

/*
 *  All that's needed for a switch statement is to insert a "value"
 *  method call in the argument to the switch keyword.  We should
 *  not need to worry about the block start and end.
 */

int switch_stmt (MESSAGE_STACK messages, int ptr) {

  CTRLBLK *c;
  int i, stack_end;

  loop_linemarker (messages, ptr);

  stack_end = get_stack_top (messages);

  if ((c = new_ctrl_blk ()) == NULL)
    error (messages[ptr], "if_stmt: %s.", strerror (errno));

  ctrlblk_push (c);

  c -> stmt_type = stmt_switch;
  c -> id = loop_ext++;
  c -> keyword_ptr = ptr;
  c -> level = current_parser () -> level;

  if ((i = nextlangmsg (messages, ptr)) == ERROR) {
    warning (messages[ptr], "Switch statement syntax error.");
    return ERROR;
  }

  if (messages[i] -> tokentype != OPENPAREN) {
    warning (messages[ptr], "Switch statement syntax error.");
    return ERROR;
  } else {
    c -> pred_start_ptr = i;
  }
  
  if ((c -> pred_end_ptr = match_paren (messages, c -> pred_start_ptr, 
					stack_end)) == ERROR) {
    warning (messages[ptr], "Switch statement syntax error.");
    return ERROR;
  }

  /*
   *  Set blk_end_ptr so that ctrlblk_state deletes the ctrlblk
   *  structure after the predicate when it is no longer needed.
   */

  c -> blk_end_ptr = c -> pred_end_ptr;

  return SUCCESS;
}

/*
 *  Called by method_args ().  
 */

int ctrlblk_push_args (OBJECT *rcvr, METHOD *method, OBJECT *arg_object) {

  CTRLBLK *c;
  LIST *l;

  c = C_CTRL_BLK;
  switch (c -> stmt_type)
    {
    case stmt_if:
    case stmt_while:
    case stmt_do:
      /*
       *  If, while, and do statements get evaled at run time.
       */
      break;
    case stmt_for:
      if (ctrlblk_pred) {
	l = new_list ();
	l -> data = strdup (fmt_store_arg_call (rcvr, method, arg_object));
	if (for_init) {
	  if (!c -> for_init_args) {
	    c -> for_init_args = l;
	  } else {
	    list_push (&(c -> for_init_args), &l);
	  }
	}
	if (for_term) {
	  if (!c -> for_term_args) {
	    c -> for_term_args = l;
	  } else {
	    list_push (&(c -> for_term_args), &l);
	  }
	}
	if (for_inc) {
	  if (!c -> for_inc_args) {
	    c -> for_inc_args = l;
	  } else {
	    list_push (&(c -> for_inc_args), &l);
	  }
	}
      }
      break;
    case stmt_break:
    case stmt_case:
    case stmt_else:
    case stmt_goto:
    case stmt_switch:
      break;
    }
  return SUCCESS;
}

/*
 *  Called by register_c_var () for methods within for
 *  loop predicates.
 *
 *  Stmt_switch, stmt_case, and stmt_if C variables only 
 *  need to be registered before the predicate.
 *
 *  Stmt_while and stmt_do C variables need to be registered
 *  before the predicate, and stored if c -> pred_args_d is
 *  True, so they can be included in a block before each
 *  iteration.
 *
 *  Stmt_for variables are always stored for the init block,
 *  the term block, or the iteration block.
 */

int ctrlblk_register_c_var (char *func_call, int output_idx) {
  CTRLBLK *c;
  LIST *l;

  c = C_CTRL_BLK;

  switch (c -> stmt_type)
    {
    case stmt_switch:
    case stmt_case:
    case stmt_if:
      fileout (func_call, 0, output_idx);
      break;
    case stmt_while:
    case stmt_do:
      l = new_list ();
      l -> data = strdup (func_call);
      if (!c -> pred_args) {
	c -> pred_args = l;
      } else {
	list_push (&(c -> pred_args), &l);
      }
      break;
/*     case stmt_while: */
/*     case stmt_do: */
/*       fileout (func_call, 0, output_idx); */
/*       if (c -> pred_args_d) { */
/* 	l = new_list (); */
/* 	l -> data = strdup (func_call); */
/* 	if (!c -> pred_args) { */
/* 	  c -> pred_args = l; */
/* 	} else { */
/* 	  list_push (&(c -> pred_args), &l); */
/* 	} */
/*       } */
/*       break; */
    case stmt_for:
      if (ctrlblk_pred) {
 	l = new_list ();
	l -> data = strdup (func_call);

	if (for_init) {
 	  if (!c -> for_init_args) {
 	    c -> for_init_args = l;
 	  } else {
 	    list_push (&(c -> for_init_args), &l);
 	  }
	}

	if (for_term) {
 	  if (!c -> for_term_args) {
 	    c -> for_term_args = l;
 	  } else {
 	    list_push (&(c -> for_term_args), &l);
 	  }
	}

	if (for_inc) {
 	  if (!c -> for_inc_args) {
	    c -> for_inc_args = l;
 	  } else {
 	    list_push (&(c -> for_inc_args), &l);
 	  }
	}
      }
      break;
    case stmt_break:
    case stmt_else:
    case stmt_goto:
      break;
    }
  return SUCCESS;
}

/*
 *  Called by method_call ().  Methods within predicates are
 *  so far within C contexts, so they need obj_2_c_wrapper ()
 *  calls.
 *
 *  Register_c_var_method () is used by register_c_var in method.c
 *  if necessary.
 */

extern METHOD *register_c_var_method;

int ctrlblk_method_call (OBJECT *rcvr, METHOD *method, int method_msg_ptr) {

  CTRLBLK *c;
  LIST *l;
  MESSAGE *m_orig;

  c = C_CTRL_BLK;
  m_orig = message_stack_at (method_msg_ptr);
  switch (c -> stmt_type)
    {
    case stmt_if:
    case stmt_while:
    case stmt_do:
    case stmt_switch:
      /*
       *  The predicate gets evaled at run time.
       */
      register_c_var_method = method;
      ctrlblk_pred_rt_expr (message_stack (), method_msg_ptr);
      register_c_var_method = NULL;
      break;
    case stmt_case:
      break;
    case stmt_for:
      if (ctrlblk_pred) {
 	l = new_list ();
	/*
	 *  Fmt_method_call () here should be replaced with 
	 *  __ctalkEvalExpr () or its equivalent that handles
	 *  assignments, not with rt_library_method_call ().
	 */
 	l -> data = strdup (obj_2_c_wrapper (m_orig, rcvr, method, 
 					     fmt_method_call (rcvr, 
						      method -> selector),
					     TRUE));
	if (for_init) {
 	  if (!c -> for_init_methods) {
 	    c -> for_init_methods = l;
 	  } else {
 	    list_push (&(c -> for_init_methods), &l);
 	  }
	}
	if (for_term) {
 	  if (!c -> for_term_methods) {
 	    c -> for_term_methods = l;
 	  } else {
 	    list_push (&(c -> for_term_methods), &l);
 	  }
	}
	if (for_inc) {
 	  if (!c -> for_inc_methods) {
	    c -> for_inc_methods = l;
 	  } else {
 	    list_push (&(c -> for_inc_methods), &l);
 	  }
	}
      }
      break;
    case stmt_break:
    case stmt_else:
    case stmt_goto:
      break;
    }
  return SUCCESS;
}

int ctrlblk_cleanup_args (METHOD *method, int ptr) {

  CTRLBLK *c;
  LIST *l;
  int i;

  c = C_CTRL_BLK;
  switch (c -> stmt_type)
    {
    case stmt_if:
    case stmt_while:
    case stmt_do:
      /*
       * If, while, and do statements get evaled at run time.
       */
      break;
    case stmt_for:
      if (ctrlblk_pred) {
	for (i = method -> n_args; i; i--) {
	  l = new_list ();
	  l -> data = strdup ("__ctalk_arg_cleanup ();\n");
	  if (method -> args[i]) delete_object (method -> args[i]);
	  method -> args[i] = NULL;
	  if (for_init) {
	    if (!c -> for_init_cleanup) {
	      c -> for_init_cleanup = l;
	    } else {
	      list_push (&(c -> for_init_cleanup), &l);
	    }
	  }
	  if (for_term) {
	    if (!c -> for_term_cleanup) {
	      c -> for_term_cleanup = l;
	    } else {
	      list_push (&(c -> for_term_cleanup), &l);
	    }
	  }
	  if (for_inc) {
	    if (!c -> for_inc_cleanup) {
	      c -> for_inc_cleanup = l;
	    } else {
	      list_push (&(c -> for_inc_cleanup), &l);
	    }
	  }
	}
      }
      break;
    case stmt_break:
    case stmt_case:
    case stmt_else:
    case stmt_goto:
    case stmt_switch:
      break;
    }

  /*
   *  Then clean up the method arguments.
   * 
   *  This is the same as cleanup_args () in method.c, but we've only
   *  queued the __ctalk_arg_cleanup () calls.
   */

  for (i = method -> n_args - 1; i >= 0; i--) {
    if (method -> args[i] -> scope == ARG_VAR) 
      delete_arg_object (method -> args[i]);
    method -> args[i] = NULL;
    method -> n_args = i;
  }
  return SUCCESS;
}

void loop_linemarker (MESSAGE_STACK messages, int ptr) {

  char buf[MAXMSG];

  if (nolinemarker_opt)
    return;

  if (interpreter_pass == parsing_pass) {
    fmt_line_info (buf, error_line, input_source_file, 1, TRUE);
    fileout (buf, FALSE, ptr);
  }

}

int ctrlblk_pred_rt_expr (MESSAGE_STACK messages, int msg_ptr) {

  int i, j, lookahead, fn_idx = -1;
  int rt_fn_start_paren, rt_fn_end_paren;
  char expr_buf[MAXMSG];
  MESSAGE *m = NULL;   /* Avoid a warning. */

  if (expr_paren_check (messages, msg_ptr)) {
    __ctalkExceptionInternal (messages[msg_ptr], mismatched_paren_x, NULL);
    return ERROR;
  }

  switch (C_CTRL_BLK -> stmt_type)
    {
    case stmt_switch:
    case stmt_if:
    case stmt_while:
      *expr_buf = 0;
      rt_expr_class = rt_expr_null;
      check_constant_expr (message_stack (), C_CTRL_BLK -> pred_start_ptr - 1,
			   C_CTRL_BLK -> pred_end_ptr + 1);
      for (i = C_CTRL_BLK -> pred_start_ptr - 1; 
	   i >= C_CTRL_BLK -> pred_end_ptr + 1; i--) {
	m = message_stack_at (i);

	if (m -> obj && IS_OBJECT (m -> obj) &&
	    !strcmp (m -> obj -> __o_classname, CFUNCTION_CLASSNAME)) {
	  if ((lookahead = nextlangmsg (messages, i)) != ERROR) {
	    if ((messages[i] -> tokentype == LABEL) && 
		(messages[lookahead] -> tokentype == OPENPAREN)) {
	      char *e;
  	      if ((e = clib_fn_expr (messages, i, i, m -> obj)) != NULL) {
		strcat (expr_buf, e);
		rt_expr_class = rt_expr_fn;
		i = 
		  match_paren (messages, lookahead, get_stack_top (messages));
		for (j = lookahead; j >= i; j--) {
		  ++messages[j] -> evaled;
		  ++messages[j] -> output;
		}
	      }
	    }
	  }
	} else {
	  if (IS_C_OP(M_TOK(m))) {
	    operand_type_check (message_stack (), i);
	    strcat (expr_buf, m -> name);
	  } else {
	    if (get_function (M_NAME(m))) {
	      if (rt_expr_class == rt_expr_null)
		rt_expr_class = rt_c_fn;
	      strcat (expr_buf, m -> name);
	      fn_idx = i;
	    }  else {
	      strcat (expr_buf, m -> name);
 	    }
	  }
	}

	/*
	 *  If the expression also contains C variables or functions,
	 *  they get registered now in register_c_var () called by
	 *  method_args (), etc., so check here if the token has
	 *  been evaluated and register the C variable here if 
	 *  necessary.
	 */
 	if ((m -> tokentype == LABEL) && !m -> evaled) {
 	  CVAR *c;
 	  if (((c = get_local_var (m -> name)) != NULL) ||
 	      ((c = get_global_var (m -> name)) != NULL)) {
  	    register_c_var (message_stack (), i);
	  } else {
	    if (!strcmp (M_NAME(m), "self")) {
	      if (self_class_or_instance_variable_lookahead 
		  (messages, i)) {
		rt_expr_class = rt_self_instvar_simple_expr;
	      }
	    }
	  }
 	}
	++m -> evaled;
	++m -> output;
      }
      
      switch (rt_expr_class) 
	{
	case rt_expr_fn:
	case rt_expr_user_fn:
	  fileout (fmt_user_fn_rt_expr (messages, 
					C_CTRL_BLK -> pred_start_ptr - 1, 
					C_CTRL_BLK -> pred_end_ptr + 1,
					expr_buf), 
		   FALSE, 
		   C_CTRL_BLK -> pred_start_ptr - 1);
	  break;
	case rt_c_fn:
	  /*
	   *  If there are any tokens in the predicate after the 
	   *  function, output them here.
	   *
	   *  Also check for further functions in the expression.
	   */
	  strcpy (expr_buf, fmt_c_fn_obj_args_expr (messages, fn_idx));
	  rt_fn_start_paren = nextlangmsg (messages, fn_idx);
	  rt_fn_end_paren = match_paren (messages, rt_fn_start_paren,
					 get_stack_top (messages));
	  /*
	   *  If the function is not the first token in a control
	   *  predicate, prepend the preceding tokens.
	   */
	  if (ctrlblk_pred) {
	    int __pred_expr_start_idx;
	    __pred_expr_start_idx = 
	      nextlangmsg (messages, C_CTRL_BLK -> pred_start_ptr);
	    if (__pred_expr_start_idx > fn_idx) {
	      char *_pre_buf, _t[MAXMSG];
	      
	      _pre_buf = collect_tokens (messages, __pred_expr_start_idx,
					 fn_idx + 1);
	      sprintf (_t, "%s%s", _pre_buf, expr_buf);
	      free (_pre_buf);
	      strcpy (expr_buf, _t);
	    }
	  }

	  for (i = rt_fn_end_paren - 1; 
	       i > C_CTRL_BLK -> pred_end_ptr; i--) {
	    if (get_function (M_NAME(m))) {
	      strcat (expr_buf, fmt_c_fn_obj_args_expr (messages, i));
	      i = match_paren (messages, nextlangmsg (messages, i),
					 get_stack_top (messages));
	    } else {
	      strcat (expr_buf, M_NAME(messages[i]));
	    }
	  }
	  fileout (expr_buf, FALSE, C_CTRL_BLK -> pred_start_ptr - 1);
	  break;
	case rt_self_instvar_simple_expr:
	  fileout 
	    (fmt_rt_return (fmt_eval_expr(expr_buf), INTEGER_CLASSNAME, TRUE),
	     FALSE, C_CTRL_BLK -> pred_start_ptr - 1);
	  break;
	default:
	  fileout 
	    (fmt_default_ctrlblk_expr 
	     (messages, C_CTRL_BLK -> pred_start_ptr - 1, 
	      C_CTRL_BLK -> pred_end_ptr + 1, 
	      ((messages[msg_ptr]-> attrs & TOK_IS_METHOD_ARG)? TRUE:FALSE)), 
	     FALSE, 
	     C_CTRL_BLK -> pred_start_ptr - 1);
	  break;
	}
      /*
       *  loop_block_start () checks this.
       */
      C_CTRL_BLK -> pred_expr_evaled = TRUE;
      break; /* case stmt_switch, stmt_if, stmt_while */
    case stmt_case:
      /*
       *  Case arguments are still constants as in C99.
       */
      break;
    case stmt_for:
      /*
       *   The function for_stmt () and others handle 
       *   the for loop predicates.
       */
      break;
    default:
      break;
    }
  return SUCCESS;
}

STMT_CLASS get_ctrl_class (char *keyword) {
  if (!strcmp (keyword, "break"))
    return stmt_break;
  if (!strcmp (keyword, "case"))
    return stmt_case;
  if (!strcmp (keyword, "do"))
    return stmt_do;
  if (!strcmp (keyword, "else"))
    return stmt_else;
  if (!strcmp (keyword, "for"))
    return stmt_for;
  if (!strcmp (keyword, "goto"))
    return stmt_goto;
  if (!strcmp (keyword, "if"))
    return stmt_if;
  if (!strcmp (keyword, "switch"))
    return stmt_switch;
  if (!strcmp (keyword, "while"))
    return stmt_while;
  return ERROR;
}

/*
 *  Returns the index of the first character of a predicate
 *  if it is a leading unary op, the original index of the
 *  predicate start - usually the opening parenthesis - 
 *  otherwise.
 *
 *  __ctalkEvalExpr () can also handle leading unary ops,
 *  though there are not yet handlers for pre-increment and
 *  pre-decrement operators, because it only handles constant
 *  expressions.
 */

int leading_unary_op (MESSAGE_STACK messages, int idx) {

  int i, stack_end, tok, n_parens;

  stack_end = get_stack_top (messages);

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

    if (M_ISSPACE (messages[i]))
      continue;

    tok = M_TOK (messages[i]);

    if ((tok == AMPERSAND) ||
	(tok == EXCLAM) ||
	(tok == PLUS) ||
	(tok == INCREMENT) ||
	(tok == MINUS) ||
	(tok == DECREMENT) ||
	(tok == ASTERISK) ||
	(tok == BIT_COMP)) {
      return i;
    } else {
      if (tok == OPENPAREN && !n_parens) {
	++n_parens;
	continue;
      } else {
	return idx;
      }
    }
  }

  return idx;
}

int expr_paren_check (MESSAGE_STACK messages, int msg_ptr) {

  int i, n_parens;

  switch (C_CTRL_BLK -> stmt_type)
    {
    case stmt_if:
      for (i = C_CTRL_BLK -> keyword_ptr, n_parens = 0; 
	   i >= C_CTRL_BLK -> blk_start_ptr; i--) {
	if (M_TOK(messages[i]) == OPENPAREN) ++n_parens;
	if (M_TOK(messages[i]) == CLOSEPAREN) --n_parens;
      }
      if (!n_parens) 
	return SUCCESS;
      else 
	return ERROR;
      break;
    default:
      break;
    }
  return SUCCESS;
}
