class Stack:
    def __init__(self):
        "Creates a new empty Stack object"
        self._items = []
        
    def __str__(self):
        output = ""
        for x in self._items:
            output = output + str(x) + " "
        return "[ " + output + "]"

    def isEmpty(self):
        return self._items == []

    def push(self, item):
        self._items.append(item)

    def pop(self):
        return self._items.pop()

    def peek(self):
        return self._items[len(self._items) - 1]
    
    def size(self):
        return len(self._items)

def isOperator(c):
    return c in "+*-"

def applyOp (op, arg1, arg2):
    """Apply the operator to its arguments.  This only
    accepts +, * and -."""
    if op == "+":
        return arg2 + arg1
    elif op == "*":
        return arg2 * arg1
    elif op == "-":
        return arg2 - arg1
    else:
        print ("Operator " + op + " not recognized")

import string

def infixToPostfix(infixexpr):
    """Converts an infix expression into the corresponding
    postfix expression.  Tokens in the input expression are
    separated by white space."""
    prec = {}
    prec["*"] = 3
    prec["/"] = 3
    prec["+"] = 2
    prec["-"] = 2
    prec["("] = 1

    opStack = Stack()
    outputList = []

    # Notice that this splits on any whitespace, so
    # multiple spaces are OK.
    tokenList = infixexpr.split()
    
    for token in tokenList:
        # allows both upper and lowercase
        if token in string.ascii_letters or token.isdigit():
            outputList.append(token)
        elif token == '(':
            opStack.push(token)
        elif token == ')':
            topToken = opStack.pop()
            while topToken != '(':
                outputList.append(topToken)
                topToken = opStack.pop()
        else:
            while (not opStack.isEmpty()) and \
                  (prec[opStack.peek()] >= prec[token]):
                outputList.append(opStack.pop())

            opStack.push(token)

    while not opStack.isEmpty():
        outputList.append(opStack.pop())

    return ' '.join(outputList)


def postEval(postfixExpr):
    """Given a postfix expression, evaluate it"""
    stack = Stack()
    tokenList = postfixExpr.split()
    # print ("tokenList: " + str( tokenList ))
    for t in tokenList:
        if not ( t.isdigit() or t in "+-*" ):
            print ("Ill-formed expression", end=" ")
            print (" bad token " + t)
            return
    for c in tokenList:
        # print (stack)
        if c.isdigit():
            stack.push(int(c))
        elif isOperator(c):
            if stack.size() < 2:
                print ("Ill-formed expression")
                return
            arg1 = stack.pop()
            arg2 = stack.pop()
            val = applyOp(c, arg1, arg2)
            stack.push(val)
        else:
            print ("Unrecognized token: " + c)
            return
    ans = stack.pop()
    if stack.isEmpty():
        return ans
    else:
        print ("Ill-formed expression")
            
    
def calc():
    """This is a simple calculator.  Enter expressions in infix (default)
    or postfix notation.  Enter 'infix' or 'postfix' to toggle mode.  Enter
    'stop' to exit.  Tokens in your expression must be separated by spaces.
    Expressions can involve integers and any of +, *, -.  Infix expressions
    can use parentheses to disambiguate."""
    
    print (calc.__doc__)
    
    mode = "infix"
    done = False
    
    while (not done):
        if mode == "infix":
            infixExpr = input("Enter an infix expression: ")
            infixExpr = infixExpr.strip()
            if infixExpr == "stop":
                done = True
            elif infixExpr == "postfix":
                mode = "postfix"
            elif infixExpr == "infix":
                pass
            else:
                postfixExpr = infixToPostfix(infixExpr)
                print(" Evaluating the following postfix expression: ", postfixExpr)
                ans = postEval(postfixExpr)
                if ans:
                    print ("The answer is: " + str(ans))
        else:
            postfixExpr = input("Enter a postfix expression: ")
            postfixExpr = postfixExpr.strip()
            if postfixExpr == "stop":
                done = True
            elif postfixExpr == "infix":
                mode = "infix"
            elif postfixExpr == "postfix":
                pass
            else:
                ans = postEval( postfixExpr )
                if ans:
                    print ( "The answer is: " + str(ans) )
            
        
calc()

