# 帳票設計書 4-AST.dot

## 概要

本ドキュメントは、Lua Language ServerのAST可視化機能によって出力される`AST.dot`ファイルの帳票設計を定義する。この帳票は、LuaソースコードのAST（抽象構文木）をGraphviz DOT形式で出力し、コードの構造を視覚的に理解できるようにする。

### 本帳票の処理概要

本帳票は、Lua Language ServerのCLIコマンド`--visualize`オプションによって生成されるAST可視化データであり、Luaコードの構文構造をグラフとして表現する。

**業務上の目的・背景**：Luaコードの構文解析結果を視覚的に確認するために必要である。パーサーの動作理解、構文エラーのデバッグ、言語仕様の学習、コンパイラ開発時のAST構造確認に活用される。

**帳票の利用シーン**：パーサー開発・デバッグ時、構文解析の学習・教育時、複雑なコード構造の可視化・説明時、また構文エラーの原因調査時に利用される。

**主要な出力内容**：
1. ASTノードの定義（ノードID、ラベル、ツールチップ）
2. 親子関係のエッジ定義
3. ノードの位置情報（start/finish）
4. ノードの追加属性（型、名前など）

**帳票の出力タイミング**：CLIから`--visualize`オプションを指定して実行した際に標準出力に出力される。

**帳票の利用者**：パーサー開発者、言語仕様研究者、教育者、デバッガー

## 帳票種別

グラフデータ（AST可視化）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| CLI | コマンドラインインターフェース | `lua-language-server --visualize=<file>` | --visualizeオプション指定 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | DOT (Graphviz) |
| 用紙サイズ | N/A（グラフデータ） |
| 向き | N/A |
| ファイル名 | 標準出力（リダイレクトでファイル保存可能） |
| 出力方法 | 標準出力 |
| 文字コード | UTF-8 |

### DOT固有設定

| 項目 | 内容 |
|-----|------|
| グラフ種別 | digraph（有向グラフ） |
| ノード形状 | rect（矩形） |
| ノードラベル | 2行（ノード型 + キー名） |
| ツールチップ | ノードの詳細属性 |

## 帳票レイアウト

### レイアウト概要

DOTファイルは有向グラフ（digraph）として構成され、各ASTノードが頂点、親子関係がエッジとなる。

```dot
digraph AST {
    node [shape = rect]
    "{ノードID}" [
        label="{ノード型}\l{キー名}\l"
        tooltip="{ツールチップ}"
    ]
    "{親ノードID}" -> "{子ノードID}"
    ...
}
```

### ヘッダー部

グラフ定義のヘッダー。

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | グラフ名 | 固定値"AST" | 定数 | digraph AST { |
| 2 | ノード属性 | デフォルトノード形状 | 定数 | node [shape = rect] |

### 明細部

各ASTノードの定義:

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ノードID | ノードの一意識別子 | nodeId(node) | {type}:{start}:{finish} |
| 2 | ノード型 | ASTノードの種別 | node.type | 文字列（1行目） |
| 3 | キー名 | ノードの識別名 | guide.getKeyName(node) | 文字列（2行目、20文字制限） |
| 4 | ツールチップ | ノード属性の詳細 | getTooltip(node) | 複数行文字列 |
| 5 | エッジ | 親子関係 | guide.eachChild() | "{親}" -> "{子}" |

### ツールチップ内容

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | start | 開始位置 | node.start | 整数 |
| 2 | finish | 終了位置 | node.finish | 整数 |
| 3 | 各属性 | ノード固有属性 | node[k] | k: v\n形式 |
| 4 | 配列要素 | インデックス付き値 | node[1..15] | i: v\n形式 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | グラフ終端 | グラフ定義の終了 | 定数 | } |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| VISUALIZEパス | 可視化対象ファイル | Yes |
| ファイル読み込み成功 | io.openが成功 | Yes |
| パース成功 | parser.compileが成功 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 深さ優先 | ツリー走査順 |

### 改ページ条件

N/A（グラフデータのため改ページなし）

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベースを使用せず、パーサーの解析結果を参照する。

| データソース | 用途 | 取得方法 |
|-------------|------|---------|
| parser.compile() | AST生成 | ソースコード解析 |
| guide.eachChild() | 子ノード走査 | ASTトラバース |
| guide.getKeyName() | キー名取得 | ノード属性取得 |

### テーブル別参照項目詳細

