public Calculator(String[] sa, Map<String, String> vars) { /* Populates the atom list. * Also collapses 2-part negative numbers into single Atoms. */ if (vars.size() < 1) throw new IllegalArgumentException("No expression supplied"); this.vars = vars; Atom atom = null, prePrevAtom; int prevIndex; NEXT_TOKEN: for (String token : sa) try { atom = new Atom(token); prevIndex = atoms.size() - 1; if (prevIndex < 0) continue; if (atoms.get(prevIndex).op != MathOp.SUBTRACT) continue; prePrevAtom = (prevIndex > 0) ? atoms.get(prevIndex - 1) : null; if (prePrevAtom != null && !TradOrLParen.contains(prePrevAtom.op)) continue; if (atom.op == null) { atoms.remove(prevIndex); atom.val *= -1; } else if (atom.op == MathOp.LPAREN) { atoms.remove(prevIndex); atoms.add(new Atom(-1L)); atoms.add(new Atom(MathOp.MULTIPLY)); } } finally { atoms.add(atom); } }
/** * If atoms[startAtomIndex] == '(', then last visited atoms will be the next top-level (un-paired) * ')'. Otherwise, all remainign atoms will be visited. Every visited atom will be removed from * 'atoms'. * * @return Value that all visited atoms reduce to. */ public long reduce(int startAtomIndex, boolean stopAtParenClose) { // Every occurence of atoms.remove() below is an instance of reduction. int i; Long prevValue = null; Atom atom; // Reduce parens via recursion i = startAtomIndex - 1; PAREN_SEEKER: while (atoms.size() >= ++i) { if (atoms.size() == i) { if (stopAtParenClose) throw new IllegalStateException("Unbalanced '" + MathOp.LPAREN + "'"); break; } atom = atoms.get(i); if (atom.op != null) switch (atom.op) { case RPAREN: if (!stopAtParenClose) throw new IllegalStateException("Unbalanced '" + MathOp.RPAREN + "'"); atoms.remove(i); break PAREN_SEEKER; case LPAREN: // Recurse. Reduction inside of reduce(). atoms.remove(i); atoms.add(i, new Atom(reduce(i, true))); break; default: // Intentionally empty } } int remaining = i - startAtomIndex; if (remaining < 1) throw new IllegalStateException("Empty expression"); // System.out.println("Need to consume " + remaining + " after parens removed"); Atom nextAtom; MathOp op; // Reduce powers i = startAtomIndex; atom = atoms.get(i); if (atom.op != null) throw new IllegalStateException( "Expected initial value expected but got operation " + atom.op); while (startAtomIndex + remaining > i + 1) { if (startAtomIndex + remaining < i + 3) throw new IllegalStateException("No operator/operand pairing remaining"); nextAtom = atoms.get(i + 1); if (nextAtom.op == null) throw new IllegalStateException("Operator expected but got value " + nextAtom.val); op = nextAtom.op; nextAtom = atoms.get(i + 2); if (nextAtom.op != null) throw new IllegalStateException("Value expected but got operator " + nextAtom.op); if (op != MathOp.POWER) { // Skip 'atom' (current) and the operand that we'll handle later i += 2; atom = nextAtom; continue; } // Reduce the operator and right operand Atoms remaining -= 2; atoms.remove(i + 1); atoms.remove(i + 1); long origVal = atom.val; atom.val = 1; for (int j = 0; j < nextAtom.val; j++) atom.val *= origVal; } // Reduce multiplication and division i = startAtomIndex; atom = atoms.get(i); if (atom.op != null) throw new IllegalStateException( "Expected initial value expected but got operation " + atom.op); while (startAtomIndex + remaining > i + 1) { if (startAtomIndex + remaining < i + 3) throw new IllegalStateException("No operator/operand pairing remaining"); nextAtom = atoms.get(i + 1); if (nextAtom.op == null) throw new IllegalStateException("Operator expected but got value " + nextAtom.val); op = nextAtom.op; nextAtom = atoms.get(i + 2); if (nextAtom.op != null) throw new IllegalStateException("Value expected but got operator " + nextAtom.op); if (op != MathOp.MULTIPLY && op != MathOp.DIVIDE && op != MathOp.REM) { // Skip 'atom' (current) and the operand that we'll handle later i += 2; atom = nextAtom; continue; } // Reduce the operator and right operand Atoms remaining -= 2; atoms.remove(i + 1); atoms.remove(i + 1); if (op == MathOp.MULTIPLY) atom.val *= nextAtom.val; else if (op == MathOp.DIVIDE) atom.val /= nextAtom.val; else atom.val %= nextAtom.val; } // Reduce addition and subtraction // Reduce the leading value atom = atoms.remove(startAtomIndex); remaining--; if (atom.op != null) throw new IllegalStateException("Value expected but got operation " + atom.op); long total = atom.val; while (remaining > 0) { // Reduce the operator Atom --remaining; atom = atoms.remove(startAtomIndex); op = atom.op; // System.err.println("Trying +/- for " + op); if (op == null) throw new IllegalStateException("Operator expected but got value " + atom.val); if (remaining <= 0) throw new IllegalStateException("No operand for operator " + op); // Reduce the right operand --remaining; atom = atoms.remove(startAtomIndex); if (atom.op != null) throw new IllegalStateException("Value expected but got operation " + atom.op); switch (op) { case ADD: total += atom.val; break; case SUBTRACT: total -= atom.val; break; default: throw new IllegalStateException("Unknown operator: " + op); } } return total; }