// // Postfix.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) 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 Library General Public License for more details. You should have received a copy of the GNU Library 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 */ package visad.formula; import java.util.*; /** represents a formula in postfix notation.<P> */ class Postfix { /** code for binary operator */ static final int BINARY = 0; /** code for unary operator */ static final int UNARY = 1; /** code for function name */ static final int FUNC = 2; /** code for constant that represents number of function arguments */ static final int FUNCCONST = 3; /** code for variable, constant, or other */ static final int OTHER = 4; /** String representation of an implicit function */ private static final String IMPLICIT = " "; /** postfix tokens */ String[] tokens = null; /** postfix codes representing token types */ int[] codes = null; /** construct a Postfix object by converting infix formula */ Postfix(String formula, FormulaManager fm) throws FormulaException { // convert expression to postfix notation String[] postfix = null; int[] pfixcode = null; String infix; // convert string to char array char[] charStr = formula.toCharArray(); // remove spaces and check parentheses int numSpaces = 0; int paren = 0; for (int i=0; i<charStr.length; i++) { if (charStr[i] == ' ') numSpaces++; if (charStr[i] == '(') paren++; if (charStr[i] == ')') paren--; if (paren < 0) { throw new FormulaException("Unable to convert to postfix notation: " + "illegal placement of parentheses"); } } if (paren != 0) { throw new FormulaException("Unable to convert to postfix notation: " + "parentheses are mismatched!"); } int j = 0; int newlen = charStr.length - numSpaces; if (newlen == 0) return; char[] exp = new char[newlen]; for (int i=0; i<charStr.length; i++) { if (charStr[i] != ' ') exp[j++] = charStr[i]; } infix = new String(exp); // tokenize string String ops = "(,)"; for (int i=0; i<fm.uOps.length; i++) ops = ops + fm.uOps[i]; for (int i=0; i<fm.bOps.length; i++) ops = ops + fm.bOps[i]; StringTokenizer tokenizer = new StringTokenizer(infix, ops, true); int numTokens = tokenizer.countTokens(); // set up stacks String[] funcStack = new String[numTokens]; // function stack String[] opStack = new String[numTokens]; // operator stack int[] opCodes = new int[numTokens]; // operator code stack String[] pfix = new String[numTokens]; // final postfix ordering int[] pcode = new int[numTokens]; // final postfix codes int opPt = 0; // pointer into opStack int funcPt = 0; // pointer into funcStack int pfixlen = 0; // pointer into pfix // flag for detecting unary operators boolean unary = true; // flag for detecting no-argument functions (e.g., x()) boolean zero = false; // flag for detecting floating point numbers boolean numeral = false; // convert to postfix String ntoken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; String token = ntoken; while (token != null) { ntoken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; if (token.equals(")")) { // right paren - pop ops until left paren reached (inclusive) if (opPt < 1) { throw new FormulaException("Unable to convert to postfix " + "notation: operator stack " + "unexpectedly empty"); } int opcode = opCodes[--opPt]; String op = opStack[opPt]; while (!op.equals("(")) { pcode[pfixlen] = opcode; pfix[pfixlen++] = "" + op; if (opPt < 1) { throw new FormulaException("Unable to convert to postfix " + "notation: operator stack " + "unexpectedly empty"); } opcode = opCodes[opPt-1]; op = opStack[--opPt]; } if (opcode == FUNC) { if (funcPt < 1) { throw new FormulaException("Unable to convert to postfix " + "notation: function stack " + "unexpectedly empty"); } String f = funcStack[--funcPt]; boolean implicit; if (zero) { implicit = f.equals(IMPLICIT); pcode[pfixlen] = implicit ? FUNC : FUNCCONST; pfix[pfixlen++] = "0"; } else { int n = 1; while (f.equals(",")) { n++; if (funcPt < 1) { throw new FormulaException("Unable to convert to postfix " + "notation: function stack " + "unexpectedly empty"); } f = funcStack[--funcPt]; } implicit = f.equals(IMPLICIT); pcode[pfixlen] = implicit ? FUNC : FUNCCONST; pfix[pfixlen++] = "" + n; } if (!implicit) { pcode[pfixlen] = FUNC; pfix[pfixlen++] = f; } } unary = false; zero = false; numeral = false; } if (token.equals("(")) { // left paren - push onto operator stack opCodes[opPt] = OTHER; opStack[opPt++] = "("; unary = true; zero = false; numeral = false; } else if (token.equals(",")) { // comma - pop ops until left paren reached (exclusive), push comma if (opPt < 1) { throw new FormulaException("Unable to convert to postfix " + "notation: operator stack " + "unexpectedly empty"); } int opcode = opCodes[opPt-1]; String op = opStack[opPt-1]; while (!op.equals("(")) { pcode[pfixlen] = opcode; pfix[pfixlen++] = "" + op; opPt--; if (opPt < 1) { throw new FormulaException("Unable to convert to postfix " + "notation: operator stack " + "unexpectedly empty"); } opcode = opCodes[opPt-1]; op = opStack[opPt-1]; } funcStack[funcPt++] = ","; unary = true; zero = false; numeral = false; } else if ((unary && fm.isUnaryOp(token)) || fm.isBinaryOp(token)) { int num = -1; if (numeral && token.equals(".") && ntoken != null) { // special case for detecting floating point numbers try { num = Integer.parseInt(ntoken); } catch (NumberFormatException exc) { } } if (num > 0) { pfix[pfixlen-1] = pfix[pfixlen-1] + "." + ntoken; token = ntoken; ntoken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; unary = false; zero = false; numeral = false; } else { // operator - pop ops with higher precedence, push op boolean isUnary = (unary && fm.isUnaryOp(token)); int prec = (isUnary ? fm.getUnaryPrec(token) : fm.getBinaryPrec(token)); String sop; int scode; if (opPt < 1) { sop = null; scode = 0; } else { sop = opStack[opPt-1]; scode = opCodes[opPt-1]; } while (sop != null && prec >= (scode == UNARY ? fm.getUnaryPrec(sop) : fm.getBinaryPrec(sop))) { opPt--; pcode[pfixlen] = scode; pfix[pfixlen++] = "" + sop; if (opPt < 1) { sop = null; scode = 0; } else { sop = opStack[opPt-1]; scode = opCodes[opPt-1]; } } opCodes[opPt] = (isUnary ? UNARY : BINARY); opStack[opPt++] = token; unary = true; zero = false; numeral = false; } } else if (ntoken != null && ntoken.equals("(")) { // function - push function name and left paren if (fm.isFunction(token)) funcStack[funcPt++] = token; else { // implicit function - append token to postfix expression funcStack[funcPt++] = IMPLICIT; if (!token.equals(")")) { pcode[pfixlen] = OTHER; pfix[pfixlen++] = token; } // pop ops with higher precedence String sop; int scode; if (opPt < 1) { sop = null; scode = 0; } else { sop = opStack[opPt-1]; scode = opCodes[opPt-1]; } while (sop != null && fm.iPrec >= (scode == UNARY ? fm.getUnaryPrec(sop) : fm.getBinaryPrec(sop))) { opPt--; pcode[pfixlen] = scode; pfix[pfixlen++] = "" + sop; if (opPt < 1) { sop = null; scode = 0; } else { sop = opStack[opPt-1]; scode = opCodes[opPt-1]; } } } opCodes[opPt] = FUNC; opStack[opPt++] = "("; token = ntoken; ntoken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; unary = true; zero = true; numeral = false; } else if (!token.equals(")")) { // variable - append token to postfix expression pcode[pfixlen] = OTHER; pfix[pfixlen++] = token; unary = false; zero = false; try { int num = Integer.parseInt(token); numeral = true; } catch (NumberFormatException exc) { numeral = false; } } token = ntoken; } // pop remaining ops from stack while (opPt > 0) { pcode[pfixlen] = opCodes[opPt-1]; pfix[pfixlen++] = "" + opStack[--opPt]; } // make sure stacks are empty if (opPt != 0 || funcPt != 0) { throw new FormulaException("Unable to convert to postfix notation: " + "stacks are not empty"); } // return postfix array of tokens tokens = new String[pfixlen]; codes = new int[pfixlen]; System.arraycopy(pfix, 0, tokens, 0, pfixlen); System.arraycopy(pcode, 0, codes, 0, pfixlen); } }