Created
February 20, 2026 21:02
-
-
Save lhotakj/6931b62a6e1a1125edfaa5026cc97e88 to your computer and use it in GitHub Desktop.
python_math_eval.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ast | |
| import operator as op | |
| import math | |
| OPS = { | |
| ast.Add: op.add, | |
| ast.Sub: op.sub, | |
| ast.Mult: op.mul, | |
| ast.Div: op.truediv, | |
| ast.FloorDiv: op.floordiv, | |
| ast.Mod: op.mod, | |
| ast.Pow: op.pow, | |
| ast.USub: op.neg, | |
| ast.UAdd: op.pos, | |
| } | |
| CONSTANTS = { | |
| 'pi': math.pi, | |
| 'e': math.e, | |
| 'tau': math.tau, | |
| } | |
| FUNCTIONS = { | |
| 'sin': math.sin, | |
| 'cos': math.cos, | |
| 'tan': math.tan, | |
| 'asin': math.asin, | |
| 'acos': math.acos, | |
| 'atan': math.atan, | |
| 'sqrt': math.sqrt, | |
| 'pow': math.pow, | |
| 'exp': math.exp, | |
| 'log': math.log, | |
| 'factorial': math.factorial, | |
| 'degrees': math.degrees, | |
| 'radians': math.radians, | |
| 'abs': abs, | |
| 'square': lambda x: x * x, | |
| } | |
| def _unparse(node): | |
| if isinstance(node, ast.Constant): | |
| return str(node.value) | |
| if isinstance(node, ast.Name): | |
| return node.id | |
| if isinstance(node, ast.BinOp): | |
| op_map = { | |
| ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', | |
| ast.Pow: '**', ast.Mod: '%' | |
| } | |
| if type(node.op) in op_map: | |
| return f"({_unparse(node.left)} {op_map[type(node.op)]} {_unparse(node.right)})" | |
| if isinstance(node, ast.Call): | |
| args = ", ".join([_unparse(arg) for arg in node.args]) | |
| func = node.func.id if isinstance(node.func, ast.Name) else str(node.func) | |
| return f"{func}({args})" | |
| return str(node) | |
| def _derive(node, var): | |
| if isinstance(node, ast.Constant): | |
| return "0" | |
| if isinstance(node, ast.Name): | |
| return "1" if node.id == var else "0" | |
| if isinstance(node, ast.BinOp): | |
| u, v = _unparse(node.left), _unparse(node.right) | |
| du, dv = _derive(node.left, var), _derive(node.right, var) | |
| if isinstance(node.op, ast.Add): return f"({du} + {dv})" | |
| if isinstance(node.op, ast.Sub): return f"({du} - {dv})" | |
| if isinstance(node.op, ast.Mult): return f"({du} * {v} + {u} * {dv})" | |
| if isinstance(node.op, ast.Div): return f"(({du} * {v} - {u} * {dv}) / ({v} ** 2))" | |
| if isinstance(node.op, ast.Pow) and isinstance(node.right, ast.Constant): | |
| n = node.right.value | |
| return f"({n} * ({u}) ** ({n-1}) * {du})" | |
| if isinstance(node, ast.Call): | |
| func = node.func.id if isinstance(node.func, ast.Name) else "" | |
| if len(node.args) == 1: | |
| u, du = _unparse(node.args[0]), _derive(node.args[0], var) | |
| if func == 'sin': return f"(cos({u}) * {du})" | |
| if func == 'cos': return f"(-sin({u}) * {du})" | |
| if func == 'exp': return f"(exp({u}) * {du})" | |
| if func == 'sqrt': return f"(0.5 / sqrt({u}) * {du})" | |
| return f"diff({_unparse(node)}, {var})" | |
| def _eval_node(node): | |
| if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): | |
| return node.value | |
| if isinstance(node, ast.BinOp) and type(node.op) in OPS: | |
| return OPS[type(node.op)](_eval_node(node.left), _eval_node(node.right)) | |
| if isinstance(node, ast.UnaryOp) and type(node.op) in OPS: | |
| return OPS[type(node.op)](_eval_node(node.operand)) | |
| if isinstance(node, ast.Name): | |
| if node.id in CONSTANTS: return CONSTANTS[node.id] | |
| raise ValueError(f"Unknown constant: {node.id}") | |
| if isinstance(node, ast.Call): | |
| func = node.func.id if isinstance(node.func, ast.Name) else "" | |
| if func == 'diff' and len(node.args) == 2 and isinstance(node.args[1], ast.Name): | |
| return _derive(node.args[0], node.args[1].id) | |
| if func in FUNCTIONS: | |
| return FUNCTIONS[func](*[_eval_node(a) for a in node.args]) | |
| raise ValueError("Unsupported expression") | |
| def safe_eval(expr: str): | |
| try: | |
| node = ast.parse(expr, mode="eval").body | |
| return _eval_node(node) | |
| except Exception as e: | |
| return f"Error: {e}" | |
| if __name__ == "__main__": | |
| expr = input("Enter a numeric expression: ") | |
| print("Result:", safe_eval(expr)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment