/* $Id: output.c,v 1.20 2009/03/01 03:50:23 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.
*/

/*
 *   Code generation functions to generate ctalk run time code.  
 */

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

extern char input_source_file[],    /* Declared in main.c.              */
  output_file[];
extern int include_dir_opt;   /* Include directories given as     */  
extern char *include_dirs[];  /* command line options.            */
extern int n_include_dirs;
extern int outputfile_opt;

extern I_PASS interpreter_pass;

extern int buffer_function_output;   /* Declared in fnbuf.c.      */

int ctalk_init = FALSE;              /* __ctalk_init and its function     */ 
int ctalk_init_call = FALSE;         /* call when main is reached.        */

extern int preamble;                 /* Preamble and main declaration     */
extern int main_declaration;         /* relative locations, declared in   */
                                     /* cparse.c.                         */

extern int do_block;                 /* Declared in control.c.            */


static PENDING *pending_statements = NULL; /* List of pending statements. */
static PENDING *pending_head_ptr = NULL;

static int tmp_var_ext;

/* 
 *  The stack index of the last message output.
 */

static int parser_output_ptr = 0;

int get_parser_output_ptr (void) {
  return parser_output_ptr;
}

void init_output_vars (void) {
  tmp_var_ext = 0;
}

/* 
 * Output the call to __ctalk_init() after the local variable
 * declarations in main().
 */

/*
 *  Generic output function interface.
 */

int output_buffer (char *buf, int when) {
  if (is_global_frame ()) {
    fn_init (buf, FALSE);
  } else {
    fileout (buf, 0, when);
  }
  return SUCCESS;
}

void generate_ctalk_init_call (void) {
  char buf[64];

  sprintf (buf, "     __ctalk_init (argv[0]);\n");
  if (buffer_function_output)
    buffer_function_statement (buf);
  else
    fileout (buf, 0, 0);
}

/*
 *  Format and output a __ctalk_self_internal () call.
 */

int generate_self_call (int when) {
  char buf[MAXMSG];

  sprintf (buf, "%s ()", SELF_ACCESSOR_FN);
  fileout (buf, 0, when);
  return SUCCESS;
}

/*
 *  Format a __ctalk_method call for output.
 */

char *fmt_method_call (OBJECT *rcvr, char *method) {

  static char buf[MAXMSG];
  char namebuf[MAXLABEL];

  strcpy (namebuf, rcvr -> __o_name);

  sprintf (buf, "__ctalk_method (\"%s\", %s)", 
	   ((_lextype (namebuf) == LITERAL_T) ? 
	    escape_str (namebuf) : namebuf), 
	   method);

  return buf;
}

/* 
 *  Output a __ctalk_method() function call at the
 *  source token index given by the parameter, "when."
 */

int generate_method_call (OBJECT *rcvr, char *method, int when) {

  if (is_global_frame ())
    fn_init (fmt_method_call (rcvr, method), FALSE);
  else
    fileout (fmt_method_call (rcvr, method), 0, when);

  return SUCCESS;
}

/*
 *  Generate either a __ctalkGetInstanceVariable () or 
 *  __ctalkGetInstanceVariableByName () call, depending on 
 *  the class of the receiver.
 *
 *  The call should probably not generate a semicolon, because
 *  it may be used in place in an expression.
 */

char *fmt_rt_instancevar_call (OBJECT *receiver, char *varname) {
  static char buf[MAXMSG];

  if (!strcmp (receiver -> __o_classname, "Expr") ||
      !strcmp (receiver -> __o_classname, "CFunction"))
    sprintf (buf, "__ctalkGetInstanceVariable (%s, \"%s\", 1)",
	     receiver -> __o_name, varname);
  else
    sprintf (buf, "__ctalkGetInstanceVariableByName (\"%s\", \"%s\", 1)",
	     receiver -> __o_name, varname);
  return buf;
}

int generate_rt_instancevar_call (OBJECT *receiver, char *varname, int when) {

  static char buf[MAXMSG];

  strcpy (buf, fmt_rt_instancevar_call (receiver, varname));

  if (is_global_frame ())
    fn_init (buf, FALSE);
  else
    fileout (buf, 0, when);

  return SUCCESS;
}

int generate_global_object_definition (char *name, char *class, 
				       char *superclass,
				       char *buf, int scope) {
  if (!superclass)
    sprintf (buf, 
	     "__ctalk_dictionary_add(__ctalkCreateObjectInit (\"%s\", \"%s\", %s, %d, %s));\n", 
	     name, class, STR_VAL(NULL), scope, STR_VAL(NULL));
  else
    sprintf (buf, "__ctalk_dictionary_add(__ctalkCreateObjectInit (\"%s\", \"%s\", \"%s\", %d, %s));\n", 
	   name, class, superclass, scope, STR_VAL(NULL));
    
  if (is_global_frame ())
    global_init_statement (buf, FALSE);
  else 
    fileout (buf, 0, 0);
  return SUCCESS;
}

/* 
 *  Generate the C language declaration for a method function.
 */

int generate_method_c_declaration (char *buf) {
  sprintf (buf, "char * ");
  fileout (buf, 0, 0);
  return SUCCESS;
}

int generate_c_expr_store_arg_call (OBJECT *rcvr, METHOD *method, OBJECT *arg,
				    char *fn_name, int when) {
  if (is_global_frame () || IS_CONSTRUCTOR(method))
    fn_init (fmt_c_expr_store_arg_call (rcvr, method, arg, fn_name), FALSE);
  else
    fileout (fmt_c_expr_store_arg_call (rcvr, method, arg, fn_name), 0, when);

   return SUCCESS;
}

char arglistbuf[MAXMSG];

/* 
 *    Format a __ctalk_store_arg_object() function call for a complex
 *    C expression.  
 */

char *fmt_c_expr_store_arg_call (OBJECT *rcvr, METHOD *method, OBJECT *arg,
				 char *fn_name) {
  static char buf[MAXMSG];
  char arg_to_obj_buf[MAXMSG];
  CVAR *c_var;
  CFUNC *c_fn;
  OBJECT *t_arg;

  if (((c_var = get_global_var (fn_name)) == NULL) &&
      ((c_var = get_local_var (fn_name)) == NULL) &&
      ((c_fn = get_function (fn_name)) == NULL)) {
    _warning ("fmt_c_expr_store_arg_call: Undefined or unprototyped C function %s.  Return class defaulting to Integer.\n",
	     fn_name);
    t_arg = create_object_init (INTEGER_CLASSNAME,
				INTEGER_SUPERCLASSNAME,
				arg -> __o_name,
				arg -> __o_name);
  } else {
    t_arg = arg;
  }

  strcpy (arg_to_obj_buf, fmt_c_to_obj_call (rcvr, method, t_arg));

  sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", %s);\n",
	   rcvr->__o_name, method->name, arg_to_obj_buf);

  return buf;

}

int generate_store_arg_call (OBJECT *rcvr, METHOD *method, OBJECT *arg,
			     int when) {
  if (is_global_frame () || IS_CONSTRUCTOR(method))
    fn_init (fmt_store_arg_call (rcvr, method, arg), FALSE);
  else
    fileout (fmt_store_arg_call (rcvr, method, arg), 0, when);

   return SUCCESS;
}

/* 
 *    Format a __ctalk_store_arg_object() function call.
 */

char *fmt_store_arg_call (OBJECT *rcvr, METHOD *method, OBJECT *arg) {
  static char buf[MAXMSG];
  char argnamebuf[MAXMSG],
    rcvrnamebuf[MAXMSG];
  CVAR *c_var;
  CFUNC *c_fn;

  /*
   *  Quote the argument depending on its C type or object class.
   */

  if (((c_var = is_c_variable (arg)) != NULL) ||
      ((c_fn = is_c_fn (arg)) != NULL)) {

    strcpy (argnamebuf, arg -> __o_name);
    while (*argnamebuf == '\"')
      TRIM_LITERAL (argnamebuf);
  } else {
    if (!strcmp (arg -> __o_classname, "String") &&
 	(_lextype (arg -> __o_name) == LITERAL_T)) {

      sprintf (argnamebuf, "%s", escape_str_quotes (arg -> __o_name));

    } else {
      /*
       *  Handle constant expressions; e.g., "1 + 2 + 3."
       */
      
      if (!strcmp (arg -> __o_classname, "Expr") ||
  	  !strcmp (arg -> __o_classname, "CFunction")) {
  	strcpy (argnamebuf, esc_expr_quotes (arg -> __o_name));
      } else {
  	strcpy (argnamebuf, arg -> __o_name);
      }
    }
  }

  if (!strcmp (rcvr -> __o_classname, "Expr") ||
      !strcmp (rcvr -> __o_classname, "CFunction")) {
    sprintf (buf, "__ctalk_arg (%s, \"%s\", (void *)\"%s\");\n",
  	     rcvr->__o_name, method->name, argnamebuf);
  } else {
    if (!strcmp (rcvr -> __o_classname, "String") &&
	(_lextype (rcvr -> __o_name) == LITERAL_T)) {
      strcpy (rcvrnamebuf, rcvr -> __o_name);
      strcpy (rcvrnamebuf, escape_str_quotes (rcvrnamebuf));
      sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", (void *)\"%s\");\n",
  	       rcvrnamebuf, method->name, argnamebuf);
    } else {
      sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", (void *)\"%s\");\n",
  	       rcvr->__o_name, method->name, argnamebuf);
    }
  }
  return buf;

}

