Files

7.1 KiB
Raw Permalink Blame History

План доработки калькулятора

Ревизия кода: calculator.py (227 строк, рекурсивный спуск), cli.py (53), test_calculator.py (136, 22 теста, unittest). Оценка объёма: ~14–20 ч суммарно, фазы независимы, каждую можно релизить отдельно.

Фаза 0 — баги и полировка (P0, ~1–2 ч)

  1. Ложный символ в сообщении об ошибке — calculator.py, _atom(): "Не хватает закрывающей скобки '}'" → должно быть ')'.
  2. Хрупкое tok.value or 0.0 в _atom() — чинит число 0, но маскирует None. Заменить на явную проверку tok.value is None.
  3. Комплексные результаты: (-8) ** 0.5 возвращает Python-complex, аннотация обещает float. Либо ловить и давать «Ошибка: комплексный результат», либо поддержать явно (см. Фазу 2).
  4. OverflowError при 2 ** 100000 — parse_and_eval бросает не-CalcError. Обернуть возведение в try/except → CalcError.
  5. Мусорный тест: TestExitCommands.test_non_exit содержит "exit" в цикле с заглушкой-условием. Переписать: assertFalse для "выходить", "exit!", "2 + 2".
  6. Docstring cli.py — пример python cli.py "sqrt" недостижим (функций нет). Актуализировать после Фазы 1.

Критерий: все существующие тесты зелёные + новые регрессионные на 1–5.

Фаза 1 — расширения языка (P1, ~3–4 ч)

Порядок по частоте запросов:

  1. Экспоненциальная запись: 1e3, 2.5E-4. Поправить _tokenize (сейчас e → «Недопустимый символ»). Аккуратно: e после числа, опциональный +/-.
  2. Целочисленные операции: // (floor div) и настоящий бинарный % (mod). Конфликт с постфиксным %: правило — % без правого операнда = процент, с операндом = mod; либо ввести mod как слово. Обсудить и зафиксировать в README.
  3. Константы: pi, e, tau, inf. Через _KEYWORDS-подобный словарь в _atom().
  4. Функции: sqrt, abs, round, floor, ceil, sin/cos/tan/asin/..., log, log2, ln, exp. Синтаксис имя ( args ) или имя args (без скобок для одноаргументных). Реализация: таблица {name: (arity, callable)} + разбор аргументов через существующий _expr.
  5. Разделители разрядов: 1_000_000 → парсить как число.

Критерий: таблица «приммер → ожидание» в README, тесты на каждую функцию/константу/оператор + приоритеты (2 ** 1e2, sqrt 16 + 1).

Фаза 2 — состояние и UX (P1–P2, ~3–5 ч)

  1. Настоящий REPL для TTY: сейчас cli.py читает весь stdin до EOF и печатает приветствие после — в терминале это выглядит как зависание. Ветвление: sys.stdin.isatty() → цикл с input("calc> "), иначе текущий батч-режим.
  2. ans / Ans: последнее значение как идентификатор; плюс _ не нужен (конфликт с литералом из Фазы 1.5 — решить в пользу ans).
  3. История и редактирование: readline (import readline в cli) — стрелки ↑/↓, history-файл ~/.calc_history.
  4. Команды сессии: help, hist, vars, reset (очистить ans/переменные).
  5. Форматирование вывода: float-артефакты (0.1 + 0.2). Сейчас лечится %.17g→round эвристикой в try_evaluate; надёжнее — округление до 12 значащих цифр при представлении, вычисления оставлять float. Опционально: переписать ядро на decimal.Decimal (скачок точности, но ломает функции из math — завести на Фазу 3).
  6. Немного дружелюбнее сообщения об ошибках: указывать позицию символа в строке (токенизатор уже знает i).

Критерий: ручной прогон в TTY + юнит-тесты форматирования/ans.

Фаза 3 — архитектура и инфраструктура (P2, ~4–6 ч)

  1. AST вместо eval-во-время: _Parser строит дерево узлов (Node/Num/Binary/Unary/Postfix/FCall), затем eval(node, env). Зачем: переменные (x = 5), история выражений для hist, повторный вывод (pprint), тестирование парсера отдельно от вычисления. Это фундамент для всего, что дальше.
  2. Переменные и присваивание: x = 2 + 3, потом x * 2. Env как dict, vars из Фазы 2.
  3. Пакетизация: pyproject.toml, entry point calc = calculator.cli:main, убрать PyInstaller-помойку (build/, dist/, калькулятор.spec — в gitignore, либо собрать отдельно как артефакт CI).
  4. pytest + coverage + CI: миграция unittest→pytest (параметризованные таблицы тестов), --cov, min 90%; Gitea Actions pipeline: lint (ruff) + type-check (mypy) + pytest.
  5. Типы: mypy --strict calculator/ после рефакторинга (value: float | None решается нормализацией AST).
  6. Опционально/далеко: decimal-режим, комплексные числа как тип, unit-тесты фuzz-подобным перебором строк (property-based, hypothesis).

Не делать (осознанно)

  • Графический интерфейс — вне SCOPE CLI-калькулятора.
  • Свой токенизатор с zero-copy/скоростью — узкое место отсутствует, не профиль.
  • Поддержка «2+3*4-1%100» как калькулятора скидок (процент от чего?) — семантика постфикса задокументирована, не трогаем.

Порядок работ

Фаза 0 → 1 → 2 → 3. Каждая фаза = отдельная ветка + PR в admin/calculator, squash-merge, тег v0.x.