/* $Id: parser.c,v 1.5 2009/01/04 20:28:41 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.
*/

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

extern char input_source_file[FILENAME_MAX]; /* Declared in main.c.       */
extern char *source_files[MAXARGS]; /* Declared in infiles.c */
extern int n_input_files, input_idx;
extern I_PASS interpreter_pass;
extern int main_declaration;      /* Determined in the first pass.        */
static int main_start_frame;
extern int error_line,            /* Declared in errorloc.c.              */
  error_column;
extern int library_input;         /* Declared in class.c.  TRUE if        */
                                  /* evaluating class libraries.          */

                                  /* The main parser message stack.       */
static MESSAGE *messages[P_MESSAGES+1];
static int messageptr;            /* Message stack pointer.               */
static int main_start = 0;        /* Stack index of main() declaration.   */

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

extern EXCEPTION parse_exception;  /* Declared in pexcept.c. */

/*
 *   Functions to manage the message stack.
 */

int init_message_stack (void) {
  messageptr = P_MESSAGES;
  return SUCCESS;
}

int get_messageptr (void) {
  return messageptr;
}

int message_push (MESSAGE *m) {
  if (messageptr == 0) {
    _error ("Message_push: stack_overflow.");
    return ERROR;
  }

  if (!m)
    _error ("Message_push: null pointer, messageptr = %d.", messageptr);

  messages[messageptr] = m;

#ifdef STACK_TRACE
  debug ("Message_push %d. %s.", messageptr, 
	 (messages[messageptr] && IS_MESSAGE (messages[messageptr])) ?
	  messages[messageptr] -> name : "(null)"); 
#endif

  --messageptr;

  return messageptr + 1;
}

MESSAGE *message_pop (void) {

  MESSAGE *m;

  if (messageptr == P_MESSAGES) {
    return (MESSAGE *)NULL;
  }

  if (messageptr > P_MESSAGES)
    _error ("Message_pop: Message stack overflow, messageptr = %d.", 
		  messageptr);

  m = messages[++messageptr];
  messages[messageptr] = NULL;

#ifdef STACK_TRACE
  debug ("Message_pop %d. %s.", messageptr, m -> name);
#endif

  if (!m || !IS_MESSAGE (m))
    _error ("message_pop (): Bad message, stack index %d.", messageptr);
  else
    return m;
}

MESSAGE *message_stack_at (int n) {
  if (n > P_MESSAGES) return NULL;
  return messages[n];
}

MESSAGE **message_stack (void) {
  return messages;
}

int reset_messageptr (int new_ptr) {
  messageptr = new_ptr;
  return messageptr;
}

void clear_frame_messages  (int this_frame_ptr) {

  int first_message;
  MESSAGE *m;

  first_message = message_frame_top_n (this_frame_ptr);
  while (first_message > messageptr) {
    m = message_pop ();

    if (m && IS_MESSAGE (m)) {
      delete_message (m);
    }
  }
}

void clear_all_messages (void) {

  MESSAGE *m = NULL;
  int i, t;

  t = message_frame_top ();
  while ((i = get_messageptr ()) <= MAXARGS) {
    m = message_pop ();
    if (m) delete_message (m);
  }
}

/* 
 *  Return FALSE if all of the statements in a frame have
 *  been evaluated.  Return the stack index of the first
 *  unevaluated message otherwise.
 */

int unevaled (int this_frame_ptr) {

  int j,  msg_frame_top, next_msg_frame_top;
  MESSAGE *m;

  if ((msg_frame_top = message_frame_top_n (this_frame_ptr)) == -1)
    msg_frame_top = P_MESSAGES;

  if ((next_msg_frame_top = message_frame_top_n (this_frame_ptr - 1)) == -1)
    next_msg_frame_top = get_messageptr ();

  for ( j = msg_frame_top; j > next_msg_frame_top; j--) {
    m = message_stack_at (j);
    if (m && IS_MESSAGE (m) && !m -> evaled)
      return j;
  }

  return FALSE;
}

/* 
 * The same as above, but check for evaluated messages 
 * from a specific stack index.
 */
int check_evaled (int message_ptr) {

  int j, frame_top;
  MESSAGE *m;

  frame_top = message_frame_top ();
  for (j = message_ptr; j <= frame_top; j++) {
    m = message_stack_at (j);
    if (! m -> evaled)
      return j;
  }
  return FALSE;
}

int set_declaration_evaled (MESSAGE_STACK messages, int ptr) {
  int i, end_ptr;

  end_ptr = find_declaration_end (messages, ptr);

  for ( i = ptr; i >= end_ptr; i--) {
    if (!messages[i] || !IS_MESSAGE (messages[i]))
      break;
    ++messages[i] -> evaled;
  }

  if (messages[i] && IS_MESSAGE (messages[i]))
    ++messages[i] -> evaled;
  return i;
}

/*
 *  Note - When calling parser_pass () separately, these must be 
 *  saved and restored.  TO DO - Use current_parser () -> frame
 *  instead.
 */

static int frame_level = 0;
static int frame_ptr = 0;

/*
 *   Functions that handle the parser stack.
 */

extern PARSER *parsers[MAXARGS+1];           /* Declared in rtinfo.c. */
extern int current_parser_ptr;

void init_parsers (void) {
  current_parser_ptr = MAXARGS + 1;
}

static int end_of_input (void) {
  return ((current_parser_ptr >= MAXARGS) &&
	  (input_idx == (n_input_files - 1)));
}

int parser_ptr (void) {
  return current_parser_ptr;
}

void push_parser (PARSER *p) {
  if (current_parser_ptr == 0)
    _error ("push_parser: stack overflow.");
  parsers[--current_parser_ptr] = p;
}

PARSER *pop_parser (void) {
  PARSER *p;
  if (current_parser_ptr > MAXARGS)
    _error ("pop_parser: stack underflow.");
  p = parsers[current_parser_ptr] ;
  parsers[current_parser_ptr++] = NULL;
  return p;
}

PARSER *current_parser (void) {
  return parsers[current_parser_ptr];
}

PARSER *parser_at (int i) {
  return parsers[i];
}

/* 
 *  TO DO - This needs to be a stack, or added in the
 *  parserinfo struct.
 */

struct {
  int line;
  char filename[FILENAME_MAX];
  int flag;
} line_marker;


/* 
 *  Parse the statements within the stack frame given by
 *  the frame stack index given in the argument.
 */

