1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
8 * A VERY crude, inefficient Java preprocessor
10 * //#define FOO bar baz -- replace all instances of token FOO with "bar baz"
11 * //#replace foo/bar baz/bop -- DUPLICATE everything between here and //#end,
12 * replacing foo with bar and baz with bop in the *second* copy
13 * //#switch(EXPR) -- switch on strings
18 * Replacements are done on a token basis. Tokens are defined as a
19 * sequence of characters which all belong to a single class. The
20 * two character classes are:
23 * - all other non-whitespace characters
25 public class Preprocessor {
27 static Hashtable replace = new Hashtable();
28 static Hashtable repeatreplace = null;
29 static Vector sinceLastRepeat = null;
31 public static void main(String[] args) throws IOException {
32 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
35 while((s = br.readLine()) != null) {
36 if (sinceLastRepeat != null) sinceLastRepeat.addElement(s);
37 String trimmed = s.trim();
38 if (trimmed.startsWith("//#define ")) {
39 trimmed = trimmed.substring(10).trim();
40 String key = trimmed.substring(0, trimmed.indexOf(' '));
41 String val = trimmed.substring(trimmed.indexOf(' ')).trim();
42 replace.put(key, val);
43 System.out.println(); // preserve line numbers
45 } else if (trimmed.startsWith("//#repeat ")) {
46 StringTokenizer st = new StringTokenizer(trimmed.substring(9), " ");
47 repeatreplace = (Hashtable)replace.clone();
48 while (st.hasMoreTokens()) {
49 String tok = st.nextToken().trim();
50 String key = tok.substring(0, tok.indexOf('/'));
51 String val = tok.substring(tok.indexOf('/') + 1);
52 repeatreplace.put(key, val);
54 sinceLastRepeat = new Vector();
55 System.out.println(); // preserve line numbers
57 } else if (trimmed.startsWith("//#end")) {
58 Hashtable save = replace;
59 replace = repeatreplace;
61 for(int i=0; i<sinceLastRepeat.size() - 1; i++) processLine((String)sinceLastRepeat.elementAt(i), true);
62 sinceLastRepeat = null;
65 } else if (trimmed.startsWith("//#switch")) {
66 String expr = trimmed.substring(trimmed.indexOf('(') + 1, trimmed.lastIndexOf(')'));
67 System.out.println("final String neverUseThis = (String)("+expr+"); switch(neverUseThis.length()) {");
68 Hashtable[] byLength = new Hashtable[255];
70 for(trimmed = br.readLine().trim(); !trimmed.startsWith("//#end"); trimmed = br.readLine().trim()) {
72 if (trimmed.startsWith("case ")) {
73 trimmed = trimmed.substring(trimmed.indexOf('\"') + 1);
74 key = trimmed.substring(0, trimmed.indexOf('\"'));
75 Hashtable thisCase = (Hashtable)byLength[key.length()];
76 if (thisCase == null) byLength[key.length()] = thisCase = new Hashtable();
77 thisCase.put(key, "");
78 trimmed = trimmed.substring(trimmed.indexOf('\"') + 1);
79 trimmed = trimmed.substring(trimmed.indexOf(':') + 1);
82 Hashtable hash = byLength[key.length()];
83 hash.put(key, (String)hash.get(key) + trimmed + "\n");
85 else System.out.println(trimmed);
88 for(int i=0; i<255; i++) {
89 if (byLength[i] == null) continue;
90 System.out.println("case " + i + ": { switch(neverUseThis.charAt(0)) {");
91 buildTrie("", byLength[i]);
92 System.out.println("}; break; }");
94 System.out.println("} //switch");
97 processLine(s, false);
104 static void buildTrie(String prefix, Hashtable cases) {
105 Enumeration caseKeys = cases.keys();
106 Vec keys = new Vec();
107 while(caseKeys.hasMoreElements()) keys.addElement(caseKeys.nextElement());
108 keys.sort(new Vec.CompareFunc() { public int compare(Object a, Object b) {
109 return ((String)a).compareTo((String)b);
112 for(int i=0; i<keys.size(); i++) {
113 if (!((String)keys.elementAt(i)).startsWith(prefix)) continue;
114 String prefixPlusOne = ((String)keys.elementAt(i)).substring(0, prefix.length() + 1);
115 if (i<keys.size()-1 && prefixPlusOne.equals((((String)keys.elementAt(i + 1)).substring(0, prefix.length() + 1)))) {
116 System.out.println("case \'" + prefixPlusOne.charAt(prefixPlusOne.length() - 1) + "\': {");
117 System.out.println("switch(neverUseThis.charAt(" + prefix.length() + ")) {");
118 buildTrie(prefixPlusOne, cases);
119 System.out.println("} break; }");
120 while(i<keys.size() && prefixPlusOne.equals(((String)keys.elementAt(i)).substring(0, prefix.length() + 1))) i++;
121 if (i<keys.size()) { i--; continue; }
123 System.out.println("case \'" + prefixPlusOne.charAt(prefixPlusOne.length() - 1) + "\':");
124 String code = (String)cases.get(keys.elementAt(i));
125 code = code.substring(0, code.length() - 1);
126 String key = (String)keys.elementAt(i);
127 System.out.println("if (\""+key+"\".equals(neverUseThis)) { " + code + " } break; ");
132 static void processLine(String s, boolean deleteLineEndings) throws IOException {
133 if (deleteLineEndings && s.indexOf("//") != -1) s = s.substring(0, s.indexOf("//"));
134 for(int i=0; i<s.length(); i++) {
135 char c = s.charAt(i);
136 if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_') {
141 for(j = i; j < s.length(); j++) {
143 if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_') break;
145 String tok = s.substring(i, j);
146 String val = (String)replace.get(tok);
147 if (val != null) System.out.print(val);
148 else System.out.print(tok);
151 if (!deleteLineEndings) System.out.println();