В основе работы SWC лежит строго типизированное представление
синтаксического дерева программы. Abstract Syntax Tree (AST) формализует
структуру JavaScript и TypeScript кода в виде набора вложенных узлов,
где каждый узел соответствует языковой конструкции: выражению,
оператору, объявлению или модулю. В Rust-реализации SWC AST определяется
как набор структур и перечислений, сгенерированных и организованных в
крейтах семейства swc_ecma_ast.
Каждый узел AST реализуется как struct или
enum, где форма данных строго фиксирует допустимые варианты
синтаксиса. Такой подход позволяет компилятору на этапе компиляции
гарантировать корректность обработки дерева и исключить множество
классов ошибок, характерных для динамических представлений.
Корневой элемент программы в SWC представлен через Module:
pub struct Module {
pub body: Vec<ModuleItem>,
pub span: Span,
}
ModuleItem описывает элементы верхнего уровня:
pub enum ModuleItem {
ModuleDecl(ModuleDecl),
Stmt(Stmt),
}
Такое разделение позволяет унифицировать обработку ES-модулей и обычных
инструкций. Любой файл JavaScript или TypeScript преобразуется в
Module, даже если он фактически не содержит импортов или
экспортов.
Span используется для хранения информации о позиции в
исходном коде. Это ключевой элемент инфраструктуры SWC, обеспечивающий
корректную трансформацию, sourcemaps и диагностику.
Центральный элемент AST — перечисление Expr, которое
охватывает все возможные выражения языка:
pub enum Expr {
Lit(Lit),
Ident(Ident),
Call(CallExpr),
Binary(BinaryExpr),
Arrow(ArrowExpr),
Fn(FnExpr),
Object(ObjectLit),
Array(ArrayLit),
Member(MemberExpr),
Cond(CondExpr),
Assign(AssignExpr),
}
Каждый вариант представляет отдельную категорию синтаксических
конструкций. Например, BinaryExpr описывает бинарные
операции:
pub struct BinaryExpr {
pub left: Box<Expr>,
pub op: BinaryOp,
pub right: Box<Expr>,
pub span: Span,
}
BinaryOp строго типизирован:
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
EqEq,
NotEq,
StrictEq,
StrictNotEq,
And,
Or,
}
Такой дизайн исключает необходимость строкового сравнения операторов и делает обработку выражений предсказуемой и быстрой.
Идентификаторы представлены структурой Ident:
pub struct Ident {
pub sym: JsWord,
pub optional: bool,
pub span: Span,
}
JsWord используется как оптимизированный тип строк,
основанный на интернировании. Это уменьшает потребление памяти при
многократном использовании одинаковых идентификаторов.
Привязка имён в SWC AST не хранится напрямую в узлах. Вместо этого используется отдельная система анализа областей видимости (scope analysis), которая строит таблицы символов поверх AST.
Функции представлены несколькими уровнями абстракции. Основной тип:
pub struct FnExpr {
pub function: Function,
}
Структура Function включает параметры, тело и модификаторы:
pub struct Function {
pub params: Vec<Param>,
pub body: Option<BlockStmt>,
pub is_async: bool,
pub is_generator: bool,
pub span: Span,
}
Каждый параметр описывается отдельно:
pub struct Param {
pub pat: Pat,
pub span: Span,
}
Pat (pattern) является универсальной конструкцией для
деструктуризации:
pub enum Pat {
Ident(BindingIdent),
Array(ArrayPat),
Object(ObjectPat),
Rest(RestPat),
Assign(AssignPat),
}
Классы представлены через Class и ClassDecl,
где учитываются наследование, методы и статические члены.
Инструкции языка представлены перечислением Stmt:
pub enum Stmt {
Expr(ExprStmt),
Decl(Decl),
Block(BlockStmt),
If(IfStmt),
For(ForStmt),
While(WhileStmt),
Return(ReturnStmt),
}
Блок кода — это контейнер:
pub struct BlockStmt {
pub stmts: Vec<Stmt>,
pub span: Span,
}
Такое дерево обеспечивает единообразную обработку любых конструкций управления потоком через рекурсивный обход.
ESM-конструкции представлены через ModuleDecl:
pub enum ModuleDecl {
Import(ImportDecl),
ExportDecl(ExportDecl),
ExportNamed(NamedExport),
ExportDefaultDecl(ExportDefaultDecl),
}
Импорт:
pub struct ImportDecl {
pub specifiers: Vec<ImportSpecifier>,
pub src: Str,
pub type_only: bool,
pub span: Span,
}
Спецификаторы импорта:
pub enum ImportSpecifier {
Named(ImportNamedSpecifier),
Default(ImportDefaultSpecifier),
Namespace(ImportStarAsSpecifier),
}
Такая модель позволяет точно различать формы импорта и корректно трансформировать их при транспиляции в CommonJS или другие форматы.
Для обхода дерева используется система трейтов Visit и
Fold, реализованных в крейте swc_ecma_visit.
Базовый трейт обхода:
pub trait Visit {
fn visit_expr(&mut self, n: &Expr) { ... }
fn visit_stmt(&mut self, n: &Stmt) { ... }
}
Пример реализации:
impl Visit for MyVisitor {
fn visit_expr(&mut self, n: &Expr) {
match n {
Expr::Call(call) => {
// обработка вызова функции
}
_ => {}
}
}
}
Этот механизм обеспечивает эффективный рекурсивный обход дерева без необходимости вручную управлять стеком вызовов.
В отличие от Visit, трейт Fold используется
для трансформации AST:
pub trait Fold {
fn fold_expr(&mut self, n: Expr) -> Expr;
}
Пример:
impl Fold for MyTransformer {
fn fold_expr(&mut self, n: Expr) -> Expr {
match n {
Expr::Binary(mut bin) => {
bin.left = Box::new(self.fold_expr(*bin.left));
bin.right = Box::new(self.fold_expr(*bin.right));
Expr::Binary(bin)
}
_ => n,
}
}
}
Этот подход позволяет строить трансформационные пайплайны: минификация, транспиляция, преобразование синтаксиса.
SWC поддерживает TypeScript через расширенные узлы AST, включающие типовую информацию:
pub enum TsType {
TsKeywordType(TsKeywordType),
TsTypeRef(TsTypeRef),
TsUnionOrIntersectionType(TsUnionOrIntersectionType),
}
Типы не смешиваются с выражениями JavaScript AST, но интегрируются через отдельные поля в декларациях:
pub struct TsTypeAnn {
pub type_ann: Box<TsType>,
}
Такой дизайн позволяет отделять синтаксический анализ от типовой семантики, сохраняя возможность последующего анализа без усложнения базовой модели AST.
Каждый узел содержит Span:
pub struct Span {
pub lo: BytePos,
pub hi: BytePos,
}
Эта структура обеспечивает:
Rust-реализация SWC использует несколько ключевых оптимизаций:
Box<T> для уменьшения размера enum-ов
JsWord)
swc_ecma_ast,
swc_ecma_parser, swc_ecma_transforms)
Такой дизайн позволяет SWC достигать производительности, сравнимой с низкоуровневыми компиляторами, при сохранении строгой типизации и безопасности памяти.
AST в SWC является центральным объектом всех этапов обработки:
swc_ecma_parser) строит дерево
swc_ecma_transforms) изменяют AST
swc_ecma_codegen) преобразует AST обратно в код
Каждый этап работает с одними и теми же структурами Rust, что исключает необходимость сериализации промежуточных форм и повышает скорость обработки.
Добавление новых конструкций в SWC требует расширения:
Expr или Stmt
Rust-система типов гарантирует, что все места использования нового узла будут обработаны на этапе компиляции, что критически важно для поддерживаемости большого компилятора.