/* 
 *    Generate a __ctalk_arg_cleanup () function call.
 */

int generate_method_pop_arg_call (int when) {
  static char buf[MAXMSG];

  sprintf (buf, "__ctalk_arg_cleanup ();\n");

  if (is_global_frame ())
    fn_init (buf, FALSE);
  else
    fileout (buf, 0, when);

  return SUCCESS;
}

/*
 *  Queue an init statement for output as a global init statement,
 *  a statement in a function init block, or a method init block.
 *  If, "promote," is true, then allow the statement to be promoted
 *  to the calling function's init block.  See the comments in 
 *  fninit.c for the levels of global_defer.
 */

int queue_init_statement (char *s, int promote, int global_defer) {
  if (is_global_frame ()) {
      global_init_statement (s, global_defer);
  } else {
    switch (interpreter_pass)
      {
      case method_pass:
	method_init_statement (s);
	break;
      case parsing_pass:
      case library_pass:
	fn_init (s, promote);
	break;
      default:
	break;
      }
  }
  return SUCCESS;
}

/* 
 *  Generate the ctalk lib function calls to save a method's 
 *  arguments and call the method.  If given in the preamble,
 *  include the statements in __ctalk_init().  
 */

int generate_primitive_method_call (OBJECT *o, METHOD *m,char *buf,int attrs) {
  int i;
  char arg_fn_buf[MAXLABEL];
  switch (interpreter_pass)
    {
    case method_pass:
      strcpy (arg_fn_buf, "__ctalkCreateArg");
      break;
    default:
      if (!strcmp (m -> name, "new")) {
	strcpy (arg_fn_buf, "__ctalkCreateArg");
      } else {
	strcpy (arg_fn_buf, "__ctalk_arg");
      }
      break;
    }

  for (i = 0; i < m -> n_args; i++) {
    if (!strncmp (m -> args[i] -> __o_name, METHOD_ARG_ACCESSOR_FN, 
		  strlen (METHOD_ARG_ACCESSOR_FN))) {
      sprintf (buf, "%s (\"%s\", \"%s\", %s);\n",
	       arg_fn_buf, o -> __o_name, m -> name, m -> args[i] -> __o_name);
    } else {
      sprintf (buf, "%s (\"%s\", \"%s\", \"%s\");\n",
	       arg_fn_buf, o -> __o_name, m -> name, m -> args[i] -> __o_name);
    }
    switch (interpreter_pass)
      {
      case method_pass:
	method_init_statement (buf);
	break;
      default:
	queue_init_statement (buf, FALSE, 3);
	break;
      }
  }
  sprintf (buf, "__ctalk_primitive_method (\"%s\", \"%s\", %d);\n", o -> __o_name, 
	   m -> name, attrs);
    switch (interpreter_pass)
      {
      case method_pass:
	method_init_statement (buf);
	break;
      default:
	queue_init_statement (buf, FALSE, 3);
	break;
      }
  return SUCCESS;
}

/*
 *  As above, but generates a standard method call from a primitive
 *  in cases where a subclass method replaces a primitive method.
 */
int generate_method_call_from_primitive (OBJECT *o, METHOD *m, char *buf, 
					 int attrs) {
  int i;
  for (i = 0; i < m -> n_args; i++) {
    if (!strncmp (m -> args[i] -> __o_name, METHOD_ARG_ACCESSOR_FN, 
		  strlen (METHOD_ARG_ACCESSOR_FN))) {
      sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", %s);\n",
	       o -> __o_name, m -> name, m -> args[i] -> __o_name);
    } else {
      sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", \"%s\");\n",
	       o -> __o_name, m -> name, m -> args[i] -> __o_name);
    }
    queue_init_statement (buf, FALSE, 3);
  }
  sprintf (buf, "__ctalk_method (\"%s\", %s);\n", o -> __o_name, m -> selector);
  queue_init_statement (buf, FALSE, 3);
  sprintf (buf, "__ctalk_arg_pop_deref ();\n");
  queue_init_statement (buf, FALSE, 3);
  return SUCCESS;
}

char *fmt_eval_expr (char *expr) {
  static char buf[MAXMSG];
  char tmpbuf[MAXMSG];
  strcpy (tmpbuf, expr);
  de_newline_buf (tmpbuf);
  sprintf (buf, "%s (\"%s\")", EVAL_EXPR_FN, escape_str_quotes (tmpbuf));
  return buf;
}

/*
 *  Once again, there seems to be a problem with line markers
 *  in DJGPP, which generates warning messages when returning
 *  to a file from more than one included files.  For now,
 *  simply omitting the flag in DJGPP line markers should
 *  avoid the warning messages.
 */

