Search code examples
javabcpostfix-operator

bc utility in java, fails for some cases


I am trying to write simple bc utility in java. My program works fine for most cases but is failing for expression 4+4*3-3-2-1/3. basically consecutive - operators. not sure if i covered all edge cases. any suggestions to make it better would be very helpful. Thanks

//ENUM for operations allowed.
private enum Operators {
    ADD         (1, "+"),
    SUBTRACT   (1, "-"),
    MULTIPLY    (2, "*"),
    DIVIDE      (2, "/");

    String operator;
    // Operator priority
    int weight;
    Operators(int w, String o) {
        this.operator = o;
        this.weight = w;
    }

    public String getOperator() {
        return operator;
    }

    public int getWeight() {
        return weight;
    }
}
// Stack and Queue to convert expression to postfix
LinkedList<String> resultQueue = new LinkedList<String>();
Stack<Operators> operatorStack = new Stack<Operators>();
//Stack to evaluate postfix expression
Stack<Integer> evalStack = new Stack<Integer>();

public int eval(String exp) {
    char[] expChar = exp.toCharArray();
    int i = 0;
    StringBuilder sb = new StringBuilder();
    while (i < expChar.length){
        if (expChar[i] >= '0' && expChar[i] <= '9') {
            sb.append(expChar[i]);

            if (i == expChar.length - 1) {
                resultQueue.add(sb.toString());
                while (!operatorStack.isEmpty()){
                    resultQueue.add(operatorStack.pop().getOperator());
                }
            }
        } else if (expChar[i] == '+' || expChar[i] == '-' || expChar[i] == '/' || expChar[i] == '*'){
            if (sb.length() > 0) {
                resultQueue.add(sb.toString());
                sb = new StringBuilder();
            }
                pushOperator(expChar[i]);
        } else
        return 0;
        i++;
    }
    for (String r : resultQueue){
        if (r.charAt(0) > '0' && r.charAt(0) < '9'){
            evalStack.push(Integer.valueOf(r));
        }else {
            try {
                int op2 = evalStack.pop();
                int op1 = evalStack.pop();
                if (r.charAt(0) == '+')
                    evalStack.push(Integer.valueOf(op1+op2));
                else if (r.charAt(0) == '-')
                    evalStack.push(Integer.valueOf(op1-op2));
                else if (r.charAt(0) == '/')
                    evalStack.push(Integer.valueOf(op1/op2));
                else if (r.charAt(0) == '*')
                    evalStack.push(Integer.valueOf(op1*op2));
            } catch (Exception e){
                System.out.println("Parse Error");
                return 0;
            }
        }
    }
    int result=0;
    try{
        result = evalStack.pop();
    }catch (Exception e){
        System.out.println("Parse Error");
    }

    return result;
}

private void pushOperator(char c) {
    Operators val = getOperator(c);
    if (operatorStack.isEmpty() || val.getWeight() >= operatorStack.peek().getWeight()){
        operatorStack.push(val);
    } else {
        while (!operatorStack.isEmpty() && val.getWeight() <= operatorStack.peek().getWeight()){
            resultQueue.add(operatorStack.pop().getOperator());
        }
        operatorStack.push(val);
    }
}

private Operators getOperator(char c) {
    for (Operators o: Operators.values()){
        if (o.getOperator().equals(String.valueOf(c)))
            return o;
    }
    throw new IllegalArgumentException("Operator not supported");
}

public static void main(String[] args) {

    BC bc = new BC();
    System.out.println("Please enter expression: ");
    Scanner scn = new Scanner(System.in);
    String exp = scn.nextLine();
    //remove white spaces
    exp = exp.replaceAll(" ", "");
    System.out.println("Result: " + bc.eval(exp));
    scn.close();
}

Solution

  • The problem is that for consecutive minus operations you compute in the wrong direction. You compute from right to left whereas your need to computer from left to right.

    If you have on the stack the operants 12 3 2 0 - - - you compute effectively

    12 - (3 - (2 - 0)) = 11
    

    wheras correct would be

    12 - (3 + 2 + 0) = 7
    

    One possible soulution to fix it would be. Just before you start the computation you need to change the operants on the stack to 12 3 2 0 + + -.

    See this snippet which could be executed before the computation loop.

    // your code
    ...
        }
        i++;
    }
    
    for (i = 0; i < resultQueue.size() - 1; i++) {
        if ("-".equals(resultQueue.get(i)) && "-".equals(resultQueue.get(i+1))) {
            resultQueue.set(i, "+");
        }
    }
    
    // your code
    for (String r : resultQueue) {
        if (r.charAt(0) > '0' && r.charAt(0) < '9') {
    ...