Spaces:
Paused
Paused
| { | |
| "id": "calculator", | |
| "name": "Calculator", | |
| "version": "1.0.0", | |
| "description": "Safe math expression evaluator. Supports arithmetic, powers, roots, trig, logarithms, and constants (pi, e, inf). Uses Python's ast module — no eval() security risks.", | |
| "author": "Chris4K", | |
| "tags": [ | |
| "math", | |
| "utility", | |
| "calculator", | |
| "safe-eval" | |
| ], | |
| "dependencies": [], | |
| "schema": { | |
| "input": { | |
| "expression": "str — math expression, e.g. 'sqrt(144) + 2**8 - pi'" | |
| }, | |
| "output": { | |
| "result": "float", | |
| "expression": "str", | |
| "formatted": "str" | |
| } | |
| }, | |
| "code": "import ast\nimport math\nimport operator\n\n# Whitelist of safe operators\n_OPERATORS = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n ast.USub: operator.neg,\n ast.UAdd: operator.pos,\n ast.Mod: operator.mod,\n ast.FloorDiv: operator.floordiv,\n}\n\n# Whitelist of safe functions and constants\n_SAFE_NAMES = {\n \"pi\": math.pi,\n \"e\": math.e,\n \"inf\": math.inf,\n \"tau\": math.tau,\n \"sqrt\": math.sqrt,\n \"cbrt\": lambda x: x ** (1 / 3),\n \"abs\": abs,\n \"round\": round,\n \"floor\": math.floor,\n \"ceil\": math.ceil,\n \"log\": math.log,\n \"log2\": math.log2,\n \"log10\": math.log10,\n \"exp\": math.exp,\n \"sin\": math.sin,\n \"cos\": math.cos,\n \"tan\": math.tan,\n \"asin\": math.asin,\n \"acos\": math.acos,\n \"atan\": math.atan,\n \"atan2\": math.atan2,\n \"degrees\": math.degrees,\n \"radians\": math.radians,\n \"hypot\": math.hypot,\n \"factorial\": math.factorial,\n \"gcd\": math.gcd,\n \"min\": min,\n \"max\": max,\n \"sum\": sum,\n \"pow\": pow,\n}\n\n\ndef _eval_node(node):\n if isinstance(node, ast.Constant):\n if isinstance(node.value, (int, float, complex)):\n return node.value\n raise ValueError(f\"Unsupported constant: {node.value!r}\")\n elif isinstance(node, ast.BinOp):\n op = _OPERATORS.get(type(node.op))\n if op is None:\n raise ValueError(f\"Unsupported operator: {type(node.op).__name__}\")\n return op(_eval_node(node.left), _eval_node(node.right))\n elif isinstance(node, ast.UnaryOp):\n op = _OPERATORS.get(type(node.op))\n if op is None:\n raise ValueError(f\"Unsupported unary op: {type(node.op).__name__}\")\n return op(_eval_node(node.operand))\n elif isinstance(node, ast.Call):\n if not isinstance(node.func, ast.Name):\n raise ValueError(\"Only simple function calls allowed\")\n fname = node.func.id\n if fname not in _SAFE_NAMES:\n raise ValueError(f\"Function not allowed: {fname!r}\")\n args = [_eval_node(a) for a in node.args]\n return _SAFE_NAMES[fname](*args)\n elif isinstance(node, ast.Name):\n if node.id not in _SAFE_NAMES:\n raise ValueError(f\"Name not allowed: {node.id!r}\")\n return _SAFE_NAMES[node.id]\n elif isinstance(node, ast.Expression):\n return _eval_node(node.body)\n else:\n raise ValueError(f\"Unsupported AST node: {type(node).__name__}\")\n\n\ndef execute(expression: str) -> dict:\n \"\"\"Safely evaluate a mathematical expression.\"\"\"\n if not expression or not expression.strip():\n return {\"error\": \"Expression cannot be empty\"}\n try:\n tree = ast.parse(expression.strip(), mode=\"eval\")\n result = _eval_node(tree)\n if isinstance(result, float) and result.is_integer() and abs(result) < 1e15:\n formatted = str(int(result))\n else:\n formatted = f\"{result:.10g}\"\n return {\n \"expression\": expression,\n \"result\": float(result) if not isinstance(result, complex) else None,\n \"formatted\": formatted,\n }\n except ZeroDivisionError:\n return {\"error\": \"Division by zero\", \"expression\": expression}\n except Exception as exc:\n return {\"error\": str(exc), \"expression\": expression}\n", | |
| "downloads": 1, | |
| "created_at": 1710000002 | |
| } |