/******************************************************************************* SiJaPP - Simple Java PreProcessor Copyright (C) 2003 Manuel Linsmayer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *******************************************************************************/ package sijapp; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import java.util.Stack; import java.util.Vector; public class Preprocessor { // Defines private Hashtable defines; private Hashtable localDefines = new Hashtable(); // Input stream private BufferedReader reader; // Output stream private BufferedWriter writer; // Line counter private int lineNum; // Stop flag private boolean stop; // Skip flag/stack private boolean skip; private Stack skipStack = new Stack(); // Done flag/stack private boolean done; private Stack doneStack = new Stack(); // Constructor public Preprocessor(Hashtable defines) { this.defines = defines; } // Evaluate expression public Scanner.Token[] evalExpr(Scanner.Token[] tokens) throws SijappException { // Create token vector Vector t = new Vector(); for (int i = 0; i < tokens.length; i++) { t.add(tokens[i]); } main: while(true) { // T_EXPR_PRS_LEFT T_BOOL T_EXPR_PRS_RIGHT for (int i = 0; i < t.size()-2; i++) { Scanner.Token t1 = (Scanner.Token) t.get(i); Scanner.Token t2 = (Scanner.Token) t.get(i+1); Scanner.Token t3 = (Scanner.Token) t.get(i+2); if ((t1.getType() == Scanner.Token.T_EXPR_PRS_LEFT) && (t2.getType() == Scanner.Token.T_BOOL) && (t3.getType() == Scanner.Token.T_EXPR_PRS_RIGHT)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, t2); continue main; } } // T_IDENT/T_STRING/T_EXPR_DEF T_EXPR_EQ T_IDENT/T_STRING/T_EXPR_DEF for (int i = 0; i < t.size()-2; i++) { Scanner.Token t1 = (Scanner.Token) t.get(i); Scanner.Token t2 = (Scanner.Token) t.get(i+1); Scanner.Token t3 = (Scanner.Token) t.get(i+2); if ((t1.getType() == Scanner.Token.T_IDENT) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_IDENT)) { t.remove(i+2); t.remove(i+1); t.remove(i); String left = (String) this.localDefines.get(t1.getValue()); String right = (String) this.localDefines.get(t3.getValue()); if (((left == null) && (right != null)) || ((left != null) && (right == null))) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } else if ((left == null) && (right == null)) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } else { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(left.equals(right)))); continue main; } } else if ((t1.getType() == Scanner.Token.T_IDENT) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_STRING)) { t.remove(i+2); t.remove(i+1); t.remove(i); String left = (String) this.localDefines.get(t1.getValue()); String right = (String) t3.getValue(); if (left == null) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } else { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(left.equals(right)))); continue main; } } else if ((t1.getType() == Scanner.Token.T_IDENT) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_EXPR_DEF)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(this.localDefines.containsKey(t1.getValue())))); continue main; } else if ((t1.getType() == Scanner.Token.T_STRING) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_IDENT)) { t.remove(i+2); t.remove(i+1); t.remove(i); String right = (String) this.localDefines.get(t1.getValue()); String left = (String) t1.getValue(); if (left == null) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } else { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(left.equals(right)))); continue main; } } else if ((t1.getType() == Scanner.Token.T_STRING) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_STRING)) { t.remove(i+2); t.remove(i+1); t.remove(i); String left = (String) t1.getValue(); String right = (String) t3.getValue(); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(left.equals(right)))); continue main; } else if ((t1.getType() == Scanner.Token.T_STRING) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_EXPR_DEF)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } else if ((t1.getType() == Scanner.Token.T_EXPR_DEF) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_IDENT)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(this.localDefines.containsKey(t3.getValue())))); continue main; } else if ((t1.getType() == Scanner.Token.T_EXPR_DEF) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_STRING)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } else if ((t1.getType() == Scanner.Token.T_EXPR_DEF) && (t2.getType() == Scanner.Token.T_EXPR_EQ) && (t3.getType() == Scanner.Token.T_EXPR_DEF)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } } // T_IDENT/T_STRING/T_EXPR_DEF T_EXPR_NEQ T_IDENT/T_STRING/T_EXPR_DEF for (int i = 0; i < t.size()-2; i++) { Scanner.Token t1 = (Scanner.Token) t.get(i); Scanner.Token t2 = (Scanner.Token) t.get(i+1); Scanner.Token t3 = (Scanner.Token) t.get(i+2); if ((t1.getType() == Scanner.Token.T_IDENT) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_IDENT)) { t.remove(i+2); t.remove(i+1); t.remove(i); String left = (String) this.localDefines.get(t1.getValue()); String right = (String) this.localDefines.get(t3.getValue()); if (((left == null) && (right != null)) || ((left != null) && (right == null))) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } else if ((left == null) && (right == null)) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } else { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!left.equals(right)))); continue main; } } else if ((t1.getType() == Scanner.Token.T_IDENT) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_STRING)) { t.remove(i+2); t.remove(i+1); t.remove(i); String left = (String) this.localDefines.get(t1.getValue()); String right = (String) t3.getValue(); if (left == null) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } else { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!left.equals(right)))); continue main; } } else if ((t1.getType() == Scanner.Token.T_IDENT) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_EXPR_DEF)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!this.localDefines.containsKey(t1.getValue())))); continue main; } else if ((t1.getType() == Scanner.Token.T_STRING) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_IDENT)) { t.remove(i+2); t.remove(i+1); t.remove(i); String right = (String) this.localDefines.get(t1.getValue()); String left = (String) t1.getValue(); if (left == null) { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(true))); continue main; } else { t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!left.equals(right)))); continue main; } } else if ((t1.getType() == Scanner.Token.T_STRING) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_STRING)) { t.remove(i+2); t.remove(i+1); t.remove(i); String left = (String) t1.getValue(); String right = (String) t3.getValue(); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!left.equals(right)))); continue main; } else if ((t1.getType() == Scanner.Token.T_STRING) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_EXPR_DEF)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } else if ((t1.getType() == Scanner.Token.T_EXPR_DEF) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_IDENT)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!this.localDefines.containsKey(t3.getValue())))); continue main; } else if ((t1.getType() == Scanner.Token.T_EXPR_DEF) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_STRING)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } else if ((t1.getType() == Scanner.Token.T_EXPR_DEF) && (t2.getType() == Scanner.Token.T_EXPR_NEQ) && (t3.getType() == Scanner.Token.T_EXPR_DEF)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(false))); continue main; } } // T_EXPR_NOT T_BOOL for (int i = 0; i < t.size()-1; i++) { Scanner.Token t1 = (Scanner.Token) t.get(i); Scanner.Token t2 = (Scanner.Token) t.get(i+1); if ((t1.getType() == Scanner.Token.T_EXPR_NOT) && (t2.getType() == Scanner.Token.T_BOOL)) { t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(!((Boolean) t2.getValue()).booleanValue()))); continue main; } } // T_BOOL T_EXPR_AND T_BOOL for (int i = 0; i < t.size()-2; i++) { Scanner.Token t1 = (Scanner.Token) t.get(i); Scanner.Token t2 = (Scanner.Token) t.get(i+1); Scanner.Token t3 = (Scanner.Token) t.get(i+2); if ((t1.getType() == Scanner.Token.T_BOOL) && (t2.getType() == Scanner.Token.T_EXPR_AND) && (t3.getType() == Scanner.Token.T_BOOL)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(((Boolean) t1.getValue()).booleanValue() && ((Boolean) t3.getValue()).booleanValue()))); continue main; } } // T_BOOL T_EXPR_OR T_BOOL for (int i = 0; i < t.size()-2; i++) { Scanner.Token t1 = (Scanner.Token) t.get(i); Scanner.Token t2 = (Scanner.Token) t.get(i+1); Scanner.Token t3 = (Scanner.Token) t.get(i+2); if ((t1.getType() == Scanner.Token.T_BOOL) && (t2.getType() == Scanner.Token.T_EXPR_OR) && (t3.getType() == Scanner.Token.T_BOOL)) { t.remove(i+2); t.remove(i+1); t.remove(i); t.add(i, new Scanner.Token(Scanner.Token.T_BOOL, new Boolean(((Boolean) t1.getValue()).booleanValue() || ((Boolean) t3.getValue()).booleanValue()))); continue main; } } break; } // Return resulting tokens as array Scanner.Token[] ret = new Scanner.Token[t.size()]; t.copyInto(ret); return (ret); } // Evaluate condition (if/unless/elseif/else/end) statement public void evalCond(Scanner.Token[] tokens) throws SijappException { // Copy non-evaluted tokens to new array Scanner.Token[] remainingTokens = new Scanner.Token[tokens.length - 2]; System.arraycopy(tokens, 2, remainingTokens, 0, tokens.length - 2); // Command condition.if if ((tokens.length >= 2) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_IF)) { // Evaluate expression and check result type Scanner.Token[] resultingTokens = this.evalExpr(remainingTokens); if ((resultingTokens.length != 1) || (resultingTokens[0].getType() != Scanner.Token.T_BOOL)) { throw (new SijappException("Syntax error")); } boolean result = ((Boolean) resultingTokens[0].getValue()).booleanValue(); // Save state variables this.skipStack.push(new Boolean(this.skip)); this.doneStack.push(new Boolean(this.done)); // Set state variables this.skip |= !result; this.done = result; } // Command condition.elseif else if ((tokens.length >= 2) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_ELSEIF)) { // Evaluate expression and check result type Scanner.Token[] resultingTokens = this.evalExpr(remainingTokens); if ((resultingTokens.length != 1) || (resultingTokens[0].getType() != Scanner.Token.T_BOOL)) { throw (new SijappException("Syntax error")); } boolean result = ((Boolean) resultingTokens[0].getValue()).booleanValue(); // Set state variables this.skip = ((Boolean) this.skipStack.peek()).booleanValue() || this.done || !result; this.done |= result; } // Command condition.else else if ((tokens.length == 2) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_ELSE)) { // Set state variables this.skip = ((Boolean) this.skipStack.peek()).booleanValue() || this.done; this.done = true; } // Command condition.end else if ((tokens.length == 2) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_END)) { // Reset state variables this.skip = ((Boolean) this.skipStack.pop()).booleanValue(); this.done = ((Boolean) this.doneStack.pop()).booleanValue(); } // Unknown command/Syntax error else { throw (new SijappException("Syntax error")); } } // Evaluate echo statement public void evalEcho(Scanner.Token[] tokens) throws SijappException { if ((tokens.length != 1) || (tokens[0].getType() != Scanner.Token.T_STRING)) { throw (new SijappException("Syntax error")); } else if (!this.skip) { String s = (String) tokens[0].getValue(); try { writer.write(s, 0, s.length()); writer.newLine(); } catch (IOException e) { throw (new SijappException("An I/O error occured")); } } } // Evaluate enviroment statement public void evalEnv(Scanner.Token[] tokens) throws SijappException { if ((tokens.length == 3) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_DEF) && (tokens[2].getType() == Scanner.Token.T_IDENT)) { if (!this.skip) { this.localDefines.put(tokens[2].getValue(), "defined"); } } else if ((tokens.length == 4) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_DEF) && (tokens[2].getType() == Scanner.Token.T_IDENT) && (tokens[3].getType() == Scanner.Token.T_STRING)) { if (!this.skip) { this.localDefines.put(tokens[2].getValue(), tokens[3].getValue()); } } else if ((tokens.length == 3) && (tokens[0].getType() == Scanner.Token.T_SEP) && (tokens[1].getType() == Scanner.Token.T_CMD2_UNDEF) && (tokens[2].getType() == Scanner.Token.T_IDENT)) { if (!this.skip) { this.localDefines.remove(tokens[2].getValue()); } } else { throw (new SijappException("Syntax error")); } } // Evaluate exit statement public void evalExit(Scanner.Token[] tokens) throws SijappException { if (tokens.length != 0) { throw (new SijappException("Syntax error")); } else if (!this.skip) { this.stop = true; } } // Evaluate SiJaPP statement public void eval(Scanner.Token[] tokens) throws SijappException { // Valid statement consists of at least 3 tokens if (tokens.length < 3) { throw (new SijappException("Syntax error")); } // Look for MAGIC_BEGIN and MAGIC_END if ((tokens[0].getType() != Scanner.Token.T_MAGIC_BEGIN) || (tokens[tokens.length-1].getType() != Scanner.Token.T_MAGIC_END)) { throw (new SijappException("Syntax error")); } // Copy non-evaluted tokens to new array Scanner.Token[] remainingTokens = new Scanner.Token[tokens.length - 3]; System.arraycopy(tokens, 2, remainingTokens, 0, tokens.length - 3); // Delegate evaluation switch (tokens[1].getType()) { case Scanner.Token.T_CMD1_COND: this.evalCond(remainingTokens); break; case Scanner.Token.T_CMD1_ECHO: this.evalEcho(remainingTokens); break; case Scanner.Token.T_CMD1_ENV: this.evalEnv(remainingTokens); break; case Scanner.Token.T_CMD1_EXIT: this.evalExit(remainingTokens); break; default: throw (new SijappException("Syntax error")); } } private boolean startsWith(String what, String with) { for (int i = 0; i < what.length(); ++i) { if (!Character.isSpaceChar(what.charAt(i))) return what.startsWith(with, i); } return false; } // Preprocess public void run(BufferedReader reader, BufferedWriter writer) throws SijappException { // Restore global defines this.localDefines.clear(); for (Enumeration keys = this.defines.keys(); keys.hasMoreElements(); ) { String key = new String((String) keys.nextElement()); String value = new String((String) this.defines.get(key)); this.localDefines.put(key, value); } // Save input/output stream this.reader = reader; this.writer = writer; // Reset line counter this.lineNum = 1; // Reset stop flag this.stop = false; // Reset skip flag this.skip = false; // Read until EOF try { String line; J2mizer j2mizer = defines.containsKey("modules_ANDROID") ? null : new J2mizer(); while ((line = this.reader.readLine()) != null) { if ((null != j2mizer) && !startsWith(line, "//")) line = j2mizer.j2mize(line); // Scan read line for s SiJaPP statement Scanner.Token[] tokens = Scanner.scan(line); // Get rid of special sijapp comments //# if inside an sijapp statement if (tokens.length == 0 && startsWith(line, "//#") && this.doneStack.size() > 0) line = line.trim().substring(3); // No statement has been found if (tokens.length == 0) { if (!skip) { this.writer.write(line, 0, line.length()); this.writer.newLine(); } else { this.writer.write("//# "+line, 0, line.length()+4); this.writer.newLine(); } } // A statement has been found else { this.eval(tokens); this.writer.write(line, 0, line.length()); this.writer.newLine(); if (this.stop) { return; } } // Advance line counter this.lineNum++; } } catch (SijappException e) { throw (new SijappException(this.lineNum + ": " + e.getMessage())); } catch (IOException e) { throw (new SijappException("An I/O error occured")); } } }