# 機能設計書: Nimpretty (No.26)

## 1. 機能概要

### 1.1 機能名
Nimpretty (nimpretty/nimpretty.nim)

### 1.2 機能説明
Nimソースコードの自動整形ツール。インデント調整、行の最大長制限、コードスタイルの統一を行い、読みやすく一貫性のあるコードに整形する。元のファイルの上書きまたは別ファイルへの出力が可能。

### 1.3 関連画面
- CLI（コマンドラインインターフェース）

## 2. 機能詳細

### 2.1 入力
| 項目 | 型 | 必須 | 説明 |
|------|-----|------|------|
| nimfiles | string[] | Yes* | 入力ファイルパス（複数可） |
| --stdin | flag | No | stdinからの入力 |
| --out | string | No | 出力ファイルパス |
| --outDir | string | No | 出力ディレクトリ |
| --indent | int | No | インデント幅（0=自動検出、デフォルト） |
| --maxLineLen | int | No | 最大行長（デフォルト: 80） |

### 2.2 処理フロー
1. **コマンドライン解析**
   - getopt()でオプション取得
   - ファイル/ディレクトリの展開

2. **出力先決定**
   - outfile/outdirの排他チェック
   - 指定なしの場合は入力ファイルを上書き

3. **ファイル処理**
   - 各入力ファイルに対してprettyPrint()実行
   - 必要に応じてバックアップ作成

4. **パース＆整形**
   - setupParser()でパーサー初期化
   - parseAll()でAST生成
   - closeEmitter()で整形済みコード出力

5. **整合性検証**
   - finalCheck()で整形結果の構文検証

### 2.3 出力
| 項目 | 型 | 説明 |
|------|-----|------|
| 整形済みファイル | file | 指定ファイルまたは元ファイルを上書き |
| stdout | stream | --stdin時の出力 |

### 2.4 エラーハンドリング
| エラー条件 | 処理 |
|------------|------|
| 入力ファイルなし | quit("[Error] no input file.") |
| out/outDir同時指定 | quit("[Error] out and outDir cannot both be specified") |
| 構文エラー | conf.errorCounterで検出 |

## 3. ビジネスルール

### 3.1 整形オプション
| オプション | デフォルト | 説明 |
|------------|------------|------|
| indWidth | 0（自動検出） | インデント幅（スペース数） |
| maxLineLen | 80 | 最大行長 |

### 3.2 ファイル処理ルール
1. ディレクトリ指定時: `.nim`, `.nimble` ファイルを再帰走査
2. 出力先未指定時: 入力ファイルを上書き
3. outDir指定時: 各ファイルをoutDir配下に出力
4. 同一ファイルの上書き時のみバックアップ可能

### 3.3 AST等価性検証
整形後のコードが元のコードと意味的に等価であることを確認:
```nim
proc goodEnough(a, b: PNode): bool =
  # ノードの種類と子ノード数が一致
  # リテラル値が一致
  # 識別子が一致
```

## 4. 技術仕様

### 4.1 使用ライブラリ
- compiler/idents, llstream, ast, msgs
- compiler/syntaxes, options, pathutils, layouter
- parseopt, strutils, os, sequtils
- std/tempfiles

### 4.2 主要データ構造
```nim
type
  PrettyOptions* = object
    indWidth*: Natural      # インデント幅
    maxLineLen*: Positive   # 最大行長
```

### 4.3 コア関数
```nim
proc prettyPrint*(infile, outfile: string; opt: PrettyOptions) =
  var conf = newConfigRef()
  var parser: Parser
  parser.em.indWidth = opt.indWidth
  if setupParser(parser, fileIdx, newIdentCache(), conf):
    parser.em.maxLineLen = opt.maxLineLen
    let fullAst = parseAll(parser)
    closeParser(parser)
    closeEmitter(parser.em, fullAst, finalCheck)
```

