import java.io.*;
import java.lang.*;
import java.util.*;

/** MacroUse discovers how names are used in source files. MacroUse
 * generates an expansion template that can be used to create a flat
 * header file.
 * 
 * MacroUse inspects the input files as streams of tokens, looking for
 * identifiers with matching prefixes. When such identifier is found,
 * MacroUse parses its argument list, if any, and determines how many
 * arguments were passed to the identifier. In doing that it properly
 * descends into nested sub-forms, tracking name use in
 * subexpressions. For example, expression foo(1, bar((int)2), buz(),
 * boo) is parsed as a call to foo with arity 4, a call to bar with
 * arity 1, a call to buz with arity 0 and an arg-less use of name
 * boo.
 *
 * Suppose MacroUse discovers that macro M was used with arity 2. It
 * emits
 * 
 *    JTC_DEFMACRO_M(a1, a2) M(a1, a2)
 * 
 * Suppose this line macroexpands to 
 * 
 *    JTC_DEFMACRO_M(a1, a2) (a1 + a2)
 * 
 * Then a flattened macro-definition for M can be produced by
 * replacing "JTC_DEFMACRO_" with "#define ":
 * 
 *    #define M(a1, a2) (a1 + a2)
 *
 * @see java.io.StreamTokenizer
 * @author Dmitry Nizhegorodov */

public class MacroUse {

  static Hashtable names = new Hashtable();
  static String[] prefixes;

  /** To run:  java MacroUse prefix+ file+
   * Example:  java MacroUse EO JTC_ src/j2c/*.h
   */
  public static void main(String argv[]) {
    Stack prefixBag = new Stack();
    Stack fileBag = new Stack();
    for (int i = 0; i < argv.length; i++) {
      String arg = argv[i];
      if (arg.indexOf('.') > -1) 
        fileBag.push(arg);
      else
        prefixBag.push(arg);
    }

    prefixes = new String[prefixBag.size()];
    prefixBag.copyInto(prefixes);

    Enumeration e = fileBag.elements();

    while(e.hasMoreElements()) {
      new MacroUse((String)e.nextElement()).parse();
    }

    e = names.keys();
  }

  static void emit (String signature) {
    System.out.println("JTC_DEFMACRO_" + signature + " " + signature);
  }

  // example: JVMC_GET_ARG_EOSB8(a1, a2, a3)
  static String makeSignature (String name, int arity) {
    StringBuffer args = new StringBuffer(name).append("(");
    if (arity == 0) args.append(")");
    else {
      for (int i = 1; i <= arity; i++) {
        args
          .append("a")
          .append(Integer.toString(i))
          .append((i == arity) ? ")" : ", ");
      }
    }
    return args.toString();
  }

  StreamTokenizer source;
  InputStream in;

  MacroUse (String file) {
    try {
      in = new FileInputStream(file);      
      source = new StreamTokenizer(in);
      source.wordChars('_', '_');
      source.wordChars('_', '_');
      source.whitespaceChars('\\', '\\');
    } catch (Exception e) {
      System.err.print("/*" + e + "*/");
    }
  }

  static boolean startsWith (String string) {
    for (int i = 0; i < prefixes.length; i++)
      if (string.startsWith(prefixes[i]))
        return true;
    return false;
  }

  void parse () {
    if (source != null) {
      parseParenForm();
      try {in.close();} catch (Exception e) {}
    }
  }

  void parseMacroArgs (String macro) {
    if (names.get(macro) != null) return;
    try {
      int token = source.nextToken();
      if (token == '(') {
        int arity = parseParenForm();
        names.put(macro, new Integer(arity));
        emit(makeSignature(macro, arity));
      }
      else {
        source.pushBack();
        names.put(macro, macro);
        emit(macro);
      }
    } catch (Exception e) {}
  }

  int parseParenForm () {
    int arity = 1;
    try {
      for (int counter = 0;; counter++) {
        int token = source.nextToken();
        switch (token) {
        case ')': case StreamTokenizer.TT_EOF: 
          return (counter == 0) ? 0 : arity;
        case ',': arity++; break;
        case '(': parseParenForm(); break;
        case StreamTokenizer.TT_WORD:
          if (startsWith(source.sval)) {
            parseMacroArgs(source.sval);
          }
          break;
        default:
        }
      }
    } catch (Exception e) {}
    return arity;
  }

}
