// Copyright 2000-2005 the Contributors, as shown in the revision logs. // Licensed under the Apache Public Source License 2.0 ("the License"). // You may not use this file except in compliance with the License. package org.ibex.tool; //import gnu.regexp.*; import java.util.*; import java.io.*; /** * A VERY crude, inefficient Java preprocessor * * //#define FOO bar baz -- replace all instances of token FOO with "bar baz" * //#replace foo/bar baz/bop -- DUPLICATE everything between here and //#end, * replacing foo with bar and baz with bop in the *second* copy * //#switch(EXPR) -- switch on strings * case "case1": * //#end * * //#ifdef FOO -- includes contents if FOO passed as define to preprocessor * [code] * //#else * [code run if !FOO] * //#endif * * Replacements are done on a token basis. Tokens are defined as a * sequence of characters which all belong to a single class. The * two character classes are: * * - [a-zA-Z0-9_] * - all other non-whitespace characters * * Preprocessor makes use of several optional system properties: * * - ibex.tool.preprocessor.define * - ibex.tool.preprocessor.inputdir * - ibex.tool.preprocessor.outputdir * * @author adam@ibex.org, crawshaw@ibex.org */ public class Preprocessor { public static String replaceAll(String source, String regexp, String replaceWith) { return source.replaceAll(regexp, replaceWith); /* try { RE re = new RE(regexp, 0, RESyntax.RE_SYNTAX_PERL5); return (String)re.substituteAll(source, replaceWith); } catch (Exception e) { e.printStackTrace(); return null; } */ } public static void main(String[] args) throws Exception { List defs = new ArrayList(); String define = System.getProperty("ibex.tool.preprocessor.define"); if (define != null) { StringTokenizer st = new StringTokenizer(define.toUpperCase(), ","); while (st.hasMoreTokens()) defs.add(st.nextToken().trim()); } String inputdir = System.getProperty("ibex.tool.preprocessor.inputdir"); if (inputdir == null) inputdir = "src/"; String outputdir = System.getProperty("ibex.tool.preprocessor.outputdir"); if (outputdir == null) outputdir = "build/java/"; if (args.length == 0) { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // process stdin to stdout Preprocessor cc = new Preprocessor(br, bw, defs); Vector err = cc.process(); bw.flush(); // handle errors boolean errors = false; for (int i=0; i < err.size(); i++) { if (err.get(i) instanceof Error) errors = true; System.err.println(err.get(i)); } if (errors) throw new Exception(); } else { for(int i=0; i source.lastModified()) continue; System.err.println("preprocessing " + args[i]); new File(target.getParent()).mkdirs(); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(source))); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(target))); Preprocessor cc = new Preprocessor(br, bw, defs); Vector err = cc.process(); bw.flush(); boolean errors = false; for (int j=0; j < err.size(); j++) { if (err.get(j) instanceof Error) errors = true; System.err.println(err.get(j)); } if (errors) System.exit(-1); } } } private Reader r; private Writer w; private LineNumberReader in; private PrintWriter out; private Hashtable replace = new Hashtable(); private Hashtable[] repeatreplaces = null; private Vector sinceLastRepeat = null; private Vector err = new Vector(); private List defs; private int enumSwitch = 0; // number appended to variable used in switch implementation public Preprocessor(Reader reader, Writer writer, List d) { setReader(reader); setWriter(writer); defs = d; } public void setReader(Reader reader) { r = reader; if (r != null) in = new LineNumberReader(r); } public Reader getReader() { return r; } public void setWriter(Writer writer) { w = writer; if (w != null) out = new PrintWriter(w); } public Writer getWriter() { return w; } /** process data from reader, write to writer, return vector of errors */ public Vector process() throws IOException { err.clear(); String s = null; PROCESS: while((s = in.readLine()) != null) { if (sinceLastRepeat != null) sinceLastRepeat.addElement(s); String trimmed = s.trim(); if (trimmed.startsWith("//#define ")) { if (trimmed.length() == 9 || trimmed.charAt(9) != ' ') { err.add(new Error("#define badly formed, ignored")); continue PROCESS; } int keyStart = indexOfNotWS(trimmed, 9); if (keyStart == -1) { err.add(new Error("#define requires KEY")); continue PROCESS; } int keyEnd = indexOfWS(trimmed, keyStart); int macroStart = trimmed.indexOf('('); int macroEnd = trimmed.indexOf(')'); if (macroStart > keyEnd) { // no macro is defined, just make sure something dumb like KEYNA)ME hasn't been done if (macroEnd < keyEnd) { err.add(new Error("#define key contains invalid char: ')'")); continue PROCESS; } macroStart = macroEnd = -1; } if (macroStart == 0) { err.add(new Error("#define macro requires name")); continue PROCESS; } else if (macroStart > 0) { if (macroStart > macroEnd) { err.add(new Error("#define macro badly formed")); continue PROCESS; } if (macroStart+1 == macroEnd) { err.add(new Error("#define macro requires property name")); continue PROCESS; } JSFunctionMacro fm = new JSFunctionMacro(); String key = trimmed.substring(keyStart, macroStart); String unbound = trimmed.substring(macroStart +1, macroEnd); int unboundDiv = unbound.indexOf(','); if (unboundDiv == -1) { fm.unbound1 = unbound; } else { fm.unbound1 = unbound.substring(0, unboundDiv); fm.unbound2 = unbound.substring(unboundDiv +1); if (fm.unbound1.length() == 0) { err.add(new Error("#define macro property 1 requires name")); continue PROCESS; } if (fm.unbound2.length() == 0) { err.add(new Error("#define macro property 1 requires name")); continue PROCESS; } } fm.expression = trimmed.substring(keyEnd).trim(); replace.put(key, fm); } else { String key = trimmed.substring(keyStart, keyEnd); String val = trimmed.substring(keyEnd).trim(); replace.put(key, val); } out.print("\n"); // preserve line numbers } else if (trimmed.startsWith("//#repeat ")) { trimmed = trimmed.substring(9); while(trimmed.charAt(trimmed.length() - 1) == '\\') { String s2 = in.readLine().trim(); if (s2.startsWith("//")) s2 = s2.substring(2).trim(); trimmed = trimmed.substring(0, trimmed.length() - 1) + " " + s2; out.print("\n"); // preserve line numbers } StringTokenizer st = new StringTokenizer(trimmed, " "); repeatreplaces = null; while (st.hasMoreTokens()) { String tok = st.nextToken().trim(); String key = tok.substring(0, tok.indexOf('/')); String vals = tok.substring(tok.indexOf('/') + 1); StringTokenizer st2 = new StringTokenizer(vals,"/"); if(repeatreplaces == null) { repeatreplaces = new Hashtable[st2.countTokens()]; for(int i=0;i= store.length - 1) grow(); store[size++] = o; } public Object peek() { return lastElement(); } public Object elementAt(int i) { return store[i]; } public Object lastElement() { if (size == 0) return null; return store[size - 1]; } public void push(Object o) { addElement(o); } public Object pop() { Object ret = lastElement(); if (size > 0) store[size--] = null; return ret; } public int size() { return size; } public void setSize(int newSize) { if (newSize < 0) throw new RuntimeException("tried to set size to negative value"); if (newSize > store.length) grow(newSize * 2); if (newSize < size) for(int i=newSize; i= size || i < 0) throw new RuntimeException("tried to remove an element outside the vector's limits"); for(int j=i; j= size) setSize(i); store[i] = o; } public void removeElement(Object o) { int idx = indexOf(o); if (idx != -1) removeElementAt(idx); } public void insertElementAt(Object o, int at) { if (size == store.length) grow(); for(int i=size; i>at; i--) store[i] = store[i-1]; store[at] = o; size++; } public interface CompareFunc { public int compare(Object a, Object b); } public void sort(CompareFunc c) { sort(this, null, c, 0, size-1); } public static void sort(MyVec a, MyVec b, CompareFunc c) { if (b != null && a.size != b.size) throw new IllegalArgumentException("MyVec a and b must be of equal size"); sort(a, b, c, 0, a.size-1); } private static final void sort(MyVec a, MyVec b, CompareFunc c, int start, int end) { Object tmpa, tmpb = null; if(start >= end) return; if(end-start <= 6) { for(int i=start+1;i<=end;i++) { tmpa = a.store[i]; if (b != null) tmpb = b.store[i]; int j; for(j=i-1;j>=start;j--) { if(c.compare(a.store[j],tmpa) <= 0) break; a.store[j+1] = a.store[j]; if (b != null) b.store[j+1] = b.store[j]; } a.store[j+1] = tmpa; if (b != null) b.store[j+1] = tmpb; } return; } Object pivot = a.store[end]; int lo = start - 1; int hi = end; do { while(c.compare(a.store[++lo],pivot) < 0) { } while((hi > lo) && c.compare(a.store[--hi],pivot) > 0) { } swap(a, lo,hi); if (b != null) swap(b, lo,hi); } while(lo < hi); swap(a, lo,end); if (b != null) swap(b, lo,end); sort(a, b, c, start, lo-1); sort(a, b, c, lo+1, end); } private static final void swap(MyVec vec, int a, int b) { if(a != b) { Object tmp = vec.store[a]; vec.store[a] = vec.store[b]; vec.store[b] = tmp; } } } private void buildTrie(String prefix, Hashtable cases) { Enumeration caseKeys = cases.keys(); MyVec keys = new MyVec(); while(caseKeys.hasMoreElements()) keys.addElement(caseKeys.nextElement()); keys.sort(new MyVec.CompareFunc() { public int compare(Object a, Object b) { return ((String)a).compareTo((String)b); } } ); for(int i=0; i= s.length()) return -1; for (; beginIndex < s.length(); beginIndex++) { if (s.charAt(beginIndex) == ' ') return beginIndex; } return s.length(); } private static int indexOfNotWS(String s) { return indexOfWS(s, 0); } private static int indexOfNotWS(String s, int beginIndex) { if (s == null || beginIndex >= s.length()) return -1; for (; beginIndex < s.length(); beginIndex++) { if (s.charAt(beginIndex) != ' ') return beginIndex; } return -1; } public class Warning { protected String msg; protected int line; public Warning() { msg = ""; } public Warning(String m) { msg = m; if (in != null) line = in.getLineNumber(); } public int getLine() { return line; } public String getMessage() { return msg; } public String toString() { return "WARNING Line "+line+": "+msg; } } public class Error extends Warning { public Error() { super(); } public Error(String m) { super(m); } public String toString() { return "ERROR Line "+line+": "+msg; } } public static class JSFunctionMacro { public String unbound1 = null; public String unbound2 = null; public String expression = null; public String process(String args) { String bound1 = null; String bound2 = null; if (unbound2 == null) { bound1 = args; return replaceAll(expression, unbound1, bound1); } else { bound1 = args.substring(0, args.indexOf(',')); bound2 = args.substring(args.indexOf(',') + 1); return replaceAll(replaceAll(expression, unbound1, bound1), unbound2, bound2); } } } }