#### ASTノード（parser.object）

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| type | ノード型 | なし | ASTノード種別 |
| start | 開始位置 | なし | バイトオフセット |
| finish | 終了位置 | なし | バイトオフセット |
| op.type | 演算子型 | binary/unary時 | 演算子種別 |
| [1] | ラベル名 | label/goto時 | ラベル識別子 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| ノードID | `{type}:{start}:{finish}` | N/A | 一意識別子生成 |
| 短縮文字列 | `str:sub(1, 17) .. '...'` | N/A | 20文字超過時に短縮 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[CLI実行 --visualize=file] --> B[ファイル読み込み]
    B --> C{読み込み成功?}
    C -->|No| D[エラー出力・終了]
    C -->|Yes| E[parser.compile実行]
    E --> F[writer = io.stdout]
    F --> G["digraph AST {"出力]
    G --> H[ノード属性定義出力]
    H --> I[ルートノードからツリー走査]
    I --> J{ノードあり?}
    J -->|Yes| K[ノードラベル生成]
    K --> L[ノード定義出力]
    L --> M{親ノードあり?}
    M -->|Yes| N[エッジ定義出力]
    M -->|No| O[子ノード走査]
    N --> O
    O --> J
    J -->|No| P["}"出力]
    P --> Q[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ファイルオープン失敗 | io.openが失敗 | failed to open {file}: {err} | ファイルパス・権限確認 |
| パースエラー | 構文解析失敗 | （内部でエラーAST生成） | ソースコード修正 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | ファイルサイズ依存（数百〜数千ノード） |
| 目標出力時間 | 小〜中規模ファイルで1秒以内 |
| 同時出力数上限 | 1（CLI実行単位） |

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

- 出力にはソースコードの構造が含まれるため、機密コードの漏洩に注意
- ツールチップには文字列リテラル等のコード内容が部分的に含まれる

## 備考

- Lua 5.4がデフォルトバージョン（`_G['LUA_VER']`で変更可能）
- Graphvizツール（dot、neato等）でPNG/SVG等に変換可能
- 大規模ファイルでは出力サイズが大きくなるため注意
- 配列要素は最大15件まで表示、超過分は省略表示

---

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

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

### 推奨読解順序

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

出力されるDOTファイルの構造と、入力となるASTノードの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | visualize.lua | `script/cli/visualize.lua` | nodeEntry定数（56行目）でノード出力形式を理解 |
| 1-2 | guide.lua | `script/parser/guide.lua` | parser.object型の構造を理解 |

**読解のコツ**: `nodeEntry`定数がDOTノードのテンプレート。`%s`プレースホルダーにノードID、ノード型、キー名、ツールチップが挿入される。

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

CLIからの処理フローを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | init.lua | `script/cli/init.lua` | VISUALIZE分岐（31-34行目） |
| 2-2 | visualize.lua | `script/cli/visualize.lua` | runCLI関数（94-104行目）がCLIエントリーポイント |

**主要処理フロー**:
1. **95行目**: lang(LOCALE)でロケール設定
2. **96行目**: VISUALIZEグローバルからファイルパス取得
3. **97-101行目**: ファイル読み込み
4. **102行目**: ファイル全体読み込み
5. **103行目**: visualizeAst呼び出し

#### Step 3: AST生成・出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | visualize.lua | `script/cli/visualize.lua` | visualizeAst関数（86-92行目） |
| 3-2 | visualize.lua | `script/cli/visualize.lua` | getVisualizeVisitor関数（67-81行目） |

**主要処理フロー**:
- **87行目**: `parser.compile(code, 'Lua', 'Lua 5.4')`でAST生成
- **88行目**: `digraph AST {`出力
- **89行目**: `node [shape = rect]`出力
- **90行目**: `getVisualizeVisitor(writer)(state.ast)`でツリー走査
- **91行目**: `}`出力

#### Step 4: ノード出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | visualize.lua | `script/cli/visualize.lua` | getNodeLabel関数（57-65行目） |
| 4-2 | visualize.lua | `script/cli/visualize.lua` | getTooltip関数（35-54行目） |
| 4-3 | visualize.lua | `script/cli/visualize.lua` | nodeId関数（6-8行目） |
| 4-4 | visualize.lua | `script/cli/visualize.lua` | shorten関数（10-19行目） |

**主要処理フロー**:
- **6-8行目**: `nodeId()`でノードID生成（type:start:finish形式）
- **57-65行目**: `getNodeLabel()`でラベル・ツールチップ生成
- **35-54行目**: `getTooltip()`でノード属性を列挙

#### Step 5: ツリー走査処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | visualize.lua | `script/cli/visualize.lua` | visitNode関数（68-80行目） |

**主要処理フロー**:
- **70行目**: `getNodeLabel(node)`でラベル生成
- **71行目**: `writer:write(...)`でノード出力
- **72-75行目**: 親ノードがあればエッジ出力
- **76-78行目**: `guide.eachChild()`で子ノード再帰走査

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

```
script/cli/init.lua
    │
    └─ VISUALIZE分岐
           └─ require 'cli.visualize'.runCLI()
                  │
                  ├─ io.open(file) → ファイル読み込み
                  │
                  └─ visualizeAst(code, io.stdout)
                         │
                         ├─ parser.compile(code, 'Lua', version)
                         │      └─ state.ast 生成
                         │
                         ├─ writer:write('digraph AST {\n')
                         │
                         ├─ writer:write('node [shape = rect]\n')
                         │
                         ├─ getVisualizeVisitor(writer)(state.ast)
                         │      │
                         │      └─ visitNode(node, parent)  [再帰]
                         │             │
                         │             ├─ getNodeLabel(node)
                         │             │      ├─ nodeId(node)
                         │             │      ├─ guide.getKeyName(node)
                         │             │      ├─ shorten(keyName)
                         │             │      └─ getTooltip(node)
                         │             │
                         │             ├─ writer:write(ノード定義)
                         │             │
                         │             ├─ writer:write(エッジ定義)
                         │             │
                         │             └─ guide.eachChild(node, visitNode)
                         │
                         └─ writer:write('}\n')
```

### データフロー図

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

Luaソースファイル ──▶ io.open().read('a')
                              │
                              ▼
                      parser.compile()
                              │
                              ▼
                        AST生成 (state.ast)
                              │
                              ▼
                   getVisualizeVisitor()
                              │
                              ├─ ノード定義生成
                              │      ├─ nodeId()
                              │      ├─ getNodeLabel()
                              │      └─ getTooltip()
                              │
                              ├─ エッジ定義生成
                              │
                              └─ 子ノード再帰
                                    │
                                    ▼
                          ─────────────────────▶ DOT (stdout)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| visualize.lua | `script/cli/visualize.lua` | ソース | CLIエントリーポイント、DOT生成 |
| init.lua | `script/cli/init.lua` | ソース | CLIオプション分岐 |
| parser.lua | `script/parser/init.lua` | ソース | Luaパーサー |
| guide.lua | `script/parser/guide.lua` | ソース | AST操作ユーティリティ |
| language.lua | `script/language.lua` | ソース | ロケール設定 |
