تفاصيل العمل

# ملف: safe_calculator.py

# وصف: آلة حاسبة تقيّم تعابير رياضية بأمان (بدون eval المباشر).

# كيف تستعمل: شغّل الملف: python safe_calculator.py

# ثم اكتب تعابير مثل:

# 2+3*4

# (1+2)**3

# sqrt(16) + sin(pi/2)

# اكتب "exit" للخروج، أو "help" للمساعدة.

import ast

import math

import operator

# دوال وعوامل مسموح بها

_ALLOWED_NAMES = {

# ثوابت

'pi': math.pi,

'e': math.e,

# دوال شائعة

'sin': math.sin,

'cos': math.cos,

'tan': math.tan,

'sqrt': math.sqrt,

'log': math.log, # natural log

'log10': math.log10,

'abs': abs,

'round': round,

'floor': math.floor,

'ceil': math.ceil,

'factorial': math.factorial,

}

_ALLOWED_BINOPS = {

ast.Add: operator.add,

ast.Sub: operator.sub,

ast.Mult: operator.mul,

ast.Div: operator.truediv,

ast.Pow: operator.pow,

ast.Mod: operator.mod,

ast.FloorDiv: operator.floordiv,

}

_ALLOWED_UNARYOPS = {

ast.UAdd: operator.pos,

ast.USub: operator.neg,

}

class SafeEvalError(Exception):

pass

def safe_eval(node):

"""قيّم شجرة AST node بطريقة آمنة."""

if isinstance(node, ast.Expression):

return safe_eval(node.body)

if isinstance(node, ast.Constant): # Python 3.8+

if isinstance(node.value, (int, float)):

return node.value

raise SafeEvalError(f"غير مسموح بالقيمة: {node.value!r}")

if isinstance(node, ast.Num): # for older AST nodes

return node.n

if isinstance(node, ast.BinOp):

op_type = type(node.op)

if op_type in _ALLOWED_BINOPS:

left = safe_eval(node.left)

right = safe_eval(node.right)

try:

return _ALLOWED_BINOPS[op_type](left, right)

except Exception as e:

raise SafeEvalError(f"خطأ أثناء الحساب: {e}")

raise SafeEvalError(f"عامل ثنائي غير مسموح: {op_type}")

if isinstance(node, ast.UnaryOp):

op_type = type(node.op)

if op_type in _ALLOWED_UNARYOPS:

operand = safe_eval(node.operand)

return _ALLOWED_UNARYOPS[op_type](operand)

raise SafeEvalError("عامل أحادي غير مسموح")

if isinstance(node, ast.Call):

# دوال فقط من _ALLOWED_NAMES، ولا نقبل استدعاءات مع سمات أو غير ذلك

if isinstance(node.func, ast.Name):

func_name = node.func.id

if func_name not in _ALLOWED_NAMES:

raise SafeEvalError(f"دالة غير مسموح بها: {func_name}")

func = _ALLOWED_NAMES[func_name]

args = [safe_eval(arg) for arg in node.args]

try:

return func(*args)

except Exception as e:

raise SafeEvalError(f"خطأ في استدعاء الدالة {func_name}: {e}")

raise SafeEvalError("مكالمات غير مسموح بها (فقط اسم دالة بسيط مسموح)")

if isinstance(node, ast.Name):

if node.id in _ALLOWED_NAMES:

val = _ALLOWED_NAMES[node.id]

if isinstance(val, (int, float)):

return val

raise SafeEvalError(f"اسم غير مسموح: {node.id}")

if isinstance(node, ast.Tuple):

return tuple(safe_eval(elt) for elt in node.elts)

raise SafeEvalError(f"تركيب غير مدعوم: {type(node).__name__}")

def evaluate(expr: str):

"""تقيّم التعبير النصي وترجع الناتج أو ترفع SafeEvalError."""

try:

parsed = ast.parse(expr, mode='eval')

except SyntaxError as e:

raise SafeEvalError(f"خطأ في بناء الجملة: {e}")

# تحقق من أن الشجرة لا تحتوي على عناصر غير متوقعة (هذه خطوة إضافية بسيطة)

return safe_eval(parsed)

def print_help():

print("آلة حاسبة آمنة — أمثلة:")

print(" 2 + 3 * 4")

print(" (1+2)**3")

print(" sqrt(16) + sin(pi/2)")

print("دوال مسموحة:", ", ".join(sorted([k for k in _ALLOWED_NAMES.keys()])))

print('أدخل "exit" للخروج.')

def main():

print(">>> آلة حاسبة بايثون آمنة. اكتب 'help' للمساعدة، 'exit' للخروج.")

while True:

try:

s = input("expr> ").strip()

except (EOFError, KeyboardInterrupt):

print("\nخروج.")

break

if not s:

continue

if s.lower() in ('exit', 'quit'):

print("خروج.")

break

if s.lower() in ('help', '?'):

print_help()

continue

try:

result = evaluate(s)

print(result)

except SafeEvalError as e:

print("خطأ:", e)

except Exception as e:

print("خطأ غير متوقع:", e)

if __name__ == "__main__":

main()

بطاقة العمل

اسم المستقل
عدد الإعجابات
0
عدد المشاهدات
1
تاريخ الإضافة