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; } }