5232be5c7b2eeb66b889664cf6ddcdd66b5ec44d
[org.ibex.core.git] / src / org / xwt / util / Preprocessor.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt.util;
3
4 import java.util.*;
5 import java.io.*;
6
7 /**
8  *   A VERY crude, inefficient Java preprocessor
9  *
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
14  *   {
15  *       case "case1":
16  *   }
17  *
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:
21  *
22  *     - [a-zA-Z0-9_]
23  *     - all other non-whitespace characters
24  */
25 public class Preprocessor {
26
27     static Hashtable replace = new Hashtable();
28     static Hashtable repeatreplace = null;
29     static Vector sinceLastRepeat = null;
30
31     public static void main(String[] args) throws IOException {
32         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
33         String s = null;
34
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                 if (trimmed.indexOf('(') >= 0 && trimmed.indexOf('(') < trimmed.indexOf(' ')) {
41                     JSFunctionMacro fm = new JSFunctionMacro();
42                     String key = trimmed.substring(0, trimmed.indexOf('('));
43                     String unbound = trimmed.substring(trimmed.indexOf('(') + 1, trimmed.indexOf(')'));
44                     if (unbound.indexOf(',') == -1) {
45                         fm.unbound1 = unbound;
46                     } else {
47                         fm.unbound1 = unbound.substring(0, unbound.indexOf(','));
48                         fm.unbound2 = unbound.substring(unbound.indexOf(',') + 1);
49                     }
50                     fm.expression = trimmed.substring(trimmed.indexOf(')')+1).trim();
51                     replace.put(key, fm);
52                 } else {
53                     String key = trimmed.substring(0, trimmed.indexOf(' '));
54                     String val = trimmed.substring(trimmed.indexOf(' ')).trim();
55                     replace.put(key, val);
56                 }
57                 System.out.println(); // preserve line numbers
58                 
59             } else if (trimmed.startsWith("//#repeat ")) {
60                 trimmed = trimmed.substring(9);
61                 while(trimmed.charAt(trimmed.length() - 1) == '\\') {
62                     String s2 = br.readLine().trim();
63                     if (s2.startsWith("//")) s2 = s2.substring(2).trim();
64                     trimmed += s2;
65                     System.out.println();  // preserve line numbers
66                 }
67                 StringTokenizer st = new StringTokenizer(trimmed, " ");
68                 repeatreplace = (Hashtable)replace.clone();
69                 while (st.hasMoreTokens()) {
70                     String tok = st.nextToken().trim();
71                     String key = tok.substring(0, tok.indexOf('/'));
72                     String val = tok.substring(tok.indexOf('/') + 1);
73                     repeatreplace.put(key, val);
74                 }
75                 sinceLastRepeat = new Vector();
76                 System.out.println(); // preserve line numbers
77
78             } else if (trimmed.startsWith("//#end")) {
79                 Hashtable save = replace;
80                 replace = repeatreplace;
81                 System.out.println();
82                 for(int i=0; i<sinceLastRepeat.size() - 1; i++) System.out.print(processLine((String)sinceLastRepeat.elementAt(i), true));
83                 sinceLastRepeat = null;
84                 replace = save;
85
86             } else if (trimmed.startsWith("//#switch")) {
87                 String expr = trimmed.substring(trimmed.indexOf('(') + 1, trimmed.lastIndexOf(')'));
88                 System.out.println("final String neverUseThis = (String)("+expr+"); switch(neverUseThis.length()) {");
89                 Hashtable[] byLength = new Hashtable[255];
90                 String key = null;
91                 String Default = null;
92                 for(trimmed = br.readLine().trim(); !trimmed.startsWith("//#end"); trimmed = br.readLine().trim()) {
93                     if (trimmed.startsWith("default:")) {
94                         Default = processLine(trimmed.substring(8), false);
95                         continue;
96                     }
97                     if (trimmed.startsWith("case ")) {
98                         trimmed = trimmed.substring(trimmed.indexOf('\"') + 1);
99                         key = trimmed.substring(0, trimmed.indexOf('\"'));
100                         Hashtable thisCase = (Hashtable)byLength[key.length()];
101                         if (thisCase == null) byLength[key.length()] = thisCase = new Hashtable();
102                         thisCase.put(key, "");
103                         trimmed = trimmed.substring(trimmed.indexOf('\"') + 1);
104                         trimmed = trimmed.substring(trimmed.indexOf(':') + 1);
105                     }
106                     if (key != null) {
107                         Hashtable hash = byLength[key.length()];
108                         hash.put(key, (String)hash.get(key) + processLine(trimmed, false) + "\n");
109                     } else {
110                         System.out.print(processLine(trimmed, false));
111                     }
112                 }
113
114                 for(int i=0; i<255; i++) {
115                     if (byLength[i] == null) continue;
116                     System.out.println("case " + i + ": { switch(neverUseThis.charAt(0)) {");
117                     buildTrie("", byLength[i]);
118                     System.out.println("}; break; }");
119                 }
120                 if (Default != null) System.out.println("default: { " + Default + " }");
121                 System.out.println("} //switch");
122
123             } else {
124                 System.out.print(processLine(s, false));
125             }
126            
127         }
128
129     }
130
131         static void buildTrie(String prefix, Hashtable cases) {
132             Enumeration caseKeys = cases.keys();
133             Vec keys = new Vec();
134             while(caseKeys.hasMoreElements()) keys.addElement(caseKeys.nextElement());
135             keys.sort(new Vec.CompareFunc() { public int compare(Object a, Object b) {
136                 return ((String)a).compareTo((String)b);
137             } } );
138             
139             for(int i=0; i<keys.size(); i++) {
140                 if (!((String)keys.elementAt(i)).startsWith(prefix)) continue;
141                 String prefixPlusOne = ((String)keys.elementAt(i)).substring(0, prefix.length() + 1);
142                 if (i<keys.size()-1 && prefixPlusOne.equals((((String)keys.elementAt(i + 1)).substring(0, prefix.length() + 1)))) {
143                     System.out.println("case \'" + prefixPlusOne.charAt(prefixPlusOne.length() - 1) + "\': {");
144                     System.out.println("switch(neverUseThis.charAt(" + (prefix.length()+1) + ")) {");
145                     buildTrie(prefixPlusOne, cases);
146                     System.out.println("} break; }");
147                     while(i<keys.size() && prefixPlusOne.equals(((String)keys.elementAt(i)).substring(0, prefix.length() + 1))) i++;
148                     if (i<keys.size()) { i--; continue; }
149                 } else {
150                     System.out.println("case \'" + prefixPlusOne.charAt(prefixPlusOne.length() - 1) + "\':");
151                     String code = (String)cases.get(keys.elementAt(i));
152                     code = code.substring(0, code.length() - 1);
153                     String key = (String)keys.elementAt(i);
154                     System.out.println("if (\""+key+"\".equals(neverUseThis)) { " + code + " } break; ");
155                 }
156             }
157         }
158
159     static String processLine(String s, boolean deleteLineEndings) throws IOException {
160         if (deleteLineEndings && s.indexOf("//") != -1) s = s.substring(0, s.indexOf("//"));
161         String ret = "";
162         for(int i=0; i<s.length(); i++) {
163             char c = s.charAt(i);
164             if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_') {
165                 ret += c;
166                 continue;
167             }
168             int j;
169             for(j = i; j < s.length(); j++) {
170                 c = s.charAt(j);
171                 if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_') break;
172             }
173             String tok = s.substring(i, j);
174             Object val = replace.get(tok);
175             if (val == null) {
176                 ret += tok;
177                 i = j - 1;
178             } else if (val instanceof JSFunctionMacro) {
179                 if (s.charAt(j) != '(') System.err.println("open paren must follow macro binding for macro " + tok);
180                 ret += ((JSFunctionMacro)val).process(s.substring(j+1, s.indexOf(')', j)));
181                 i = s.indexOf(')', j);
182             } else {
183                 ret += val;
184                 i = j - 1;
185             }
186         }
187         if (!deleteLineEndings) ret += "\n";
188         return ret;
189     }
190
191     public static class JSFunctionMacro {
192         public String unbound1 = null;
193         public String unbound2 = null;
194         public String expression = null;
195         public String process(String args) {
196             String bound1 = null;
197             String bound2 = null;
198             if (unbound2 == null) {
199                 bound1 = args;
200                 return expression.replaceAll(unbound1, bound1);
201             } else {
202                 bound1 = args.substring(0, args.indexOf(','));
203                 bound2 = args.substring(args.indexOf(',') + 1);
204                 return (expression.replaceAll(unbound1, bound1).replaceAll(unbound2, bound2));
205             }
206         }
207     }
208 }
209