//
// Copyright (C) 1991 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: MBN 06/05/89 -- Initial design and implementation.
// Changed: MJF 05/07/90 -- Fixed keyargs in constructors and member functions.
// Changed: MJF 05/10/90 -- Redid scan of keyword arguments -fixing lots.
//
// The EXPAND_KEY defmacro implements  the expansion for the use of
// a C++ function which was defined with keyword arguments by specifying
// KEYARGS before the funtions defintions type name and argument list.
// 
// Use of KEYARGS in a function definition causes the expansion of a "#pragma
// defmacro" statement for EXPAND_KEY on the specific function name.
// For example, 
//
//   KEYARGS Boolean foo (int size, KEY: int low = 0, high = MAX_VAL) { ... }
// expands:
//   #pragma defmacro foo <cool/expand_key> delimiter=)
//                        condition=( 5 foo size , low = 0 , high = MAX_VAL
// so that:
//  foo (11, low = 10)
// expands to
//  foo (11, 10, MAX_VAL)
//
// This file implements the expansion of calling a keyargs function (such as,
// foo with argument (such as low=10)
//
// In this example, main is passed:
// argv[0]  cool/expand_key
// argv[1]  5            <-- the index in argv to the first keyarg
// argv[2]  foo
// argv[3]  size
// argv[4]  ,
// argv[5]  low          
// argv[6]  =
// argv[7]  0
// argv[8]  ,
// argv[9]  high
// argv[10] =
// argv[11] MAX_VAL
//

#include <cool/String.h>

#if defined(DOS)
extern "C" {
#include <ctype.h>
#include <stdlib.h>	// defines exit() and abort()
}
#else
#include <ctype.h>
#include <stdlib.h>	// defines exit() and abort()
#endif

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

//
// Skip whitespace characters, returning the next character
//
int
skipws(char c)
{
  while (isspace(c))
    cin.get(c);
  return(c);
}

//
// Copy the next alphanumeric token to the end of string.
// The argument, c, is the first character.
// Returns the next non-whitespace character.
// Dies if at least one character isn't copied.
//
int gettoken (char c, String& string) {
  c = skipws (c);
  while (isalnum (c) || (c == '$') || (c == '_')) {
    string += c;
    cin.get (c);
  }
  return (skipws(c));
}

#define MAX_ARGS 100
  String arg_list[MAX_ARGS];		// Pointer to a list of argument names
  String val_list[MAX_ARGS];		// Pointer to a list of argument value

void main (int argc, char* argv[]) {
  int arg_count = 0;			// function argument number
  int first_key = atoi (argv[1]);	// Index to first key arg
  int i, n;				// Temp indices of arglist
  char c;				// Current character
  String fname;				// Function name
  String farg;				// Function arg tokens
  
  c = gettoken (' ', fname);		// Get function name
  if (c != '(') {			// If no open paren
    cout << fname << c;			// No function call, so output name
    exit (0);				// and exit
  }
      
  // Scan expand_key command line arguments
  // Start at 3rd position skipping  macro name, key count and function name
  for (n = 3, arg_count = 0; n < argc; n++, arg_count++) {
    if (n == first_key)			// If first KEY arg
      first_key = arg_count;		// Make index relative to arg_list[]

    arg_list[arg_count] = argv[n++];	// Save arg name

    if (n > first_key && n < argc && strcmp (argv[n], "=")) {
      fprintf (stderr,		// KEY arg has no default value
	    "EXPAND_KEY: Syntax error, missing '=' after argument '%s'\n",
	     argv[n-1]);
	exit (1);
      }

    if (!strcmp (argv[n], "=")) {	
      val_list[arg_count] = argv[++n];	// Save default value
      n++;				// Move past '=default'
    } else
    if (n < argc && strcmp (argv[n], ",")) { // Comma should be next
      fprintf (stderr,			
	     "EXPAND_KEY: Syntax error, missing ',' after argument '%s'\n",
	     argv[n-1]);
      exit (1);
    }
  }

  // Scan function call statement
  for (n = 0; n < arg_count; n++) {
    farg.clear();
    c = gettoken (' ', farg);			// Get an argument 

    if (c == ',' || c == ')') {			// A regular argument
      if (farg != "") {
	if (n >= first_key) {			// Should be a keyword argument
	  fprintf (stderr,			
		   "EXPAND_KEY: Syntax error, missing '=' after keyword argument '%s'\n",
		   (char*) farg);
	  exit (1);
	}
	val_list[n] = farg;			// Save the argument value
      }
      if (c == ',') continue;
    }

    if (c == '=') {				// A keyword argument 
      for (i = 0; i < arg_count; i++)	// Look for keyword name
	if (!strcmp(farg, arg_list[i])) break;	

      if (i < first_key || i > arg_count) {
	fprintf (stderr,			
		 "EXPAND_KEY: '%s' is not a valid keyword argument name\n",
		 (char*) farg);
	exit (1);
      }

      farg.clear();
      c = gettoken (' ', farg);

      if (c != ',' && c != ')') {
	fprintf (stderr,			
        "EXPAND_KEY: Syntax error, missing ',' or ')' after keyword value '%s'\n",
		 (char*) farg);
	exit (1);
      }
      val_list[i] = farg;			// Save the new keyword value
      if (c == ',') continue;
    }

    if (c == ')') {				// End of function call
      cout << fname << " (" << val_list[0];	// Expand new function call
      for (int i = 1; i < arg_count; i++) {	// New arglist
	cout << ", " << val_list[i];		
      }
      cout << ")";				// Close of function call
      exit (0);
    }

    fprintf (stderr,			
        "EXPAND_KEY: Syntax error, missing ',' or '=' or ')' after '%s'\n",
	 (char*) farg);
    exit (1);
    }
  
  fprintf (stderr,			
     "EXPAND_KEY: Syntax error, too many arguments after '%s'\n",
      (char*) farg);
  exit (1);
}
