Анатомия интерпретатора SOOT: три инженерных решения
Продолжаю серию материалов о SOOT — собственном Lisp-диалекте, который я разрабатываю. В новой статье разбираю три инженерных решения из интерпретатора, которые не видны в синтаксисе языка, но определяют, насколько предсказуемо он ведёт себя при ошибках.
Речь идёт о том, как организовать взаимодействие трёх подсистем так, чтобы диагностика оставалась цельной и не превращалась в десятки разрозненных проверок по всему коду:
• TextDb — отдельное хранилище позиций, которое связывает S-выражения с исходным текстом, не «загрязняя» сами объекты языка. Разбираю, почему позиции есть только у пар, что происходит с ними при квазицитате и какую цену за это платим. • vararg_check — декларативное описание контракта аргументов встроенных функций: одна строка вместо повторяющихся проверок длины и типов, с единообразными сообщениями об ошибках. • Обработка ошибок — накопление трассировки при раскручивании стека и пользовательские обработчики на самом языке (with-error-handler), включая честный разговор о том, что перехват ошибки не гарантирует восстановления состояния.
По сути, статья отвечает на три вопроса, которые возникают у человека при любой ошибке: где она произошла, что именно не так с вызовом и что делать дальше. Статья построена вокруг одного примера — от записи (+ 10 (* 2 "abc")) до готового отчёта с файлом, строкой и стрелкой под проблемным символом, — и показывает, как три механизма сходятся в одной точке. Отдельно оговариваю происхождение идей: интерпретатор вырос из GOOS проекта OpenGOAL, и в статье описано, что именно в SOOT изменено по сравнению с ним.
Ключевая мысль: все три решения локальны и решают конкретные задачи, но вместе дают целостную диагностику — тот же принцип «простое решение там, где сложное не окупается», что и в остальной архитектуре SOOT.
🔗 Читать: https://hww.github.io/articles/2026/soot/ru/script-debug.html