# 機能設計書 54-マクロ

## 概要

本ドキュメントは、Julia の `macro` 定義と `@マクロ` 呼び出しによるコンパイル時コード生成（メタプログラミング）機能の設計を記述する。

### 本機能の処理概要

本機能は、Julia のマクロシステムを提供する。マクロはコンパイル時にコードを生成・変換するメタプログラミング機構であり、`macro` キーワードで定義し `@` プレフィックスで呼び出す。マクロは引数として式（Expr）を受け取り、変換された式を返す。返された式はマクロ呼び出し箇所に挿入され、通常のコードとして評価される。マクロはhygienic（衛生的）であり、マクロ内で生成された変数名が呼び出し元のスコープと衝突しないように自動的に処理される。

**業務上の目的・背景**: 繰り返しの多いボイラープレートコードの自動生成、DSL（ドメイン固有言語）の構築、コンパイル時の最適化ヒントの付与、デバッグ・テスト・ロギング用の特殊構文の提供など、コードの抽象化と再利用を促進する。関数では表現できないコード変換（制御フローの変更、変数束縛の導入、構文レベルの変換）を可能にする。

**機能の利用シーン**: `@time` によるコード計測、`@test` によるテストアサーション、`@show` によるデバッグ出力、`@enum` による列挙型定義、`@generated` によるステージドプログラミング、ユーザー定義 DSL の構築。

**主要な処理内容**:
1. `macro name(args...) ... end` によるマクロ定義
2. `@name(args...)` / `@name args...` によるマクロ呼び出し
3. `esc(expr)` によるハイジーン制御（呼び出し元スコープへのエスケープ）
4. `gensym()` / `@gensym` による一意シンボル生成
5. `quote ... end` / `:(...)` による式のクォート
6. `$` による式の補間（アンクォート）
7. `__module__` / `__source__` による呼び出しコンテキスト情報の取得

**関連システム・外部連携**: Julia パーサー（JuliaSyntax.jl）によるマクロ呼び出しの構文解析。Julia ランタイムの lowering フェーズでのマクロ展開。`macroexpand` 関数によるマクロ展開結果の確認。

**権限による制御**: 特に権限制御は行われない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Juliaプロンプト（julia>） | 補助機能 | 入力コード内のマクロ展開処理。REPLCompletionsでのマクロ補完サポート |

## 機能種別

コード生成（メタプログラミング）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| マクロ名 | Symbol | Yes | マクロの識別名 | 有効な Julia 識別子であること |
| 引数 | Expr / Symbol / リテラル | No | マクロに渡される式。パース済みの AST が渡される | マクロ定義の引数パターンに一致すること |
| __module__ | Module | 暗黙 | マクロが呼び出されたモジュール。自動的に渡される | - |
| __source__ | LineNumberNode | 暗黙 | マクロ呼び出し箇所のソース位置情報。自動的に渡される | - |

### 入力データソース

Julia ソースコードのパース結果（AST）。マクロ呼び出し構文 `@name args...` がパーサーにより `Expr(:macrocall, Symbol("@name"), __source__, args...)` に変換される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 生成式 | Expr / リテラル | マクロが返す変換後の式。呼び出し箇所に挿入される |

### 出力先

マクロの返り値はコンパイラの lowering フェーズに渡され、通常のコードとして評価される。

## 処理フロー

### 処理シーケンス

```
1. ソースコードのパース
   └─ @name args... → Expr(:macrocall, Symbol("@name"), source, args...)
2. マクロ展開（lowering フェーズ）
   └─ マクロ関数の検索と呼び出し
   └─ 引数は未評価の式として渡される
   └─ __module__ と __source__ が自動的に追加
3. ハイジーン処理
   └─ マクロ内の変数名に gensym 的なスコープ情報を付与
   └─ esc() で明示的にエスケープされた式は呼び出し元スコープで解決
4. 式の挿入
   └─ マクロが返した式を呼び出し箇所に展開
5. lowering の継続
   └─ 展開後の式に対して通常の lowering を実行
   └─ 再帰的なマクロ展開（ネストしたマクロ呼び出しがある場合）
```

