/*


 Copyright (C) 1990 Texas Instruments Incorporated.

 Permission is granted to any individual or institution to use, copy, modify,
 and distribute this software, provided that this complete copyright and
 permission notice is maintained, intact, in all copies and supporting
 documentation.

 Texas Instruments Incorporated provides this software "as is" without
 express or implied warranty.


 *
 * Created: MJF 07/05/89 -- Initial design and implementation.
 * Changed: MJF 02/20/90 -- Added group name support.
 * Changed: MJF 03/01/91 -- Added throw code written by grahamd@otc.otca.oz.au
 *
 * The C++ exception handling defmacro
 *
 * To use, declare:
 *     #pragma defmacro EXCEPTION "exception" exception_g format_msg err_msg
 *                                hprintf delimiter=)
 *
 * where: exception_g is the name of the global variable to use as a temporary,
 *        format_msg is the name of the slot in the exception class, err_msg
 *        is the name of the text package for error message strings, and 
 *        hprintf is  the name of the function which returns a formatted string
 *        from the heap.
*/

/*
 * EXCEPTION is a cpp macro used to construct an Exception object.
 * EXCEPTION has the following argument options:
 *
 * EXCEPTION(excp_name [, group_names] [, format_string] [rest_args])
 *
 * where   group_names := group_name [, group_names ]
 *           rest_args := rest_arg | [, rest_arg]
 *            rest_arg := format_arg | key_value_slot_arg
 *          format_arg := name
 *  key_value_slot_arg := name = value
 *       format_string := "string"
 *          group_name := SYM(name)
 *          excp_name  := name
 *
 * In general:
 *
 * EXCEPTION(excp_name, SYM(group_name1), SYM(group_name2), format_string, 
 *           key1=val1, arg1, key2=val2, arg2);
 *
 * would generate:
 *
 * (Exception_g = new excp_name(2, SYM(group_name1), SYM(group_name2)), 
 *  Exception_g->format_msg = hprintf(ERR_MSG(format_string), 
 *                                    val1, arg1, val2, arg2),
 *  Exception_g->key1 = val1, 
 *  Exception_g->key2 = val2, 
 *  Exception_g);
 *
 * Note that the value of the key-val-slot-args and the non-key-val-slot-args
 * are all gathered as format args for the hprintf function.  Also note that
 * the key-val-slot-args and the non-key-args are intermixed.  It mainly
 * depends on the format_string requirements.
 *
 * More specific examples using EXCEPTION:
 *
 * (1) EXCEPTION with group_name, error type and format string.
 *
 * EXCEPTION(Error, Serios_Error, "Serious problem here.");
 *
 * would generate:
 *
 *  (Exception_g = new Error(SYM(Serious_Error)), 
 *   Exception_g->format_msg = hprintf(ERR_MSG("Serious problem here.")),
 *   Exception_g);
 *  
 *
 * (2) EXCEPTION with error type, format string and mixture of slot arguments
 *    and format arguments.
 * 
 * Assume a defined error type, Bad_Argment_Error, with two slots, arg_name and 
 * arg_value.
 *
 * Class Bad_Argument_Error : public Fatal {
 * public:
 *  char* arg_name;
 *  int arg_value;
 *  Bad_Argment_Error();
 * }
 *
 * then...
 *
 * EXCEPTION(Bad_Argument_Error, 
 *           "Arg %s has value %d which is out of range for %s."
 *           arg_name="foo", arg_value=x, vec1);
 *
 * would generate...
 *
 *  (Exception_g = new Bad_Argument_Error(),
 *   Exception_g->arg_name = "foo",
 *   Exception_g->arg_value = x,
 *   Exception_g->format_msg =
 *     hprintf(ERR_MSG("Arg %s has value %d which is out of range for %s."),
 *             "foo", x, vec1),
 *   Exception_g);
 *
 * (3) EXCEPTION with error type, and slot arguments (but no format message).
 *
 * Assume a defined error type, Out_Of_Range, with two slots, value and vector.
 * 
 * Class Out_of_Range : public Fatal {
 * public:
 *  int value;
 *  Generic vector;
 *  Out_of_Range() {
 *    format_msg = "Value of %d is out of range for Vector %s."
 *  }
 *  void report(ostream& os) {
 *    os << this->message_prefix();
 *    os << form(format_msg, value, vector);
 *  }
 * };
 *  then...
 *
 *  EXCEPTION(Out_of_Range, value=n, vector=v1);
 *
 *  would generate...
 *
 *  (Exception_g = new Out_of_Range(),
 *   Exception_g->value =  n,
 *   Exception_g->vector = v1,
 *   Exception_g);
 *
*/

#include "defmacio.h"
#include "macro.h"

#define MAXBUF   512
#define MAXGROUP 32

exception(argc, argv)
 int argc;
 char* argv[];
{
  char* global_excp;	/* Exception_g */
  char* format_msg;	/* exception format message slot name */
  char* group_fcn;	/* function which updates the group slot */
  char* err_pkg;        /* text package name for error message strings */
  char* format_fcn;	/* hprintf (heap print) function */
  
  char buffer[MAXBUF];

  char* macname;	/* EXCEPTION is the macro name */
  char* excp_name;      /* the name of the exception type */
  char* group_names[MAXGROUP];
  int group_count;
  Arg* argp = NULL;	/* always points to first arg in macro */
  Arg* excp_args;	/* EXCEPTION macro arg pointer */
  Arg* fs_args;		/* pointer to format or slot arguments*/

  if(argc <= 3) {
    fprintf(stderr, "Not enough arguments to #pragma defmacro exception\n");
    return(1);
  }
  global_excp = argv[1];
  format_msg = argv[2];
  group_fcn = argv[3];
  err_pkg = argv[4];
  format_fcn = argv[5];

  copytoken(buffer);		/* Grab the macro name */
  macname = savestring(buffer);

  /* Gather all EXCEPTION arguments */
  if((argp = macro_args(macname)) == &arg_error) {  
    fprintf(stderr, ".\n");
    return(1);
  }

  /* EXCEPTION takes at least one argument */
  if((excp_args = argp) == NULL) {
    fprintf(stderr, "EXCEPTION: No arguments specified.");
    return(1);
  }

  /* first EXCEPTION argument should be name of exception type */
  excp_name = excp_args->name;
  /* call exception constructor */
  sprintf(buffer, "\n(%s = new %s()", global_excp, excp_name);
  puts(buffer);

  group_count = 0;
  /* the next EXCEPTION argument might be the optional group names */
  for(excp_args = excp_args->next;
      excp_args !=NULL && excp_args->name[0] != '"' 
      && *excp_args->value == EOS && excp_args->value != NULL;
      excp_args = excp_args->next, group_count++) {
    group_names[group_count] = excp_args->name;
  }

  if (group_count == 1) {
    /* only one group name */
    /* output: global_excp->group_fcn(group_name) */
    sprintf(buffer, ",\n%s->%s(%s)", global_excp, group_fcn,
                                     group_names[--group_count]);
    puts(buffer);
  }

  if (group_count != 0) {
    /* more than one group name */
    /* output: global_excp->group_fcn(group_count, group_name[0], ...,
                                      ..., group_name[n-1])              */
    sprintf(buffer, ",\n%s->%s(%d", 
                    global_excp, group_fcn, group_count);
    puts(buffer);
    /* output all of the group names */
    while (group_count != 0) {
      sprintf(buffer, ", %s", group_names[--group_count]);
      puts(buffer);
    }
    puts(")");   /* closing paren on group_fcn */
  }

  /* the next EXCEPTION argument might be the optional format string */
  if ((excp_args  != NULL) && (excp_args->name[0] == '"')) {
    puts(",\n");
    /* output: global_excp->format_msg = hprintf(err_msg(format_string) ...   */
    sprintf(buffer, "%s->%s = %s(%s(%s)", global_excp, format_msg, format_fcn,
	                                  err_pkg, excp_args->name);
    puts(buffer);
    /* output: format_arg1, format_arg2, ...)  */
    excp_args = excp_args->next;   /* skip format string */
    for(fs_args=excp_args;	   /* now output each format arg */
        fs_args != NULL;
        fs_args=fs_args->next) {
      puts(", ");
      if (*fs_args->value != EOS)  /* check key=value arg */
	puts(fs_args->value);
      else puts(fs_args->name);
    }
    putchar(')');   /* closing paren for format function, hprintf */
  }

  /* the next EXCEPTION argument might be key-value slots */
  for(fs_args = excp_args; fs_args != NULL; fs_args=fs_args->next) {
    if (*fs_args->value != EOS) {
      /* output: global_excp->key = value, */
      puts(",\n");
      sprintf(buffer, "((%s*) %s)->%s = %s", excp_name, global_excp, 
	                                     fs_args->name, fs_args->value);
      puts(buffer);
    }
  }
  
  
  /* last output is the global exception name */
  sprintf(buffer, ",\n(%s*) %s)", excp_name, global_excp);
  puts(buffer);

  for(argp = argp; argp != NULL;) {
    Arg* old = argp;
    argp = argp->next;
    free(old->name);
    if (*old->value) free(old->value);
    free(old);
  }
  free(macname);
  return 0;
}

/*
 * Handle throw translation
 *
 * throw Exception(__LINE__,__FILE__,"error");
 *
 * expands to
 *
 *  (new Exception(__LINE__,__FILE__,"error"), terminate());
 *
 */

int throw (argc, argv)
  int argc;
  char** argv;
{
  char buff[256];
  char* line;
  if (copytoken(buff) == NULL)
    return 1;
  puts("( new ");
  line = scan_token(";");
  puts(line);
  puts(", terminate() );");
  return 0;
}

