Exemplo n.º 1
0
    def test_simple_function(self):
        expression = "sin 5"
        computed_token_list = tokenize(expression)
        postfix_token_list = infix_to_postfix(computed_token_list)

        token_list = [
            tokens.OperandToken(5),
            tokens.SinFunctionToken(),
        ]

        self.assertListEqual(postfix_token_list, token_list)
Exemplo n.º 2
0
    def test_simple_operator(self):
        expression = "2 + 1"
        computed_token_list = tokenize(expression)
        postfix_token_list = infix_to_postfix(computed_token_list)

        token_list = [
            tokens.OperandToken(2),
            tokens.OperandToken(1),
            tokens.PlusOperatorToken(),
        ]

        self.assertListEqual(postfix_token_list, token_list)
Exemplo n.º 3
0
    def test_parenthesis(self):
        expression = "2 * (1 + 5)"
        computed_token_list = tokenize(expression)
        postfix_token_list = infix_to_postfix(computed_token_list)

        token_list = [
            tokens.OperandToken(2),
            tokens.OperandToken(1),
            tokens.OperandToken(5),
            tokens.PlusOperatorToken(),
            tokens.ProductOperatorToken()
        ]

        self.assertListEqual(postfix_token_list, token_list)
Exemplo n.º 4
0
    def test_multiple_operators_reversed(self):
        expression = "2 * 1 + 5"
        computed_token_list = tokenize(expression)
        postfix_token_list = infix_to_postfix(computed_token_list)

        token_list = [
            tokens.OperandToken(2),
            tokens.OperandToken(1),
            tokens.ProductOperatorToken(),
            tokens.OperandToken(5),
            tokens.PlusOperatorToken(),
        ]

        self.assertListEqual(postfix_token_list, token_list)
