# 機能設計書 131-Enum

## 概要

本ドキュメントは、Julia の `@enum` マクロによるユーザー定義列挙型の生成機能について、その設計・処理仕様を記述するものである。

### 本機能の処理概要

`@enum` マクロは、名前付き整数定数の集合をプリミティブ型として定義するための機能である。C 言語の enum に相当するが、Julia の型システムと統合されており、型安全な列挙型を提供する。

**業務上の目的・背景**：プログラムにおいて有限個の名前付き状態や分類を表現する場合、マジックナンバーの使用を避け、型安全かつ可読性の高いコードを実現する必要がある。`@enum` はこのニーズに対して、コンパイル時にプリミティブ型として列挙型を生成し、多重ディスパッチとの統合も実現する。

**機能の利用シーン**：状態管理（状態マシンの状態定義）、分類コード（ファイル種別、色、方向など）、フラグ値の定義、API の戻り値ステータスコードの定義など、有限個の名前付き定数が必要なすべての場面で利用される。

**主要な処理内容**：
1. `@enum` マクロの構文解析（型名、ベース型、メンバー名と値の抽出）
2. プリミティブ型としての列挙型の生成（`primitive type ... <: Enum{BaseType} ... end`）
3. コンストラクタ関数の生成（整数値からの変換と値域チェック）
4. namemap（整数値からシンボルへのマッピング）の定義
5. `typemin` / `typemax` / `instances` / `hash` / `show` / `print` 等の基本メソッドの定義
6. 各メンバー名のグローバル定数としてのエクスポート

**関連システム・外部連携**：Julia の型システム（Core の型定義機構）、Base の I/O システム（show/print）、ブロードキャスト機構（broadcastable）、ハッシュ関数（hash）と連携する。

**権限による制御**：特に権限による制御はない。モジュールスコープ内で定義され、export によって外部公開を制御する。

## 関連画面

本機能はREPL画面からの直接利用はあるが、特定の画面との紐付けはない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | REPLから`@enum`マクロを呼び出して列挙型を定義・利用する |

## 機能種別

メタプログラミング（マクロによるコンパイル時コード生成）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| T | Symbol または Expr | Yes | 列挙型の名前。`EnumName` または `EnumName::BaseType` の形式 | Symbol であること。`::BaseType` 指定時は BaseType が Integer のプリミティブサブタイプであること |
| syms | Symbol または Expr の可変長引数 | Yes | メンバー定義。`value1[=x]` の形式。begin ... end ブロックも可 | 1つ以上必要。名前は有効な識別子であること。値指定時は Integer であること。名前の重複不可。値の重複不可（明示指定時） |

### 入力データソース

Julia ソースコード内のマクロ呼び出し。コンパイル時に処理される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 列挙型定義 | primitive type | `Enum{BaseType}` を継承するプリミティブ型 |
| コンストラクタ | Function | 整数から列挙型への変換関数（値域チェック付き） |
| namemap | Function | 整数値からSymbolへのマッピング関数 |
| typemin/typemax | Function | 最小値・最大値を返す関数 |
| instances | Function | 全メンバーのタプルを返す関数 |
| hash | Function | ハッシュ値計算の特殊化メソッド |
| 定数群 | const | 各メンバー名のグローバル定数（型名がSymbolの場合） |

### 出力先

定義元モジュールのグローバルスコープに型と定数が登録される。

## 処理フロー

### 処理シーケンス

```
1. マクロ引数の解析
   └─ 型名とベース型（デフォルト Int32）を抽出
2. メンバー定義の走査
   └─ 各シンボルについて名前と値を抽出し、重複チェック・オーバーフローチェックを実施
3. membershiptest関数によるバリデーションコード生成
   └─ 値の範囲に応じて効率的な所属テスト式を生成（範囲チェック / OR連鎖 / Set検索）
4. コード生成ブロックの構築
   └─ primitive type定義、コンストラクタ、namemap、typemin/typemax、hash、instancesの各メソッドを生成
5. グローバル定数の追加
   └─ 各メンバー名を定数として定義
6. :toplevelブロックとして返却
   └─ 生成したコードをトップレベル式として返す
```

### フローチャート

