/* * JMEP - Java Mathematical Expression Parser. * Copyright (C) 1999 Jo Desmet * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * You can contact the Original submitter of this library by * email at: Jo_Desmet@yahoo.com. * */ package com.iabcinc.jmep; import static com.iabcinc.jmep.tokens.BINToken.Operator.ADD; import static com.iabcinc.jmep.tokens.BINToken.Operator.AND; import static com.iabcinc.jmep.tokens.BINToken.Operator.DIV; import static com.iabcinc.jmep.tokens.BINToken.Operator.EQ; import static com.iabcinc.jmep.tokens.BINToken.Operator.GE; import static com.iabcinc.jmep.tokens.BINToken.Operator.GT; import static com.iabcinc.jmep.tokens.BINToken.Operator.LAND; import static com.iabcinc.jmep.tokens.BINToken.Operator.LE; import static com.iabcinc.jmep.tokens.BINToken.Operator.LOR; import static com.iabcinc.jmep.tokens.BINToken.Operator.LT; import static com.iabcinc.jmep.tokens.BINToken.Operator.MOD; import static com.iabcinc.jmep.tokens.BINToken.Operator.MUL; import static com.iabcinc.jmep.tokens.BINToken.Operator.NE; import static com.iabcinc.jmep.tokens.BINToken.Operator.OR; import static com.iabcinc.jmep.tokens.BINToken.Operator.POW; import static com.iabcinc.jmep.tokens.BINToken.Operator.SDIV; import static com.iabcinc.jmep.tokens.BINToken.Operator.SUB; import static com.iabcinc.jmep.tokens.BINToken.Operator.XOR; import java.text.StringCharacterIterator; import java.util.Iterator; import java.util.LinkedList; import java.util.Vector; import com.iabcinc.jmep.hooks.Constant; import com.iabcinc.jmep.hooks.Function; import com.iabcinc.jmep.hooks.Unit; import com.iabcinc.jmep.hooks.Variable; import com.iabcinc.jmep.tokens.BINToken; import com.iabcinc.jmep.tokens.FNCToken; import com.iabcinc.jmep.tokens.Token; import com.iabcinc.jmep.tokens.UNAToken; import com.iabcinc.jmep.tokens.UNIToken; import com.iabcinc.jmep.tokens.VALToken; import com.iabcinc.jmep.tokens.VARToken; /** * The container for a Mathematical Expression. This class enables the * user to define a mathematical expression and evaluate it with support * for a precompile to enable faster execution for within a loop.<p> * * The expression string supports variables and functions, number expressions * (resulting in either an int or double) and string expressions.<p> * * Literal strings has to be quited using the double quote (").<p> * * Supported operators are: <code>( ) + - * / ^ and or xor & | * < <= > >= = <> % not inv</code> * and follows mostly the rules as in most programming languages.<p> * * Be carefull with the use of the division parameter (<code>/</code>), when * both operands are of the type Integer, then the result will also be an * Integer when used in the default strict mode. You can overrule this by * multiplying the first operand with 1.0. * * @author <a href="mailto:jo_desmet@yahoo.com>Jo Desmet</a> * @see com.iabcinc.jmep.Environment */ public class Expression { private static final int D_TokenToOS = 0x0001; /* Move current token to Operator Stack, next token is taken as current */ private static final int D_TokenToRS = 0x0002; /* Move current token to Result (RPN) Stack, next token is taken as current */ private static final int D_NextToken = 0x0004; /* Skip Current Token and take next as current */ private static final int D_PopOS = 0x0008; /* Remove token from Operator Stack */ private static final int D_PopRS = 0x0010; /* Remove token from Result Stack */ private static final int D_OSToRS = 0x0020; /* Move token from Operator Stack to Result Stack */ private static final int D_Internal = 0x0040; /* Internal Error <- status not possible */ private static final int D_Syntax = 0x0080; /* Syntax Error */ private static final int D_Done = 0x0100; /* Done, Result after Evaluating Result Stack */ private static final int D_CheckFNCPar = 0x0200; /* Check function parameters (number and type) */ private static final int D_IncParCount = 0x0400; /* Increase parameter count */ private static final int D_Precedence = 0x0800; /* Two binary operators following each other */ private static final int D_PushParCount= 0x1000; /* Stacking the parameter count (nested functions) */ private static final int D_PopParCount = 0x2000; /* Unstacking the parameter count (nested functions) */ private static final int [][] arr_kDispatch = new int [][] { /* OS TP - TOKEN */ /*****************/ { /* MRK - MRK */ D_Done , /* OPA */ D_TokenToOS , /* FNC */ D_TokenToOS|D_PushParCount , /* CMA */ D_Syntax , /* UNA */ D_TokenToOS , /* BIN */ D_TokenToOS , /* VAL */ D_TokenToRS , /* VAR */ D_TokenToRS , /* CPA */ D_Syntax , /* ERR */ D_Syntax , /* UNI */ D_TokenToRS },{/* OPA - MRK */ D_Syntax , /* OPA */ D_TokenToOS , /* FNC */ D_TokenToOS|D_PushParCount , /* CMA */ D_Syntax , /* UNA */ D_TokenToOS , /* BIN */ D_TokenToOS , /* VAL */ D_TokenToRS , /* VAR */ D_TokenToRS , /* CPA */ D_PopOS|D_NextToken , /* ERR */ D_Syntax , /* UNI */ D_TokenToRS },{/* FNC - MRK */ D_Syntax , /* OPA */ D_TokenToOS , /* FNC */ D_TokenToOS|D_PushParCount , /* CMA */ D_TokenToOS , /* UNA */ D_TokenToOS , /* BIN */ D_TokenToOS , /* VAL */ D_TokenToRS , /* VAR */ D_TokenToRS , /* CPA */ D_NextToken|D_OSToRS|D_PopParCount|D_IncParCount|D_CheckFNCPar , /* ERR */ D_Syntax , /* UNI */ D_TokenToRS },{/* CMA - MRK */ D_Syntax , /* OPA */ D_TokenToOS , /* FNC */ D_TokenToOS|D_PushParCount , /* CMA */ D_TokenToOS , /* UNA */ D_TokenToOS , /* BIN */ D_TokenToOS , /* VAL */ D_TokenToRS , /* VAR */ D_TokenToRS , /* CPA */ D_PopOS|D_IncParCount , /* ERR */ D_Syntax , /* UNI */ D_TokenToRS },{/* UNA - MRK */ D_OSToRS , /* OPA */ D_TokenToOS , /* FNC */ D_TokenToOS|D_PushParCount , /* CMA */ D_OSToRS , /* UNA */ D_TokenToOS , /* BIN */ D_OSToRS|D_TokenToOS , /* VAL */ D_TokenToRS , /* VAR */ D_TokenToRS , /* CPA */ D_OSToRS , /* ERR */ D_Syntax , /* UNI */ D_TokenToRS },{/* BIN - MRK */ D_OSToRS , /* OPA */ D_TokenToOS , /* FNC */ D_TokenToOS|D_PushParCount , /* CMA */ D_OSToRS , /* UNA */ D_TokenToOS , /* BIN */ D_Precedence /* OS >= Token => D_OSToRS|D_TokenToOS */ /* OS < Token => D_TokenToOS */ , /* VAL */ D_TokenToRS , /* VAR */ D_TokenToRS , /* CPA */ D_OSToRS , /* ERR */ D_Syntax , /* UNI */ D_TokenToRS },{/* VAL - MRK */ D_Internal , /* OPA */ D_Internal , /* FNC */ D_Internal , /* CMA */ D_Internal , /* UNA */ D_Internal , /* BIN */ D_Internal , /* VAL */ D_Internal , /* VAR */ D_Internal , /* CPA */ D_Internal , /* ERR */ D_Internal , /* UNI */ D_Internal },{/* VAR - MRK */ D_Internal , /* OPA */ D_Internal , /* FNC */ D_Internal , /* CMA */ D_Internal , /* UNA */ D_Internal , /* BIN */ D_Internal , /* VAL */ D_Internal , /* VAR */ D_Internal , /* CPA */ D_Internal , /* ERR */ D_Internal , /* UNI */ D_Internal },{/* CPA - MRK */ D_Syntax , /* OPA */ D_Syntax , /* FNC */ D_Syntax , /* CMA */ D_Syntax , /* UNA */ D_Syntax , /* BIN */ D_Syntax , /* VAL */ D_Syntax , /* VAR */ D_Syntax , /* CPA */ D_Syntax , /* ERR */ D_Syntax , /* UNI */ D_Syntax },{/* ERR - MRK */ D_Syntax , /* OPA */ D_Syntax , /* FNC */ D_Syntax , /* CMA */ D_Syntax , /* UNA */ D_Syntax , /* BIN */ D_Syntax , /* VAL */ D_Syntax , /* VAR */ D_Syntax , /* CPA */ D_Syntax , /* ERR */ D_Syntax , /* UNI */ D_Syntax },{/* UNI - MRK */ D_Internal , /* OPA */ D_Internal , /* FNC */ D_Internal , /* CMA */ D_Internal , /* UNA */ D_Internal , /* BIN */ D_Internal , /* VAL */ D_Internal , /* VAR */ D_Internal , /* CPA */ D_Internal , /* ERR */ D_Internal , /* UNI */ D_Internal } }; /*****************/ private String expression; private Environment environment; private Vector tokenList; private LinkedList rpnStack; private Token[] rpnStack_array; private boolean strict; /** * Constructs a mathematical expression from a String. This will do all * initial compiling of the string with all necesairy optimalizations. * Expressions are evaluated strictly by default. * @param expression the string containing the mathematical expression. * @see com.iabcinc.jmep.XExpression */ public Expression(String expression) throws XExpression { this(expression,new Environment(),true); } /** * Constructs a mathematical expression from a String using a specific * user environment. This will do all initial compiling of the string with * all necesairy optimalizations. Expressions are evaluated strictly by default. * @param expression the string containing the mathematical expression. * @param environment the environment that contains all user defined variables, * functions and units. * @see com.iabcinc.jmep.Environment * @see com.iabcinc.jmep.XExpression */ public Expression(String expression,Environment environment) throws XExpression { this(expression,environment,true); } /** * Constructs a mathematical expression from a String using a specific * user environment. This will do all initial compiling of the string with * all necesairy optimalizations. * @param expression the string containing the mathematical expression. * @param environment the environment that contains all user defined variables, * functions and units. * @param strict when true, the expressions will be interpreted strictly, * meaning that a division of 2 Integer values will return an Integer value. * @see com.iabcinc.jmep.Environment * @see com.iabcinc.jmep.XExpression */ public Expression(String expression,Environment environment,boolean strict) throws XExpression { this.expression = expression; this.environment = environment; this.strict = strict; tokenize(); compile(); optimize(); } private static String parseIdentifier(StringCharacterIterator iterString) { StringBuffer identifier = new StringBuffer(); char cc = iterString.current(); if (!Character.isUnicodeIdentifierStart(cc)) return null; identifier.append(cc); cc = iterString.next(); while (Character.isIdentifierIgnorable(cc)) cc = iterString.next(); while (Character.isUnicodeIdentifierPart(cc) || cc == '.') { identifier.append(cc); cc = iterString.next(); while (Character.isIdentifierIgnorable(cc)) cc = iterString.next(); } return identifier.toString(); } private static Number parseNumber(StringCharacterIterator iterString) { char cc = iterString.current(); if (Character.isDigit(cc) || cc == '.') { int intValue = 0; while (Character.isDigit(cc)) { intValue *= 10; intValue += Character.digit(cc,10); cc = iterString.next(); } if (cc != '.' && cc != 'e' && cc != 'E') { /* it is an int */ return new Integer(intValue); } else { /* it is a double */ double doubleValue = intValue; if (cc == '.') { double scale = 1.0; cc = iterString.next(); while (Character.isDigit(cc)) { scale *= 0.1; doubleValue += scale * Character.digit(cc,10); cc = iterString.next(); } } if (cc == 'e' || cc == 'E') { boolean bPositive = true; intValue = 0; cc = iterString.next(); if (!Character.isDigit(cc)) { if (cc == '+') { bPositive = true; cc = iterString.next(); } else if (cc == '-') { bPositive = false; cc = iterString.next(); } else { return null; /* not a legal number */ } } while (Character.isDigit(cc)) { intValue *= 10; intValue += Character.digit(cc,10); cc = iterString.next(); } doubleValue *= Math.pow(10,bPositive?intValue:-intValue); } return new Double(doubleValue); } } return null; } private String parseString(StringCharacterIterator iterString) { char cc = iterString.current(); StringBuffer string = new StringBuffer(); if (cc == '"') { cc = iterString.next(); while (cc != StringCharacterIterator.DONE && cc != '"') { if (cc == '\\') { int iSavePos = iterString.getIndex(); char nc = iterString.next(); iterString.setIndex(iSavePos); if (nc == '"') cc = iterString.next(); else if (nc == '\\') cc = iterString.next(); } string.append(cc); cc = iterString.next(); } if (cc == '"') cc = iterString.next(); return string.toString(); } return null; } private void tokenize() throws XExpression { char cc; StringCharacterIterator iterString = new StringCharacterIterator(this.expression); this.tokenList = new Vector(); this.tokenList.addElement(new Token(Token.MRK,0)); cc = iterString.first(); while (true) { while (Character.isWhitespace(cc)) cc = iterString.next(); switch (cc) { case StringCharacterIterator.DONE: this.tokenList.addElement(new Token(Token.MRK,iterString.getIndex())); return; case '(': this.tokenList.addElement(new Token(Token.OPA,iterString.getIndex())); cc = iterString.next(); continue; case ')': this.tokenList.addElement(new Token(Token.CPA,iterString.getIndex())); cc = iterString.next(); continue; case ',': this.tokenList.addElement(new Token(Token.CMA,iterString.getIndex())); cc = iterString.next(); continue; } if (Character.isUnicodeIdentifierStart(cc)) { int identifierPosition = iterString.getIndex(); String identifier = parseIdentifier(iterString); Token previousToken = (Token)this.tokenList.lastElement(); cc = iterString.current(); if (identifier == null) throw new XIllegalStatus(identifierPosition); if ( previousToken.getKindOfToken() == Token.VAL || previousToken.getKindOfToken() == Token.VAR || previousToken.getKindOfToken() == Token.UNI || previousToken.getKindOfToken() == Token.CPA ) { /* Check if the identifier is a Binary Operator */ if (identifier.equalsIgnoreCase("and")) { this.tokenList.addElement(new BINToken(LAND,identifierPosition)); continue; } if (identifier.equalsIgnoreCase("xor")) { this.tokenList.addElement(new BINToken(XOR,identifierPosition)); continue; } if (identifier.equalsIgnoreCase("or")) { this.tokenList.addElement(new BINToken(LOR,identifierPosition)); continue; } else { /* Else it must be a unit operator */ Unit oUnit = (Unit)this.environment.getUnit(identifier); if (oUnit == null) this.tokenList.addElement(new UNIToken(identifier,identifierPosition)); else this.tokenList.addElement(new UNIToken(identifier,oUnit,identifierPosition)); continue; } } else if (previousToken.getKindOfToken() != Token.UNI) { /* Check if the identifier is an unary operator */ if (identifier.equalsIgnoreCase("not")) { this.tokenList.addElement(new UNAToken(UNAToken.NOT,identifierPosition)); continue; } if (identifier.equalsIgnoreCase("inv")) { this.tokenList.addElement(new UNAToken(UNAToken.INV,identifierPosition)); continue; } } while (Character.isWhitespace(cc)) cc = iterString.next(); if (cc == '(') { /* it is a function */ Function oFunction = (Function)this.environment.getFunction(identifier); cc = iterString.next(); if (oFunction == null) this.tokenList.addElement(new FNCToken(identifier,identifierPosition)); else this.tokenList.addElement(new FNCToken(identifier,oFunction,identifierPosition)); continue; } else { /* it is a variable */ Variable oVariable = this.environment.getVariable(identifier); if (oVariable == null) this.tokenList.addElement(new VARToken(identifier,identifierPosition)); else if (oVariable instanceof Constant) this.tokenList.addElement(new VALToken(VARToken.getValue(oVariable),identifierPosition)); else this.tokenList.addElement(new VARToken(identifier,(Variable)oVariable,identifierPosition)); //else if (oVariable instanceof Integer) // this.tokenList.addElement(new VARToken(identifier,((Integer)oVariable).intValue(),identifierPosition)); //else if (oVariable instanceof Double) // this.tokenList.addElement(new VARToken(identifier,((Double)oVariable).doubleValue(),identifierPosition)); //else if (oVariable instanceof String) // this.tokenList.addElement(new VARToken(identifier,(String)oVariable,identifierPosition)); //else // throw new XIllegalStatus(identifierPosition); continue; } } if (Character.isDigit(cc) || cc == '.') { /* is numerical */ Number oNumber; int iNumberPos = iterString.getIndex(); oNumber = parseNumber(iterString); cc = iterString.current(); if (oNumber == null) throw new XIllegalStatus(iNumberPos); else if (oNumber instanceof Integer) this.tokenList.addElement(new VALToken(((Integer)oNumber).intValue(),iNumberPos)); else if (oNumber instanceof Double) this.tokenList.addElement(new VALToken(((Double)oNumber).doubleValue(),iNumberPos)); else throw new XIllegalStatus(iNumberPos); continue; } if (cc == '"') { int iStringPos = iterString.getIndex(); String sValue = parseString(iterString); cc = iterString.current(); if (sValue == null) throw new XIllegalStatus(iStringPos); this.tokenList.addElement(new VALToken(sValue,iStringPos)); continue; } switch (cc) { case '^': this.tokenList.addElement(new BINToken(POW,iterString.getIndex())); cc = iterString.next(); continue; case '*': this.tokenList.addElement(new BINToken(MUL,iterString.getIndex())); cc = iterString.next(); continue; case '/': if (this.strict) { this.tokenList.addElement(new BINToken(SDIV,iterString.getIndex())); } else { this.tokenList.addElement(new BINToken(DIV,iterString.getIndex())); } cc = iterString.next(); continue; case '&': this.tokenList.addElement(new BINToken(AND,iterString.getIndex())); cc = iterString.next(); continue; case '%': this.tokenList.addElement(new BINToken(MOD,iterString.getIndex())); cc = iterString.next(); continue; case '|': this.tokenList.addElement(new BINToken(OR,iterString.getIndex())); cc = iterString.next(); continue; case '=': this.tokenList.addElement(new BINToken(EQ,iterString.getIndex())); cc = iterString.next(); continue; case '+': { Token oPrevToken = (Token)this.tokenList.lastElement(); if ( oPrevToken.getKindOfToken() == Token.VAL || oPrevToken.getKindOfToken() == Token.VAR || oPrevToken.getKindOfToken() == Token.UNI || oPrevToken.getKindOfToken() == Token.CPA ) { this.tokenList.addElement(new BINToken(ADD,iterString.getIndex())); cc = iterString.next(); continue; } else { this.tokenList.addElement(new UNAToken(UNAToken.PLS,iterString.getIndex())); cc = iterString.next(); continue; } } case '-': { Token oPrevToken = (Token)this.tokenList.lastElement(); if ( oPrevToken.getKindOfToken() == Token.VAL || oPrevToken.getKindOfToken() == Token.VAR || oPrevToken.getKindOfToken() == Token.UNI || oPrevToken.getKindOfToken() == Token.CPA ) { this.tokenList.addElement(new BINToken(SUB,iterString.getIndex())); cc = iterString.next(); continue; } else { this.tokenList.addElement(new UNAToken(UNAToken.MIN,iterString.getIndex())); cc = iterString.next(); continue; } } case '<': { int iPos = iterString.getIndex(); cc = iterString.next(); if (cc == '>') { this.tokenList.addElement(new BINToken(NE,iPos)); cc = iterString.next(); } /* BUGFIX 9/30/2005 by Graham (further identification witheld); Add 'else'. */ else if (cc == '=') { this.tokenList.addElement(new BINToken(LE,iPos)); cc = iterString.next(); } else this.tokenList.addElement(new BINToken(LT,iPos)); continue; } case '>': { int iPos = iterString.getIndex(); cc = iterString.next(); if (cc == '=') { this.tokenList.addElement(new BINToken(GE,iPos)); cc = iterString.next(); } else this.tokenList.addElement(new BINToken(GT,iPos)); continue; } case '!': { int iPos = iterString.getIndex(); cc = iterString.next(); if (cc == '=') { this.tokenList.addElement(new BINToken(NE,iPos)); cc = iterString.next(); } else this.tokenList.addElement(new UNAToken(UNAToken.NOT,iPos)); continue; } default: { throw new XExpression(iterString.getIndex(),"Unknown Symbol '"+cc+"'"); } } /* throw new XIllegalStatus(iterString.getIndex()); */ } /* while() */ } private void compile() throws XExpression{ int action = 0; int parameterCount = 0; LinkedList operatorStack = new LinkedList(); LinkedList parameterCountStack = new LinkedList(); Iterator iToken; Token topTokenOnOperatorStack; Token token; this.rpnStack = new LinkedList(); iToken = this.tokenList.iterator(); token = (Token)iToken.next(); operatorStack.addLast(token); token = (Token)iToken.next(); for(;;) { //2 8 topTokenOnOperatorStack = ((Token)operatorStack.getLast()); action = arr_kDispatch[topTokenOnOperatorStack.getKindOfToken()][token.getKindOfToken()]; if (action == D_Precedence) { /* Two binary operators following each other */ if ( ((BINToken)topTokenOnOperatorStack).getPrecedence() >= ((BINToken)token).getPrecedence() ) { action = D_OSToRS; } else { action = D_TokenToOS; } } if ((action & D_IncParCount) != 0) { /* Increase parameter count */ parameterCount++; } if ((action & D_PushParCount) != 0) { parameterCountStack.addLast(new Integer(parameterCount)); parameterCount = 0; } if ((action & D_PopParCount) != 0) { ((FNCToken)topTokenOnOperatorStack).setNumberOfParameters(parameterCount); parameterCount = ((Integer)parameterCountStack.removeLast()).intValue(); } if ((action & D_OSToRS) != 0) { /* Move token from Operator Stack to RPN Stack */ /* Should check for empty stack, if so then give internal error */ Token oMoveToken = (Token)operatorStack.removeLast(); this.rpnStack.addLast(oMoveToken); } if ((action & D_TokenToOS) != 0) { /* Move current token to Operator Stack */ operatorStack.addLast(token); token = (Token)iToken.next(); } if ((action & D_TokenToRS) != 0) { /* Move current token to RPN Stack */ this.rpnStack.addLast(token); token = (Token)iToken.next(); } if ((action & D_NextToken) != 0) { /* Current Token is next from string */ token = (Token)iToken.next(); } if ((action & D_PopOS) != 0) { /* Remove token from Operator Stack */ operatorStack.removeLast(); } if ((action & D_PopRS) != 0) { /* Remove token from RPN Stack */ this.rpnStack.removeLast(); } if ((action & D_Internal) != 0) { this.rpnStack = null; throw new XIllegalStatus(token.getPosition()); } if ((action & D_Syntax) != 0) { this.rpnStack = null; throw new XExpression(topTokenOnOperatorStack.getPosition(),"General Syntax error"); } if ((action & D_Done) != 0) { /* Done, Result after Evaluating RPN Stack */ // Remove last marker. operatorStack.removeLast(); if (!operatorStack.isEmpty() || !parameterCountStack.isEmpty()) { //Token oNewTopToken = (Token)oOperatorStack.getLast(); // There should an exception been trown. */ this.rpnStack = null; throw new XIllegalStatus(); } return; } if ((action & D_CheckFNCPar) != 0) { /* Check function parameters (number and type) */ } } } private void optimize() throws XExpression { /* * TODO: * - implement a method to optimize the resultint RPNStack. * call this method 'optimize()'. This can wait, and should have * low priority because the MEP can work without it at * reasonable speed. */ rpnStack_array = new Token[rpnStack.size()]; Token oToken; int count = 0; for (Iterator iToken = this.rpnStack.iterator() ; iToken.hasNext() ;) { oToken = (Token)iToken.next(); rpnStack_array[count] = oToken; count++; } //How many arguments do we allow? let's try 16. oValuesStack = new NumberCustom[16][]; for(int k = 0; k < 16; k++){ oValuesStack[k] = new NumberCustom[k]; for(int i = 0; i < k; i++){ oValuesStack[k][i] = new NumberCustom(); } } } private static class NumberCustom extends Number { private float fval; public void setFloat(float fval){this.fval = fval;} public void setDouble(double fval){this.fval = (float) fval;} public void setInt(int fval){this.fval = fval;} public void setByte(byte bval){this.fval = bval;} public void setLong(long fval){this.fval = fval;} public double doubleValue() { return fval; } public float floatValue() { return fval; } public int intValue() { return (int) fval; } public long longValue() { return (long) fval; } } private static class ExpressionStack { private final Object[] stack; private int size; public ExpressionStack(){ stack = new Object[8]; size = 0; } public final Object remove() throws XExpression { if (size <= 0){ throw new XExpression(0,"Wrong number of arguments"); } return stack[--size]; } public final void add(Object evaluate) { stack[size++] = evaluate; } } private interface TokenEvaluable { public void run(Expression e, Token oToken) throws XExpression; } /** * Evaluation stack */ private ExpressionStack oResultStack = new ExpressionStack(); private NumberCustom[][] oValuesStack; /** * Evaluates the expression. This will do all necesairy late binding. * @return the evaluated expression, wich can be Double, Integer or String. * NEW: contextID is passed to the Variable object. This allows multithreading, * in that the context determines which set of Variables to grab. */ public Object evaluate(int contextID) throws XExpression { //Reset stack: oResultStack.size = 0; //Iterate through rpn tree for(int cycle = 0; cycle < rpnStack_array.length; cycle++){ Token IF_EXE_oToken = null; Object IF_EXE_arg2 = null, IF_EXE_arg1 = null; IF_EXE_oToken = rpnStack_array[cycle]; int INSTCT = countArgs_token(IF_EXE_oToken); if (INSTCT>1){ IF_EXE_arg2 = oResultStack.remove(); } if (INSTCT>0){ IF_EXE_arg1 = oResultStack.remove(); } evaluate_token(IF_EXE_oToken,IF_EXE_arg1,IF_EXE_arg2); } Object oResult = oResultStack.remove(); if (oResultStack.size!=0) throw new XExpression(0,"Wrong number of arguments"); //Have to cast to object... return oResult; } private int countArgs_token(Token oToken) throws XExpression{ switch (oToken.m_kToken) { case Token.FNC: { return 0; } case Token.UNI:{ return 1; } case Token.VAR: { return 0; } case Token.UNA:{ return 1; } case Token.BIN:{ return 2; } case Token.VAL:{ return 0; } default: throw new XIllegalStatus(oToken.getPosition()); } } private void evaluate_token(Token oToken, Object arg1, Object arg2) throws XExpression { switch (oToken.m_kToken) { case Token.MRK: case Token.OPA: case Token.CMA: case Token.CPA: throw new XIllegalStatus(oToken.getPosition()); case Token.FNC: { evaluate_FNCToken((FNCToken)oToken); }break; case Token.UNI:{ evaluate_UNIToken((UNIToken)oToken,arg1); }break; case Token.VAR: { evaluate_VARToken((VARToken)oToken); }break; case Token.UNA:{ evaluate_UNAToken((UNAToken)oToken,arg1); }break; case Token.BIN:{ evaluate_BINToken((BINToken)oToken,arg1,arg2); }break; case Token.VAL:{ oResultStack.add(((VALToken)oToken).getValue()); }break; }; } private void evaluate_BINToken(BINToken oToken, Object oValue1, Object oValue2) throws XExpression { Object oValue; //DEBUG //Each bintoken is unique... should they have their own state? oValue = ((BINToken)oToken).evaluate(oValue1,oValue2,0); oResultStack.add(oValue); } private void evaluate_VARToken(VARToken oToken) throws XExpression { Object oValue; oValue = ((VARToken)oToken).evaluate(0); oResultStack.add(oValue); } private void evaluate_UNAToken(UNAToken oToken, Object oValue) throws XExpression { Object evaluate = ((UNAToken)oToken).evaluate(oValue,0); oResultStack.add(evaluate); } private void evaluate_UNIToken(UNIToken oToken, Object oValue) throws XExpression { oValue = ((UNIToken)oToken).evaluate(oValue); oResultStack.add(oValue); } private void evaluate_FNCToken(FNCToken oFNCToken) throws XExpression { int nParams = oFNCToken.getNumberOfParameters(); //Hack: 0 args functions aren't correctly done. //However, the @ execution time stack should be empty in these cases: if (oResultStack.size==0){ nParams = 0; } if (nParams < 0 || nParams >= oValuesStack.length){ throw new XExpression(oFNCToken.getPosition(),"Bad number of arguments: "+nParams); } Object oValue; if (nParams != 0) { NumberCustom[] oValues = oValuesStack[nParams]; for (int iParam = nParams-1; iParam >= 0; iParam--){ Object remove = oResultStack.remove(); //extract arguments if (remove instanceof float[]){ oValues[iParam].setFloat(((float[])remove)[0]); } if (remove instanceof int[]){ oValues[iParam].setInt(((int[])remove)[0]); } } oValue = oFNCToken.evaluate(oValues,0); } else oValue = oFNCToken.evaluate(null,0); oResultStack.add(oValue); } }