Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """ Evaluate given RPN expression. """
- import sys
- import math
- import random
- class RPNCalculator:
- """ Reverse Polish Notation calculator. """
- # Variable ans holds result of last operation.
- ans = 0
- def operator(self, tok):
- """ Return operator handler if tok is a valid operator. Otherwise return False. """
- def stacksum(stack):
- """ Return sum of all elements on stack (++ operator). """
- acc = 0
- while stack:
- acc += stack.pop()
- return acc
- def stackproduct(stack):
- """ Return product of all elements on stack (** operator). """
- acc = 1
- while stack:
- acc *= stack.pop()
- return acc
- # Operator handlers.
- operators = {
- '++': stacksum,
- '**': stackproduct,
- '+': lambda stack: stack.pop(-2) + stack.pop(),
- '-': lambda stack: stack.pop(-2) - stack.pop(),
- '*': lambda stack: stack.pop(-2) * stack.pop(),
- '/': lambda stack: stack.pop(-2) / stack.pop(),
- '%': lambda stack: stack.pop(-2) % stack.pop(),
- '^': lambda stack: stack.pop(-2) ** stack.pop(),
- '!': lambda stack: math.factorial(stack.pop()),
- 'e': lambda stack: math.e,
- 'pi': lambda stack: math.pi,
- 'ln': lambda stack: math.log(stack.pop()),
- 'log': lambda stack: math.log(stack.pop(-2), stack.pop()),
- 'log2': lambda stack: math.log(stack.pop(), 2),
- 'log10': lambda stack: math.log(stack.pop(), 10),
- 'rand': lambda stack: random.random(),
- 'sqrt': lambda stack: math.sqrt(stack.pop()),
- 'exp': lambda stack: math.exp(stack.pop()),
- 'sin': lambda stack: math.sin(stack.pop()),
- 'cos': lambda stack: math.cos(stack.pop()),
- 'tan': lambda stack: math.tan(stack.pop()),
- 'sinh': lambda stack: math.sinh(stack.pop()),
- 'cosh': lambda stack: math.cosh(stack.pop()),
- 'tanh': lambda stack: math.tanh(stack.pop()),
- 'arcsin': lambda stack: math.asin(stack.pop()),
- 'arccos': lambda stack: math.acos(stack.pop()),
- 'arctan': lambda stack: math.atan(stack.pop()),
- 'arcsinh': lambda stack: math.asinh(stack.pop()),
- 'arccosh': lambda stack: math.acosh(stack.pop()),
- 'arctanh': lambda stack: math.atanh(stack.pop()),
- 'max': lambda stack: max(stack.pop(), stack.pop()),
- 'min': lambda stack: min(stack.pop(), stack.pop()),
- 'abs': lambda stack: abs(stack.pop()),
- 'ans': lambda stack: self.ans,
- }
- # Return operation handler if exists, otherwise return False.
- if tok in operators:
- return operators[tok]
- return False
- def eval(self, expr, stack=None):
- """ Evaluate RPN expression and return result. """
- if stack is None:
- stack = []
- for token in expr:
- # If token is a valid number, push to stack.
- try:
- num = float(token)
- stack.append(num)
- continue
- except ValueError:
- pass
- # If token is an operator, execute the operation.
- operation = self.operator(token)
- if operation:
- self.ans = operation(stack)
- stack.append(self.ans)
- continue
- # Otherwise, raise exception.
- raise SyntaxError(f'invalid token "{token}"')
- if len(stack) != 1:
- raise Warning('there should be exactly one element left on stack')
- return stack
- def interactive():
- """ Interactive mode. """
- calc = RPNCalculator()
- stack = []
- while True:
- try:
- expr = input('=> ').split()
- if not expr or expr[0] == 'quit':
- break
- elif expr[0] == 'del':
- stack.pop()
- else: stack = calc.eval(expr, stack)
- except Warning:
- # Ignore that there is more than one element left on stack.
- pass
- except BaseException as error:
- # Print error if occurred and continue.
- print(f'Error: {error}')
- finally:
- # Print out the stack.
- for i, num in enumerate(stack):
- print('{:2<}: {}'.format(i, round(num, 6)))
- def main():
- """ Evaluate expression passed as argument. If no arguments, run interactive mode. """
- if len(sys.argv) > 1:
- try:
- print('ans:', round(RPNCalculator().eval(sys.argv[1:])[0], 6))
- except (SyntaxError, Warning) as error:
- print(f'Error: {error}')
- else: interactive()
- if __name__ == '__main__':
- main()
Add Comment
Please, Sign In to add comment