diff --git a/calc/__init__.py b/calc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calc/engine/__init__.py b/calc/engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calc/engine/builtins.py b/calc/engine/builtins.py new file mode 100644 index 0000000..bfd1606 --- /dev/null +++ b/calc/engine/builtins.py @@ -0,0 +1,26 @@ +import math + +SAFE_NAMES = { + # trig (radians) + "sin": math.sin, "cos": math.cos, "tan": math.tan, + "asin": math.asin, "acos": math.acos, "atan": math.atan, + "atan2": math.atan2, + # trig (degrees) + "sind": lambda x: math.sin(math.radians(x)), + "cosd": lambda x: math.cos(math.radians(x)), + "tand": lambda x: math.tan(math.radians(x)), + # hyperbolic + "sinh": math.sinh, "cosh": math.cosh, "tanh": math.tanh, + # logs & exp + "log": math.log, "log2": math.log2, "log10": math.log10, + "ln": math.log, "exp": math.exp, + # powers & roots + "sqrt": math.sqrt, "pow": math.pow, + # rounding + "abs": abs, "round": round, "floor": math.floor, "ceil": math.ceil, + # misc + "factorial": math.factorial, "gcd": math.gcd, + "degrees": math.degrees, "radians": math.radians, + # constants + "pi": math.pi, "e": math.e, "tau": math.tau, "inf": math.inf, +} diff --git a/calc/engine/evaluator.py b/calc/engine/evaluator.py new file mode 100644 index 0000000..3a5280a --- /dev/null +++ b/calc/engine/evaluator.py @@ -0,0 +1,22 @@ +from calc.engine.builtins import SAFE_NAMES + + +def evaluate(expr: str, extra_names: dict = None) -> tuple[str, bool]: + """ + Evaluate a math expression safely. + Returns (result_string, is_error). + """ + namespace = dict(SAFE_NAMES) + if extra_names: + namespace.update(extra_names) + try: + result = eval(expr, {"__builtins__": {}}, namespace) + return str(result), False + except ZeroDivisionError: + return "Error: Division by zero", True + except ValueError as err: + return f"Error: {err}", True + except NameError as err: + return f"Error: {err}", True + except Exception as err: + return f"Error: {err}", True