Skip to content

抽象構文木 (AST)

Oxc の AST は、すべての Oxc ツールの基盤です。パーサー、リンター、トランスフォーマー、その他のコンポーネントへの貢献を行うには、その構造と扱い方を理解することが不可欠です。

AST アーキテクチャ

設計原則

Oxc の AST は以下の原則に基づいて設計されています:

  1. パフォーマンス最優先:速度とメモリ効率に最適化
  2. 型安全:Rust の型システムを活用して一般的なエラーを防止
  3. 仕様準拠:ECMAScript 標準に近接して従う
  4. 明確な意味論:他の 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 ノードの追加

  1. 構造体の定義

    rust
    #[ast(visit)]
    pub struct MyNewNode<'a> {
        pub span: Span,
        pub name: Atom<'a>,
        pub value: Expression<'a>,
    }
  2. 列挙型への追加

    rust
    pub enum Statement<'a> {
        // ... 既存のバリエーション
        MyNewStatement(Box<'a, MyNewNode<'a>>),
    }
  3. コード生成の実行

    bash
    just ast
  4. パースロジックの実装

    rust
    impl<'a> Parser<'a> {
        fn parse_my_new_node(&mut self) -> Result<MyNewNode<'a>> {
            // パースの実装
        }
    }

他の AST 形式との比較

AST エクスプローラーの利用

他のパーサーとの比較には ast-explorer.dev を使用してください:

  1. より良いインターフェース:現代的なユーザーインターフェースと構文強調表示
  2. 最新版:最新のパーサー版
  3. 複数のパーサー:Oxc、Babel、TypeScript などと比較可能
  4. エクスポート形式: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);