/*


 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 08/20/90 -- Initial design and implementation.
 *
 * The TYPE_CASE defmacro
 *
 * To use, declare:
 *     #pragma defmacro TYPE_CASE "type_case" delimiter=}
 *
 * TYPECASE (generic_ptr) { case_statements }
 * case_statements = case sym_name : statements [case_statements]
 * 
 * THE TYPE_CASE macro is analogous to the C++ switch statment 
 * It gathers all possible cases and allows the user to symbolically 
 * dispatch on the type  of object  represented  by  the case statements.
 *
 *     Generic* g;
 *     TYPE_CASE (g) {
 *       case Vector:                // If the object is a vector
 *         ....                      // Do something for Vector
 *         break;
 *       case List:                  // If the object is a list
 *         ....                      // Do something for List
 *         break;
 *       default:                    // Else do the rest
 *          ....
 *     }
 *
 * expands to:
 *
 *     Generic* g;
 *     static Symbol* switch_symbols_g[3] = {SYM(Vector), SYM(List), NULL};
 *     switch (g->select_type_of(switch_symbols_g)) {
 *       case 0:                     // If the object is a vector
 *         ....                      // Do something for Vector
 *         break;
 *       case 1:                     // If the object is a list
 *         ....                      // Do something for List
 *         break;
 *       default:                    // Else do the rest
 *          ....
 *     }
 *   
*/

#include "defmacio.h"

#define MAXCASE  64
#define MAXBUF   512

typedef struct case_body {
  int default_case;
  char* body;
} Case_Body;


typecase(argc, argv)
 int argc;
 char* argv[];
{
  char junk[MAXBUF];
  char c;
  char* macname;		/* TYPE_CASE is the macro name */
  char* genptr;			/* the name of a generic pointer */
  char* name;
  char *str1, *str2;
  Case_Body  cbody[30];
  int ncases, i;
  char* syms[30];		/* Contains all symbols in TYPE_CASE */
  int nsyms;
  char body[512];		/* Contains body of each case  */
  int done;

  if(copytoken(junk) == NULL)	/* Skip macro name */
    return(1);
  macname = savestring(junk);

  c = skip_blanks();
  if(c != '(') {
    fprintf(stderr, "%s: '%c' found instead of '('\n", macname, c);
    return 1;
  }

  done = 0;
  nsyms = ncases = 0;
  name = scan_next(' ');
  c = getchar();
  if (c != ')') {
    fprintf(stderr, "%s: '%c' found instead of ')' after %s\n", 
	    macname, c,  name);
    return 1;
  }
  genptr = savestring(name);

  c = skip_blanks();
  if (c != '{') {
    fprintf(stderr, "%s: '%c' found instead of '{' after %s\n", 
	    macname, c,  genptr);
    return 1;
  }

  name = scan_list(' ');

  for (i=0;i< MAXCASE;i++) {
    str2 = body;
    if (name == NULL) return 1;


    if(!strcmp(name, "case")) {		  /* case Vector: */
      cbody[i].default_case = 0;
      cbody[i].body = 0;
      name = scan_next(' ');
      c = skip_blanks();
      if (c != ':') {
	fprintf(stderr, "%s: Missing ':' after '%s'\n", 
		macname, name);
	return 1;
      }
      syms[nsyms++] = savestring(name);
    } else if (!strcmp(name, "default")) { /* default: */
      c = getchar();
      if (c != ':') {
	fprintf(stderr, "%s: Missing ':' after '%s'\n", 
		macname, name);
	return 1;
      }
      cbody[i].default_case = 1;
      cbody[i].body = 0;
    } else  {
      str1 = name;
      while (*str1 != '\0')  *str2++ = *str1++;
    }

    /* collect body of case */
    for (;;) {
      c = getchar();
      while (!isalnum(c) && c !=EOF) {
	*str2++ = c;
	c = getchar();
      }
      if (c == EOF) {			  /* TYPE_CASE is done */
	done = 1;
	break;
      } else unget();
      
      name = scan_list(' ');
      if (!strcmp(name,"case") || !strcmp(name,"default")) break;
      
      str1 = name;
      while (*str1 != '\0') *str2++ = *str1++;
    }
    ncases++;
   *str2 = EOS;
    cbody[i].body = savestring(body);
    if (done) break;
  }

  /* expand TYPE_CASE macro*/

  sprintf(junk, "\n{\nstatic Symbol* switch_symbols_%s[%d] = {", 
	  genptr, nsyms+1);
  puts(junk);				  /* start of SYM list */
  for (i = 0; i < nsyms; i++) {		  /* output each SYM name */
      sprintf(junk,"SYM(%s),", syms[i]);
      puts(junk);
  }
  puts("NULL};\n");			  /* end of SYM list */
  
  sprintf(junk, "switch(%s->select_type_of(switch_symbols_%s)) {\n",
	  genptr, genptr);
  puts(junk);				  /* start of switch statement */



  for (i = 0; i< ncases; i++) {		  /* output body for each case */
    if (cbody[i].default_case)
     sprintf(junk, "default:", i);
    else
     sprintf(junk, "case %d:", i);
    puts(junk);
    if (cbody[i].body)
      puts(cbody[i].body);
  }
  puts ("\n} \n");			  /* end of switch statement */
  free(macname);
  return 0;
}




