Files
calculator/calculator.py

59 lines
2.6 KiB
Python

"""Публичный API калькулятора: вычисление математических выражений.
Передана на AST-конвейер: parser.build_ast (токенизация + синтаксический
анализ) -> ast.Evaluator (вычисление в env) -> format_result (вывод).
Поддерживает:
- операции: + - * / // %(бинарный остаток) и скобки;
- возведение в степень ** (правостороннее);
- унарный минус и плюс (-5, +3);
- проценты (50% => 0.5);
- экспоненциальную запись (1e3, 2.5E-4) и разделители разрядов (1_000_000);
- константы: pi, e, tau, inf;
- функции: sqrt, abs, exp, ln, log, log2, log10, round, floor, ceil,
sin, cos, tan, asin, acos, atan (аргументы тригонометрии — в радианах).
Правило для '%': если после % идёт число, константа, функция или '(' — это
бинарный остаток от деления (7 % 3 = 1); иначе — постфиксный процент (2 * 50 % = 1).
"""
from __future__ import annotations
from nodes import (
ASTError,
DivisionByZeroError, # noqa: F401 (re-export для тестов и CLI)
Evaluator,
SyntaxError_, # noqa: F401 (re-export)
format_result,
)
from parser import build_ast
# Backward-compat: CalcError устарел, используйте ASTError
CalcError = ASTError
def parse_and_eval(expression: str, env: dict[str, float] | None = None) -> float:
"""Вычисляет выражение и возвращает float.
Raises ASTError (SyntaxError_ / DivisionByZeroError) при некорректном вводе.
"""
node = build_ast(expression)
return Evaluator().eval(node, env or {})
def is_exit_command(text: str) -> bool:
"""True, если введённое значение — команда выхода ('выход'/'exit')."""
return text.strip().lower() in {"выход", "exit"}
def try_evaluate(expression: str, env: dict[str, float] | None = None) -> str:
"""Пытается вычислить выражение.
Возвращает человекочитаемый результат или сообщение об ошибке.
"""
try:
return format_result(parse_and_eval(expression, env))
except ASTError as exc:
return f"Ошибка: {exc}"
except Exception as exc: # на всякий случай — не падаем с traceback
return f"Ошибка: {exc}"