抽象構文木 (AST)
Oxc の AST は、すべての Oxc ツールの基盤です。パーサー、リンター、トランスフォーマー、その他のコンポーネントへの貢献を行うには、その構造と扱い方を理解することが不可欠です。
AST アーキテクチャ
設計原則
Oxc の AST は以下の原則に基づいて設計されています:
- パフォーマンス最優先:速度とメモリ効率に最適化
- 型安全:Rust の型システムを活用して一般的なエラーを防止
- 仕様準拠:ECMAScript 標準に近接して従う
- 明確な意味論:他の AST 形式に見られる曖昧さを排除
AST との連携
AST 関連コードの生成
AST 定義を変更した場合、コード生成ツールを実行してください:
bash
just astこれにより以下のものが生成されます:
- ビジターパターン:AST を走査するため
- ビルダーメソッド:AST ノードを構築するため
- トレイト実装:共通操作のため
- TypeScript 型:Node.js バインディングのため
AST ノード構造
すべての AST ノードは一貫したパターンに従います:
rust
#[ast(visit)]
pub struct FunctionDeclaration<'a> {
pub span: Span,
pub id: Option<BindingIdentifier<'a>>,
pub generator: bool,
pub r#async: bool,
pub params: FormalParameters<'a>,
pub body: Option<FunctionBody<'a>>,
pub type_parameters: Option<TSTypeParameterDeclaration<'a>>,
pub return_type: Option<TSTypeAnnotation<'a>>,
}主要なコンポーネント:
span:ソース位置情報#[ast(visit)]:ビジターメソッドを生成- ライフタイム
'a:アレーン割り当てされたメモリへの参照
メモリ管理
AST は効率的な割り当てのためにメモリアレーンを使用します:
rust
use oxc_allocator::Allocator;
let allocator = Allocator::default();
let ast = parser.parse(&allocator, source_text, source_type)?;利点:
- 高速な割り当て:個別の malloc 呼び出しを必要としない
- 高速な解放:アレーン全体を一度に解放可能
- キャッシュに優しい:線形メモリレイアウト
- 参照カウンティング不要:シンプルなライフタイム管理
AST の走査
ビジターパターン
生成されたビジターを使って AST を走査します:
rust
use oxc_ast::visit::{Visit, walk_mut};
struct MyVisitor;
impl<'a> Visit<'a> for MyVisitor {
fn visit_function_declaration(&mut self, func: &FunctionDeclaration<'a>) {
println!("関数を発見: {:?}", func.id);
walk_mut::walk_function_declaration(self, func);
}
}
// 使用方法
let mut visitor = MyVisitor;
visitor.visit_program(&program);モディファイ可能なビジター
トランスフォーメーションには、モディファイ可能なビジターを使用します:
rust
use oxc_ast::visit::{VisitMut, walk_mut};
struct MyTransformer;
impl<'a> VisitMut<'a> for MyTransformer {
fn visit_binary_expression(&mut self, expr: &mut BinaryExpression<'a>) {
// 式を変換
if expr.operator == BinaryOperator::Addition {
// AST ノードを修正
}
walk_mut::walk_binary_expression_mut(self, expr);
}
}AST の構築
ビルダーパターン
AST ビルダーを使ってノードを作成します:
rust
use oxc_ast::AstBuilder;
let ast = AstBuilder::new(&allocator);
// 二項演算式を作成: a + b
let left = ast.expression_identifier_reference(SPAN, "a");
let right = ast.expression_identifier_reference(SPAN, "b");
let expr = ast.expression_binary_expression(
SPAN,
left,
BinaryOperator::Addition,
right,
);ヘルパー関数
一般的なパターンはヘルパーとして提供されています:
rust
impl<'a> AstBuilder<'a> {
pub fn expression_numeric_literal(&self, span: Span, value: f64) -> Expression<'a> {
self.alloc(Expression::NumericLiteral(
self.alloc(NumericLiteral { span, value, raw: None })
))
}
}開発ワークフロー
新しい AST ノードの追加
構造体の定義:
rust#[ast(visit)] pub struct MyNewNode<'a> { pub span: Span, pub name: Atom<'a>, pub value: Expression<'a>, }列挙型への追加:
rustpub enum Statement<'a> { // ... 既存のバリエーション MyNewStatement(Box<'a, MyNewNode<'a>>), }コード生成の実行:
bashjust astパースロジックの実装:
rustimpl<'a> Parser<'a> { fn parse_my_new_node(&mut self) -> Result<MyNewNode<'a>> { // パースの実装 } }
他の AST 形式との比較
AST エクスプローラーの利用
他のパーサーとの比較には ast-explorer.dev を使用してください:
- より良いインターフェース:現代的なユーザーインターフェースと構文強調表示
- 最新版:最新のパーサー版
- 複数のパーサー:Oxc、Babel、TypeScript などと比較可能
- エクスポート形式:JSON、コード生成
パフォーマンスに関する考慮事項
メモリレイアウト
AST はキャッシュ効率を考慮して設計されています:
rust
// 良い例:コンパクトな表現
struct CompactNode<'a> {
span: Span, // 8バイト
flags: u8, // 1バイト
name: Atom<'a>, // 8バイト
}
// 避けるべき:ボックスングがない大きな列挙型
enum LargeEnum {
Small,
Large { /* 200バイトのデータ */ },
}アレーン割り当て
すべての AST ノードはアレーン内に割り当てられます:
rust
// #[ast] マクロによって自動的に処理される
let node = self.ast.alloc(MyNode {
span: SPAN,
value: 42,
});列挙型サイズのテスト
小さな列挙型サイズを強制的に確認しています:
rust
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
#[test]
fn no_bloat_enum_sizes() {
use std::mem::size_of;
assert_eq!(size_of::<Statement>(), 16);
assert_eq!(size_of::<Expression>(), 16);
assert_eq!(size_of::<Declaration>(), 16);
}高度なトピックス
カスタム AST 属性
特定のツール向けにカスタム属性を追加できます:
rust
#[ast(visit)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct MyNode<'a> {
#[cfg_attr(feature = "serialize", serde(skip))]
pub internal_data: u32,
pub public_field: Atom<'a>,
}しみつ分析との統合
AST ノードをセマンティック情報とリンクします:
rust
#[ast(visit)]
pub struct IdentifierReference<'a> {
pub span: Span,
pub name: Atom<'a>,
#[ast(ignore)]
pub reference_id: Cell<Option<ReferenceId>>,
}これにより、ツールが走査中にバインディング情報、スコープコンテキスト、型情報にアクセスできるようになります。
デバッグのヒント
美しく出力
デバッグフォーマッターを使って AST を確認します:
rust
println!("{:#?}", ast_node);Span 情報
エラーレポート用にソース位置を追跡します:
rust
let span = node.span();
println!("エラー発生位置 {}:{}", span.start, span.end);