# 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")
To embed this program on your website, copy the following code and paste it into your website's HTML: