/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.numerics; /*----------------------------------------------------------------------------------------* * Parser.java version 1.0 Jun 16 1996 * * Parser.java version 2.0 Aug 25 1996 * * Parser.java version 2.1 Oct 14 1996 * * Parser.java version 2.11 Oct 25 1996 * * Parser.java version 2.2 Nov 8 1996 * * Parser.java version 3.0 May 17 1997 * * Parser.java version 3.01 Oct 18 2001 * * * * Parser.java version 4.0 Oct 25 2001 * * * * Copyright (c) 1996 Yanto Suryono. All Rights Reserved. * * Version 4 Modifications by Wolfgang Christian * * * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License, or (at your option) * * any later version. * * * * This program 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 General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * *----------------------------------------------------------------------------------------*/ import java.util.Hashtable; import java.util.Vector; /** * The class <code>Parser</code> is a mathematical expression parser.<p> * Example of code that uses this class:<p> * * <pre> * Parser parser = new Parser(1); // creates parser with one variable * parser.defineVariable(1,"x"); // lets the variable be 'x' * parser.define("sin(x)/x"); // defines function: sin(x)/x * parser.parse(); // parses the function * * // calculates: sin(x)/x with x = -5.0 .. +5.0 in 20 steps * // and prints the result to standard output. * * float result; * for (i=-10; i <= 10; i++) { * parser.setVariable(1,(float)i/2.0f); * result = parser.evaluate(); * System.out.println(result); * } * </pre> */ public final class SuryonoParser extends MathExpParser { // global variables private int var_count; // number of variables private String var_name[]; // variables' name private double var_value[]; // value of variables private double number[]; // numeric constants in defined function private String function = ""; // function definition //$NON-NLS-1$ private String postfix_code = ""; // the postfix code //$NON-NLS-1$ private boolean valid = false; // postfix code status private int error; // error code of last process private boolean ISBOOLEAN = false; // boolean flag private boolean INRELATION = false; // relation flag // variables used during parsing private int position; // parsing pointer private int start; // starting position of identifier private int num; // number of numeric constants private char character; // current character // variables used during evaluating private boolean radian; // radian unit flag private int numberindex; // pointer to numbers/constants bank private double[] refvalue = null; // value of references // private static final int MAX_NUM = 100; // max numeric constants // changed by W. Christian private static final int MAX_NUM = 200; // max numeric constants // private static final int NO_FUNCS = 24; // no. of built-in functions // changed from 24 function by W. Christian to add step and random function private static final int NO_FUNCS = 26; // no. of built-in functions private static final int NO_EXT_FUNCS = 4; // no. of extended functions private static final int STACK_SIZE = 50; // evaluation stack size private double[] stack = new double[STACK_SIZE]; // moved by W. Christian from evaluate to global variables for speed // constants private static final double DEGTORAD = Math.PI/180; private static final double LOG10 = Math.log(10); // references - version 3.0 private Hashtable<String, String> references = null; private Vector<String> refnames = null; // error codes /** * No error. * * Moved to superclass by W. Christian */ // public static final int NO_ERROR = 0; /** * Syntax error. * * Moved to superclass by W. Christian */ // public static final int SYNTAX_ERROR = 1; /** * Parentheses expected. */ public static final int PAREN_EXPECTED = 2; /** * Attempt to evaluate an uncompiled function. */ public static final int UNCOMPILED_FUNCTION = 3; /** * Expression expected. */ public static final int EXPRESSION_EXPECTED = 4; /** * Unknown identifier. */ public static final int UNKNOWN_IDENTIFIER = 5; /** * Operator expected. */ public static final int OPERATOR_EXPECTED = 6; /** * Parenthesis mismatch. */ public static final int PAREN_NOT_MATCH = 7; /** * Code damaged. */ public static final int CODE_DAMAGED = 8; /** * Stack overflow. */ public static final int STACK_OVERFLOW = 9; /** * Too many constants. */ public static final int TOO_MANY_CONSTS = 10; /** * Comma expected. */ public static final int COMMA_EXPECTED = 11; /** * Invalid operand. */ public static final int INVALID_OPERAND = 12; /** * Invalid operator. */ public static final int INVALID_OPERATOR = 13; /** * No function definition to parse. */ public static final int NO_FUNC_DEFINITION = 14; /** * Referenced name could not be found. */ public static final int REF_NAME_EXPECTED = 15; // postfix codes private static final int FUNC_OFFSET = 1000; private static final int EXT_FUNC_OFFSET = FUNC_OFFSET+NO_FUNCS; private static final int VAR_OFFSET = 2000; private static final int REF_OFFSET = 3000; private static final char PI_CODE = (char) 253; private static final char E_CODE = (char) 254; private static final char NUMERIC = (char) 255; // Jump, followed by n : Displacement private static final char JUMP_CODE = (char) 1; // Relation less than (<) private static final char LESS_THAN = (char) 2; // Relation greater than (>) private static final char GREATER_THAN = (char) 3; // Relation less than or equal (<=) private static final char LESS_EQUAL = (char) 4; // Relation greater than or equal (>=) private static final char GREATER_EQUAL = (char) 5; // Relation not equal (<>) private static final char NOT_EQUAL = (char) 6; // Relation equal (=) private static final char EQUAL = (char) 7; // Conditional statement IF, followed by a conditional block : // * Displacement (Used to jump to condition FALSE code) // * Condition TRUE code // * Jump to next code outside conditional block // * Condition FALSE code // * ENDIF private static final char IF_CODE = (char) 8; private static final char ENDIF = (char) 9; private static final char AND_CODE = (char) 10; // Boolean AND private static final char OR_CODE = (char) 11; // Boolean OR private static final char NOT_CODE = (char) 12; // Boolean NOT // built in functions private String funcname[] = { "sin", "cos", "tan", "ln", "log", "abs", "int", "frac", "asin", "acos", "atan", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "ceil", "floor", "round", "exp", "sqr", "sqrt", "sign", "step","random" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ //$NON-NLS-13$ //$NON-NLS-14$ //$NON-NLS-15$ //$NON-NLS-16$ //$NON-NLS-17$ //$NON-NLS-18$ //$NON-NLS-19$ //$NON-NLS-20$ //$NON-NLS-21$ //$NON-NLS-22$ //$NON-NLS-23$ //$NON-NLS-24$ //$NON-NLS-25$ //$NON-NLS-26$ }; // extended functions private String extfunc[] = {"min", "max", "mod", "atan2"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ // set when evaluate() method converts NaN to zero--added by D Brown 15 Sep 2010 private boolean isNaN; /** * The constructor of <code>Parser</code>. * * Added by W. Christian to make it easy to construct a parser for with one variable. * * @param f function * @param v variable * * @throws ParserException */ public SuryonoParser(String f, String v) throws ParserException { this(1); defineVariable(1, v); // lets the variable be v define(f); // defines function: f parse(); // parses the function if(getErrorCode()!=NO_ERROR) { String msg = "Error in function string: "+f; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+getErrorString(); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ throw new ParserException(msg); } } /** * The constructor of <code>Parser</code>. * * Added by W. Christian to make it easy to construct a parser for with two variables. * * @param f the function * @param v1 variable 1 * @param v2 variable 2 * @throws ParserException */ public SuryonoParser(String f, String v1, String v2) throws ParserException { this(2); defineVariable(1, v1); defineVariable(2, v2); define(f); // defines function: f parse(); // parses the function if(getErrorCode()!=NO_ERROR) { String msg = "Error in function string: "+f; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+getErrorString(); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ throw new ParserException(msg); } } /** * The constructor of <code>Parser</code>. * * Added by W. Christian to make it easy to construct a parser for with multiple variables. * * @param f the function * @param v variables * @throws ParserException */ public SuryonoParser(String f, String[] v) throws ParserException { this(v.length); for(int i = 0; i<v.length; i++) { defineVariable(i+1, v[i]); } define(f); // defines function: f parse(); // parses the function if(getErrorCode()!=NO_ERROR) { String msg = "Error in function string: "+f; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+getErrorString(); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ throw new ParserException(msg); } } /** * The constructor of <code>Parser</code>. * * @param variablecount the number of variables */ public SuryonoParser(int variablecount) { var_count = variablecount; references = new Hashtable<String, String>(); refnames = new Vector<String>(); radian = true; // arrays are much faster than vectors (IMHO) var_name = new String[variablecount]; var_value = new double[variablecount]; number = new double[MAX_NUM]; } /** * Sets the funtion to zero. */ public void setToZero() { try { setFunction("0"); //$NON-NLS-1$ } catch(ParserException ex) {} } boolean appendVariables = false; /** * Sets the angle unit to radian. Default upon construction. */ public void useRadian() { radian = true; } /** * Sets the angle unit to degree. */ public void useDegree() { radian = false; } /** * Remove any escape sequences such as \, and replace with the character. * by W. Christian. * * @param function the function * * @return the new function */ private String removeEscapeCharacter(String str) { if((str==null)||(str.length()<1)) { return str; } StringBuffer sb = new StringBuffer(str.length()); for(int i = 0; i<str.length(); i++) { if(str.charAt(i)!='\\') { sb.append(str.charAt(i)); } } return sb.toString(); } /** * Sets the variable names. * Nothing happens if variable index > number of variables. * * @param index the variable index (one based) * @param name the variable name */ public void defineVariable(int index, String name) { if(index>var_count) { return; } var_name[index-1] = name; } /** * Sets the variable value. * The variable is accessed by index. * Nothing happens if variable index > number of variables. * * @param index the variable index (one based) * @param value the variable value */ public void setVariable(int index, double value) { if(index>var_count) { return; } var_value[index-1] = value; } /** * Sets the variable value. * The variable is accessed by name. * Nothing happens if variable could not be found. * * @param name the variable name * @param value the variable value */ public void setVariable(String name, double value) { for(int i = 0; i<var_count; i++) { if(var_name[i].equals(name)) { var_value[i] = value; break; } } } /** * Defines a function. Current postfix code becomes invalid. * * @param definition the function definition */ public void define(String definition) { function = definition; function.toLowerCase(); function = removeEscapeCharacter(function); // added by W. Christian valid = false; } /** * Parses defined function. */ public void parse(String function) throws ParserException { define(function); parse(); if(getErrorCode()!=NO_ERROR) { String msg = "Error in function string: "+function; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+getErrorString(); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ throw new ParserException(msg); } } /** * Parses a function looking for unknown variables. Unknown tokens are used to create the variable list in the order * that they are found. */ public String[] parseUnknown(String function) throws ParserException { var_name = new String[0]; var_value = new double[0]; var_count = 0; appendVariables = true; define(function); parse(); if(getErrorCode()!=NO_ERROR) { String msg = "Error in function string: "+function; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+getErrorString(); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ appendVariables = false; throw new ParserException(msg); } appendVariables = false; return var_name; } public String[] getVariableNames() { return var_name; } /** * Returns all built-in and extended function names. * Added by D. Brown 06 Jul 2008 * * @return array of function names */ public String[] getFunctionNames() { int len = funcname.length; String[] names = new String[len+extfunc.length]; System.arraycopy(funcname, 0, names, 0, len); System.arraycopy(extfunc, 0, names, len, extfunc.length); return names; } /** * Parses defined function. */ public void parse() { String allFunction = new String(function); String orgFunction = new String(function); int index; if(valid) { return; } num = 0; error = NO_ERROR; references.clear(); refnames.removeAllElements(); while((index = allFunction.lastIndexOf(";"))!=-1) { //$NON-NLS-1$ function = allFunction.substring(index+1)+')'; allFunction = allFunction.substring(0, index++); // references are of form: refname1:reffunc1;refname2:reffunc2;... String refname = null; int separator = function.indexOf(":"); //$NON-NLS-1$ if(separator==-1) { error = NO_FUNC_DEFINITION; for(position = 0; position<function.length(); position++) { if(function.charAt(position)!=' ') { break; } } position++; } else { refname = function.substring(0, separator); function = function.substring(separator+1); refname = refname.trim(); if(refname.equals("")) { //$NON-NLS-1$ error = REF_NAME_EXPECTED; position = 1; } else { index += ++separator; parseSubFunction(); } } if(error!=NO_ERROR) { position += index; break; } references.put(refname, postfix_code); refnames.addElement(refname); } if(error==NO_ERROR) { function = allFunction+')'; parseSubFunction(); } function = orgFunction; valid = (error==NO_ERROR); } public double evaluate(double x, double y) // added by Wolfgang Christian to make it easier to call parser. { if(var_count!=2) { return 0; } var_value[0] = x; var_value[1] = y; return evaluate(); } public double evaluate(double x, double y, double z) // added by Wolfgang Christian to make it easier to call parser. { if(var_count!=3) { return 0; } var_value[0] = x; var_value[1] = y; var_value[2] = z; return evaluate(); } public double evaluate(double x) // added by Wolfgang Christian to make it easier to call parser. { if(var_count!=1) { return 0; } var_value[0] = x; return evaluate(); } public double evaluate(double[] v) // added by Wolfgang Christian to make it easier to call parser with an array. { if(var_value.length!=v.length) { System.out.println("JEParser Error: incorrect number of variables."); //$NON-NLS-1$ return 0; } System.arraycopy(v, 0, var_value, 0, v.length); return evaluate(); } /** * Evaluates compiled function. * * @return the result of the function */ public double evaluate() { int size = refnames.size(); double result; if(!valid) { error = UNCOMPILED_FUNCTION; return 0; } error = NO_ERROR; numberindex = 0; if(size!=0) { String orgPFC = postfix_code; refvalue = new double[size]; for(int i = 0; i<refnames.size(); i++) { String name = refnames.elementAt(i); postfix_code = references.get(name); result = evaluateSubFunction(); if(error!=NO_ERROR) { postfix_code = orgPFC; refvalue = null; return result; } refvalue[i] = result; } postfix_code = orgPFC; } result = evaluateSubFunction(); refvalue = null; // added by D Brown to flag NaN results isNaN = Double.isNaN(result); // added by W. Christian to trap for NaN if(isNaN) { result = 0.0; } return result; } /** * Determines if last evaluation resulted in NaN. Added by D Brown 15 Sep 2010. * * @return true if result was converted from NaN to zero */ public boolean evaluatedToNaN() { return isNaN; } /** * Gets error code of last operation. * * @return the error code */ public int getErrorCode() { return error; } /** * Gets error string/message of last operation. * * @return the error string */ public String getErrorString() { return toErrorString(error); } /** * Gets error position. Valid only if error code != NO_ERROR * * @return error position (one based) */ public int getErrorPosition() { return position; } /** * Converts error code to error string. * * @return the error string */ public static String toErrorString(int errorcode) { String s = ""; //$NON-NLS-1$ switch(errorcode) { case NO_ERROR : s = "no error"; //$NON-NLS-1$ break; case SYNTAX_ERROR : s = "syntax error"; //$NON-NLS-1$ break; case PAREN_EXPECTED : s = "parenthesis expected"; //$NON-NLS-1$ break; case UNCOMPILED_FUNCTION : s = "uncompiled function"; //$NON-NLS-1$ break; case EXPRESSION_EXPECTED : s = "expression expected"; //$NON-NLS-1$ break; case UNKNOWN_IDENTIFIER : s = "unknown identifier"; //$NON-NLS-1$ break; case OPERATOR_EXPECTED : s = "operator expected"; //$NON-NLS-1$ break; case PAREN_NOT_MATCH : s = "parentheses not match"; //$NON-NLS-1$ break; case CODE_DAMAGED : s = "internal code damaged"; //$NON-NLS-1$ break; case STACK_OVERFLOW : s = "execution stack overflow"; //$NON-NLS-1$ break; case TOO_MANY_CONSTS : s = "too many constants"; //$NON-NLS-1$ break; case COMMA_EXPECTED : s = "comma expected"; //$NON-NLS-1$ break; case INVALID_OPERAND : s = "invalid operand type"; //$NON-NLS-1$ break; case INVALID_OPERATOR : s = "invalid operator"; //$NON-NLS-1$ break; case NO_FUNC_DEFINITION : s = "bad reference definition (: expected)"; //$NON-NLS-1$ break; case REF_NAME_EXPECTED : s = "reference name expected"; //$NON-NLS-1$ break; } return s; } /** * Gets function string of last operation. * * Added by W. Christian to implement the MathExpParser interface. * * @return the function string */ public String getFunction() { return function; } /** * Parse the function string using the existing variables. * * Added by W. Christian to implement the MathExpParser interface. */ public void setFunction(String funcStr) throws ParserException { function = funcStr; define(function); parse(); if(error!=NO_ERROR) { String msg = "Error in function string: "+funcStr; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+toErrorString(error); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ throw new ParserException(msg); } } /** * Parse the function string using new variable names. * * Added by W. Christian to implement the MathExpParser interface. */ public void setFunction(String funcStr, String[] vars) throws ParserException { function = funcStr; if(vars.length!=var_count) { var_count = vars.length; references.clear(); refnames.clear(); var_name = new String[var_count]; var_value = new double[var_count]; } for(int i = 0; i<vars.length; i++) { defineVariable(i+1, vars[i]); } define(function); parse(); if(error!=NO_ERROR) { String msg = "Error in function string: "+funcStr; //$NON-NLS-1$ msg = msg+'\n'+"Error: "+toErrorString(error); //$NON-NLS-1$ msg = msg+'\n'+"Position: "+getErrorPosition(); //$NON-NLS-1$ throw new ParserException(msg); } } /*----------------------------------------------------------------------------------------* * Private methods begin here * *----------------------------------------------------------------------------------------*/ /** * Advances parsing pointer, skips pass all white spaces. * * @exception ParserException */ private void skipSpaces() throws ParserException { try { while(function.charAt(position-1)==' ') { position++; } character = function.charAt(position-1); } catch(StringIndexOutOfBoundsException e) { throw new ParserException(PAREN_NOT_MATCH); } } /** * Advances parsing pointer, gets next character. * * @exception ParserException */ private void getNextCharacter() throws ParserException { position++; try { character = function.charAt(position-1); } catch(StringIndexOutOfBoundsException e) { throw new ParserException(PAREN_NOT_MATCH); } } /** * Appends postfix code to compiled code. * * @param code the postfix code to append */ private void addCode(char code) { postfix_code += code; } /** * Scans a number. Valid format: xxx[.xxx[e[+|-]xxx]] * * @exception ParserException */ private void scanNumber() throws ParserException { // changed by W. Christian to parse numbers with leading zeros. String numstr = ""; //$NON-NLS-1$ double value; if(num==MAX_NUM) { throw new ParserException(TOO_MANY_CONSTS); } if(character!='.') { // added by W. Christian do { numstr += character; getNextCharacter(); } while((character>='0')&&(character<='9')); } else { numstr += '0'; } // added by W. Christian if(character=='.') { do { numstr += character; getNextCharacter(); } while((character>='0')&&(character<='9')); } // if(character=='e') { if((character=='e')||(character=='E')) { // changed by Doug Brown May 2007 numstr += character; getNextCharacter(); if((character=='+')||(character=='-')) { numstr += character; getNextCharacter(); } while((character>='0')&&(character<='9')) { numstr += character; getNextCharacter(); } } try { value = Double.valueOf(numstr).doubleValue(); } catch(NumberFormatException e) { position = start; throw new ParserException(SYNTAX_ERROR); } number[num++] = value; addCode(NUMERIC); } /** * Scans a non-numerical identifier. Can be function call, * variable, reference, etc. * * @exception ParserException */ private void scanNonNumeric() throws ParserException { String stream = ""; //$NON-NLS-1$ if((character=='*')||(character=='/')||(character=='^')||(character==')')||(character==',')||(character=='<')||(character=='>')||(character=='=')||(character=='&')||(character=='|')) { throw new ParserException(SYNTAX_ERROR); } do { stream += character; getNextCharacter(); } while(!((character==' ')||(character=='+')||(character=='-')||(character=='*')||(character=='/')||(character=='^')||(character=='(')||(character==')')||(character==',')||(character=='<')||(character=='>')||(character=='=')||(character=='&')||(character=='|'))); if(stream.equals("pi")) { //$NON-NLS-1$ addCode(PI_CODE); return; } else if(stream.equals("e")) { //$NON-NLS-1$ addCode(E_CODE); return; } // if if(stream.equals("if")) { //$NON-NLS-1$ skipSpaces(); if(character!='(') { throw new ParserException(PAREN_EXPECTED); } scanAndParse(); if(character!=',') { throw new ParserException(COMMA_EXPECTED); } addCode(IF_CODE); String savecode = new String(postfix_code); postfix_code = ""; //$NON-NLS-1$ scanAndParse(); if(character!=',') { throw new ParserException(COMMA_EXPECTED); } addCode(JUMP_CODE); savecode += (char) (postfix_code.length()+2); savecode += postfix_code; postfix_code = ""; //$NON-NLS-1$ scanAndParse(); if(character!=')') { throw new ParserException(PAREN_EXPECTED); } savecode += (char) (postfix_code.length()+1); savecode += postfix_code; postfix_code = new String(savecode); getNextCharacter(); return; } // built-in function for(int i = 0; i<NO_FUNCS; i++) { if(stream.equals(funcname[i])) { skipSpaces(); if(character!='(') { throw new ParserException(PAREN_EXPECTED); } scanAndParse(); if(character!=')') { throw new ParserException(PAREN_EXPECTED); } getNextCharacter(); addCode((char) (i+FUNC_OFFSET)); return; } } // extended functions for(int i = 0; i<NO_EXT_FUNCS; i++) { if(stream.equals(extfunc[i])) { skipSpaces(); if(character!='(') { throw new ParserException(PAREN_EXPECTED); } scanAndParse(); if(character!=',') { throw new ParserException(COMMA_EXPECTED); } String savecode = new String(postfix_code); postfix_code = ""; //$NON-NLS-1$ scanAndParse(); if(character!=')') { throw new ParserException(PAREN_EXPECTED); } getNextCharacter(); savecode += postfix_code; postfix_code = new String(savecode); addCode((char) (i+EXT_FUNC_OFFSET)); return; } } // registered variables for(int i = 0; i<var_count; i++) { if(stream.equals(var_name[i])) { addCode((char) (i+VAR_OFFSET)); return; } } // references int index = refnames.indexOf(stream); if(index!=-1) { addCode((char) (index+REF_OFFSET)); return; } // appendVariables option added by W. Christian if(appendVariables&&append(stream)) { return; } position = start; throw new ParserException(UNKNOWN_IDENTIFIER); } // W. Christian addition to automatically add variables private boolean append(String stream) { String[] var_name2 = new String[var_count+1]; double[] var_value2 = new double[var_count+1]; System.arraycopy(var_name, 0, var_name2, 0, var_count); System.arraycopy(var_value, 0, var_value2, 0, var_count); var_name2[var_count] = stream; var_name = var_name2; var_value = var_value2; var_count++; // System.out.println("appended=" + stream); for(int i = 0; i<var_count; i++) { if(stream.equals(var_name[i])) { addCode((char) (i+VAR_OFFSET)); return true; } } return false; } /** * Gets an identifier starting from current parsing pointer. * * @return whether the identifier should be negated * @exception ParserException */ private boolean getIdentifier() throws ParserException { boolean negate = false; getNextCharacter(); skipSpaces(); if(character=='!') { getNextCharacter(); skipSpaces(); if(character!='(') { throw new ParserException(PAREN_EXPECTED); } scanAndParse(); if(character!=')') { throw new ParserException(PAREN_EXPECTED); } if(!ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } addCode(NOT_CODE); getNextCharacter(); return false; } ISBOOLEAN = false; while((character=='+')||(character=='-')) { if(character=='-') { negate = !negate; } getNextCharacter(); skipSpaces(); } start = position; // if ((character >= '0') && (character <= '9')) changed be W. Christian to handle leanding zeros. if(((character>='0')&&(character<='9'))||(character=='.')) { scanNumber(); } else if(character=='(') { scanAndParse(); getNextCharacter(); } else { scanNonNumeric(); } skipSpaces(); return(negate); } /** * Scans arithmetic level 3 (highest). Power arithmetics. * * @exception ParserException */ private void arithmeticLevel3() throws ParserException { boolean negate; if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } negate = getIdentifier(); if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } if(character=='^') { arithmeticLevel3(); } addCode('^'); if(negate) { addCode('_'); } } /** * Scans arithmetic level 2. Multiplications and divisions. * * @exception ParserException */ private void arithmeticLevel2() throws ParserException { boolean negate; if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } do { char operator = character; negate = getIdentifier(); if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } if(character=='^') { arithmeticLevel3(); } if(negate) { addCode('_'); } addCode(operator); } while((character=='*')||(character=='/')); } /** * Scans arithmetic level 1 (lowest). * Additions and substractions. * * @exception ParserException */ private void arithmeticLevel1() throws ParserException { boolean negate; if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } do { char operator = character; negate = getIdentifier(); if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } if(character=='^') { arithmeticLevel3(); if(negate) { addCode('_'); } } else if((character=='*')||(character=='/')) { if(negate) { addCode('_'); } arithmeticLevel2(); } addCode(operator); } while((character=='+')||(character=='-')); } /** * Scans relation level. * * @exception ParserException */ private void relationLevel() throws ParserException { char code = (char) 0; if(INRELATION) { throw new ParserException(INVALID_OPERATOR); } INRELATION = true; if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } switch(character) { case '=' : code = EQUAL; break; case '<' : code = LESS_THAN; getNextCharacter(); if(character=='>') { code = NOT_EQUAL; } else if(character=='=') { code = LESS_EQUAL; } else { position--; } break; case '>' : code = GREATER_THAN; getNextCharacter(); if(character=='=') { code = GREATER_EQUAL; } else { position--; } break; } scanAndParse(); INRELATION = false; if(ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } addCode(code); ISBOOLEAN = true; } /** * Scans boolean level. * * @exception ParserException */ private void booleanLevel() throws ParserException { if(!ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } char operator = character; scanAndParse(); if(!ISBOOLEAN) { throw new ParserException(INVALID_OPERAND); } switch(operator) { case '&' : addCode(AND_CODE); break; case '|' : addCode(OR_CODE); break; } } /** * Main method of scanning and parsing process. * * @exception ParserException */ private void scanAndParse() throws ParserException { boolean negate; negate = getIdentifier(); if((character!='^')&&(negate)) { addCode('_'); } do { switch(character) { case '+' : case '-' : arithmeticLevel1(); break; case '*' : case '/' : arithmeticLevel2(); break; case '^' : arithmeticLevel3(); if(negate) { addCode('_'); } break; case ',' : case ')' : return; case '=' : case '<' : case '>' : relationLevel(); break; case '&' : case '|' : booleanLevel(); break; default : throw new ParserException(OPERATOR_EXPECTED); } } while(true); } /** * Parses subfunction. */ private void parseSubFunction() { position = 0; postfix_code = ""; //$NON-NLS-1$ INRELATION = false; ISBOOLEAN = false; try { scanAndParse(); } catch(ParserException e) { error = e.getErrorCode(); if((error==SYNTAX_ERROR)&&(postfix_code=="")) { //$NON-NLS-1$ error = EXPRESSION_EXPECTED; } } if((error==NO_ERROR)&&(position!=function.length())) { error = PAREN_NOT_MATCH; } } /** * Built-in one parameter function call. * * @return the function result * @param function the function index * @param parameter the parameter to the function */ private double builtInFunction(int function, double parameter) { switch(function) { case 0 : if(radian) { return Math.sin(parameter); } return Math.sin(parameter*DEGTORAD); case 1 : if(radian) { return Math.cos(parameter); } return Math.cos(parameter*DEGTORAD); case 2 : if(radian) { return Math.tan(parameter); } return Math.tan(parameter*DEGTORAD); case 3 : return Math.log(parameter); case 4 : return Math.log(parameter)/LOG10; case 5 : return Math.abs(parameter); case 6 : return Math.rint(parameter); case 7 : return parameter-Math.rint(parameter); case 8 : if(radian) { return Math.asin(parameter); } return Math.asin(parameter)/DEGTORAD; case 9 : if(radian) { return Math.acos(parameter); } return Math.acos(parameter)/DEGTORAD; case 10 : if(radian) { return Math.atan(parameter); } return Math.atan(parameter)/DEGTORAD; case 11 : return(Math.exp(parameter)-Math.exp(-parameter))/2; case 12 : return(Math.exp(parameter)+Math.exp(-parameter))/2; case 13 : double a = Math.exp(parameter); double b = Math.exp(-parameter); return(a-b)/(a+b); case 14 : return Math.log(parameter+Math.sqrt(parameter*parameter+1)); case 15 : return Math.log(parameter+Math.sqrt(parameter*parameter-1)); case 16 : return Math.log((1+parameter)/(1-parameter))/2; case 17 : return Math.ceil(parameter); case 18 : return Math.floor(parameter); case 19 : return Math.round(parameter); case 20 : return Math.exp(parameter); case 21 : return parameter*parameter; case 22 : return Math.sqrt(parameter); case 23 : if(parameter==0.0d) { return 0; } else if(parameter>0.0d) { return 1; } else { return -1; } case 24 : if(parameter<0) { return 0; } return 1; // added by W. Christian for step function case 25 : return parameter*Math.random(); // added by W. Christian for random function default : error = CODE_DAMAGED; return Double.NaN; } } /** * Built-in two parameters extended function call. * * @return the function result * @param function the function index * @param param1 the first parameter to the function * @param param2 the second parameter to the function */ private double builtInExtFunction(int function, double param1, double param2) { switch(function) { case 0 : return Math.min(param1, param2); case 1 : return Math.max(param1, param2); case 2 : return Math.IEEEremainder(param1, param2); case 3 : return Math.atan2(param1, param2); default : error = CODE_DAMAGED; return Double.NaN; } } /** * Evaluates subfunction. * * @return the result of the subfunction */ private double evaluateSubFunction() { // double stack[]; moved by W. Christian int stack_pointer = -1; int code_pointer = 0; int destination; char code; // stack = new double[STACK_SIZE]; moved by W. Christian int codeLength = postfix_code.length(); // added bt W. Christian to check the length. while(true) { try { if(code_pointer==codeLength) { return stack[0]; // added by W. Christian. Do not use doing an Exception! } code = postfix_code.charAt(code_pointer++); } catch(StringIndexOutOfBoundsException e) { return stack[0]; } try { switch(code) { case '+' : stack[stack_pointer-1] += stack[stack_pointer]; stack_pointer--; break; case '-' : stack[stack_pointer-1] -= stack[stack_pointer]; stack_pointer--; break; case '*' : stack[stack_pointer-1] *= stack[stack_pointer]; stack_pointer--; break; case '/' : if(stack[stack_pointer]!=0) { stack[stack_pointer-1] /= stack[stack_pointer]; } else { stack[stack_pointer-1] /= 1.0e-128; // added by W.Christian to trap for divide by zero. } stack_pointer--; break; case '^' : stack[stack_pointer-1] = Math.pow(stack[stack_pointer-1], stack[stack_pointer]); stack_pointer--; break; case '_' : stack[stack_pointer] = -stack[stack_pointer]; break; case JUMP_CODE : destination = code_pointer+postfix_code.charAt(code_pointer++); while(code_pointer<destination) { if(postfix_code.charAt(code_pointer++)==NUMERIC) { numberindex++; } } break; case LESS_THAN : stack_pointer--; stack[stack_pointer] = (stack[stack_pointer]<stack[stack_pointer+1]) ? 1.0 : 0.0; break; case GREATER_THAN : stack_pointer--; stack[stack_pointer] = (stack[stack_pointer]>stack[stack_pointer+1]) ? 1.0 : 0.0; break; case LESS_EQUAL : stack_pointer--; stack[stack_pointer] = (stack[stack_pointer]<=stack[stack_pointer+1]) ? 1.0 : 0.0; break; case GREATER_EQUAL : stack_pointer--; stack[stack_pointer] = (stack[stack_pointer]>=stack[stack_pointer+1]) ? 1.0 : 0.0; break; case EQUAL : stack_pointer--; stack[stack_pointer] = (stack[stack_pointer]==stack[stack_pointer+1]) ? 1.0 : 0.0; break; case NOT_EQUAL : stack_pointer--; stack[stack_pointer] = (stack[stack_pointer]!=stack[stack_pointer+1]) ? 1.0 : 0.0; break; case IF_CODE : if(stack[stack_pointer--]==0.0) { destination = code_pointer+postfix_code.charAt(code_pointer++); while(code_pointer<destination) { if(postfix_code.charAt(code_pointer++)==NUMERIC) { numberindex++; } } } else { code_pointer++; } break; case ENDIF : break; // same as NOP case AND_CODE : stack_pointer--; if((stack[stack_pointer]!=0.0)&&(stack[stack_pointer+1]!=0.0)) { stack[stack_pointer] = 1.0; } else { stack[stack_pointer] = 0.0; } break; case OR_CODE : stack_pointer--; if((stack[stack_pointer]!=0.0)||(stack[stack_pointer+1]!=0.0)) { stack[stack_pointer] = 1.0; } else { stack[stack_pointer] = 0.0; } break; case NOT_CODE : stack[stack_pointer] = (stack[stack_pointer]==0.0) ? 1.0 : 0.0; break; case NUMERIC : stack[++stack_pointer] = number[numberindex++]; break; case PI_CODE : stack[++stack_pointer] = Math.PI; break; case E_CODE : stack[++stack_pointer] = Math.E; break; default : if(code>=REF_OFFSET) { stack[++stack_pointer] = refvalue[code-REF_OFFSET]; } else if(code>=VAR_OFFSET) { stack[++stack_pointer] = var_value[code-VAR_OFFSET]; } else if(code>=EXT_FUNC_OFFSET) { stack[stack_pointer-1] = builtInExtFunction(code-EXT_FUNC_OFFSET, stack[stack_pointer-1], stack[stack_pointer]); stack_pointer--; } else if(code>=FUNC_OFFSET) { stack[stack_pointer] = builtInFunction(code-FUNC_OFFSET, stack[stack_pointer]); } else { error = CODE_DAMAGED; return Double.NaN; } } } catch(ArrayIndexOutOfBoundsException oe) { error = STACK_OVERFLOW; return Double.NaN; } catch(NullPointerException ne) { error = CODE_DAMAGED; return Double.NaN; } } } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */