パーサのアーキテクチャ
Oxc は独自の AST とパーサを持ち、Rust で書かれた JavaScript / TypeScript(JSX・TSX を含む)パーサの中で、現時点でもっとも高速で適合性が高い部類にあります。
パーサは JavaScript ツールングではしばしばボトルネックになるため、わずかな改善でも下流ツールに連鎖的な効果があります。自前のパーサによって、よく研究された性能技法を試し実装する機会も得ています。
AST 設計の考え方
多くの既存ツールは AST 仕様として estree に依存しますが、estree には曖昧なノードが多いという欠点があります。開発時の混乱を招きやすいです。
Oxc の AST は estree とは異なり、曖昧なノードを削除し、区別された型を導入します。例として、汎用の estree Identifier の代わりに、BindingIdentifier、IdentifierReference、IdentifierName のような具体的な型を用意します。
この区別により、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)
パフォーマンス設計
なぜこれほど速いか
- メモリアリーナ: 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) 確保: ポインタを進めるだけ
- 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 パーサは二段階です。
- パース段階: 最小限の意味解析で AST を構築
- 意味段階: スコープ解析、シンボル解決、高度な構文エラー検査
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);パーサの構成要素
Lexer(レキサ)
- トークン生成: ソースを構造化トークンへ
- 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 テキスト処理: ベクトル化された文字列操作
- キャッシュ最適化: メモリアクセスパターンの最小化
- 分岐予測: ホットなパース経路の最適化
- ゼロコピーパース: 不要な文字列コピーの排除