Пример #1
0
class Calculator:
    """
    Takes a string input in correct format and returns an answer.
    Parsing included.
    """

    def __init__(self):
        self.functions = {
            'EXP': Function(numpy.exp),
            'LOG': Function(numpy.log),
            'SIN': Function(numpy.sin),
            'COS': Function(numpy.cos),
            'SQRT': Function(numpy.sqrt),
            'ABS': Function(numpy.abs)
        }

        self.operators = {
            'PLUSS': Operator(numpy.add, strength=0),
            'MINUS': Operator(numpy.subtract, strength=0),
            'DELE': Operator(numpy.divide, strength=1),
            'GANGE': Operator(numpy.multiply, strength=1),
        }

        # Parse text to fills this queue with RPN,
        # the evaluate_output_queue evaluates it to find answer
        self.output_queue = Queue()

    def calculate(self):
        """The running of the calculator"""
        print("Welcome to 'COOLCULATOR'.\n" +
              "Exit by pressing 'ENTER' without providing input\n" +
              "All operators are written in norwegian!\n" +
              "(E.g. '+' is written 'pluss')")
        equation = " "
        while equation != "":
            equation = input(">>> ")
            try:
                self.output_queue_generator(self.parse_string_to_list(equation))
                answer = self.evaluate_output_queue()
                print(">>>", answer)
            except IndexError:
                pass

    def evaluate_output_queue(self):
        """Evaluates the RPN in the queue"""
        stack = Stack()
        while not self.output_queue.is_empty():
            elem = self.output_queue.pop()
            if isinstance(elem, numbers.Number):
                stack.push(elem)
            elif isinstance(elem, Function):
                _input = stack.pop()
                stack.push(elem.execute(_input))
            elif isinstance(elem, Operator):
                _input_1 = stack.pop()
                _input_2 = stack.pop()
                stack.push(elem.execute(_input_2, _input_1))

        return stack.pop()

    def output_queue_generator(self, input_list):
        """
        Uses the shunting yard algorithm to tak a standard list of calculations
        and turn it intro a list of RPN, to be calculated
        :param input_list: a list of numbers, parentheses, operators and functions as its elements
        """
        self.output_queue = Queue()
        operator_stack = Stack()

        for elem in input_list:

            if isinstance(elem, numbers.Number):
                self.output_queue.push(elem)

            elif isinstance(elem, Function):
                operator_stack.push(elem)

            elif elem == '(':
                operator_stack.push(elem)

            elif elem == ')':
                stack_elem = operator_stack.pop()
                while stack_elem != '(':
                    self.output_queue.push(stack_elem)
                    stack_elem = operator_stack.pop()

            elif isinstance(elem, Operator):
                if not operator_stack.is_empty():
                    top = operator_stack.peek()
                    while (top is not None) and self.precedence_calculator(top, elem):
                        self.output_queue.push(operator_stack.pop())
                        if not operator_stack.is_empty():
                            top = operator_stack.peek()
                        else:
                            top = None
                operator_stack.push(elem)

        while not operator_stack.is_empty():
            item = operator_stack.pop()
            self.output_queue.push(item)

    def parse_string_to_list(self, input_string):
        """
        Parses string to be used in 'output_queue_generator'
        :param input_string: a user-written string to be calculated; assumed correct format
        :return: a string of numbers, functions and operators in a 'normal' syntax
        """

        # Make the string uppercase with no spaces,
        # ready for regex; re methods
        input_string = input_string.replace(" ", "").upper()

        regex_list = (
            '|'.join(
                self.functions.keys()),
            '|'.join(
                self.operators.keys()),
            r'\(',
            r'\)',
            r'\d+\.\d+',        # Positive float
            r'-\d+\.\d+',       # Negative float
            r'\d+',             # Positive integer
            r'-\d+')            # Negative integer

        regex = '|'.join(regex_list)

        # re.findall returns a list containing all matches
        matches = re.findall(regex, input_string)
        result = []
        for match in matches:
            # print(match)
            if match in self.functions.keys():
                result.append(self.functions[match])

            elif match in self.operators.keys():
                result.append(self.operators[match])

            elif match in ('(', ')'):
                result.append(match)

            else:   # It's a number or trash
                try:
                    result.append(float(match))
                except ValueError:
                    pass

        return result

    @staticmethod
    def precedence_calculator(top, elem):
        """
        :param top: top element of stack, can be function, operator, number
        :param elem: is a operator with a strength
        :return: if top has precedence over elem
        """

        if isinstance(top, (numbers.Number, Function)) or top in ('(', ')'):
            return False
        if isinstance(top, Operator):
            return top.strength > elem.strength
