/* ParseInfix.java */

import java.lang.*;
import java.io.*;
import DataStructures.*;
import Exceptions.*;

/**
 *  The ParseInfix class is a very simple demonstration of the use of stacks
 *  to process an infix expression.
 *
 *  The only allowable operations are +, -, *, and ^ (exponential).  Each
 *  operand must be a single (base 36) digit between zero and 'Z'.  The
 *  expression must appear as the first command-line argument, and must
 *  alternate between digit and operator.  Parentheses are ignored, as are
 * uninterpretable characters.  For example,
 *
 *    java ParseInfix '7+2*3^2^2+5'
 *
 *  prints "174".  The quotes are recommended to prevent the shell from
 *  expanding asterisks (or interpreting any whitespace as the start of
 *  a separate command line argument).
 *
 *  +, -, and * are left-associative; ^ is right-associative.
 *
 *  Error handling is nonexistent.  This example is compact so that it can
 *  be easily presented in a single lecture, but is no example of robust code.
 *
 *  @author Jonathan Shewchuk
 */

public class ParseInfix {
  public static void operate(Stack numStack, Stack opStack) throws Underflow {
    // Retrieve the operator and operands from the stacks.
    char operator = ((Character) opStack.top()).charValue();
    opStack.pop();
    int operand2 = ((Integer) numStack.top()).intValue();
    numStack.pop();
    int operand1 = ((Integer) numStack.top()).intValue();
    numStack.pop();
    System.out.print(operator + " ");
    // Perform the operation and push the result on the number stack.
    if (operator == '+') {                                          // Addition
      numStack.push(new Integer(operand1 + operand2));
    } else if (operator == '-') {                                // Subtraction
      numStack.push(new Integer(operand1 - operand2));
    } else if (operator == '*') {                             // Multiplication
      numStack.push(new Integer(operand1 * operand2));
    } else if (operator == '^') {                             // Exponentiation
      int result = 1;
      for (int i = 0; i < operand2; i++) {
        result = result * operand1;
      }
      numStack.push(new Integer(result));
    }
  }

  public static int precedence(char operator) {
    switch (operator) {
      case '^':  return 3;
      case '*':  case '/':  return 2;
      case '+':  case '-':  return 1;
      default:  return 0;
    }
  }

  public static void main(String[] args) throws Underflow {
    StackLi numStack = new StackLi();                  // Create a number stack
    StackLi opStack = new StackLi();                 // Create an operand stack
    // Process each character of the first command-line argument.
    for (int i = 0; i < args[0].length(); i++) {
      char c = args[0].charAt(i);
      if (Character.isDigit(c)) {
        // Print each digit; push each digit on number stack.
        System.out.print(c + " ");
        numStack.push(new Integer(Character.digit(c, 36)));
      } else if ((c == '+') || (c == '-') || (c == '*') || (c == '^')) {
        // Before pushing an operator onto the operand stack, check if
        //   operators currently atop the stack have greater precedence.
        while (!opStack.isEmpty() && (c != '^') &&
               (precedence(((Character) opStack.top()).charValue()) >=
                precedence(c))) {
          // Perform the operation atop the operand stack.
          operate(numStack, opStack);
        }
        opStack.push(new Character(c));  // Push new operation on operand stack
      }
    }
    while (!opStack.isEmpty()) {
      operate(numStack, opStack);
    }
    System.out.println("   result:" + ((Integer) numStack.top()).intValue());
  }
}