# https://[Log in to view URL]

import re
import math
import operator

funcs = {
    "sin": math.sin,
    "cos": math.cos,
    "tan": math.tan,
    "asin": math.asin,
    "acos": math.acos,
    "atan": math.atan,
    "ln": math.log,
    "log": math.log10,
    "sqrt": math.sqrt,
    "exp": math.exp,
    "abs": abs,
    "sinh": math.sinh,
    "cosh": math.cosh,
    "tanh": math.tanh,
}
operators = {
    "u": {
        "priority": 4,
        "associativity": "right",
    },
    "^": {"priority": 4, "associativity": "right", "name": "pow"},
    "*": {"priority": 3, "associativity": "left", "name": "mul"},
    "/": {"priority": 3, "associativity": "left", "name": "div"},
    "+": {"priority": 2, "associativity": "left", "name": "add"},
    "-": {"priority": 2, "associativity": "left", "name": "sub"},
}


def tokenize(expression):
    if expression == "":
        return []
    regex = re.compile(
        r"\s*([-+*^\/\(\)]|[A-Za-z_][A-Za-z0-9_]*|\d+\.?(?:\d*)?(?:\.\d*)?(?:e[+-]?\d+)?)\s*"
    )
    tokens = regex.findall(expression)
    return [s for s in tokens if not s.isspace()]


def is_number(st):
    return re.match(r"\d+\.?(?:\d*)?(?:\.\d*)?(?:e[+-]?\d+)?", st)


def evaluate(inp):
    op_symbols = operators.keys()
    stack = []
    output = []

    peek = lambda: stack[-1] if stack else None

    def add_to_output(token):
        output.append(token)

    def handle_pop():
        op = stack.pop()

        if op == "(":
            return None
        if op == "u":
            return -float(output.pop())

        if op in funcs:
            value = output.pop()
            try:
                return funcs[op](value)
            except:
                raise ValueError("Math domain error")

        right = float(output.pop())
        left = float(output.pop())

        if op in "+-*/^":
            return getattr(operator, operators[op]["name"])(left, right)
        else:
            raise Exception("No such operator")

    def handle_token(token):
        if is_number(token):
            add_to_output(float(token))
        elif token in funcs:
            stack.append(token)
        elif token in op_symbols:
            o1 = token
            o2 = peek()

            while o2 and o2 != '(' and (
                operators[o2]["priority"] > operators[o1]["priority"]
                or (
                    operators[o2]["priority"] == operators[o1]["priority"]
                    and operators[o1]["associativity"] == "left"
                )
            ):
                add_to_output(handle_pop())
                o2 = peek()
            stack.append(o1)
        elif token == "(":
            stack.append(token)
        elif token == ")":
            top = peek()
            while top != "(":
                assert len(stack) > 0, "Empty stack"
                try:
                    add_to_output(handle_pop())
                    top = peek()
                except:
                    raise Exception("Something went wrong")
            assert peek() == "(", "Parenthesis problem!"
            try:
                handle_pop()
            except:
                raise Exception("Bad parenthesis problem")
            top = peek()
            if top and top in funcs:
                try:
                    add_to_output(handle_pop())
                except:
                    raise ValueError(f"Cannot apply function :{top}")
        else:
            raise Exception(f"Invalid token: {token}")

    tokens = tokenize(inp)


    try:        
        prev_token = None
        while tokens:
            token = tokens.pop(0)
            # Unary minus handling
            if token == "-" and (
                prev_token is None or prev_token == "(" or prev_token in op_symbols
            ):
                handle_token("u")
            else:
                handle_token(token)
            prev_token = token
        while stack:
            assert peek() != "(", "Parenthesis problem"
            add_to_output(handle_pop())
        return output[0]
    except:
        return "ERROR"


print(tokenize(""))
tests = [
    ('-3.1*sin(2* 4*cos(-5 + 2e-1))', '-1.9970555481625483'),
    ("4 ^ 3 ^ 2", "262144"),
    ("-5^2", "-25"),
    ("-1+2", "1"),
    ("1e-2", "0.01"),
    ("5 -- 6", "11"),
    ("2^3", "8"),
    ("2 * 3 + 2 ^ 2 * 4", "22"),
    ("sqrt (sin(2 + 3)*cos (1+2)) * 4 ^ 2", "15.5893529757165"),
    ("((2 + 3) * (1 + 2)) * 4 ^ 2", "240"),
    ("((2 + 3) * (1 + 2)) * 4 ^ -2", "0.9375"),
    
    ("sqrt (-2)", "ERROR"),
    ("1 / 0", "ERROR"),
    ("abs(-2 * 1e-3)", "0.002"),
    ("---1", "-1"),
    ("sqrt(-1)", "ERROR"),
    ("sqrt(-5^(-11+1--1+--11))", "ERROR"),
    ("6--2", "8"),
    ("(6--2*1e-3)^2", "36.024004"),
    ("(-1--2*1e-3)^2", "0.996004"),
    ("abs(-(-1+(2*(4--3)))^2)", "169")
]
for t, r in tests:
    print(evaluate(t), r, "\n")

Embed on website

To embed this program on your website, copy the following code and paste it into your website's HTML: