![JAR search and dependency download from the Maven repository](/logo.png)
org.nfunk.jep.EvaluatorVisitor Maven / Gradle / Ivy
Show all versions of jep Show documentation
/*****************************************************************************
JEP 2.4.1, Extensions 1.1.1
April 30 2007
(c) Copyright 2007, Nathan Funk and Richard Morris
See LICENSE-*.txt for license information.
*****************************************************************************/
package org.nfunk.jep;
import java.util.*;
import org.nfunk.jep.function.*;
/**
* This class is used for the evaluation of an expression. It uses the Visitor
* design pattern to traverse the function tree and evaluate the expression
* using a stack.
*
* Function nodes are evaluated by first evaluating all the children nodes,
* then applying the function class associated with the node. Variable and
* constant nodes are evaluated by pushing their value onto the stack.
*
* Some changes implemented by rjm. Nov 03.
* Added hook to SpecialEvaluationI.
* Clears stack before evaluation.
* Simplifies error handling by making visit methods throw ParseException.
* Changed visit(ASTVarNode node) so messages not calculated every time.
*/
public class EvaluatorVisitor implements ParserVisitor, EvaluatorI {
/** Stack used for evaluating the expression */
protected Stack stack;
/** The current error list */
//protected Vector errorList;
/** The symbol table for variable lookup */
protected SymbolTable symTab;
/** Flag for errors during evaluation */
//protected boolean errorFlag;
/** Debug flag */
protected static final boolean debug = false;
/** TrapNull **/
protected boolean trapNullValues = true;
/** Constructor. Initialise the stack member */
public EvaluatorVisitor() {
//errorList = null;
symTab = null;
stack = new Stack();
}
/**
* Adds an error message to the list of errors
*/
/*protected void addToErrorList(String errorStr) {
if (errorList != null) {
errorList.addElement(errorStr);
}
}*/
/**
* Returns the value of the expression as an object. The expression
* tree is specified with its top node. The algorithm uses a stack
* for evaluation.
*
* The symTab parameter can be null, if no variables are expected in the
* expression. If a variable is found, an error is added to the error list.
*
* An exception is thrown, if an error occurs during evaluation.
* @return The value of the expression as an object.
* @throws ParseException if there is a problem with the evaluation.
*/
public Object getValue(Node topNode,SymbolTable symTab_in)
throws ParseException {
// check if arguments are ok
if (topNode == null) {
throw new ParseException("topNode parameter is null");
}
// set member vars
//errorList = errorList_in;
symTab = symTab_in;
//errorFlag = false;
stack.removeAllElements();
// rjm addition ensure stack is correct before beginning.
// njf changed from clear() to removeAllElements for 1.1 compatibility
// evaluate by letting the top node accept the visitor
topNode.jjtAccept(this,null);
/*
} catch (ParseException e) {
this.addToErrorList("Error: "+e.getMessage());
return null;
}
if(errorFlag) return null;
*/
// something is wrong if not exactly one item remains on the stack
// or if the error flag has been set
if (stack.size() != 1) {
throw new ParseException("Stack corrupted");
}
// return the value of the expression
return stack.pop();
}
/*
* The following methods was used to facilitate
* using visitors which implemented a interface
* which sub-classed ParserVisitor.
*
* If sub-classed to extend to implement a different visitor
* this method should be overwritten to ensure the correct
* accept method is called.
* This method simply calls the jjtAccept(ParserVisitor this,Object data) of node.
*
* We no longer need this as we use ParseVisitor everywhere,
* but kept for future reference.
*
private Object nodeAccept(Node node, Object data) throws ParseException
{
return node.jjtAccept(this,data);
}
*/
/**
* Evaluates a given node, in the current context.
* @param node The node to evaluate
* @return result of the evaluation
*/
public Object eval(Node node) throws ParseException {
node.jjtAccept(this,null);
return stack.pop();
}
/**
* Evaluates a PostfixMathCommandI with given arguments.
* Not used in normal use.
*
* @param pfmc the command to evaluate.
* @param children the parameters to the function.
* @return the value of the function
* @throws ParseException
*/
public Object eval(PostfixMathCommandI pfmc,Node children[]) throws ParseException
{
if (pfmc instanceof SpecialEvaluationI) {
ASTFunNode node = new ASTFunNode(ParserTreeConstants.JJTFUNNODE);
node.setFunction("TmpFun",pfmc);
node.jjtOpen();
for(int i=0;i
* If a function implements SpecialEvaluationI then the
* evaluate method of PFMC is called.
*/
public Object visit(ASTFunNode node, Object data) throws ParseException {
if (node == null)
return null;
PostfixMathCommandI pfmc = node.getPFMC();
// check if the function class is set
if (pfmc == null)
throw new ParseException(
"No function class associated with " + node.getName());
// Some operators (=) need a special method for evaluation
// as the pfmc.run method does not have enough information
// in such cases we call the evaluate method which passes
// all available info. Note evaluating the children is
// the responsibility of the evaluate method.
if (pfmc instanceof SpecialEvaluationI) {
return ((SpecialEvaluationI) pfmc).evaluate(
node,data,this,stack,this.symTab);
}
if(pfmc instanceof CallbackEvaluationI) {
Object val = ((CallbackEvaluationI) pfmc).evaluate(node,this);
stack.push(val);
return val;
}
if (debug == true) {
System.out.println(
"Stack size before childrenAccept: " + stack.size());
}
// evaluate all children (each leaves their result on the stack)
data = node.childrenAccept(this, data);
if (debug == true) {
System.out.println(
"Stack size after childrenAccept: " + stack.size());
}
if (pfmc.getNumberOfParameters() == -1) {
// need to tell the class how many parameters it can take off
// the stack because it accepts a variable number of params
pfmc.setCurNumberOfParameters(node.jjtGetNumChildren());
}
// try to run the function
pfmc.run(stack);
if (debug == true) {
System.out.println("Stack size after run: " + stack.size());
}
return data;
}
/**
* Visit a variable node. The value of the variable is obtained from the
* symbol table (symTab) and pushed onto the stack.
*/
public Object visit(ASTVarNode node, Object data) throws ParseException {
// old code
// if (symTab == null)
// throw new ParseException(message += "the symbol table is null");
// Optimise (table lookup is costly?)
// Object temp = symTab.get(node.getName());
// new code
// try to get the variable object
Variable var = node.getVar();
if (var == null) {
String message = "Could not evaluate " + node.getName() + ": ";
throw new ParseException(message + "the variable was not found in the symbol table");
}
// get the variable value
Object temp = var.getValue();
if (trapNullValues && temp == null) {
String message = "Could not evaluate " + node.getName() + ": ";
throw new ParseException(message + "variable not set");
}
// all is fine
// push the value on the stack
stack.push(temp);
return data;
}
/**
* Visit a constant node. The value of the constant is pushed onto the
* stack.
*/
public Object visit(ASTConstant node, Object data) {
stack.push(node.getValue());
return data;
}
/**
* Tests whether null variable values are trapped by evaluator.
* @return true is nulls are trapped
*/
public boolean isTrapNullValues()
{
return trapNullValues;
}
/**
* Sets the behaviour when a variable's value is null.
* If true an exception will be thrown is a variable value is null.
* If false then the value will be passed to other functions, this may cause error
* else where.
*
* @param b
*/
public void setTrapNullValues(boolean b)
{
trapNullValues = b;
}
}