int fmt_line_info (char *buf, int line, char *file, int flag, 
		   int newline_before) {
#ifdef __DJGPP__
    if (newline_before)
      sprintf (buf, "\n# %d \"%s\"\n", line, file);
    else
      sprintf (buf, "# %d \"%s\"\n", line, file);
#else
  if (flag) {
    if (newline_before)
      sprintf (buf, "\n# %d \"%s\" %d\n", line, file, flag);
    else
      sprintf (buf, "# %d \"%s\" %d\n", line, file, flag);
  } else {
    if (newline_before)
      sprintf (buf, "\n# %d \"%s\"\n", line, file);
    else
      sprintf (buf, "# %d \"%s\"\n", line, file);
  }
#endif
  return SUCCESS;
}

int generate_set_global_variable_call (char *name, char *classname) {

  static char buf[MAXLABEL];

  sprintf (buf, "__ctalk_set_global (\"%s\", \"%s\");\n", name, classname);
  queue_init_statement (buf, FALSE, 3);
  return SUCCESS;
}

int generate_set_local_variable_call (char *name, char *classname) {

  static char buf[MAXLABEL];

  if (!strncmp (name, METHOD_ARG_ACCESSOR_FN, 
		strlen (METHOD_ARG_ACCESSOR_FN))) {
    sprintf (buf, "__ctalk_set_local (%s);\n", name);
  } else {
    sprintf (buf, 
     "__ctalk_set_local (__ctalk_get_object (\"%s\", %s));\n", 
	     name, STR_VAL(NULL));
  }

  queue_init_statement (buf, FALSE, 0);

  return SUCCESS;
}

/*
 *   The prototype is: 
 *   REGISTER_C_METHOD_ARG (char *decl, char *type,
 *                                  char *qualifier, char *qualifier2,
 *                                  char *storage_class, 
 *                                  char *name,
 *                                  int is_unsigned,
 *                                  int n_derefs,
 *                                  int scope,
 *                                  (void *)var);
 *
 *  The, "n_derefs," members should eventually replace the, 
 *  "is_ptr," and, "is_ptrptr," members.
 *
 *   __ctalk_method () deletes the cvars after the method call.
 */

static CVAR *__basic_type_of (CVAR *__c) {
  CVAR *__c_basic, *__c_basic2 = NULL;

  if (get_type_conv_name  (__c -> type))
    return __c;

  if ((__c_basic = get_typedef (__c -> type)) != NULL) {

    while (TRUE) {

      if (get_type_conv_name (__c_basic -> type))
	return __c_basic;

      if ((__c_basic2 = get_typedef (__c_basic -> type)) == NULL) {
	return __c_basic;
      } else {
	if (__c_basic2 == __c_basic) {
	  return __c_basic;
	} else {
	  __c_basic = __c_basic2;
	}
      }
    }
  }
  return __c;
}

static char *__fmt_c_method_arg_call (char *namebuf, char *labelbuf,
				      int scope, CVAR *c, 
				      CVAR *c_basic) {
  static char buf[MAXLABEL];
  sprintf (buf, 
	     "%s (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", %d, %d, %d, (void *)%s%s);\n",
	   REGISTER_C_METHOD_ARG,
	     ((*c_basic->decl) ? c_basic -> decl : NULLSTR),
	     ((*c_basic->type) ? c_basic -> type : NULLSTR),
	     ((*c_basic->qualifier) ? c_basic -> qualifier : NULLSTR),
	     ((*c_basic->qualifier2) ? c_basic -> qualifier2 : NULLSTR),
	     ((*c_basic->storage_class) ? c_basic -> storage_class : NULLSTR),
	     namebuf,
	   ((c -> is_unsigned == True) ? TRUE : FALSE),
	   c -> n_derefs,
	   scope,
	   ((c -> n_derefs) ? "" : "&"),
	     labelbuf);
  return buf;
}

#define __STRUCT_TYPE(__c) (((__c)->type_attrs & CVAR_TYPE_STRUCT) || \
                            ((__c)->type_attrs & CVAR_TYPE_UNION))