int parser_pass (int this_frame_ptr, PARSER *p) {

  int start_ptr,             /* Message index of frame start.      */
    end_ptr,               /* Message index of next frame start. */
    j, k, n,
    method_msg_ptr,
    stmt_unevaled;
  CVAR *local_vars;
  MESSAGE *m;
  Boolean c_var_decl;

  p -> frame = frame_ptr = this_frame_ptr;

  c_var_decl = False;

  if ((start_ptr = message_frame_top_n (this_frame_ptr)) == -1)
    start_ptr = P_MESSAGES;

  if ((end_ptr = message_frame_top_n (this_frame_ptr -1)) == -1)
    end_ptr = get_messageptr ();

  for ( j = start_ptr; j > end_ptr; j--) {

    m = message_stack_at (j);

    if (!m || !IS_MESSAGE (m)) continue;

    error_line = m -> error_line;
    error_column = m -> error_column;

    switch (ctrl_block_state (j))
      {
      case PRED_END:   /*  Write a loop beginning and predicate 
			   block for loops that have a single 
			   statement and no braces enclosing the
			   block.  The entire loop is contained
			   in this frame.
		       */
	loop_pred_end (message_stack (), j);
	break;
      case BLK_START:  /* Start outputting the loop predicate block
			  for while and for loops.  The frame ends at
			  the opening brace, so predicate blocks at 
			  the beginning of the loop can be output 
			  immediately after the frame, in addition
			  to the generic loop start.  

			  If the control block does not have braces,
			  resolve () only scans back to the first
			  label of the block.  The predicate and body 
			  are within one frame.

			  Also check for a completely empty block - 
			  a loop with only a semicolon - and handle
			  the block end here also if necessary.

			  If the code block does not have braces,
			  insert an opening brace in the output.
		       */
	loop_block_start (message_stack (), j);
	if (C_CTRL_BLK -> blk_start_ptr == C_CTRL_BLK -> blk_end_ptr)
	  loop_block_end (message_stack (), j);
	break;
      case BLK_END:    /* Output the loop predicate block for do...while
			  loops.  A closing brace should occur in a frame
			  by itself - so we can output predicate
			  blocks for do... while loops immediately before
			  the frame.

			  Insert a closing brace in the output if the
			  code block is not enclosed by braces.
		       */
	loop_block_end (message_stack (), j);
	break;
      case ELSE_END:
	if_else_end (message_stack (), j);
	break;
      }

    switch (m -> tokentype) 
      {
      case SEMICOLON:
	if ((stmt_unevaled = check_evaled(j + 1)) != 0) {
	  method_msg_ptr = scanback (message_stack (), 
	     j, message_frame_top_n (parser_frame_ptr()), 
	     METHODMSGLABEL);
	  method_call (method_msg_ptr);
	}
	++m -> evaled;
	break;
      case PREPROCESS:
	/* The only preprocessor directives should be line
	   info in its own frame, so set the line and source
	   file info, and mark as evaled. 

	   Note that this depends on the format of a line
	   marker in fmt_line_info ().
	*/
 	if (messages[j - 2] -> tokentype == INTEGER) {
 	  set_line_info (message_stack (), j, end_ptr);
	  ++m -> evaled;
 	}
	break;
      case CTYPE:
      case LABEL:
	/*
	 *  Check GNU C attributes and fall through.
	 */
#ifdef __GNUC__
	if (!strcmp (m -> name, "__attribute__")) {
	  int l, lookahead;
	  if ((lookahead = nextlangmsg (message_stack (), j)) == ERROR)
	    _error ("Parser error.\n");
	  if (message_stack_at (lookahead) -> tokentype != OPENPAREN) 
	    _error ("Parser error.\n");
	  if ((lookahead = match_paren (message_stack (), lookahead, end_ptr))
	      != ERROR) {
	    if (message_stack_at (lookahead) -> tokentype != CLOSEPAREN)
	      _error ("Parser error.\n");
	    for (l = j; l >= lookahead; l--)
	      ++message_stack_at (l) -> evaled;
	    j = lookahead;
	  } else {
	    int l, lookahead, attr_start = j;
	    if ((lookahead = scanforward (message_stack (), attr_start, 
					  end_ptr, SEMICOLON)) != ERROR) {
	      for (l = j; l >= lookahead; l--)
		++message_stack_at (l) -> evaled;
	      j = lookahead;
	    } else {
	      int l2, lookahead2, attr_start2 = j;
	      if ((lookahead2 = scanforward (message_stack (), attr_start2, 
					     end_ptr, OPENBLOCK)) != ERROR) {
		for (l2 = j; l2 >= lookahead2; l2--)
		  ++message_stack_at (l2) -> evaled;
		j = lookahead2;
	      } else {
		int l2, lookahead2, attr_start2 = j;
		if ((lookahead2 = scanforward (message_stack (), attr_start2, 
					       end_ptr, OPENBLOCK)) != ERROR){
		  for (l2 = j; l2 >= lookahead2; l2--)
		    ++message_stack_at (l2) -> evaled;
		  j = lookahead2;
		}
	      }
	    }
	  }
	  continue;
	}
#endif
      case METHODMSGLABEL:
      case EQ:          /* Cases for overloaded operators, except for */
      case BOOLEAN_EQ:  /* opening and closing parentheses.           */
      case INEQUALITY:
      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 BIT_AND_ASSIGN:
      case BIT_COMP:
      case LOG_NEG:
      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 CONDITIONAL:
      case COLON:
      case MODULUS:
	/* TO DO - 
	   Determine if the result should be m -> value_obj,
	   in the case of a method message, for example, and 
	   whether the value of m should be set in resolve ()
	   after determining if m's token is a receiver,
	   method, argument, or something else. */
	/* 
	 *  If the label does not resolve to an object - 
	 */
	if ((m -> obj = resolve (j)) == NULL) {
  	  if (is_c_fn_declaration_msg (messages, j)) {
 	    int k;
 	    if (!library_input && (FRAME_SCOPE != PROTOTYPE_VAR)) {
	      get_params ();
	      begin_function_buffer ();
	    }
 	    p -> need_init = FALSE;
 	    /*  Look ahead to the end of the declaration
 	     *  and increment the block level if the
	     *  declaration is not a prototype.
 	     */
 	    for (k = j;
 		 messages[k] && messages[k] -> tokentype != 
		   ((FRAME_SCOPE == PROTOTYPE_VAR) ? SEMICOLON : OPENBLOCK);
 		 k--)
 	      ++messages[k] -> evaled;
 	    if (messages[k])
 	      ++messages[k] -> evaled;
 	    j = k;
	    if (FRAME_SCOPE != PROTOTYPE_VAR)
	      ++p -> block_level;
	  } else { /* Check for a local variable declaration. */
	    if (frame_at (this_frame_ptr) -> scope == LOCAL_VAR) {

  	      if (is_c_var_declaration_msg (messages, j, FALSE)) {
		if ((local_vars = parser_to_cvars ()) != NULL) {
		  add_variable_from_cvar (local_vars);
		}

 		if (interpreter_pass != c_fn_pass)
		  j = set_declaration_evaled (messages, j);

	      } else {
		if (!strcmp (m -> name, "returnObjectClass")) {
		  j = set_declaration_evaled (messages, j);
		} else {
		  control_structure (messages, j);
		}
	      }
	    } else {/* if (frame_at (this_frame_ptr) -> scope == LOCAL_VAR) */
 	      if (!strcmp (M_NAME(m), "extern")) {
#ifdef __GNUC__
 		gnuc_fix_empty_extern (messages, j);
#endif
		j = set_declaration_evaled (messages, j);
 	      }
 	    }
	  } /* 	if (is_c_fn_declaration_msg (j)) */
	}
	++m -> evaled;
	break;
      case LITERAL:
      case LITERAL_CHAR:
      case INTEGER:
      case LONG:
      case LONGLONG:
      case FLOAT:
	c_constant_object (message_stack (), j);
	++m -> evaled;
	break;
      case SIZEOF:
	/*
	 *  For now, we can skip sizeof arguments.
	 */
	k = scanforward (message_stack (), j, end_ptr, OPENPAREN);
	n = match_paren (message_stack (), k, end_ptr);
	for (; j >= n; --j)
	  ++message_stack_at (j) -> evaled;
	++message_stack_at (j) -> evaled;
	break;
       case OPENBLOCK:
	 /*
	  *  The block level also is incremented after 
	  *  is_c_fn_declaration_msg (), above.
	  */
   	++p -> block_level;
 	++m -> evaled;
 	break;
      case CLOSEBLOCK:
   	--p -> block_level;
	/*
	 * End buffering a function or method.  The final closing 
	 * brace should appear in its own frame and is output in
	 * parse ().
	 *
	 * Output __ctalk_init () at the end of a function if 
	 * it has not already been output.
	 */
	if ((p -> block_level == 0) && 
	    fn_closure (messages, j, this_frame_ptr)) {
	  if (!library_input) {
 	    if (interpreter_pass != method_pass) {
	      end_function_buffer ();
	      /*
	       *  If there is no return statement at the 
	       *  end of the function, output a 
	       *  __ctalk_exitFn () call here.  The closing
	       *  brace itself gets output in parse (), 
	       *  below.
	       */
	      fn_return_at_close (message_stack (), j, p -> need_main_exit);
	      if (p -> need_main_exit) p -> need_main_exit = FALSE;
 	    }
	  }
	  /*
	   *  FIXME! - We have to wait until the 
	   *  sub-parser exits before we output a method.
	   *  Otherwise, we output only a partial method
	   *  when parsing another method from within a
	   *  method.  That means the method's local 
	   *  variables are lost when formatting the method
	   *  after returning from the parser.  The function
	   *  is_c_var_declaration_msg2 () might be able 
	   *  to do without the local variable definitions
	   *  because the data types are global and still exist.  
	   *
	   *  TO DO - 
	   *  1. Fix the method buffering so that 
	   *     complete methods get output when 
	   *     called from here.
	   *
	   *     The save_method_local_objects () and 
	   *     save_method_local_cvars () functions 
	   *     should go away if the method buffering 
	   *     can be fixed.
	   */
	  if (interpreter_pass == method_pass) {
	    save_method_local_objects ();
	    save_method_local_cvars ();
	  }
	  /*
	   *  Note that the frame that contains the closing
	   *  block itself is not buffered and is output 
	   *  from parse (), below.
	   */
	  delete_objects (LOCAL_VAR);
	  delete_local_objects ();
	  delete_local_c_vars ();
   	}
	/* Fall through. */
      default:
	++m->evaled;
	break;
      } /* switch  (m -> tokentype) */

    if (__ctalkTrapExceptionInternal (m))
      __ctalkHandleInterpreterExceptionInternal (m);

  } /* for ( j = start_ptr; j > end_ptr; j--) */

  return SUCCESS;
}

/*
  IMPORTANT - Parser_frame_ptr () should only be called from 
  parser_pass (), or from functions that parser_pass () calls.
*/

/*
 *   Return the frame index that parser_pass is evaluating.
 *   TO DO - These should return current_parser () -> frame,
 *   instead of frame_ptr, declared above, so we don't have
 *   to save and restore these individually.
 */
int parser_frame_ptr (void) {
  return frame_ptr;
}

void set_parser_frame_ptr (int ptr) {
  frame_ptr = ptr;
}

/* 
 *  Return the scope of the current parser frame.
 */

int parser_frame_scope (void) {
  FRAME *f;
  if (parser_frame_ptr () != ERROR) {
    f = frame_at (parser_frame_ptr ());
    return f -> scope;
  }
  return ERROR;
}

void init_parser_frame_ptr (void) {
  frame_ptr = ERROR;
}

/* 
 *   Functions to add frames and increment the frame level.
 */

int add_frame (int scope, int level) {
  new_frame (scope, level);
  ++frame_level;
  return frame_level;
}

int remove_frame (void) {
  delete_frame ();
  --frame_level;
  return frame_level;
}

typedef enum {
  before, 
  after,
  notneeded
} NEED_FRAME;

typedef enum {
  ctrl_null,
  ctrl_keyword1,
  ctrl_keyword2,
  ctrl_pred_start,
  ctrl_pred_end,
  ctrl_blk_start,
  ctrl_blk_end,
  ctrl_else,
  ctrl_else_start,
  ctrl_else_end
} CTRL_STATE;

/* 
 *   The first parser pass, which tokenizes the input and
 *   adds it to the message stack, and adds stack frames.
 */

int parser_level = 0;

extern int in_ctrlblk_pred;  /* Set in is_c_function_declaration, */
			     /* and cleared by an opening brace.  */

char *parse (char *inbuf, long long bufsize) {

  long long int i = 0ll;
  int token, lastlangtoken = -1;  /* Avoid a warning. */
  int token_start;
  int j;
  int prev_frame_ptr;        /* Save and restore the old frame.            */
  int comment = FALSE;
  int preprocess = FALSE;
  NEED_FRAME need_frame = notneeded;
  Boolean in_fn_decl;        /* This simplifies is_c_function_declaration. */
  int scope;
  int n_block_levels = 0;
  int n_parens = 0;
  MESSAGE *m;
  PARSER *p;
  CTRL_STATE ctrl_state;
  STMT_CLASS ctrl_class;
  Boolean ctrl_has_braces = False;   /* Avoid a warning. */
  Boolean require_stmt;

  ++parser_level;
  prev_frame_ptr = frame_ptr;
  
  p = new_parserinfo ();
  push_parser (p);
  p -> level = parser_level;

  /*
   *    If this is the top level parser, the scope begins as
   *    GLOBAL_VAR.  Otherwise, further parsers inherit the scope
   *    from the upper level parser's frame; i.e., the last frame 
   *    before parse () was called again.  Because we already pushed
   *    this parser, above, the calling parser is at
   *    parsers[current_parser_ptr + 1].
   */
  if (parser_level == 1)
    scope = GLOBAL_VAR;
  else
    scope = frame_at (parsers[current_parser_ptr + 1] -> frame) -> scope;

  in_fn_decl = FALSE;
  ctrl_state = ctrl_null;
  require_stmt = False;

  add_frame (scope, parser_level);

  p -> top_frame = get_frame_pointer ();

  while (i < bufsize) {

    if ((m = new_message()) == NULL)
      error (message_stack_at (get_messageptr () + 1),
	     "Parse: Invalid new_message.");

    /* 
     *   Is_c_func_declaration () looks ahead on untokenized
     *   input.  Use lexical () instead of tokenize () and
     *   record the inbuf index of each token start.
     */
    token_start = i;
    token = lexical (inbuf, &i, m);    /* Get token and message name. */

    set_error_location (m, &error_line, &error_column);

    if (m -> tokentype == OPENCCOMMENT) {
      comment = TRUE;
      strcpy (m -> name, " ");
      m -> tokentype = WHITESPACE;
      message_push (m);
      continue;
    }

    if (comment) {
      if (m -> tokentype == CLOSECCOMMENT)
	comment = FALSE;
      continue;
    }

    switch (m -> tokentype) 
      {
      case LABEL:
	if (is_c_function_declaration (&inbuf[token_start])) {
	  FRAME *f;
	  /* 
	     Recording the in_fn_decl state here simplifies 
	     is_c_function_declaration, and allows us
	     to set a frame regardless of the statements 
	     preceding the function declaration, at 
	     the cost of possibly setting an empty
	     frame immediately before the declaration.
	     TO DO - Try to omit empty frames that 
	     immediately precede the frame we set here. 
	  */
	  if (!in_fn_decl) {
	    in_fn_decl = True;
	    need_frame = before;
	  }
	  if (main_declaration && !main_start) {
	    main_declaration = FALSE;
	    main_start_frame = get_frame_pointer ();
	  }
	  /* The function parameters get the scope ARG_VAR,
	     although they should be treated as local 
	     variables when they are registered. */
	  f = frame_at (get_frame_pointer ());
	  f -> scope = scope = ARG_VAR;
	} else {
	  if (is_ctrl_keyword (m -> name)) {
	    ctrl_class = get_ctrl_class (m -> name);
	    if (ctrl_class == stmt_else) {
	      ctrl_state = ctrl_else;
	      if (ctrl_has_braces == False) need_frame = after;
	    } else {
	      ctrl_state = ctrl_keyword1;
	    }
	  } else {
	    /*
	     *  Require statements get their own frames
	     *  so we can position line markers more 
	     *  accurately.
	     */
	    if (!strcmp (m -> name, "require")) {
	      require_stmt = True;
	      need_frame = before;
	    } 
	  }
	}
	/*
	 *  If the parser encounters a label and 
	 *  the ctrl_state is ctrl_pred_end, it
	 *  means that the input has a control structure
	 *  without control blocks.  Pretend there's 
	 *  an opening brace and set a frame.
	 */
	if (ctrl_state == ctrl_pred_end) {
	  ctrl_state = ctrl_blk_start;
	  need_frame = before;
	  ctrl_has_braces = False;
	}
	break;
	/*
	 *  Needed for line_info.  
	 *  After the tokens are collected, if we are parsing the
	 *  source file, elide the line marker, because we will
	 *  place a new line marker in the output when the function
	 *  is unbuffered.
	 *  
	 *  A line marker placed before a function has the same name
	 *  as the source file.  Elide all source line markers except
	 *  the marker at line 1, which is placed automatically.
	 *
	 *  Note that this depends on the format of a line marker.
	 */
      case NEWLINE:
	if (preprocess) {
	  preprocess = FALSE;
	  need_frame = after;
	}
	break;
      case OPENBLOCK:
	need_frame = after;
	++n_block_levels;
 	if ((n_block_levels == 1) &&
 	    (frame_at (get_frame_pointer ()) -> scope == ARG_VAR)) {
	  scope = LOCAL_VAR;
	  in_fn_decl = False;
	}
	if (in_ctrlblk_pred)
	  in_ctrlblk_pred = FALSE;
	if (ctrl_state == ctrl_pred_start)
	  ctrl_state = ctrl_blk_start;
	if (ctrl_state == ctrl_else)
	  ctrl_state = ctrl_else_start;
	if ((ctrl_state == ctrl_pred_start) || (ctrl_state == ctrl_else_start))
	  ctrl_has_braces = True;
	break;
      case CLOSEBLOCK:
	--n_block_levels;
	if (!n_block_levels)
	  scope = GLOBAL_VAR;
	/*
	 *  A state change to ctrl_null should work as long as 
	 *  the parser doesn't need to check for matching 
	 *  if... else clauses.
	 */
	if ((ctrl_state == ctrl_blk_start) || (ctrl_state == ctrl_else_start))
	  ctrl_state = ctrl_null;
	need_frame = after;
	break;
      case OPENPAREN:
	++n_parens;
	if ((n_parens == 1) && (ctrl_state == ctrl_keyword1))
	  ctrl_state = ctrl_pred_start;
	break;
      case CLOSEPAREN:
	--n_parens;
	if ((n_parens == 0) && (ctrl_state == ctrl_pred_start))
	  ctrl_state = ctrl_pred_end;
	break;
      case CPPCOMMENT:
	break;
      case PREPROCESS:
   	preprocess = TRUE;
	break;
      case INTEGER:
	break;
      case LITERAL:
	break;
      case SEMICOLON:
	/*
	 *  Don't put frames in the middle of for loop 
	 *  statements.
	 */
	if (!n_parens)
	  need_frame = after;

	if (require_stmt == True) {
	  require_stmt = False;
	  need_frame = after;
	}

	if (ctrl_state == ctrl_null)
	  need_frame = after;
	if ((ctrl_state == ctrl_blk_start) && (ctrl_has_braces == False)) {
	  need_frame = after;
	  ctrl_state = ctrl_null;
	} 
	break;
      default:
	break;
      }

    if ((m -> tokentype != WHITESPACE) && (m -> tokentype != NEWLINE))
      lastlangtoken = m -> tokentype;

    switch (need_frame)
      {
      case notneeded:
	message_push (m);
	break;
      case before:
	need_frame = notneeded;
	add_frame (scope, parser_level);
	message_push (m);
	break;
      case after:
	message_push (m);
	need_frame = notneeded;
	add_frame (scope, parser_level);
	break;
      }

  } /* while (i < bufsize) */

  p -> last_frame = get_frame_pointer ();
  
  for (j = p -> top_frame; j >= p -> last_frame; j--) {

    if (j == main_start_frame) {
      p -> need_main_init = TRUE;
      p -> need_main_exit = TRUE;
    }

    while ((unevaled (j)) != 0)
      parser_pass (j, p);

    output_imported_methods ();

    output_frame (j);

    /*
     *  If there is a change of frame scope from LOCAL_VAR to
     *  GLOBAL_VAR, it means we are at the end of a function
     *  or method.  Output templates.
     */
    if ((j < MAXFRAMES) &&
	((frame_at (j + 1) -> scope == LOCAL_VAR) &&
	 (frame_at (j) -> scope == GLOBAL_VAR)))
      unbuffer_fn_templates ();
  }
	
  for (j = p -> last_frame; j <= p -> top_frame; j++) {
    clear_frame_messages (j);
    remove_frame ();
  }

  --parser_level;
  frame_ptr = prev_frame_ptr;

  if (interpreter_pass == expr_check) {
    /* Normally occurs at the end of a method. */
    delete_local_objects ();
    delete_local_c_vars ();
  }

  if (end_of_input ()) {  
    /*
     *  Call fileout () while there is still a parser.
     */
    output_global_init ();
    unbuffer_function_output ();
  }

  delete_parserinfo (pop_parser ());

  return  inbuf;
}

