agent-forge-bak / calculator.json
Chris4K's picture
Upload 14 files
988c7cc verified
{
"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
}