char *fmt_register_c_method_arg_call (CVAR *c, char *label, int scope) {

  char namebuf[MAXLABEL];
  char labelbuf[MAXLABEL];

  strcpy (labelbuf, trimstr (label));

  /*
   *  If a terminal struct member, then print immediately.
   */
  if ((c -> attrs & CVAR_ATTR_STRUCT_MBR) &&
      !__STRUCT_TYPE(c))
    return __fmt_c_method_arg_call (labelbuf, labelbuf, scope, 
				    c, __basic_type_of(c));

  /*
   *  If we have an aggregate type, then derive the full aggregate
   *  name.
   */
  if ((c -> type_attrs & CVAR_TYPE_STRUCT) ||
      (c -> type_attrs & CVAR_TYPE_UNION) ||
      (c -> attrs & CVAR_ATTR_STRUCT_DECL)) {
    /*
     *  Struct member.
     */
    if (strstr (labelbuf, ".") || strstr (labelbuf, "->")) {
      CVAR *struct_var, *struct_defn, *mbr_var;
      char *s;
      strcpy (namebuf, labelbuf);
      for (s = namebuf; *s; s++)
	if (!isalnum ((int) *s) && *s != '_')
	  break;
      *s = 0;
      if (((struct_var = get_local_var (namebuf)) != NULL) ||
	  ((struct_var = get_global_var (namebuf)) != NULL)) {

	/*
	 *  If we have a pointer to a struct, then 
	 *  find the definition.
	 */

	if ((struct_var -> n_derefs) && (struct_var -> members == NULL)) {
	  if (((struct_defn = get_local_struct_defn (struct_var -> type)) != NULL) ||
	      ((struct_defn = get_global_struct_defn (struct_var -> type)) != NULL) ||
	      ((struct_defn = have_struct (struct_var -> type)) != NULL)) {
	    if ((mbr_var = struct_member_from_expr (labelbuf, struct_defn))
		!= NULL) {
	      return __fmt_c_method_arg_call (labelbuf, labelbuf, scope,
					      mbr_var,
					      __basic_type_of(mbr_var));
	    }
	  }
	} else {
	  /*
	   *  Struct declaration, find the definition.
	   */
	  if (struct_var -> members == NULL) {
	    if (((struct_defn = get_local_struct_defn (struct_var -> type)) != NULL) ||
		((struct_defn = get_global_struct_defn (struct_var -> type)) != NULL)||
		((struct_defn = have_struct (struct_var -> type)) != NULL)){
	      if ((mbr_var = struct_member_from_expr (labelbuf, struct_defn))
		  != NULL) {
		return __fmt_c_method_arg_call (labelbuf, labelbuf, scope,
						mbr_var,
						__basic_type_of(mbr_var));
	      }
	    }
	  } else {
	    /*
	     *  The variable is the struct declaration.
	     */
	    if ((mbr_var = struct_member_from_expr (labelbuf, struct_var))
		!= NULL) {
	      return __fmt_c_method_arg_call (labelbuf, labelbuf, scope,
					      mbr_var,
					      __basic_type_of(mbr_var));
	    }
	  }
	}
	if (*namebuf == 0) {
	  _warning 
	    ("warning: fmt_register_c_method_arg_call: %s contains undefined member of struct or union \"%s.\"\n",
	     labelbuf,
	     c -> name);
	  strcpy (namebuf, labelbuf);
	}
      } else {
	_warning ("fmt_register_c_method_arg_call: struct not found.\n");
	strcpy (namebuf, labelbuf);
      }
    }
    /*
     *  Array member.  Not used at present, should be removed.
     */
    if (strstr (labelbuf, "[") && strstr (labelbuf, "]")) {
      /*
       *  The name and the label should be the same, unless 
       *  the variable has an expression; for example, 
       *  argv[__ctalk_to_c_int (__ctalkEvalExpr (myInt))].
       *  For now, if the label contains "__ctalkEvalExpr", use
       *  the CVAR name as the name.
       *  Register_c_variable () takes care that the CVAR 
       *  has the correct array member and data type.
       *  TO DO - We could probably move the aggregate ->
       *  aggregate member translations to separate functions
       *  for structs and arrays.
       */
      if (strstr (labelbuf, EVAL_EXPR_FN))
	strcpy (namebuf, c -> name);
      else
	strcpy (namebuf, labelbuf);
    }
  } else {
    strcpy (namebuf, c -> name);
  }
  return __fmt_c_method_arg_call (namebuf, labelbuf, scope, 
				  c, 
				  __basic_type_of(c));
}

int generate_register_c_method_arg_call (CVAR *c, char *label, int scope,
					 int when) {
  fileout (fmt_register_c_method_arg_call (c, label, scope), 0, when);
  return SUCCESS;
}

/*
 *  Write the generated output to the output file.
 */

void __fileout (char *s) {
  FILE *f;
  int r;
  if (interpreter_pass == method_pass) {

    write_tmp (s);

  } else {

    if (outputfile_opt) {
      if ((f = fopen (output_file, FILE_APPEND_MODE)) == NULL)
	_error ("fileout: %s", strerror(errno));

      r = fwrite (s, sizeof (char), strlen (s), f);

      fclose (f);
    } else {
      r = fwrite (s, sizeof (char), strlen (s), stdout);
    }

    if (r != strlen (s))
      _error ("write error %s.", strerror(errno));

  }

}

static int last_fileout_stmt;

/*
 *  Output the generated ctalk code, delaying the output 
 *  if necessary for insertion elsewhere in the output.
 */

void fileout (char *s, int now, int when) {

  PENDING *p = NULL;
  PENDING *_tmp_ptr;
  int parser_lvl;

  if (interpreter_pass == expr_check) return;

  parser_lvl = current_parser () -> level;

  if (when) {

    if ((p = (PENDING *) calloc (1, sizeof (struct _pending)))
	== NULL)
      _error ("fileout: %s.\n", strerror (errno));

    strcpy (p -> stmt, s);
    p -> n = when;
    p -> parser_lvl = parser_lvl;

    if (!pending_statements) {
      pending_statements = pending_head_ptr = p;
    } else {
      pending_head_ptr -> next = p;
      p -> prev = pending_head_ptr;
      pending_head_ptr = p;
    }
  }

  for (p = pending_statements; p; p = p -> next) {
  fileout_pending_loop:
    if (last_fileout_stmt) {
      if ((p -> parser_lvl == parser_lvl) &&
	  ((p -> n < last_fileout_stmt) && 
	   (p -> n >= parser_output_ptr))) {

	if (interpreter_pass == method_pass) {
	  buffer_method_statement (p -> stmt);
	} else {
	  if (interpreter_pass == c_fn_pass) {
	    buffer_fn_args (p, message_stack_at (p -> n) -> name);
	  } else {
	    if (buffer_function_output) {
	      buffer_function_statement (p -> stmt);
	    } else {
	      __fileout (p -> stmt);
	    }
	  }
	}

	if (p == pending_head_ptr)
	  pending_head_ptr = p -> prev;
	if (p -> next)
	  p -> next -> prev = p -> prev;
	if (p -> prev)
	  p -> prev -> next = p -> next;
	if (p == pending_statements) {
	  if (p -> next) {
	    pending_statements = p -> next;
	  } else {
	    pending_statements = pending_head_ptr = NULL;
	  }
	}
	_tmp_ptr = p -> next;
	free (p);
	if ((p = _tmp_ptr) == NULL)
	  goto fileout_now;
	else
	  goto fileout_pending_loop;
      }
    }
    if (!pending_head_ptr)
      break;
  }

 fileout_now:

  if (now)
    last_fileout_stmt = now;

  if (!when) {                 /* Output in statement above. */
    if (interpreter_pass == library_pass) {
      __fileout (s);
    } else {
      if (interpreter_pass == method_pass) {
	buffer_method_statement (s);
      } else {
	if (buffer_function_output) {
	  buffer_function_statement (s);
	} else {
	  __fileout (s);
	}
      }
    }
  }

}

/*
 *  NOTE  If a control block function has inserted a 
 *  closing brace past the end of the frame, output the
 *  pending statement below by looking ahead for pending
 *  statements in the next frame.
 *  If there  are insertions other than closing 
 *  braces,  then the functions that do the insertions 
 *  will have to use stack splicing.
 */

int output_frame (int frame) {

  int j;
  int start, end;
  int parser_lvl;
  MESSAGE *m;
  PENDING *p;

  if (interpreter_pass == expr_check) 
    return SUCCESS;

  parser_lvl = current_parser () -> level;

  if ((start = message_frame_top_n (frame)) == -1)
    start = N_MESSAGES;

  if ((end = message_frame_top_n (frame - 1)) == -1)
    end = get_messageptr ();

  for (j = start; j > end; j--) {
    parser_output_ptr = j;
    m = message_stack_at (j);
    if (!m || !IS_MESSAGE (m))
      break;
    if (!m -> output) {
      if (*(m -> name)) { fileout (m -> name, j, 0); }
      ++m -> output;
    }
  }

  if ((p = pending_statements) == NULL)
    return SUCCESS;
  if ((start = message_frame_top_n (frame - 1)) == -1)
    start = N_MESSAGES;
  if ((end = message_frame_top_n (frame - 2)) == -1)
    end = get_messageptr ();

  for (j = start; j > end; j--) {
  frame_loop_outer:
    while (p) {
    frame_loop_inner:
      if ((p -> parser_lvl == parser_lvl) &&
	  (p -> n == j)) {
	if (interpreter_pass == method_pass) {
	  buffer_method_statement (p -> stmt);
	} else {
	  if (interpreter_pass == c_fn_pass) {
	    buffer_fn_args (p, message_stack_at (p -> n) -> name);
	  } else {
	    if (buffer_function_output) {
	      buffer_function_statement (p -> stmt);
	    } else {
	      __fileout (p -> stmt);
	    }
	  }
	}

	if (p == pending_head_ptr) {
	  pending_head_ptr = p -> prev;
	  if (p == pending_statements) {
	    free (p);
	    pending_head_ptr = pending_statements = NULL;
	    return SUCCESS;
	  } else {
	    free (p);
	    --j;
	    goto frame_loop_outer;
	  }
	} /* if (p == pending_head_ptr) */
	
	if (p -> next)
	  p -> next -> prev = p -> prev;
	if (p -> prev)
	  p -> prev -> next = p -> next;
	if (p == pending_statements) {
	  if (p -> next) {
	    pending_statements = p -> next;
	    free (p);
	    p = pending_statements;
	    goto frame_loop_inner;
	  } else {
	    pending_statements = pending_head_ptr = NULL;
	    free (p);
	    return SUCCESS;
	  }
	} /* if (p == pending_statements) */
	p = p -> next;
	if (p -> prev) free (p -> prev);
	goto frame_loop_inner;
      }
      p = p -> next;
    }
  }

  return SUCCESS;
}

