Теория языков программирования

AST и семантический анализ

AST - это мост между текстом программы и её смыслом. После построения AST компилятор работает не со строками, а со структурами данных. Каждая IDE feature - автодополнение, рефакторинг, hover types - работает с AST.

  • **TypeScript Language Server**: построение и кеширование AST для каждого файла - основа instant type checking в VSCode. Инкрементальное обновление при каждом нажатии клавиши
  • **Babel**: AST-трансформации превращают ES2023 в ES5. Каждый плагин - посетитель (Visitor), трансформирующий узлы AST
  • **ESLint**: правила - это проверки AST узлов. no-unused-vars проверяет, что каждое определение в symbol table используется

Построение AST

AST (Abstract Syntax Tree) - дерево, представляющее структуру программы без деталей синтаксиса (скобки, запятые, точки с запятой). Каждый узел - операция или значение, дочерние узлы - подвыражения.

Что такое 'абстрактное' в аббревиатуре AST?

Разрешение имён

Name resolution (разрешение имён) - фаза после построения AST, где каждое использование имени (переменной, функции, типа) связывается с его определением. Результат - таблица символов (symbol table).

Что происходит при shadowing переменной в новом scope?

Проверка типов

Type checking - проверка корректности типов после разрешения имён. Бывает structural (типы совместимы если одинаковая структура - TypeScript) и nominal (типы совместимы если одно имя/иерархия - Java). Bidirectional type checking - современный стандарт.

В чём разница между structural и nominal type checking?

Scope и lifetime

Scope - лексическая область видимости переменной. Lexical scope (статический) - видимость определяется текстом программы. Dynamic scope - видимость определяется call stack. Почти все современные языки используют lexical scope.

AST - это только для компиляторов, в обычной разработке не используется

AST используется в babel (JS transpilation), eslint (linting), prettier (formatting), ts-morph (code generation), IDE hover/completion

Любой инструмент, анализирующий или трансформирующий код, работает с AST. TypeScript Compiler API предоставляет доступ к AST для любого TS кода

Почему Rust требует явные lifetime аннотации в некоторых функциях?

Итоги

  • **AST** отбрасывает синтаксис (скобки, запятые), сохраняет структуру и приоритет. Узлы - операции, листья - значения
  • **Name resolution** строит symbol table: каждое использование имени -> определение. Shadowing разрешено через scope chain
  • **Type checking**: structural (TypeScript) vs nominal (Java). H-M inference выводит типы через унификацию
  • **Scope**: lexical scope - видимость по тексту. Rust lifetimes - явная аннотация для borrow checker

Связанные темы

AST - центр пайплайна компилятора:

  • Парсер — Парсер строит AST из потока токенов
  • IR и оптимизации — Из AST генерируется IR для оптимизаций

Вопросы для размышления

  • Closure захватывает переменные из lexical scope. Как это реализовано на уровне машинного кода - где хранятся захваченные переменные?
  • Rust lifetimes - это аннотации в AST/HIR, проверяемые borrow checker. Почему нельзя автоматически вывести все lifetimes без аннотаций?
  • TypeScript structural typing означает, что два разных типа с одинаковыми полями взаимозаменяемы. Это Feature или Bug? Приведите пример, где это создаёт проблему.

Связанные уроки

  • fl-12-cfg
AST и семантический анализ

0

1

Войти