/*
 *  Check for valid state transitions between tokens,
 *  excluding newline and whitespace tokens.
 */

int check_state (int stack_idx, MESSAGE **messages, int *states, 
		 int cols) {
  register int i;
  int token;
  int next_idx = stack_idx;
  int last_idx;
  register int *states_ptr;

  states_ptr = states;
  token = messages[stack_idx] -> tokentype;

  last_idx = get_stack_top (messages);

  for (i = 0; ; i++) {
  nextrans:
    if (states_ptr[i+i] == ERROR)
      return ERROR;
      if (states_ptr[i+i] == token) {
	for (next_idx = stack_idx - 1; next_idx > last_idx; next_idx--) {
	  while (M_ISSPACE(messages[next_idx])) {
	    next_idx--;
	    if (!messages[next_idx]) return ERROR;
	  }
	  if (states_ptr[i+i+1] != messages[next_idx]->tokentype) {
	    ++i;
	    goto nextrans;
	  } else {
	    return i;
	  }
	}
      }
  }
  return ERROR;
}
      
PARSER *new_parserinfo (void) {

  PARSER *p;

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

  strcpy (p -> sig, "PARSER");
  p -> block_level = 0;
  p -> need_init = TRUE;
  p -> need_main_init = FALSE;
  p -> _p_exception = no_x;
  p -> pass = interpreter_pass;
  p -> pred_state = ctrlblk_pred;
  p -> blk_state = ctrlblk_blk;
  p -> else_state = ctrlblk_else_blk;
  return p;

}

void delete_parserinfo (PARSER *p) {
  ctrlblk_pred = p -> pred_state;
  ctrlblk_blk = p -> blk_state;
  ctrlblk_else_blk = p -> else_state;
  free (p);
}

/*
 *   Find the end of a preprocessor statement - the index of the
 *   token immediately before the newline.
 */
int end_of_p_stmt (MESSAGE_STACK messages, int msg_ptr) {

  int i = msg_ptr;
  while (1) {
    /*
     *  Check for input that is not terminated by a newline.
     */
    if (!messages[i - 1] || !IS_MESSAGE (messages[i - 1]))
      break;
    if (messages[i - 1] -> tokentype == NEWLINE 
	&& !LINE_SPLICE (messages, i - 1))
      break;
    i--;
  }
  return i;
}



