# 機能設計書 85-JSONC処理

## 概要

本ドキュメントは、lua-language-serverにおけるJSONC処理機能について記載する。この機能は、コメント付きJSON（JSONC: JSON with Comments）のデコード処理を提供する。

### 本機能の処理概要

**業務上の目的・背景**：Visual Studio Codeを含む多くのエディタでは、設定ファイルにJSONを使用するが、可読性向上のためコメントを許可したJSONC形式が広く使われている。lua-language-serverの設定ファイル（.luarc.json）もJSONC形式をサポートしており、本機能はコメント（行コメント「//」とブロックコメント「/* */」）を含むJSONを正しくパースする。標準のJSON処理（json.lua）を拡張し、コメントスキップ機能を追加している。

**機能の利用シーン**：設定ファイル（.luarc.json, .luarc.jsonc）の読み込み時に使用される。ユーザーが設定ファイルにコメントを記述できるため、設定の意図やオプションの説明を残すことができる。

**主要な処理内容**：
1. 行コメント（//）のスキップ
2. ブロックコメント（/* */）のスキップ
3. 末尾カンマ（trailing comma）の許容
4. 空ファイルの処理（json.nullを返す）
5. 標準JSON処理との互換性維持

**関連システム・外部連携**：JSON処理モジュール（json.lua）を基盤として使用し、設定ローダー（config/loader.lua）から呼び出される。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 31 | 設定変更 | 補助機能 | 設定ファイル読み込み時のJSONCパース |
| 32 | 設定取得 | 補助機能 | 設定ファイル読み込み時のJSONCパース |

## 機能種別

データ連携 / デシリアライズ

## 入力仕様

### 入力パラメータ

#### json.decode_jsonc関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| str | string | Yes | デコードするJSONC文字列 | 文字列であること |

### 入力データソース

- 設定ファイル（.luarc.json, .luarc.jsonc）の内容
- CLIからの設定入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | any | パースされたLua値（テーブル、文字列、数値、真偽値、json.null） |

### 出力先

- 設定モジュールへのLuaテーブル返却
- 呼び出し元への結果返却

## 処理フロー

### 処理シーケンス

```
decode_jsonc処理:
1. 入力が文字列かチェック
   └─ type(str) ~= "string"でエラー
2. 状態変数を初期化
   └─ statusBuf, statusPos, statusTop = str, 1, 0
3. 空ファイルチェック
   └─ next_byte() == -1 ならjson.nullを返却
4. メイン要素をデコード
   └─ decode()呼び出し
5. ネストした要素を処理
   └─ decode_item()をstatusTop > 0の間繰り返し
6. 末尾の不正文字をチェック
   └─ trailing garbageエラー

コメントスキップ処理:
1. '/'文字を検出
2. 次の文字をチェック
   └─ '*'ならブロックコメント開始
   └─ '/'なら行コメント開始
3. コメント終端を検索
   └─ ブロック: '*/'を検索
   └─ 行: '\r'または'\n'を検索
4. 位置を更新してスキップ
```

### フローチャート