Пример #2
0
class Calculator():
    '''
    The calculator takes a string in the form of an equation, parses it into
    operators, operands, functions and parentheses. It then converts from infix
    to reverse polish notation and evaluates the expression.
    '''
    def __init__(self, debug=False):
        self.functions = {
            'EXP': Function(np.exp),
            'LOG': Function(np.log),
            'SIN': Function(np.sin),
            'COS': Function(np.cos),
            'SQRT': Function(np.sqrt),
            'ABS': Function(np.abs)
        }

        self.constants = {'PI': math.pi, 'TAU': math.tau, 'E': math.e}

        self.operators = {
            '+': Operator(np.add, strength=0),
            '~': Operator(np.subtract, strength=0),
            '/': Operator(np.divide, strength=1),
            '*': Operator(np.multiply, strength=1),
        }

        self.output_queue = Queue()

        self.debug = debug

    def calculate(self):
        '''
        Calculate the value of the RPN-equation stored in output_queue.
        '''
        stack = Stack()
        while not self.output_queue.is_empty():
            elem = self.output_queue.pop()

            if isinstance(elem, numbers.Number):
                stack.push(elem)

            if isinstance(elem, Function):
                _input = stack.pop()
                stack.push(elem.execute(_input))

            if isinstance(elem, Operator):
                _second = stack.pop()
                _first = stack.pop()
                stack.push(elem.execute(_first, _second))
        return stack.pop()

    def generate_output_queue(self, input_list):
        '''
        Converts a list of operators, functions, operands, constants and
        parentheses from infix notation to reverse polish notation using the
        shunting-yard algorithm
        '''
        def operator_precedence(top, elem):
            '''
            Function to determine wether to pop from op_stack
            '''
            precedence = False
            precedence |= isinstance(top, Function)
            if isinstance(top, Operator):
                precedence |= top.strength >= elem.strength
            precedence &= top != '('

            return precedence

        self.output_queue = Queue()
        op_stack = Stack()

        for elem in input_list:
            if isinstance(elem, numbers.Number):
                self.output_queue.push(elem)

            if isinstance(elem, Function):
                op_stack.push(elem)

            if isinstance(elem, Operator):
                if not op_stack.is_empty():
                    top = op_stack.peek()
                    while top is not None and operator_precedence(top, elem):
                        self.output_queue.push(op_stack.pop())
                        if not op_stack.is_empty():
                            top = op_stack.peek()
                        else:
                            top = None
                op_stack.push(elem)

            if elem == '(':
                op_stack.push(elem)

            if elem == ')':
                next_op = op_stack.pop()
                while next_op != '(':
                    self.output_queue.push(next_op)
                    next_op = op_stack.pop()

        while not op_stack.is_empty():
            elem = op_stack.pop()
            self.output_queue.push(elem)

        if self.debug:
            print(f'\nParsed string: {input_list}')
            print(f'Output queue:  {self.output_queue._items}\n')

    def parse_string_to_list(self, input_str):
        '''
        Parse input_str into a list of operators, operands, parentheses,
        constants and functions, using regular expressions. Then substitute the
        functions and operators found with their corresponding wrapper object.
        Strings in the form of positive or negative integers/floats are
        converted to float.
        '''

        re_parts = (
            r'-?\d+\.\d+',  # Floating point numbers
            r'-?\d+',  # Integers
            r'\(|\)',  # Parentheses
            r'\+|\~|\*|/|',  # Operators
            '|'.join(self.functions.keys()),  # Functions
            '|'.join(self.constants.keys()),  # Constants
        )

        regex = '|'.join(re_parts)

        # re.findall preserves the order of the matches
        matches = re.findall(regex, input_str.upper())

        result = []
        for match in matches:
            # Function
            if match in self.functions.keys():
                result += [self.functions[match]]

            # Operator
            elif match in self.operators.keys():
                result += [self.operators[match]]

            # Constants
            elif match in self.constants.keys():
                result += [self.constants[match]]

            # Parentheses
            elif match in ('(', ')'):
                result += [match]

            # Probably a number
            else:
                try:
                    result += [float(match)]
                except ValueError:
                    pass

        return result