/** * __autoconcat__ has special optimization techniques needed, since it's really a part of the * compiler itself, and not so much a function. It being a function is merely a convenience, so * we can defer processing until after parsing. While it is tightly coupled with the compiler, * this is ok, since it's really a compiler mechanism more than a function. * * @param t * @param list * @return */ public ParseTree optimizeSpecial(List<ParseTree> list, boolean returnSConcat) throws ConfigCompileException { // If any of our nodes are CSymbols, we have different behavior boolean inSymbolMode = false; // caching this can save Xn // Assignment // Note that we are walking the array in reverse, because multiple assignments, // say @a = @b = 1 will break if they look like assign(assign(@a, @b), 1), // they need to be assign(@a, assign(@b, 1)). As a variation, we also have // to support something like 1 + @a = 2, which will turn into add(1, assign(@a, 2), // and 1 + @a = @b + 3 would turn into add(1, assign(@a, add(@b, 3))). for (int i = list.size() - 2; i >= 0; i--) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isAssignment()) { CSymbol sy = (CSymbol) node.getData(); String conversionFunction = sy.convertAssignment(); ParseTree lhs = list.get(i); if (conversionFunction != null) { ParseTree conversion = new ParseTree( new CFunction(conversionFunction, node.getTarget()), node.getFileOptions()); // grab the entire right side, and turn it into an operation with the left side. // We have to take the entire right up to the next construct not followed by an // operator (or the end) try { ParseTree rhs; if (i < list.size() - 3) { // Need to autoconcat ParseTree ac = new ParseTree( new CFunction("__autoconcat__", Target.UNKNOWN), lhs.getFileOptions()); int index = i + 2; ac.addChild(list.get(index)); list.remove(index); while (true) { if (list.size() > index && list.get(index).getData() instanceof CSymbol) { // Add the next two children, (the symbol then the item) // and continue. ac.addChild(list.get(index)); ac.addChild(list.get(index + 1)); list.remove(index); list.remove(index); continue; } else { break; } } // Set this subset into the correct slot, the rest of the // code will grab it correctly that way. list.add(i + 2, ac); } rhs = list.get(i + 2); conversion.addChild(lhs); conversion.addChild(rhs); list.set(i + 2, conversion); } catch (IndexOutOfBoundsException e) { throw new ConfigCompileException("Invalid symbol listed", node.getTarget()); } } // Simple assignment now ParseTree assign = new ParseTree(new CFunction("assign", node.getTarget()), node.getFileOptions()); ParseTree rhs; if (i < list.size() - 3) { // Need to autoconcat ParseTree ac = new ParseTree( new CFunction("__autoconcat__", Target.UNKNOWN), lhs.getFileOptions()); int index = i + 2; ac.addChild(list.get(index)); list.remove(index); while (true) { if (list.size() > index && list.get(index).getData() instanceof CSymbol) { // Add the next two children, (the symbol then the item) // and continue. ac.addChild(list.get(index)); ac.addChild(list.get(index + 1)); list.remove(index); list.remove(index); continue; } else { break; } } // Set this subset into the correct slot, the rest of the // code will grab it correctly that way. list.add(i + 2, ac); } rhs = list.get(i + 2); assign.addChild(lhs); assign.addChild(rhs); list.set(i, assign); list.remove(i + 1); list.remove(i + 1); } } // postfix for (int i = 0; i < list.size(); i++) { ParseTree node = list.get(i); if (node.getData() instanceof CSymbol) { inSymbolMode = true; } if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isPostfix()) { if (i - 1 >= 0) { // && list.get(i - 1).getData() instanceof IVariable) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion; if (sy.val().equals("++")) { conversion = new ParseTree(new CFunction("postinc", node.getTarget()), node.getFileOptions()); } else { conversion = new ParseTree(new CFunction("postdec", node.getTarget()), node.getFileOptions()); } conversion.addChild(list.get(i - 1)); list.set(i - 1, conversion); list.remove(i); i--; } } } if (inSymbolMode) { try { // look for unary operators for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isUnary()) { ParseTree conversion; if (node.getData().val().equals("-") || node.getData().val().equals("+")) { // These are special, because if the values to the left isn't a symbol, // it's not unary if ((i == 0 || list.get(i - 1).getData() instanceof CSymbol) && !(list.get(i + 1).getData() instanceof CSymbol)) { if (node.getData().val().equals("-")) { // We have to negate it conversion = new ParseTree( new CFunction("neg", node.getTarget()), node.getFileOptions()); } else { conversion = new ParseTree(new CFunction("p", node.getTarget()), node.getFileOptions()); } } else { continue; } } else { conversion = new ParseTree( new CFunction(((CSymbol) node.getData()).convert(), node.getTarget()), node.getFileOptions()); } conversion.addChild(list.get(i + 1)); list.set(i, conversion); list.remove(i + 1); i--; } } for (int i = 0; i < list.size() - 1; i++) { ParseTree next = list.get(i + 1); if (next.getData() instanceof CSymbol) { if (((CSymbol) next.getData()).isExponential()) { ParseTree conversion = new ParseTree( new CFunction(((CSymbol) next.getData()).convert(), next.getTarget()), next.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } } // Multiplicative for (int i = 0; i < list.size() - 1; i++) { ParseTree next = list.get(i + 1); if (next.getData() instanceof CSymbol) { CSymbol nextData = (CSymbol) next.getData(); if (nextData.isMultaplicative() && !nextData.isAssignment()) { ParseTree conversion = new ParseTree( new CFunction(((CSymbol) next.getData()).convert(), next.getTarget()), next.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } } // Additive for (int i = 0; i < list.size() - 1; i++) { ParseTree next = list.get(i + 1); if (next.getData() instanceof CSymbol && ((CSymbol) next.getData()).isAdditive() && !((CSymbol) next.getData()).isAssignment()) { ParseTree conversion = new ParseTree( new CFunction(((CSymbol) next.getData()).convert(), next.getTarget()), next.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // relational for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isRelational()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // equality for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isEquality()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // logical and for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isLogicalAnd()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // logical or for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isLogicalOr()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } } catch (IndexOutOfBoundsException e) { throw new ConfigCompileException( "Unexpected symbol (" + list.get(list.size() - 1).getData().val() + "). Did you forget to quote your symbols?", list.get(list.size() - 1).getTarget()); } } // Look for a CEntry here if (list.size() >= 1) { ParseTree node = list.get(0); if (node.getData() instanceof CLabel) { ParseTree value = new ParseTree( new CFunction("__autoconcat__", node.getTarget()), node.getFileOptions()); for (int i = 1; i < list.size(); i++) { value.addChild(list.get(i)); } ParseTree ce = new ParseTree(new CFunction("centry", node.getTarget()), node.getFileOptions()); ce.addChild(node); ce.addChild(value); return ce; } } // We've eliminated the need for __autoconcat__ either way, however, if there are still // arguments // left, it needs to go to sconcat, which MAY be able to be further optimized, but that will // be handled in MethodScriptCompiler's optimize function. Also, we must scan for // CPreIdentifiers, // which may be turned into a function if (list.size() == 1) { return list.get(0); } else { for (int i = 0; i < list.size(); i++) { if (list.get(i).getData().getCType() == Construct.ConstructType.IDENTIFIER) { if (i == 0) { // Yup, it's an identifier CFunction identifier = new CFunction(list.get(i).getData().val(), list.get(i).getTarget()); list.remove(0); ParseTree child = list.get(0); if (list.size() > 1) { child = new ParseTree(new CFunction("sconcat", Target.UNKNOWN), child.getFileOptions()); child.setChildren(list); } try { Function f = (Function) FunctionList.getFunction(identifier); ParseTree node = new ParseTree( f.execs(identifier.getTarget(), null, null, child), child.getFileOptions()); return node; } catch (Exception e) { throw new Error("Unknown function " + identifier.val() + "?"); } } else { // Hmm, this is weird. I'm not sure what condition this can happen in throw new ConfigCompileException( "Unexpected IDENTIFIER? O.o Please report a bug," + " and include the script you used to get this error.", Target.UNKNOWN); } } } ParseTree tree; FileOptions options = new FileOptions(new HashMap<String, String>()); if (!list.isEmpty()) { options = list.get(0).getFileOptions(); } if (returnSConcat) { tree = new ParseTree(new CFunction("sconcat", Target.UNKNOWN), options); } else { tree = new ParseTree(new CFunction("concat", Target.UNKNOWN), options); } tree.setChildren(list); return tree; } }