```mermaid
flowchart TD
    A["@enum マクロ呼び出し"] --> B{"引数が空か?"}
    B -->|Yes| E1["ArgumentError: no arguments"]
    B -->|No| C{"型名に ::BaseType あり?"}
    C -->|Yes| D["BaseType を Core.eval で評価"]
    C -->|No| D2["BaseType = Int32"]
    D --> F{"BaseType は Integer のプリミティブ?"}
    F -->|No| E2["ArgumentError: invalid base type"]
    F -->|Yes| G["メンバー定義の走査ループ"]
    D2 --> G
    G --> H{"各 sym について"}
    H --> I{"Symbol のみ?"}
    I -->|Yes| J["自動連番 i を割り当て"]
    H --> K{"name = value 形式?"}
    K -->|Yes| L["value を Core.eval で評価"]
    L --> M{"Integer か?"}
    M -->|No| E3["ArgumentError: values must be integers"]
    M -->|Yes| N["convert(basetype, i)"]
    J --> O["重複チェック（名前・値）"]
    N --> O
    O --> P["namemap / values / seen に追加"]
    P --> H
    H -->|走査完了| Q["membershiptest によるバリデーション式生成"]
    Q --> R["コード生成ブロック構築"]
    R --> S["定数定義追加"]
    S --> T[":toplevel ブロックとして返却"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-131-01 | ベース型制約 | ベース型は Integer のプリミティブサブタイプ（isbitstype）でなければならない | `::BaseType` 指定時 |
| BR-131-02 | デフォルトベース型 | ベース型省略時は Int32 を使用する | `::BaseType` 未指定時 |
| BR-131-03 | 自動連番 | 値未指定のメンバーは前メンバー+1 の値を持つ（初期値は 0） | 値を明示指定しないメンバー |
| BR-131-04 | 名前一意性 | メンバー名は列挙型内で一意でなければならない | 常時 |
| BR-131-05 | 値一意性 | 明示的に値を指定した場合、値は一意でなければならない | hasexpr = true の場合 |
| BR-131-06 | 識別子制約 | メンバー名は有効な Julia 識別子でなければならない | 常時 |
| BR-131-07 | オーバーフロー防止 | 自動連番で typemin に戻る場合はエラーとする | 常時 |

### 計算ロジック

**membershiptest 関数**（79-88行目）:
- 値が連続範囲の場合: `lo <= expr <= hi` の範囲チェック
- 値が20個未満の場合: `expr == v1 || expr == v2 || ...` のOR連鎖
- 値が20個以上の場合: `expr in Set(values)` のSet検索

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

本機能はデータベース操作を行わない。

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | 引数が空 | メンバーを1つ以上指定する |
| - | ArgumentError | 無効なベース型 | Integer のプリミティブサブタイプを指定する |
| - | ArgumentError | 無効な型式 | 正しい構文（`EnumName` または `EnumName::BaseType`）を使用する |
| - | ArgumentError | 値が整数でない | メンバー値に整数リテラルまたは整数式を指定する |
| - | ArgumentError | 値の重複 | 一意な値を指定する |
| - | ArgumentError | 名前の重複 | 一意な名前を使用する |
| - | ArgumentError | 無効な識別子 | 有効な Julia 識別子を使用する |
| - | ArgumentError | オーバーフロー | ベース型の範囲内に収まる値を指定する |
| - | ArgumentError | コンストラクタへの無効な値 | `enum_argument_error` により、定義されていない値で列挙型を構築しようとした場合にスローされる |

### リトライ仕様

コンパイル時マクロのため、リトライ仕様はない。

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

該当なし。

## パフォーマンス要件

- マクロ展開はコンパイル時に行われ、ランタイムコストはない
- 列挙型はプリミティブ型であり、isbitstype として最適化される
- membershiptest は値の分布に応じて最適なテスト方式を選択する
- hash は型ハッシュと値ハッシュの組み合わせにより効率的に計算される

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

- `Core.eval(__module__, ...)` を使用してベース型と値式を評価するため、マクロ呼び出し元のモジュールコンテキストで評価が行われる
- 通常のJuliaコードセキュリティモデルに従う

## 備考

- `@enum` で定義された型は `instances()` で全メンバーを列挙可能
- `Symbol()` でメンバー名のシンボルを取得可能
- ブロードキャストではスカラーとして扱われる（`broadcastable` が `Ref(x)` を返す）
- 無効な値でコンストラクタを呼ぶと `ArgumentError` がスローされる

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

Enum モジュールの基本的な型定義と抽象型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Enums.jl | `base/Enums.jl` | **15行目**: `abstract type Enum{T<:Integer} end` -- 全列挙型の基底抽象型。型パラメータ T が整数のサブタイプに制約されている |
| 1-2 | Enums.jl | `base/Enums.jl` | **17行目**: `basetype` 関数 -- 列挙型からベース整数型を取り出すユーティリティ |

**読解のコツ**: `Enum{T<:Integer}` はパラメトリック抽象型であり、`@enum` で生成されるプリミティブ型はすべてこの型のサブタイプとなる。`primitive type Foo <: Enum{Int32} 32 end` のような型が生成される。

#### Step 2: エントリーポイントを理解する

`@enum` マクロが実際のエントリーポイントである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Enums.jl | `base/Enums.jl` | **144-237行目**: `macro enum(T, syms...)` -- マクロ本体。引数解析からコード生成まで全処理を含む |

**主要処理フロー**:
1. **145-146行目**: 引数が空の場合のバリデーション
2. **148-159行目**: 型名とベース型の解析。`::BaseType` 構文の処理
3. **160-163行目**: values / seen / namemap の初期化
4. **166-168行目**: begin ブロック構文のアンラップ
5. **169-207行目**: メンバー走査ループ。各メンバーの名前・値抽出と重複チェック
6. **208-236行目**: コード生成ブロックの構築。primitive type 定義、コンストラクタ、namemap、typemin/typemax、hash、instances、定数定義

#### Step 3: メンバーシップテスト生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Enums.jl | `base/Enums.jl` | **79-88行目**: `membershiptest` 関数 -- 値の分布に応じた効率的なバリデーション式の生成 |

**主要処理フロー**:
- **80行目**: `extrema` で値の最小・最大を取得
- **81行目**: 値が連続範囲なら範囲チェック式を生成
- **83-84行目**: 20個未満なら OR 連鎖式を生成
- **85-86行目**: 20個以上なら Set 所属テスト式を生成

#### Step 4: 表示・比較・ハッシュを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Enums.jl | `base/Enums.jl` | **34-62行目**: Symbol変換、print、show の各メソッド |
| 4-2 | Enums.jl | `base/Enums.jl` | **24-32行目**: `_enum_hash` と `hash`、`isless` メソッド |
| 4-3 | Enums.jl | `base/Enums.jl` | **64-76行目**: `show(io, MIME"text/plain", t::Type{<:Enum})` -- 型自体の表示で全メンバーを列挙 |

**読解のコツ**: `_enum_hash` は内部関数であり、ユーザーが `Base.hash` を特殊化してもプリコンパイル警告を避けるための設計パターンである。各列挙型に対して `@enum` マクロ内でこの関数が特殊化される（223行目）。

#### Step 5: I/O サポートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | Enums.jl | `base/Enums.jl` | **19-22行目**: Integer 変換、cconvert、write、read メソッド |
| 5-2 | Enums.jl | `base/Enums.jl` | **91行目**: `broadcastable` -- ブロードキャストでのスカラー扱い |

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

```
@enum マクロ (144行目)
    |
    +-- Core.eval(__module__, T.args[2])     # ベース型の評価 (152行目)
    |
    +-- Core.eval(__module__, s.args[2])     # メンバー値の評価 (178行目)
    |
    +-- Base.isidentifier(s)                 # 識別子バリデーション (189行目)
    |
    +-- membershiptest(expr, values)         # バリデーション式生成 (79行目)
    |       |
    |       +-- extrema(values)
    |       +-- foldl (OR連鎖生成)
    |       +-- Set(values) (Set検索生成)
    |
    +-- [生成コード]
            |
            +-- primitive type ... <: Enum{basetype} ... end
            +-- コンストラクタ関数 (membershiptest + bitcast)
            +-- Enums.namemap() メソッド
            +-- Base.typemin() / Base.typemax()
            +-- Enums._enum_hash() メソッド
            +-- Base.instances() メソッド
            +-- const メンバー定数群
```

### データフロー図

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

@enum Fruit apple=1    --> マクロ引数解析              --> 型名: Fruit
      orange=2             (型名・ベース型抽出)            ベース型: Int32
      kiwi=3
                       --> メンバー走査ループ           --> values: [1, 2, 3]
                           (名前・値・重複チェック)         namemap: {1=>:apple, 2=>:orange, 3=>:kiwi}
                                                          seen: {:apple, :orange, :kiwi}

                       --> membershiptest生成           --> バリデーション式: 1 <= x <= 3

                       --> コード生成ブロック構築        --> primitive type Fruit <: Enum{Int32} 32 end
                                                          Fruit(x::Integer) 関数
                                                          namemap(::Type{Fruit}) 関数
                                                          typemin/typemax/hash/instances メソッド
                                                          const apple = Fruit(1)
                                                          const orange = Fruit(2)
                                                          const kiwi = Fruit(3)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Enums.jl | `base/Enums.jl` | ソース | Enum 抽象型定義、@enum マクロ実装、表示・比較・ハッシュメソッド |
| boot.jl | `base/boot.jl` | ソース | Core の基本型定義。primitive type 構文の基盤 |
| essentials.jl | `base/essentials.jl` | ソース | `isbitstype` 等の基本関数定義 |
| hashing.jl | `base/hashing.jl` | ソース | `hash` 関数の基本実装。Enum の hash メソッドが呼び出す |
| show.jl | `base/show.jl` | ソース | `show` / `print` の基本実装 |
| test/enums.jl | `test/enums.jl` | テスト | Enum 機能の回帰テスト |