Exemplo n.º 5
0
def evaluate(input, is_postfix=False):
    input = input.strip()

    if not input:
        raise RuntimeError("Missing expression")

    logger.debug("Evaluating input '%s'", input)

    # Tokenize the input
    token_list = tokenize(input)

    logger.debug("Tokens:")
    for tok in token_list:
        logger.debug("    %r", tok)

    # In Postfix notation, make sure there are no parenthesis, variables or equal signs
    if is_postfix:
        for tok in token_list:
            if isinstance(
                    tok,
                (tokens.OpenParenthesisToken, tokens.CloseParenthesisToken)):
                raise RuntimeError(
                    "Parenthesis are not allowed in postfix expressions")

            if isinstance(tok, (tokens.EqualSignToken, tokens.VariableToken)):
                raise RuntimeError(
                    "Equations cannot be written in postfix notation")

    # Decide whether it's an expression or an equation
    is_equation = False

    # For equations, convert the equal sign into a "minus" sign and surround the RHS of the equation in parenthesis
    for i, tok in enumerate(token_list):
        if isinstance(tok, tokens.EqualSignToken):
            logger.debug("Detected equation")
            is_equation = True
            token_list.pop(i)
            token_list.insert(i, tokens.MinusOperatorToken())
            token_list.insert(i + 1, tokens.OpenParenthesisToken())
            token_list.append(tokens.CloseParenthesisToken())
            break

    # Equation testing
    if is_equation:

        # Make sure there are variables if it's an equation
        for i, tok in enumerate(token_list):
            if isinstance(tok, tokens.VariableToken):
                break
        else:
            raise RuntimeError("Incorrect equation: missing variable")

        # Make sure there are no more equal signs
        if len([x for x in token_list
                if isinstance(x, tokens.EqualSignToken)]):
            raise RuntimeError(
                "Incorrect equation: more than one equal sign found")

        # Make sure all variables are the same
        if len(
                set([
                    x.value
                    for x in token_list if isinstance(x, tokens.VariableToken)
                ])) > 1:
            raise RuntimeError("Only one variable allowed")

    # Make sure there are no variables if it's not an equation
    if not is_equation:
        for i, tok in enumerate(token_list):
            if isinstance(tok, tokens.VariableToken):
                raise RuntimeError(
                    "Variable '{}' found in non-equation".format(tok.value))

    if not is_postfix:
        # Even if it's not postfix, run a dumb detection algorithm just to make sure
        for i, tok in enumerate(token_list[:-1]):
            if isinstance(tok, tokens.OperandToken) and isinstance(
                    token_list[i + 1], tokens.OperandToken):
                logger.debug("Expression already in postfix")
                is_postfix = True
                break

        if not is_postfix:
            logger.debug("Converting to postfix")
            token_list = infix_to_postfix(token_list)

    logger.debug("Tokens in postfix:")
    for tok in token_list:
        logger.debug("    %r", tok)

    stack = []
    logger.debug("Evaluating token list:")
    for tok in token_list:
        logger.debug("")
        logger.debug("Stack: %r", stack)
        logger.debug("%r", tok)

        node = None

        if tokens.is_operand(tok):
            node = EquationNode(constant=tok.value)

        elif tokens.is_variable(tok):
            node = EquationNode(coefficient=1)

        elif tokens.is_constant(tok):
            node = EquationNode(constant=tok.value)

        else:
            if tokens.is_operator(tok):
                try:
                    second_operand = stack.pop()
                    first_operand = stack.pop()

                except IndexError:
                    raise RuntimeError("Bad expression, missing operands")

                logger.debug("  Oper A: %r", first_operand)
                logger.debug("  Oper B: %r", second_operand)

                if isinstance(tok, tokens.PlusOperatorToken):
                    constant = None
                    coefficient = None

                    if first_operand.constant is not None:
                        constant = first_operand.constant

                    if second_operand.constant is not None:
                        if constant is None: constant = 0
                        constant += second_operand.constant

                    if first_operand.coefficient is not None:
                        coefficient = first_operand.coefficient

                    if second_operand.coefficient is not None:
                        if coefficient is None: coefficient = 0
                        coefficient += second_operand.coefficient

                    node = EquationNode(coefficient=coefficient,
                                        constant=constant)

                elif isinstance(tok, tokens.MinusOperatorToken):
                    constant = None
                    coefficient = None

                    if first_operand.constant is not None:
                        constant = first_operand.constant

                    if second_operand.constant is not None:
                        if constant is None: constant = 0
                        constant -= second_operand.constant

                    if first_operand.coefficient is not None:
                        coefficient = first_operand.coefficient

                    if second_operand.coefficient is not None:
                        if coefficient is None: coefficient = 0
                        coefficient -= second_operand.coefficient

                    node = EquationNode(coefficient=coefficient,
                                        constant=constant)

                elif isinstance(tok, tokens.ProductOperatorToken):
                    constant = None
                    coefficient = None

                    if first_operand.has_constant():
                        if second_operand.has_constant():
                            if constant is None: constant = 0
                            constant += first_operand.constant * second_operand.constant

                        if second_operand.has_coef():
                            if coefficient is None: coefficient = 0
                            coefficient += first_operand.constant * second_operand.coefficient

                    if first_operand.has_coef():
                        if second_operand.has_constant():
                            if coefficient is None: coefficient = 0
                            coefficient += first_operand.coefficient * second_operand.constant

                        if second_operand.has_coef():
                            raise RuntimeError(
                                "Unsupported expression, can't have exponential variables"
                            )

                    node = EquationNode(coefficient=coefficient,
                                        constant=constant)

                elif isinstance(tok, tokens.DivisionOperatorToken):
                    constant = None
                    coefficient = None

                    if first_operand.has_constant():
                        if second_operand.has_constant():
                            if constant is None: constant = 0
                            constant += first_operand.constant / second_operand.constant

                        if second_operand.has_coef():
                            raise RuntimeError(
                                "Unsupported expression, non-linear equations are not supported"
                            )

                    if first_operand.has_coef():
                        if second_operand.has_constant():
                            if coefficient is None: coefficient = 0
                            coefficient += first_operand.coefficient / second_operand.constant

                        if second_operand.has_coef():
                            raise RuntimeError(
                                "Unsupported expression, can't have exponential variables"
                            )

                    node = EquationNode(coefficient=coefficient,
                                        constant=constant)

            elif tokens.is_function(tok):
                try:
                    operand = stack.pop()

                except IndexError as e:
                    raise RuntimeError(
                        "Missing value for trigonometric function")

                # Functions are only allowed in simple expressions, not in equations, so there should be no coeffs.
                if operand.has_coef():
                    raise RuntimeError("Function not allowed in equations")

                # Make sure there's a value to work with
                if not operand.has_constant():
                    raise RuntimeError(
                        "Missing value for trigonometric function")

                if tokens.is_trigonometric(tok):
                    node = EquationNode(constant=tok.oper(operand.constant))

                elif isinstance(tok, tokens.LnFunctionToken):
                    node = EquationNode(
                        constant=math.log(operand.constant, math.e))

                elif isinstance(tok, tokens.LogFunctionToken):
                    base = 10

                    if tok.has_custom_base:
                        base = stack.pop().constant

                    node = EquationNode(
                        constant=math.log(operand.constant, base))

        if node is not None:
            logger.debug("Pushing to stack: %r", node)
            stack.append(node)

    logger.debug("Final stack %r", stack)

    arred = lambda x, n: x * (10**n) // 1 / (10**n)

    logger.info("Input: {}".format(input))

    if is_equation:
        value = -stack[-1].constant / stack[-1].coefficient
        value = arred(value, 10)
        logger.info("x = {}".format(value))

    else:
        value = stack[-1].constant
        value = arred(value, 10)
        logger.info(value)

    return value
Exemplo n.º 6
0
    def test_missing_right_parenthesis(self):
        expression = "2 * (1 + 5"
        computed_token_list = tokenize(expression)

        with self.assertRaises(RuntimeError):
            postfix_token_list = infix_to_postfix(computed_token_list)