Skip to content

解析器架构

Oxc 维护着自己的 AST 和解析器,这是迄今为止用 Rust 编写的最快且最符合标准的 JavaScript 和 TypeScript(包括 JSX 和 TSX)解析器。

由于解析器通常是 JavaScript 工具链中的关键性能瓶颈, 任何微小的改进都可能对我们的下游工具产生连锁反应。 通过开发我们自己的解析器,我们有机会探索和实施经过充分研究的性能技术。

AST 设计哲学

虽然许多现有的 JavaScript 工具依赖 estree 作为其 AST 规范, 但一个显著的缺点是其存在大量模糊的节点。 这种模糊性往往导致在使用 estree 开发时产生混淆。

Oxc AST 与 estree AST 的不同之处在于移除了模糊节点并引入了独特的类型。 例如,Oxc AST 不使用通用的 estree Identifier, 而是提供特定的类型,如 BindingIdentifierIdentifierReferenceIdentifierName

这种清晰的区分通过与 ECMAScript 规范更紧密地对齐,极大地增强了开发体验。

AST 节点类型

rust
// 而不是通用的 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

性能架构

为何如此快速

  • 内存竞技场(Memory Arena):AST 分配在 内存竞技场 中,以实现快速的分配和释放
  • 字符串优化:短字符串通过 CompactString 进行内联
  • 最小化堆使用:除上述两项外,不进行其他堆分配
  • 关注点分离:作用域绑定、符号解析和一些语法错误被委托给语义分析器

内存管理细节

竞技场分配

rust
use oxc_allocator::Allocator;

// 所有 AST 节点都分配在这个竞技场中
let allocator = Allocator::default();
let ast_node = allocator.alloc(Expression::NumericLiteral(
    allocator.alloc(NumericLiteral { value: 42.0, span: SPAN })
));

优势:

  • O(1) 分配:简单的指针 bump
  • O(1) 释放:一次性释放整个竞技场
  • 缓存友好:线性内存布局
  • 无碎片:连续的内存使用

使用 CompactString 进行字符串驻留

rust
// 长度 ≤ 24 字节的字符串以内联方式存储(无堆分配)
let short_name = CompactString::from("variableName");  // 栈分配
let long_name = CompactString::from("a_very_long_variable_name_that_exceeds_limit");  // 堆分配

这减少了大多数 JavaScript 标识符和字符串字面量的内存分配。

解析器架构

两阶段设计

Oxc 解析器遵循两阶段方法:

  1. 解析阶段:构建 AST 结构,进行最少的语义分析
  2. 语义阶段:执行作用域分析、符号解析和高级错误检查
rust
// 阶段 1:解析为 AST
let parser_result = Parser::new(&allocator, &source_text, source_type).parse();

// 阶段 2:语义分析
let semantic_result = SemanticBuilder::new()
    // 启用解析器未执行的额外语法检查
    .with_check_syntax_error(true)
    .build(&parser_result.program);

解析器组件

词法分析器

  • Token 生成:将源文本转换为结构化 token
  • SIMD 优化:使用 SIMD 指令跳过空白字符
  • 上下文感知:处理正则表达式与除法运算符的消歧

递归下降解析器

  • 手写:自定义实现以获得最大性能
  • 错误恢复:具有有意义消息的高级错误处理
  • 语法合规:精确遵循 ECMAScript 规范

AST 构建器

  • 类型安全:利用 Rust 的类型系统确保正确性
  • 内存效率:直接竞技场分配
  • 构建器模式:便捷的节点构建方法

一致性策略

测试套件覆盖率

  • Test262:ECMAScript 一致性测试 100% 通过率
  • Babel:与 Babel 解析器测试 99.62% 兼容
  • TypeScript:与 TypeScript 编译器测试 99.86% 兼容

错误处理哲学

rust
// 带有源位置的意义明确的错误消息
pub struct OxcDiagnostic {
    pub message: String,
    pub span: Span,
    pub severity: Severity,
    pub help: Option<String>,
}

解析器提供:

  • 精确的错误位置:确切的源位置
  • 恢复策略:错误后继续解析
  • 有用的建议:可操作的错误消息

高级特性

TypeScript 支持

  • 类型剥离:移除 TypeScript 特定语法
  • 装饰器解析:处理实验性装饰器
  • 命名空间支持:完整的模块和命名空间解析
  • JSX 集成:TypeScript + JSX (TSX) 支持

研究领域

  • SIMD 文本处理:向量化字符串操作
  • 缓存优化:最小化内存访问模式
  • 分支预测:优化热点解析路径
  • 零拷贝解析:消除不必要的字符串复制