KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > nfunk > jep > EvaluatorVisitor


1 /*****************************************************************************
2
3 JEP - Java Math Expression Parser 2.3.0
4       October 3 2004
5       (c) Copyright 2004, Nathan Funk and Richard Morris
6       See LICENSE.txt for license information.
7
8 *****************************************************************************/

9
10 package org.nfunk.jep;
11
12 import java.util.*;
13 import org.nfunk.jep.function.PostfixMathCommandI;
14 import org.nfunk.jep.function.SpecialEvaluationI;
15
16 /**
17  * This class is used for the evaluation of an expression. It uses the Visitor
18  * design pattern to traverse the function tree and evaluate the expression
19  * using a stack.
20  * <p>
21  * Function nodes are evaluated by first evaluating all the children nodes,
22  * then applying the function class associated with the node. Variable and
23  * constant nodes are evaluated by pushing their value onto the stack.
24
25  * <p>
26  * Some changes implemented by rjm. Nov 03.
27  * Added hook to SpecialEvaluationI.
28  * Clears stack before evaluation.
29  * Simplifies error handeling by making visit methods throw ParseException.
30  * Changed visit(ASTVarNode node) so messages not calculated every time.
31  */

32 public class EvaluatorVisitor implements ParserVisitor {
33     /** Stack used for evaluating the expression */
34     protected Stack stack;
35
36     /** The current error list */
37     protected Vector errorList;
38
39     /** The symbol table for variable lookup */
40     protected SymbolTable symTab;
41
42     /** Flag for errors during evaluation */
43     protected boolean errorFlag;
44
45     /** Debug flag */
46     private static final boolean debug = false;
47
48     /** Constructor. Initialize the stack member */
49     public EvaluatorVisitor() {
50         errorList = null;
51         symTab = null;
52         stack = new Stack();
53     }
54
55     /**
56      * Adds an error message to the list of errors
57      */

58     protected void addToErrorList(String JavaDoc errorStr) {
59         if (errorList != null) {
60             errorList.addElement(errorStr);
61         }
62     }
63
64     /**
65      * Returns the value of the expression as an object. The expression
66      * tree is specified with its top node. The algorithm uses a stack
67      * for evaluation.
68      * <p>
69      * The <code>errorList_in</code> parameter is used to
70      * add error information that may occur during the evaluation. It is not
71      * required, and may be set to <code>null</code> if no error information is
72      * needed.
73      * <p>
74      * The symTab parameter can be null, if no variables are expected in the
75      * expression. If a variable is found, an error is added to the error list.
76      * <p>
77      * An exception is thrown, if an error occurs during evaluation.
78      * @return The value of the expression as an object.
79      */

80     public Object JavaDoc getValue(
81         Node topNode,
82         Vector errorList_in,
83         SymbolTable symTab_in)
84         throws Exception JavaDoc {
85
86         // check if arguments are ok
87
if (topNode == null) {
88             throw new IllegalArgumentException JavaDoc("topNode parameter is null");
89         }
90
91         // set member vars
92
errorList = errorList_in;
93         symTab = symTab_in;
94         errorFlag = false;
95         stack.removeAllElements();
96         // rjm addition ensure stack is correct before beginning.
97
// njf changed from clear() to removeAllElements for 1.1 compatibility
98

99         // evaluate by letting the top node accept the visitor
100
try {
101             topNode.jjtAccept(this, null);
102         } catch (ParseException e) {
103             this.addToErrorList(e.getMessage());
104             throw e;
105         }
106
107         // something is wrong if not exactly one item remains on the stack
108
// or if the error flag has been set
109
if (errorFlag || stack.size() != 1) {
110             throw new Exception JavaDoc("EvaluatorVisitor.getValue(): Error during evaluation");
111         }
112
113         // return the value of the expression
114
return stack.pop();
115     }
116
117     /**
118      * This method should never be called when evaluation a normal
119      * expression.
120      */

121     public Object JavaDoc visit(SimpleNode node, Object JavaDoc data) throws ParseException {
122         throw new ParseException(
123             "No visit method for " + node.getClass().toString());
124     }
125
126     /**
127      * This method should never be called when evaluating a normal
128      * expression.
129      */

130     public Object JavaDoc visit(ASTStart node, Object JavaDoc data) throws ParseException {
131         throw new ParseException("Start node encountered during evaluation");
132     }
133
134     /**
135      * Visit a function node. The values of the child nodes
136      * are first pushed onto the stack. Then the function class associated
137      * with the node is used to evaluate the function.
138      * <p>
139      * If a function implements SpecialEvaluationI then the
140      * evaluate method of PFMC is called.
141      */

142     public Object JavaDoc visit(ASTFunNode node, Object JavaDoc data) throws ParseException {
143
144         if (node == null)
145             return null;
146         PostfixMathCommandI pfmc = node.getPFMC();
147
148         // check if the function class is set
149
if (pfmc == null)
150             throw new ParseException(
151                 "No function class associated with " + node.getName());
152
153         // Some operators (=) need a special method for evaluation
154
// as the pfmc.run method does not have enough information
155
// in such cases we call the evaluate method which passes
156
// all available info. Note evaluating the children is
157
// the responsability of the evaluate method.
158
if (pfmc instanceof SpecialEvaluationI) {
159             return ((SpecialEvaluationI) node.getPFMC()).evaluate(
160                 node,data,this,stack);
161         }
162
163         if (debug == true) {
164             System.out.println(
165                 "Stack size before childrenAccept: " + stack.size());
166         }
167
168         // evaluate all children (each leaves their result on the stack)
169

170         data = node.childrenAccept(this, data);
171
172         if (debug == true) {
173             System.out.println(
174                 "Stack size after childrenAccept: " + stack.size());
175         }
176
177         if (pfmc.getNumberOfParameters() == -1) {
178             // need to tell the class how many parameters it can take off
179
// the stack because it accepts a variable number of params
180
pfmc.setCurNumberOfParameters(node.jjtGetNumChildren());
181         }
182
183         // try to run the function
184

185         pfmc.run(stack);
186
187         if (debug == true) {
188             System.out.println("Stack size after run: " + stack.size());
189         }
190
191         return data;
192     }
193
194     /**
195      * Visit a variable node. The value of the variable is obtained from the
196      * symbol table (symTab) and pushed onto the stack.
197      */

198     public Object JavaDoc visit(ASTVarNode node, Object JavaDoc data) throws ParseException {
199
200         // old code
201
// if (symTab == null)
202
// throw new ParseException(message += "the symbol table is null");
203

204         // optimize (table lookup is costly?)
205
// Object temp = symTab.get(node.getName());
206

207         // new code
208

209         Variable var = node.getVar();
210         if (var == null) {
211             String JavaDoc message = "Could not evaluate " + node.getName() + ": ";
212             throw new ParseException(message + " variable not set");
213         }
214
215         Object JavaDoc temp = var.getValue();
216
217         if (temp == null) {
218             String JavaDoc message = "Could not evaluate " + node.getName() + ": ";
219             throw new ParseException(message + "the variable was not found in the symbol table");
220         } else {
221             // all is fine
222
// push the value on the stack
223
stack.push(temp);
224         }
225
226         return data;
227     }
228
229     /**
230      * Visit a constant node. The value of the constant is pushed onto the
231      * stack.
232      */

233     public Object JavaDoc visit(ASTConstant node, Object JavaDoc data) {
234         stack.push(node.getValue());
235         return data;
236     }
237 }
238
Popular Tags