### 4.4 stdin処理
```nim
proc handleStdinInput(opt: PrettyOptions) =
  var content = readAll(stdin)
  var (cfile, path) = createTempFile("nimpretty_", ".nim")
  writeFile(path, content)
  prettyPrint(path, path, opt)
  stdout.write(readAll(cfile))
```

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

### 5.1 推奨読解順序

#### 5.1.1 データ構造の理解
1. **nimpretty.nim 21-37行目**: 定数とUsage
   - Version, Usage定数

2. **nimpretty.nim 49-52行目**: PrettyOptions型
   - indWidth, maxLineLen

#### 5.1.2 エントリーポイント
3. **nimpretty.nim 119-192行目**: main関数
   - コマンドライン解析
   - ファイル処理ループ

#### 5.1.3 コア処理
4. **nimpretty.nim 90-103行目**: prettyPrint関数
   - パーサー設定
   - AST生成
   - 整形出力

5. **nimpretty.nim 78-88行目**: finalCheck関数
   - 整形結果の構文検証

6. **nimpretty.nim 54-77行目**: goodEnough関数
   - AST等価性チェック

### 5.2 読解のコツ

#### Emitterの理解
- `parser.em` がEmitter（整形出力器）
- `indWidth`, `maxLineLen` を設定
- `closeEmitter()` で最終出力

#### 一時ファイル処理
```nim
var (cfile, path) = createTempFile("nimpretty_", ".nim")
# stdinの内容を一時ファイルに書き込み
# prettyPrintで処理後、一時ファイルを読み取り
```

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

```
nimpretty [options] files...
  |
  +-- main()
        |
        +-- getopt() - オプション解析
        |     +-- infiles収集
        |     +-- opt設定（indWidth, maxLineLen）
        |
        +-- [--stdin指定時]
        |     +-- handleStdinInput(opt)
        |           +-- createTempFile()
        |           +-- prettyPrint(path, path, opt)
        |           +-- stdout.write()
        |
        +-- [ファイル指定時]
              +-- for (infile, outfile) in zip(...):
                    |
                    +-- createDir(dir)
                    +-- [backup時] copyFile()
                    +-- prettyPrint(infile, outfile, opt)
                          |
                          +-- newConfigRef()
                          +-- setupParser(parser, fileIdx)
                          |     +-- parser.em.indWidth = opt.indWidth
                          |     +-- parser.em.maxLineLen = opt.maxLineLen
                          |
                          +-- parseAll(parser) --> fullAst
                          +-- closeParser(parser)
                          +-- closeEmitter(parser.em, fullAst, finalCheck)
                                +-- finalCheck(content, origAst)
                                      +-- openParser() - 再パース
                                      +-- parseAll()
                                      +-- 構文エラーチェック
```

### 5.4 データフロー図

```
[入力ファイル / stdin]
        |
        v
[readAll() / ファイル読み込み]
        |
        v
[setupParser()]
  +-- llStreamOpen()
  +-- newIdentCache()
        |
        v
[parseAll(parser)] --> [fullAst: PNode]
        |
        v
[closeEmitter(parser.em, fullAst, finalCheck)]
  |
  +-- レイアウト整形
  +-- インデント調整
  +-- 行長制限適用
        |
        v
[整形済みコード文字列]
        |
        v
[finalCheck()]
  +-- 再パース
  +-- エラーカウント確認
        |
        v
[出力ファイル / stdout]
```

### 5.5 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| nimpretty/nimpretty.nim | メイン | 整形ツール本体 |
| compiler/layouter.nim | コンパイラ | レイアウト/整形ロジック |
| compiler/syntaxes.nim | コンパイラ | パーサー管理 |
| compiler/llstream.nim | コンパイラ | ストリーム処理 |
| compiler/ast.nim | コンパイラ | AST定義 |
| compiler/parser.nim | コンパイラ | 構文解析 |
| nimpretty/tester.nim | テスト | テストランナー |

## 6. 変更履歴

| 日付 | 変更者 | 変更内容 |
|------|--------|----------|
| 2026-02-17 | Claude | 初版作成 |