### フローチャート

```mermaid
flowchart TD
    A["ソースコード: @name args..."] --> B["パーサー: Expr(:macrocall, ...)"]
    B --> C["lowering フェーズ"]
    C --> D["マクロ関数を検索"]
    D --> E["マクロ関数を呼び出し\n引数: AST ノード"]
    E --> F["マクロ関数が式を返す"]
    F --> G["ハイジーン処理"]
    G --> H{"ネストしたマクロ呼び出し?"}
    H -->|Yes| D
    H -->|No| I["展開後の式を通常の lowering に渡す"]
    I --> J["コンパイル・実行"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-54-1 | 衛生的マクロ | マクロ内の変数はデフォルトでマクロ定義モジュールのスコープで解決される | 常時 |
| BR-54-2 | esc によるエスケープ | esc(expr) で囲まれた式は呼び出し元のスコープで解決される | マクロ内で esc() 使用時 |
| BR-54-3 | 暗黙引数 | __module__ と __source__ はマクロ定義の末尾引数として暗黙的に利用可能 | 常時 |
| BR-54-4 | コンパイル時実行 | マクロ本体はコンパイル時に実行され、ランタイムには存在しない | 常時 |
| BR-54-5 | 引数は未評価 | マクロ引数は評価されずに AST（式ツリー）として渡される | 常時 |

### 計算ロジック

マクロ自体は計算ロジックを定義するのではなく、コードを生成するメタレベルの処理。具体的な計算ロジックは各マクロの実装に依存する。

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | LoadError | マクロ展開中にエラーが発生 | マクロ実装のデバッグ。@macroexpand で展開結果を確認 |
| - | MethodError | マクロ関数のシグネチャに一致する引数パターンがない | マクロ呼び出し構文を修正 |
| - | UndefVarError | マクロが存在しないシンボルを参照 | esc() の使用を見直す |
| - | ErrorException | マクロ内で error() が呼び出された場合 | マクロ実装のロジックを修正 |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- マクロ展開はコンパイル時に1回だけ実行される
- ランタイムオーバーヘッドはゼロ（展開後のコードのみが実行される）
- マクロ内で重い計算を行うとコンパイル時間に影響する

## セキュリティ考慮事項

マクロは任意のコードをコンパイル時に実行できるため、信頼できないソースからのマクロは注意が必要。`eval` 等のランタイムコード実行をマクロ内で行うことは避けるべき。

## 備考

- Julia のマクロは Lisp 系のマクロに近いが、衛生的マクロの仕組みが組み込まれている
- `@macroexpand` でマクロ展開結果を確認できる（機能 No.57 参照）
- `@generated` 関数はマクロとは異なるステージドプログラミング機構（機能 No.56 参照）

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: マクロの基本構造を理解する

マクロは Core レベルで定義されるため、ランタイム C コードとパーサーの理解が必要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | boot.jl | `base/boot.jl` | Core で定義される基本マクロ（@inline, @noinline 等、329-330行目）の最小限の実装パターン |
| 1-2 | expr.jl | `base/expr.jl` | gensym 関数（18-23行目）: ccall(:jl_gensym) / ccall(:jl_tagged_gensym) による一意シンボル生成 |
| 1-3 | expr.jl | `base/expr.jl` | @gensym マクロ（31-38行目）: 複数変数に一括で gensym を割り当て |

**読解のコツ**: Julia のマクロは `macro name(args...) ... end` で定義され、内部的には `@name` という名前の関数として登録される。マクロ関数は通常の関数と異なり、引数が評価されずに Expr として渡される。`__module__` と `__source__` は暗黙の引数。

#### Step 2: マクロユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | expr.jl | `base/expr.jl` | pushmeta!（1101-1112行目）: 関数定義の :meta 式にタグを追加するヘルパー |
| 2-2 | expr.jl | `base/expr.jl` | findmeta / findmeta_block（1179-1205行目）: 関数本体から :meta 式を検索 |
| 2-3 | expr.jl | `base/expr.jl` | popmeta! / peekmeta（1114-1138行目）: メタ情報の取得・削除 |
| 2-4 | expr.jl | `base/expr.jl` | is_function_def / is_short_function_def（1167-1177行目）: 式が関数定義かどうかの判定 |
| 2-5 | expr.jl | `base/expr.jl` | unwrap_macrocalls（1092-1099行目）: ネストしたマクロ呼び出しを展開して内部式を取得 |

**主要処理フロー**:
- **1101-1112行目**: `pushmeta!` は関数定義式の本体先頭に `Expr(:meta, tag)` を挿入
- **1152-1165行目**: `annotate_meta_def_or_block` は関数定義とコードブロックの両方に対応

#### Step 3: 標準マクロの実装例を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | expr.jl | `base/expr.jl` | @inline マクロ（383-385行目）: 関数定義またはコードブロックに :inline メタを付与 |
| 3-2 | expr.jl | `base/expr.jl` | @noinline マクロ（460-462行目）: :noinline メタの付与 |
| 3-3 | expr.jl | `base/expr.jl` | @constprop マクロ（499-507行目）: 定数伝播制御のメタ付与 |
| 3-4 | expr.jl | `base/expr.jl` | @assume_effects マクロ（870-892行目）: コンパイラ効果アノテーション |
| 3-5 | expr.jl | `base/expr.jl` | @generated マクロ（1284-1300行目）: ステージド関数のマクロ実装 |

**主要処理フロー**:
- **383行目**: `@inline` は `annotate_meta_def_or_block(x, :inline)` を呼ぶ
- **1284-1300行目**: `@generated` は関数定義を `Expr(:if, Expr(:generated), body, ...)` に変換

### プログラム呼び出し階層図

```
ソースコード @name args...
    │
    ├─ パーサー (JuliaSyntax.jl)
    │      └─ Expr(:macrocall, Symbol("@name"), __source__, args...)
    │
    ├─ lowering フェーズ (C ランタイム: jl_macroexpand)
    │      ├─ マクロ関数の解決
    │      ├─ マクロ関数呼び出し（__module__, __source__ を付加）
    │      ├─ ハイジーン処理
    │      └─ 再帰的マクロ展開
    │
    └─ マクロ関数本体（Julia コード）
           ├─ 引数（Expr）の解析・変換
           ├─ esc() によるスコープ制御
           ├─ gensym() による一意変数名生成
           ├─ quote/: による式構築
           └─ 変換後の Expr を返却
```

### データフロー図

```
[入力]                        [処理]                          [出力]

ソースコード               → パーサー                       → AST (Expr)
  @time expr                  Expr(:macrocall, ...)

AST (Expr)                 → lowering/マクロ展開             → 展開後 AST
  Expr(:macrocall, ...)       マクロ関数呼び出し
                              ハイジーン処理
                              再帰展開

展開後 AST                 → lowering 続行                   → IR
                              コンパイル
                              実行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| expr.jl | `base/expr.jl` | ソース | gensym, @gensym, マクロユーティリティ関数、標準マクロ（@inline, @generated 等）の実装 |
| boot.jl | `base/boot.jl` | ソース | Core レベルの基本マクロ定義 |
| essentials.jl | `base/essentials.jl` | ソース | @boundscheck, @inbounds 等の基本マクロ |
| docs/Docs.jl | `base/docs/Docs.jl` | ソース | @doc マクロの実装 |
| show.jl | `base/show.jl` | ソース | @show マクロの実装 |
| timing.jl | `base/timing.jl` | ソース | @time, @elapsed 等のタイミングマクロ |
| ast.c | `src/ast.c` | ソース (C) | jl_macroexpand のCランタイム実装 |