```mermaid
flowchart TD
    subgraph decode_jsonc["decode_jsonc処理"]
        J1[JSONC文字列受け取り] --> J2{type == string?}
        J2 -->|No| J3[エラー発生]
        J2 -->|Yes| J4[状態初期化]
        J4 --> J5[next_byte呼び出し]
        J5 --> J6{-1?}
        J6 -->|Yes| J7[json.null返却]
        J6 -->|No| J8[decode呼び出し]
        J8 --> J9{statusTop > 0?}
        J9 -->|Yes| J10[decode_item]
        J10 --> J9
        J9 -->|No| J11{trailing garbage?}
        J11 -->|Yes| J12[エラー発生]
        J11 -->|No| J13[結果返却]
    end

    subgraph skip_comment["コメントスキップ"]
        C1[next_byte呼び出し] --> C2{'/'?}
        C2 -->|No| C3[文字を返却]
        C2 -->|Yes| C4{次の文字}
        C4 -->|'*'| C5[ブロックコメント]
        C5 --> C6["'*/'を検索"]
        C6 --> C7[位置更新]
        C7 --> C8[再度next_byte]
        C4 -->|'/'| C9[行コメント]
        C9 --> C10["'\\r'/'\\n'を検索"]
        C10 --> C7
        C4 -->|other| C3
    end

    subgraph trailing_comma["末尾カンマ処理"]
        T1[decode_item] --> T2{','検出?}
        T2 -->|Yes| T3[next_byte]
        T3 --> T4{']'または'}'?}
        T4 -->|Yes| T5[カンマをスキップ]
        T4 -->|No| T6[次の要素をデコード]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-85-01 | 行コメント | //から行末までをコメントとして無視 | //を検出した場合 |
| BR-85-02 | ブロックコメント | /*から*/までをコメントとして無視 | /*を検出した場合 |
| BR-85-03 | 末尾カンマ | 配列・オブジェクトの最後の要素の後ろのカンマを許容 | ]または}の直前のカンマ |
| BR-85-04 | 空ファイル | 空のJSONCファイルはjson.nullを返す | next_byte() == -1 |
| BR-85-05 | 未終端コメント | ブロックコメントが終端しない場合、ファイル末尾まで無視 | */がない場合 |

### 計算ロジック

特になし

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | expected argument of type string | 引数が文字列でない | 文字列を渡す |
| - | ERROR: xxx at line N col M | JSONパースエラー | JSONC構文を修正 |
| - | trailing garbage | JSON値の後に不正な文字がある | 余分な文字を削除 |

### リトライ仕様

リトライは行わない。エラー発生時は例外を発生させる。

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

本機能はトランザクションを使用しない。

## パフォーマンス要件

- 設定ファイルの読み込みは起動時に一度行われるため、極端な高速性は不要
- ただし、大きな設定ファイルでも妥当な時間でパースできること

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

- 信頼できない入力に対するバリデーションは呼び出し元で行う

## 備考

本実装はjson.luaを拡張しており、json.decode_jsonc関数としてjsonモジュールに追加される。エンコード機能（json.encode）は標準JSONと共通であり、コメント付きでのエンコードは提供しない。

---

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

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

### 推奨読解順序

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

JSONC処理で使用される状態変数とjson.luaからの継承を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | jsonc.lua | `script/jsonc.lua` | モジュール構造とjson.luaからの継承（42行目） |

**読解のコツ**:
- `local json = require "json"`: json.luaを継承
- 状態変数（statusBuf, statusPos, statusTop, statusAry, statusRef）はjson.luaと同じ構造
- コメントスキップ機能を追加するためnext_byte関数をオーバーライド

#### Step 2: コメントスキップ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | jsonc.lua | `script/jsonc.lua` | skip_comment関数（93-117行目） |

**主要処理フロー**:
1. **93-117行目**: skip_comment - コメント検出とスキップ
2. **94-96行目**: '/'でない場合は即リターン
3. **97-106行目**: ブロックコメント処理（/* */）
4. **107-116行目**: 行コメント処理（//）

#### Step 3: next_byte関数のオーバーライドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | jsonc.lua | `script/jsonc.lua` | next_byte関数（119-130行目） |

**主要処理フロー**:
- **119-130行目**: next_byte - コメントスキップ付きの次バイト取得
- **120行目**: 空白スキップ
- **123-125行目**: skip_commentがtrueならコメントをスキップして再帰
- **127行目**: 通常の文字を返却

#### Step 4: 末尾カンマ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | jsonc.lua | `script/jsonc.lua` | decode_item関数（318-351行目） |

**主要処理フロー**:
- **334-341行目**: カンマ検出後に次の文字をチェック
- **336-337行目**: 配列で']'なら末尾カンマとして許容
- **338-339行目**: オブジェクトで'}'なら末尾カンマとして許容
- **341行目**: カンマの後を位置更新

#### Step 5: decode_jsonc関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | jsonc.lua | `script/jsonc.lua` | json.decode_jsonc関数（354-372行目） |

**主要処理フロー**:
- **355-357行目**: 引数の型チェック
- **358-360行目**: 状態変数の初期化
- **361-363行目**: 空ファイルの場合json.null返却
- **364行目**: メイン要素のデコード
- **365-367行目**: ネスト要素の処理
- **368-370行目**: trailing garbageチェック

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

```
json.decode_jsonc(str)
    │
    ├─ 状態初期化 (statusBuf, statusPos, statusTop)
    │
    ├─ next_byte() [オーバーライド版]
    │      ├─ string_find(statusBuf, "[^ \t\r\n]", statusPos)
    │      └─ skip_comment(b)
    │             ├─ b == 47 ('/')?
    │             ├─ 次の文字が '*'? → ブロックコメント
    │             │      └─ string_find(statusBuf, "*/", statusPos)
    │             └─ 次の文字が '/'? → 行コメント
    │                    └─ string_find(statusBuf, "[\r\n]", statusPos)
    │
    ├─ decode() [json.luaと共通]
    │      ├─ decode_string()
    │      ├─ decode_number()
    │      ├─ decode_true/false/null()
    │      ├─ decode_array()
    │      └─ decode_object()
    │
    └─ decode_item() [末尾カンマ対応版]
           └─ 末尾カンマチェック: next_byte() == ']' or '}'
```

### データフロー図

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

JSONC文字列 ───▶ next_byte ───▶ skip_comment ───▶ 通常文字
    │                              │
    │                              ├─ // → 行末まで読み飛ばし
    │                              └─ /* → */まで読み飛ばし
    │
    └───▶ decode() [json.luaと共通] ───▶ Luaテーブル
              │
              └─ decode_item() [末尾カンマ対応]
                     └─ カンマ後に]または}ならスキップ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| jsonc.lua | `script/jsonc.lua` | ソース | JSONC処理の実装 |
| json.lua | `script/json.lua` | ソース | 基盤となるJSON処理 |
| loader.lua | `script/config/loader.lua` | ソース | 設定ファイル読み込み（JSONCを使用） |
