def test_size(self): stack = Stack() self.assertEqual(stack.size(), 0, "Stack size should be 0 at initialization") for i in range(1, 41): stack.push(i) stack.push(i + 1) stack.pop() self.assertEqual(stack.size(), 40, "Incorrect stack size") stack = Stack(str) self.assertEqual(stack.size(), 0, "Stack size should be 0 at initialization") for l in ["a", "d", "b", "m"]: stack.push(l) stack.pop() self.assertEqual(stack.size(), 3, "Incorrect stack size")
def test_push(self): stack = Stack() stack.push(23) stack.push(20) self.assertEqual(stack.peek(), 20, "Wrong stack push implementation") self.assertEqual(len(stack), 2, "Wrong stack push implementation") stack = Stack(bool) with self.assertRaises(TypeError): stack.push("word") for i in range(10): if i % 2 == 0: stack.push(True) else: stack.push(False) self.assertEqual(stack.size(), 10, "Wrong stack push implementation")
def evaluate_expression(expression): assert type(expression) is str, "Expression must be a string object" # check for errors in parenthesis if not check_parenthesis(expression): raise ArithmeticError("Invalid parenthesis") # using the global variable operators_priorities global operators_priorities # tokenizing the expression and initializing the two stack for the operands and the operators tokens = tokenize(expression) operands = Stack(elements_type=float) operators = Stack(elements_type=str) # iterating through the the tokens of the expression for token in tokens: # pushing the token into the right stack try: # if the token is a number push into operands stack operands.push(float(token)) # if the token is one of these '(', ')', '*', '/', '+' or '-' except ValueError: try: # if the operators' stack is empty, add the token there if operators.is_empty(): operators.push(token) # if the operator is a '(', add the token to the stack elif token == "(": operators.push(token) # if the operator is a ')', call the adjust_parenthesis method elif token == ")": adjust_parenthesis(operands, operators) # if the last operator's priority is less than or equal to the priority of the current operator, # add the current operator to the stack again elif operators_priorities[operators.peek()] <= operators_priorities[token]: operators.push(token) # if the last operator's priority is greater than the priority of the current operator, # call the method adjust_stacks and push the operator into the operators' stack elif operators_priorities[operators.peek()] > operators_priorities[token]: adjust_stacks(operands, operators, token) operators.push(token) # if the token is not supported, raise a Value error except KeyError: raise ValueError("Non supported attribute in the expression") # when we went through all the tokens, check if there's only one value left in the operands' stack and 0 operators # in the operators' stack, only then we know we are finished if operands.size() != 1 and operators.size() != 0: # if the finishing property is not true, then we repeat the operation from the adjust_stacks method, # while we know that we are finished while operands.size() != 1 and operators.size() != 0: # popping the last operator with the last two operands operator = operators.pop() b = operands.pop() a = operands.pop() # evaluating the value of the simple expression and pushing it back in the stack if operator == "*": operands.push(a * b) elif operator == "/": operands.push(a / b) elif operator == "+": operands.push(a + b) elif operator == "-": operands.push(a - b) # if the operator is not from the supported ones, raise an exception else: raise ValueError("The attribute you are specifying is not supported") # when we are finished, the operands' stack contains only the result from the expression, so we pop it return operands.pop() # if we know we are finished, we directly pop the result, since the operands' stack contains only the # result from the expression else: return operands.pop()