2003/11/05 06:22:11
[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                 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
44                 
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);
53                 }
54                 sinceLastRepeat = new Vector();
55                 System.out.println(); // preserve line numbers
56
57             } else if (trimmed.startsWith("//#end")) {
58                 Hashtable save = replace;
59                 replace = repeatreplace;
60                 System.out.println();
61                 for(int i=0; i<sinceLastRepeat.size() - 1; i++) processLine((String)sinceLastRepeat.elementAt(i), true);
62                 sinceLastRepeat = null;
63                 replace = save;
64
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];
69                 String key = null;
70                 for(trimmed = br.readLine().trim(); !trimmed.startsWith("//#end"); trimmed = br.readLine().trim()) {
71                     // FIXME: default
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);
80                     }
81                     if (key != null) {
82                         Hashtable hash = byLength[key.length()];
83                         hash.put(key, (String)hash.get(key) + trimmed + "\n");
84                     }
85                     else System.out.println(trimmed);
86                 }
87
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; }");
93                 }
94                 System.out.println("} //switch");
95
96             } else {
97                 processLine(s, false);
98             }
99            
100         }
101
102     }
103
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);
110             } } );
111             
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; }
122                 } else {
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; ");
128                 }
129             }
130         }
131
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 != '_') {
137                 System.out.print(c);
138                 continue;
139             }
140             int j;
141             for(j = i; j < s.length(); j++) {
142                 c = s.charAt(j);
143                 if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_') break;
144             }
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);
149             i = j - 1;
150         }
151         if (!deleteLineEndings) System.out.println();
152     }
153 }
154