Архитектура парсера
У Oxc свой AST и свой парсер — на сегодня это самый быстрый и наиболее соответствующий спецификации парсер JavaScript и TypeScript (включая JSX и TSX) на Rust.
Парсер часто узкое место по производительности в цепочке JS-инструментов, поэтому даже мелкие улучшения каскадно отражаются на всём, что ниже. Развивая свой парсер, мы можем пробовать и внедрять хорошо изучённые приёмы ускорения.
Философия проектирования AST
Многие существующие инструменты опираются на estree как на спецификацию AST, но у неё немало двусмысленных узлов. Эта двусмысленность мешает разработке с estree.
AST Oxc отличается от estree: двусмысленные узлы убраны, введены отдельные типы. Например, вместо универсального Identifier из estree в AST Oxc есть BindingIdentifier, IdentifierReference и IdentifierName.
Такое разделение сильно улучшает опыт разработки и ближе следует спецификации ECMAScript.
Типы узлов AST
// Instead of generic Identifier
pub struct BindingIdentifier<'a> {
pub span: Span,
pub name: Atom<'a>,
}
pub struct IdentifierReference<'a> {
pub span: Span,
pub name: Atom<'a>,
pub reference_id: Cell<Option<ReferenceId>>,
}
pub struct IdentifierName<'a> {
pub span: Span,
pub name: Atom<'a>,
}Ясность семантики
Так достигается ясная семантика:
BindingIdentifier: объявления переменных (let x = 1)IdentifierReference: использование переменных (console.log(x))IdentifierName: имена свойств (obj.property)
Архитектура производительности
Почему так быстро
- Arena-память: AST выделяется в arena-аллокаторе для быстрых аллокаций и освобождения
- Оптимизация строк: короткие строки инлайнятся через CompactString
- Минимум кучи: кроме этих двух механизмов дополнительных кучевых аллокаций нет
- Разделение обязанностей: привязка к областям, разрешение символов и часть синтаксических ошибок — в семантическом анализаторе
Детали управления памятью
Выделение в arena
use oxc_allocator::Allocator;
// All AST nodes are allocated in this arena
let allocator = Allocator::default();
let ast_node = allocator.alloc(Expression::NumericLiteral(
allocator.alloc(NumericLiteral { value: 42.0, span: SPAN })
));Плюсы:
- O(1) выделение: простой сдвиг указателя
- O(1) освобождение: одним сбросом всей arena
- Дружелюбно к кэшу: линейный доступ к памяти
- Без фрагментации: непрерывное использование памяти
Интернирование строк через CompactString
// Strings ≤ 24 bytes are stored inline (no heap allocation)
let short_name = CompactString::from("variableName"); // Stack allocated
let long_name = CompactString::from("a_very_long_variable_name_that_exceeds_limit"); // Heap allocatedТак сокращается число аллокаций для большинства идентификаторов и строковых литералов в JS.
Архитектура парсера
Двухфазная схема
Парсер Oxc двухфазный:
- Фаза разбора: строим AST с минимальной семантикой
- Семантическая фаза: области, символы и расширенные проверки ошибок
// Phase 1: Parse to AST
let parser_result = Parser::new(&allocator, &source_text, source_type).parse();
// Phase 2: Semantic analysis
let semantic_result = SemanticBuilder::new()
// Enable additional syntax checks not performed by the parser
.with_check_syntax_error(true)
.build(&parser_result.program);Компоненты парсера
Лексер
- Токены: исходный текст → структурированные токены
- SIMD: пропуск пробелов векторными инструкциями
- Контекст: различение regex и оператора деления
Рекурсивный спуск
- Рукописный парсер: максимальная производительность
- Восстановление после ошибок: понятные сообщения
- Соответствие грамматике: строго по ECMAScript
Построитель AST
- Типобезопасность: выгоды системы типов Rust
- Память: прямое размещение в arena
- Builder: удобное конструирование узлов
Стратегия соответствия спецификации
Охват тестами
- Test262: 100% прохождение тестов соответствия ECMAScript
- Babel: 99.62% совместимости с тестами парсера Babel
- TypeScript: 99.86% совместимости с тестами компилятора TypeScript
Философия обработки ошибок
// Meaningful error messages with source location
pub struct OxcDiagnostic {
pub message: String,
pub span: Span,
pub severity: Severity,
pub help: Option<String>,
}Парсер даёт:
- Точные позиции: абсолютные смещения в исходнике
- Восстановление: продолжение разбора после ошибок
- Подсказки: сообщения, по которым можно действовать
Расширенные возможности
Поддержка TypeScript
- Стриппинг типов: удаление синтаксиса TypeScript
- Декораторы: экспериментальный синтаксис
- Пространства имён: модули и namespace
- JSX: поддержка TSX
Направления исследований
- SIMD для текста: векторная обработка строк
- Кэш-поведение: меньше промахов при обходе памяти
- Предсказание ветвлений: горячие пути разбора
- Zero-copy: без лишнего копирования строк