/*
 *
 *  Note that method definitions are always in the global 
 *  __ctalk_init ();
 *
 */

int generate_instance_method_param_definition_call (char *classname, char *name,
					   char *paramName, char *paramClass,
					   int param_is_pointer,
					   int param_is_pointerpointer) {
  char buf[MAXMSG];
  sprintf (buf, "__ctalkInstanceMethodParam (\"%s\", \"%s\", \"%s\", \"%s\", %d);\n",
	   classname, name, paramClass, paramName, param_is_pointer);

    global_init_statement (buf, FALSE);

  return SUCCESS;
}

/****
 ****    
 ****    METHOD DEFINITION CALLS
 ****
 ****    Method definition calls should be output in
 ****    __ctalk_init ().
 ****    
 ****/

/*
 *    Generate a method definition call to be included in 
 *    the global init.
 */

int generate_instance_method_definition_call (char *classname, char *name,
				     char *funcname, int required_args) {
  char buf[MAXMSG];

  sprintf (buf, 
	   "__ctalkDefineInstanceMethod (\"%s\", \"%s\", %s, %d);\n",
	   classname, name, funcname, required_args);

  global_init_statement (buf, FALSE);

  return SUCCESS;
}

int generate_class_method_definition_call (char *classname, char *name,
				     char *funcname, int required_args) {
  char buf[MAXMSG];

  sprintf (buf, 
	   "__ctalkDefineClassMethod (\"%s\", \"%s\", %s, %d);\n",
	   classname, name, funcname, required_args);

  global_init_statement (buf, FALSE);

  return SUCCESS;
}

/*
 *  Generate an instance method return class call in __ctalk_init ().
 */

int generate_instance_method_return_class_call (char *class, char *methodName, 
				       char *returnClass) {
  char buf[MAXMSG];
  sprintf (buf, 
	   "__ctalkInstanceMethodInitReturnClass (\"%s\", \"%s\", \"%s\");\n",
	   class, methodName, returnClass);

    global_init_statement (buf, FALSE);

  return SUCCESS;
}

int generate_class_method_return_class_call (char *class, char *methodName, 
				       char *returnClass) {
  char buf[MAXMSG];
  sprintf (buf, 
	   "__ctalkClassMethodInitReturnClass (\"%s\", \"%s\", \"%s\");\n",
	   class, methodName, returnClass);

    global_init_statement (buf, FALSE);

  return SUCCESS;
}

/*
 *  Generate a global method definition call in __ctalk_init ().
 *  These calls are used define primitive methods.  At the 
 *  moment, they are the same as a normal method definition
 *  call. 
 */

int generate_global_method_definition_call (char *classname, char *name,
			    char *funcname, int required_args) {
  char buf[MAXMSG];

  sprintf (buf, 
	   "__ctalkDefineInstanceMethod (\"%s\", \"%s\", %s, %d);\n",
	   classname, name, funcname, required_args);
  global_init_statement (buf, FALSE);
  return SUCCESS;
}

/****
 ****    
 ****    CLASS DEFINITION CALLS
 ****
 ****    Class definition calls should also be output in
 ****    __ctalk_init ().
 ****    
 ****/

/*
 *   Generate an imported class definition call in the function or 
 *   global initialization.  
 *
 *   Class initialization goes in the initialization of the parent
 *   parser.  If there are superclass initializations at this
 *   level (from sub-parsers), transfer those to the parent level
 *   also.
 */

