7.1 KiB
7.1 KiB
План доработки калькулятора
Ревизия кода: calculator.py (227 строк, рекурсивный спуск), cli.py (53), test_calculator.py (136, 22 теста, unittest).
Оценка объёма: ~14–20 ч суммарно, фазы независимы, каждую можно релизить отдельно.
Фаза 0 — баги и полировка (P0, ~1–2 ч)
- Ложный символ в сообщении об ошибке —
calculator.py,_atom():"Не хватает закрывающей скобки '}'"→ должно быть')'. - Хрупкое
tok.value or 0.0в_atom()— чинит число0, но маскируетNone. Заменить на явную проверкуtok.value is None. - Комплексные результаты:
(-8) ** 0.5возвращает Python-complex, аннотация обещаетfloat. Либо ловить и давать «Ошибка: комплексный результат», либо поддержать явно (см. Фазу 2). - OverflowError при
2 ** 100000—parse_and_evalбросает не-CalcError. Обернуть возведение в try/except →CalcError. - Мусорный тест:
TestExitCommands.test_non_exitсодержит"exit"в цикле с заглушкой-условием. Переписать:assertFalseдля"выходить","exit!","2 + 2". - Docstring
cli.py— примерpython cli.py "sqrt"недостижим (функций нет). Актуализировать после Фазы 1.
Критерий: все существующие тесты зелёные + новые регрессионные на 1–5.
Фаза 1 — расширения языка (P1, ~3–4 ч)
Порядок по частоте запросов:
- Экспоненциальная запись:
1e3,2.5E-4. Поправить_tokenize(сейчасe→ «Недопустимый символ»). Аккуратно:eпосле числа, опциональный+/-. - Целочисленные операции:
//(floor div) и настоящий бинарный%(mod). Конфликт с постфиксным%: правило —%без правого операнда = процент, с операндом = mod; либо ввестиmodкак слово. Обсудить и зафиксировать в README. - Константы:
pi,e,tau,inf. Через_KEYWORDS-подобный словарь в_atom(). - Функции:
sqrt,abs,round,floor,ceil,sin/cos/tan/asin/...,log,log2,ln,exp. Синтаксисимя ( args )илиимя args(без скобок для одноаргументных). Реализация: таблица{name: (arity, callable)}+ разбор аргументов через существующий_expr. - Разделители разрядов:
1_000_000→ парсить как число.
Критерий: таблица «приммер → ожидание» в README, тесты на каждую функцию/константу/оператор + приоритеты (2 ** 1e2, sqrt 16 + 1).
Фаза 2 — состояние и UX (P1–P2, ~3–5 ч)
- Настоящий REPL для TTY: сейчас
cli.pyчитает весь stdin до EOF и печатает приветствие после — в терминале это выглядит как зависание. Ветвление:sys.stdin.isatty()→ цикл сinput("calc> "), иначе текущий батч-режим. ans/Ans: последнее значение как идентификатор; плюс_не нужен (конфликт с литералом из Фазы 1.5 — решить в пользуans).- История и редактирование:
readline(import readlineв cli) — стрелки ↑/↓, history-файл~/.calc_history. - Команды сессии:
help,hist,vars,reset(очистить ans/переменные). - Форматирование вывода: float-артефакты (
0.1 + 0.2). Сейчас лечится%.17g→roundэвристикой вtry_evaluate; надёжнее — округление до 12 значащих цифр при представлении, вычисления оставлять float. Опционально: переписать ядро наdecimal.Decimal(скачок точности, но ломает функции из math — завести на Фазу 3). - Немного дружелюбнее сообщения об ошибках: указывать позицию символа в строке (токенизатор уже знает
i).
Критерий: ручной прогон в TTY + юнит-тесты форматирования/ans.
Фаза 3 — архитектура и инфраструктура (P2, ~4–6 ч)
- AST вместо eval-во-время:
_Parserстроит дерево узлов (Node/Num/Binary/Unary/Postfix/FCall), затемeval(node, env). Зачем: переменные (x = 5), история выражений дляhist, повторный вывод (pprint), тестирование парсера отдельно от вычисления. Это фундамент для всего, что дальше. - Переменные и присваивание:
x = 2 + 3, потомx * 2. Env как dict,varsиз Фазы 2. - Пакетизация:
pyproject.toml, entry pointcalc = calculator.cli:main, убрать PyInstaller-помойку (build/,dist/,калькулятор.spec— в gitignore, либо собрать отдельно как артефакт CI). - pytest + coverage + CI: миграция unittest→pytest (параметризованные таблицы тестов),
--cov, min 90%; Gitea Actions pipeline: lint (ruff) + type-check (mypy) + pytest. - Типы:
mypy --strict calculator/после рефакторинга (value: float | None решается нормализацией AST). - Опционально/далеко:
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.