Компиляторы
AST: абстрактное синтаксическое дерево
Каждый раз когда VS Code подсвечивает ошибку типов, предлагает автодополнение, или переименовывает переменную во всём проекте - это работает TypeScript Language Server на AST. Миллиарды строк JavaScript кода в Facebook мигрировали с jQuery на React через один codemod, работающий на AST. AST - скрытый движок современной разработки.
- Babel transforms: JSX -> React.createElement через AST visitor - каждое приложение на React
- ESLint: 200+ стандартных правил - каждое это visitor на AST узлах
- Prettier: форматирование через AST - убирает все оригинальные пробелы и генерирует новые
- TypeScript: проверка типов - два прохода через AST: parsing + type checking
AST vs CST: что выбросить и почему
**CST (Concrete Syntax Tree)** - дерево точно соответствующее грамматике. Каждый токен - узел. `1 + 2 * 3` с CST включает скобки, точки с запятой, ключевые слова как отдельные узлы. **AST (Abstract Syntax Tree)** выбрасывает всё что несущественно для семантики. Остаётся только структура программы.
**Babel, TypeScript, ESLint** используют AST для трансформации и анализа JavaScript. Babel transforms: JSX -> React.createElement, TypeScript types -> JavaScript. ESLint rules: обходят AST и проверяют паттерны. Prettier: форматирует код через AST (print через printer).
Что AST выбрасывает по сравнению с CST?
Узлы AST: типы и структура
Каждый узел AST имеет тип (discriminated union в TypeScript) и специфичные поля. Стандарт **ESTree** определяет структуру AST для JavaScript - его используют Acorn, Esprima, Babel parser. Это позволяет инструментам (ESLint, Prettier, Babel) работать с любым парсером.
Зачем нужен стандарт ESTree для AST JavaScript?
Visitor Pattern: обход и трансформация AST
**Visitor Pattern** позволяет добавлять операции над AST без изменения самих узлов. Каждый узел вызывает `visitor.visit(this)`, visitor знает как обрабатывать каждый тип узла. Babel плагины - это именно visitor'ы: каждый плагин регистрирует обработчики типов узлов.
Почему Visitor Pattern выбран для обхода AST вместо методов на каждом узле?
AST в инструментах: Babel, ESLint, codemods
**ast-grep** - новый инструмент (2023) для структурного поиска по коду через AST паттерны. Как grep, но понимающий синтаксис. `$X.map($Y)` найдёт все вызовы .map() независимо от форматирования. Используется в крупных codemod'ах (Facebook Codemod, ReactMods).
**Language Server Protocol (LSP)** - IDE функции (go-to-definition, rename, autocomplete) работают на AST. TypeScript Language Server строит полный AST с типами для каждого файла и обновляет его инкрементально при каждом нажатии клавиши. Весь VS Code TypeScript support - это один LSP сервер работающий на AST.
AST нужен только компиляторам - для других задач достаточно regex
AST используется в IDE (TypeScript LS), линтерах (ESLint), форматтерах (Prettier), codemod'ах, и системах анализа безопасности
Regex не понимает вложенность и контекст: `function` в строке и в объявлении функции неразличимы для regex. AST знает структуру и контекст каждого элемента кода.
Что такое codemod и зачем он использует AST?
Ключевые идеи
- AST vs CST: AST выбрасывает пунктуацию и промежуточные грамматические узлы
- ESTree: стандарт структуры AST для JavaScript - один формат для Babel, ESLint, Prettier
- Visitor Pattern: добавляем операции без изменения узлов - каждый плагин это visitor
- Codemod: AST-based рефакторинг надёжнее regex - знает синтаксис и контекст
- LSP: IDE features работают на непрерывно обновляемом AST с типами
Связанные темы
AST - центральная структура данных компилятора, на которой строится весь последующий анализ.
- Pratt Parser — Pratt Parser строит AST выражений через led/nud обработчики
- Таблица символов — Symbol table строится при обходе AST - первый семантический анализ
- Проверка типов — Type checker - visitor по AST, аннотирующий узлы типами
Вопросы для размышления
- Как реализовать инкрементальное обновление AST при редактировании файла в IDE (не перепарсировать весь файл)?
- Что такое 'source maps' и как AST transform поддерживает маппинг позиций в исходном коде?
- Как ast-grep и структурный поиск отличается от regexp в применении к больших codebase миграциям?
Связанные уроки
- comp-15-pratt-parser — Pratt Parser строит AST - этот урок его изучает
- comp-17-symbol-table — Таблица символов заполняется при обходе AST
- comp-18-type-checking — Проверка типов работает на AST через type visitor
- comp-11-recursive-descent — Recursive descent - первый парсер, строящий AST
- plt-26-ast