int generate_primitive_class_definition_call (OBJECT *o, METHOD *m) {
  int i;
  char buf[MAXMSG];
  PARSER *p;

  p = current_parser ();

  if (p -> init) {  
    /* 
     *   Add superclass initializations from sub-parsers
     *   to the parent level.                            
     */
    LIST *l, *l_arg, *l_init, *l_method;
    char *s, pattern[MAXMSG];

    /*
     *  Make sure the patterns matches the __ctalk_arg ()
     *  call, below, and __ctalkDefineInstanceMethod ().
     *  This pattern can be used for recursive 
     *  primitive calls, but the routine promotes only 
     *  class initializations specifically.  That means
     *  we also have to promote the method definitions.
     */
    for (l = p -> init; l; l = l -> next) {
      /*
       *  The length of the loop varies, so make sure we 
       *  have a valid list element.
       */
      if (!l || !IS_LIST (l) || !l -> data) break;

      sprintf (pattern, ", \"%s\", ", m -> name);

      if ((s = strstr ((char *)l -> data, pattern)) != NULL) {

 	l_arg = list_unshift (&(p->init));
 	l_init = list_unshift (&(p->init));

	global_init_statement ((char *)l_arg -> data, FALSE);
	global_init_statement ((char *)l_init -> data, FALSE);

	l = l_init -> next;
	delete_list_element (l_arg);
	delete_list_element (l_init);
      }

      /* 
       *  TO DO - This should work without an inner loop, 
       *  but make sure it is correct.
       */
      sprintf (pattern, "__ctalkDefineInstanceMethod (");
      if ((s = strstr ((char *)l -> data, pattern)) != NULL) {
	l_method = list_unshift (&(p->init));
	global_init_statement ((char *)l_method -> data, FALSE);
	delete_list_element (l_method);
      }
    }
  }

  for (i = 0; i < m -> n_args; i++) {
     sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", \"%s\");\n",
	      o -> __o_name, m -> name, m -> args[i] -> __o_name);
      global_init_statement (buf, FALSE);
  }
  sprintf (buf, "__ctalk_primitive_method (\"%s\", \"%s\", %d);\n", 
	   o -> __o_name, m -> name, FALSE);
  global_init_statement (buf, FALSE);

  return SUCCESS;
}

/*
 *  The prototype of the library call is:
 *
 *  __ctalkRegisterCTypedef (char *type, 
 *                           char *qualifier,
 *                           char *qualifier2,
 *                           char *qualifier3,
 *                           char *qualifier4,
 *                           char *storage_class,
 *                           char *name,
 *                           int n_derefs,
 *                           int attrs,
 *                           int is_unsigned,
 *                           int scope);
 */

int generate_register_typedef_call (CVAR *c) {
  char buf[MAXMSG];

  sprintf (buf, 
	   "__ctalkRegisterCTypedef (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", %d, %d, %d, %d);\n",
	   ((*(c -> type)) ? c -> type : ""),
	   ((*(c -> qualifier)) ? c -> qualifier : ""),
	   ((*(c -> qualifier2)) ? c -> qualifier2 : ""),
	   ((*(c -> qualifier3)) ? c -> qualifier3 : ""),
	   ((*(c -> qualifier4)) ? c -> qualifier4 : ""),
	   ((*(c -> storage_class)) ? c -> storage_class : ""),
	   ((*(c -> name)) ? c -> name : ""),
	   c -> n_derefs,
	   c -> attrs,
	   c -> is_unsigned,
	   c -> scope);

  global_init_statement (buf, FALSE);
  return SUCCESS;
}

int generate_define_class_variable_call (char *class_rcvr, char *name, 
					 char *class, char *init) {
  static char buf[MAXMSG];

  sprintf(buf,"__ctalkDefineClassVariable (\"%s\", \"%s\", \"%s\", \"%s\");\n",
	  ((class_rcvr) ? class_rcvr : ""),
	  ((name) ? name : ""),
	  /*
	   *  Must have a class for the variable value.
	   */
	  ((class) ? class : class_rcvr),
	  ((init) ? init : ""));
  global_init_statement (buf, 2);

  return SUCCESS;
}

int generate_define_instance_variable_call (char *class_rcvr, char *name, 
					 char *class, char *init) {
  static char buf[MAXMSG];

  sprintf(buf,"__ctalkDefineInstanceVariable (\"%s\", \"%s\", \"%s\", \"%s\");\n",
	  ((class_rcvr) ? class_rcvr : ""),
	  ((name) ? name : ""),
	  ((class) ? class : ""),
	  ((init) ? init : ""));
  global_init_statement (buf, 1);

  return SUCCESS;
}

/*
 *  Should call is_struct_or_union_expr () first.
 */
char *struct_or_union_expr (MESSAGE_STACK messages, 
			    int start_label_idx,
			    int stack_end_idx, int *expr_end_idx) {
  static char exprbuf[MAXMSG];
  int j;

  strcpy (exprbuf, M_NAME(messages[start_label_idx]));
  for (j = start_label_idx - 1; j > stack_end_idx; j--) {
    if (M_ISSPACE(messages[j]))
      continue;
    if ((M_TOK(messages[j]) == LABEL) ||
	(M_TOK(messages[j]) == PERIOD) ||
	(M_TOK(messages[j]) == DEREF))
      strcat (exprbuf, M_NAME(messages[j]));
    else
      break;
  }
  *expr_end_idx = j;
  return exprbuf;
}
