# Calculadora prefija (c) Baltasar 2022 MIT License <jbgarcia@uvigo.es>
"""
Esta calculadora acepta entradas del tipo: + 4 5
Para calcularlas como 4 + 5 y devolver 9.
La ventaja de los formatos prefijo (los operadores preceden a los operandos),
y postfijo (los operadores siguen a los operandos), es que no necesitan paréntesis.
Un ejemplo de expresión compleja sería: + 1 * 4 / 6 2, que sería evaluada como:
+ 1 * 4 / 6 2 => + 1 * 4 3 => + 1 12 = 13
"""


import math


def opera(opr: str, op1: float, op2: float) -> float:
    """
    Calcula una expresión simple, como 4 * 2.
    :param opr: El operador, como *, +, -...
    :param op1: Un num. real.
    :param op2: Un num. real.
    :return: El resultado.
    """
    match = {
        '+': lambda x, y: x + y,
        '-': lambda x, y: x - y,
        '*': lambda x, y: x * y,
        '/': lambda x, y: x / y if y != 0 else math.nan,
        '^': lambda x, y: math.pow(op1, op2),
    }

    fn = match.get(opr)
    return math.nan if not fn else fn(op1, op2)


def calculadora_prefija_iterativa(lexpr: list) -> float:
    """
    Acepta una lista de operadores y operandos y devuelve el resultado.
    Funciona empleando una pila de operaciones. Cada elemento de la pila
    es una lista [opr, op1, op2], donde opr es el operador, op1 y op2 los operandos.
    Cada nuevo operador encontrado añade una nueva entrada en la lista, mientras
    que un operando puede suponer añadirlo a la operación actual e incluso
    resolverla, si se trata del último operando (op2).
    :param lexpr: Una lista de operadores y operandos, como texto.
    :return: El resultado numérico.
    """
    operadores = "+-*/^"
    parciales = []
    current = None

    while lexpr:
        opn = lexpr[0]
        if opn in operadores:
            current = [opn]
            parciales.append(current)
        else:
            current.append(float(opn))

            while current and len(current) == 3:
                res = opera(current[0], current[1], current[2])
                parciales.pop()
                if parciales:
                    current = parciales[-1]
                    current.append(res)
                else:
                    current = None
                    parciales.append(res)

        lexpr.pop(0)

    if len(parciales) == 1:
        return parciales[0]
    else:
        return math.nan


def calculadora_prefija_recursiva(lexpr: list) -> float:
    """
    Acepta una lista de operadores y operandos y devuelve el resultado.
    :param lexpr: Una lista de operadores y operandos, como texto.
    :return: El resultado numérico.
    """
    opr = lexpr.pop(0) if lexpr else ""

    # Primer operando
    op1 = lexpr[0] if lexpr else str(math.nan)

    try:
        op1 = float(op1)
        if lexpr: lexpr.pop(0)
    except ValueError:
        op1 = calculadora_prefija_recursiva(lexpr)

    # Segundo operando
    op2 = lexpr[0] if lexpr else str(math.nan)

    try:
        op2 = float(op2)
        if lexpr: lexpr.pop(0)
    except ValueError:
        op2 = calculadora_prefija_recursiva(lexpr)

    return opera(opr, op1, op2)


if __name__ == "__main__":
    expr = input("Dame un expr.: ").strip()
    lexpr = expr.strip().split()

    if lexpr:
        try:
            float(lexpr[0])
            lexpr.reverse()
        except ValueError:
            pass

        res = calculadora_prefija_iterativa(lexpr)
        print(expr, "=", res)
    else:
        print("Expr. incorrecta")
