001: /**
002: * MVEL (The MVFLEX Expression Language)
003: *
004: * Copyright (C) 2007 Christopher Brock, MVFLEX/Valhalla Project and the Codehaus
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: */package org.mvel.util;
019:
020: import org.mvel.Operator;
021: import org.mvel.compiler.CompiledExpression;
022: import org.mvel.ast.*;
023:
024: import java.util.Map;
025: import java.util.LinkedHashMap;
026:
027: public class CompilerTools {
028:
029: /**
030: * Optimize the AST, by reducing any stack-based-operations to dedicated nodes where possible.
031: *
032: * @param astLinkedList - AST to be optimized.
033: * @param secondPassOptimization - perform a second pass optimization to optimize boolean expressions.
034: * @return optimized AST
035: */
036: public static ASTLinkedList optimizeAST(
037: ASTLinkedList astLinkedList, boolean secondPassOptimization) {
038: ASTLinkedList optimizedAst = new ASTLinkedList();
039: ASTNode tk, tkOp, tkOp2;
040:
041: /**
042: * Re-process the AST and optimize it.
043: */
044: while (astLinkedList.hasMoreNodes()) {
045: if ((tk = astLinkedList.nextNode()).getFields() == -1) {
046: optimizedAst.addTokenNode(tk);
047: } else if (astLinkedList.hasMoreNodes()) {
048: if ((tkOp = astLinkedList.nextNode()).getFields() == -1) {
049:
050: if (tk instanceof EndOfStatement) {
051: /**
052: * If this is the last node of the script, don't bother
053: * with the end of statement.
054: */
055: if (astLinkedList.hasMoreNodes()) {
056: optimizedAst.addTokenNode(tk);
057: }
058:
059: astLinkedList.setCurrentNode(tkOp);
060: continue;
061: } else {
062: optimizedAst.addTokenNode(tk);
063: }
064:
065: optimizedAst.addTokenNode(tkOp);
066: } else if (tkOp.isOperator() && tkOp.getOperator() < 12) {
067: // handle math and equals
068: BinaryOperation bo = new BinaryOperation(tkOp
069: .getOperator(), tk, astLinkedList
070: .nextNode());
071: tkOp2 = null;
072:
073: /**
074: * If we have a chain of math/comparitive operators then we fill them into the tree
075: * right here.
076: */
077: while (astLinkedList.hasMoreNodes()
078: && (tkOp2 = astLinkedList.nextNode())
079: .isOperator()
080: && tkOp2.getFields() != -1
081: && tkOp2.getOperator() < 12) {
082: bo = new BinaryOperation(((tkOp = tkOp2)
083: .getOperator()), bo, astLinkedList
084: .nextNode());
085: }
086: optimizedAst.addTokenNode(bo);
087:
088: if (tkOp2 != null && tkOp2 != tkOp) {
089: optimizedAst.addTokenNode(tkOp2);
090: }
091: } else {
092: if (tk instanceof EndOfStatement) {
093: astLinkedList.setCurrentNode(tkOp);
094:
095: /**
096: * If this is the last node of the script, don't bother
097: * with the end of statement.
098: */
099: if (astLinkedList.hasMoreNodes()) {
100: optimizedAst.addTokenNode(tk);
101: }
102:
103: continue;
104: } else {
105: optimizedAst.addTokenNode(tk);
106: }
107:
108: optimizedAst.addTokenNode(tkOp);
109: }
110: } else {
111: optimizedAst.addTokenNode(tk);
112: }
113: }
114:
115: if (secondPassOptimization) {
116: /**
117: * Perform a second pass optimization for boolean conditions.
118: */
119: astLinkedList = optimizedAst;
120: astLinkedList.reset();
121:
122: optimizedAst = new ASTLinkedList();
123:
124: while (astLinkedList.hasMoreNodes()) {
125: if ((tk = astLinkedList.nextNode()).getFields() == -1) {
126: optimizedAst.addTokenNode(tk);
127: } else if (astLinkedList.hasMoreNodes()) {
128: if ((tkOp = astLinkedList.nextNode()).getFields() == -1) {
129: optimizedAst.addTokenNode(tk);
130: if (tk instanceof EndOfStatement) {
131: astLinkedList.setCurrentNode(tkOp);
132: }
133:
134: optimizedAst.addTokenNode(tkOp);
135: } else if (tkOp.isOperator()
136: && (tkOp.getOperator() == Operator.AND || tkOp
137: .getOperator() == Operator.OR)) {
138:
139: tkOp2 = null;
140: ASTNode bool = null;
141:
142: switch (tkOp.getOperator()) {
143: case Operator.AND:
144: bool = new And(tk, astLinkedList.nextNode());
145: break;
146: case Operator.OR:
147: bool = new Or(tk, astLinkedList.nextNode());
148: }
149:
150: /**
151: * If we have a chain of math/comparitive operators then we fill them into the tree
152: * right here.
153: */
154: while (astLinkedList.hasMoreNodes()
155: && (tkOp2 = astLinkedList.nextNode())
156: .isOperator()
157: && (tkOp2.isOperator(Operator.AND) || tkOp2
158: .isOperator(Operator.OR))) {
159:
160: switch ((tkOp = tkOp2).getOperator()) {
161: case Operator.AND:
162: bool = new And(bool, astLinkedList
163: .nextNode());
164: break;
165: case Operator.OR:
166: bool = new Or(bool, astLinkedList
167: .nextNode());
168: }
169: }
170:
171: optimizedAst.addTokenNode(bool);
172:
173: if (tkOp2 != null && tkOp2 != tkOp) {
174: optimizedAst.addTokenNode(tkOp2);
175: }
176: } else {
177: optimizedAst.addTokenNode(tk);
178: if (tk instanceof EndOfStatement) {
179: astLinkedList.setCurrentNode(tkOp);
180: }
181:
182: optimizedAst.addTokenNode(tkOp);
183: }
184: } else {
185: optimizedAst.addTokenNode(tk);
186: }
187: }
188: }
189:
190: return optimizedAst;
191: }
192:
193: /**
194: * Returns an ordered Map of all functions declared within an compiled script.
195: *
196: * @param compile
197: * @return - ordered Map
198: */
199: public static Map<String, Function> extractAllDeclaredFunctions(
200: CompiledExpression compile) {
201: Map<String, Function> allFunctions = new LinkedHashMap<String, Function>();
202: ASTIterator instructions = new ASTLinkedList(compile
203: .getInstructions());
204:
205: ASTNode n;
206: while (instructions.hasMoreNodes()) {
207: if ((n = instructions.nextNode()) instanceof Function) {
208: allFunctions.put(n.getName(), (Function) n);
209: }
210: }
211:
212: return allFunctions;
213: }
214: }
|