# プログラム設計書 26-solve

## 1. 概要

### このプログラムの目的

このプログラムは、「演算子の優先順位が曖昧」という診断警告（ambiguity-1）を自動修正するためのものです。具体的には、`a + b or 0` のような式に括弧を追加して `a + (b or 0)` のように明確にします。

### このプログラムの役割

- **呼び出し元**: LSPコマンド `lua.solve` の実行ハンドラ
- **主な処理**: 曖昧な演算子の優先順位を、括弧を追加して明確化
- **出力**: テキスト編集リクエストをエディタに送信

## 2. ファイル基本情報

| 項目 | 内容 |
|-----|------|
| ファイルパス | `script/core/command/solve.lua` |
| ファイル名 | solve.lua |
| 種別 | ソースコード |
| 使用言語 | Lua |
| 総行数 | 100行 |
| 作成目的 | 演算子の曖昧さを解消する機能を提供する |

## 3. 用語解説

このプログラムを理解するために必要な専門用語を解説します。

| 用語 | 読み方 | 意味・説明 |
|-----|--------|----------|
| Ambiguity | アンビギュイティ | 曖昧さ。演算子の優先順位が分かりにくい状態 |
| Operator Precedence | オペレータープレセデンス | 演算子の優先順位。どの演算が先に実行されるか |
| Binary Operator | バイナリオペレーター | 二項演算子。2つの値を取る演算子（+, -, or など） |
| Literal | リテラル | コード内に直接書かれた値（数値、文字列など） |

### 言語固有の概念

- **or演算子**: Luaの論理OR演算子。`a or b` は a が偽なら b を返します
- **演算子の優先順位**: Luaでは算術演算子（+, -, * など）は論理演算子（and, or）より優先度が高いです
- **例**: `a + b or 0` は実際には `(a + b) or 0` として評価されますが、`a + (b or 0)` と誤解される可能性があります

## 4. 依存関係

### このプログラムが使用している外部ファイル

```
solve.lua
    │
    ├─ require 'files' ............. ファイル管理
    │
    ├─ require 'parser.guide' ...... AST操作ユーティリティ
    │
    ├─ require 'proto' ............. LSPプロトコル通信
    │
    ├─ require 'language' .......... 多言語対応
    │
    └─ require 'proto.converter' ... LSP形式変換
```

| 参照ファイル | パス | 用途 |
|------------|------|------|
| files | `script/files.lua` | ファイルテキストとASTの取得 |
| guide | `script/parser/guide.lua` | AST走査・位置変換 |
| proto | `script/proto/` | workspace/applyEdit リクエスト送信 |
| language | `script/language.lua` | コマンドラベルの多言語化 |
| converter | `script/proto/converter.lua` | LSP形式への変換 |

## 5. データ構造詳細

### 主要な変数・データ項目

#### opMap（7-20行目）

| キー | 値 | 用途・説明 |
|-----|--------|----------|
| '+', '-', '*', '/' など | true | 対象となる算術・文字列演算子 |

**解説**: 括弧を追加する対象となる演算子のリストです。これらの演算子と `or` が組み合わさった場合に、曖昧さが発生する可能性があります。

#### literalMap（22-28行目）

| キー | 値 | 用途・説明 |
|-----|--------|----------|
| 'number', 'integer', 'boolean', 'string', 'table' | true | 対象となるリテラル型 |

**解説**: `or` の右側にこれらの型の値がある場合（例: `or 0`）、デフォルト値として使われている可能性が高いです。

## 6. 処理詳細解説

このプログラムの処理内容をステップバイステップで説明します。

### 6.1 処理一覧

| No | 処理名 | 行番号 | 概要 |
|----|-------|--------|------|
| 1 | ファイル情報取得 | 32-37 | ファイルのテキストとASTを取得 |
| 2 | 該当ソース検索 | 39-77 | 指定範囲内の該当する式を検索 |
| 3 | 編集リクエスト送信 | 79-98 | 括弧を追加する編集を適用 |

### 6.2 該当ソース検索の詳細（39-77行目）

**この処理は何をするものか**: 指定された範囲内で、括弧を追加すべき式を検索します。

**処理の流れ**:

```
ステップ1: 範囲内のソースを列挙
    │
    ↓
ステップ2: or演算子かどうか確認
    │
    ↓
ステップ3: パターン1: a + b or 0 をチェック
    │
    ↓
ステップ4: パターン2: a or b + c をチェック
    │
    ↓
ステップ5: 括弧を追加する範囲を返す
```

**ステップバイステップ解説**:

1. **ステップ1: 範囲内のソースを列挙**（41行目）

   ```lua
   local result = guide.eachSourceContain(state.ast, start, function (source)
   ```

   診断で指定された範囲内にある全てのソースノードを列挙します。

2. **ステップ2: or演算子の確認**（42-48行目）

   ```lua
   if source.start ~= start
   or source.finish ~= finish then
       return
   end
   if not source.op or source.op.type ~= 'or' then
       return
   end
   ```

   - 正確に診断範囲と一致するソースのみを対象とする
   - `or` 演算子を持つ式のみを処理

3. **ステップ3: パターン1の検出**（52-62行目）

   ```lua
   -- a + b or 0 --> a + (b or 0)
   do
       if  first.op
       and opMap[first.op.type]
       and first.type ~= 'unary'
       and not second.op
       and literalMap[second.type] then
           return {
               start  = source[1][2].start,
               finish = source[2].finish,
           }
       end
   end
   ```

   パターン1: `a + b or 0` の形式を検出
   - 左辺（`first`）が二項演算（例: `a + b`）
   - 右辺（`second`）がリテラル（例: `0`）
   - この場合、`b or 0` に括弧を追加（`a + (b or 0)`）

4. **ステップ4: パターン2の検出**（64-75行目）

   ```lua
   -- a or b + c --> (a or b) + c
   do
       if  second.op
       and opMap[second.op.type]
       and second.type ~= 'unary'
       and not first.op
       and literalMap[second[1].type] then
           return {
               start  = source[1].start,
               finish = source[2][1].finish,
           }
       end
   end
   ```

   パターン2: `a or b + c` の形式を検出
   - 左辺（`first`）が単純な値
   - 右辺（`second`）が二項演算（例: `b + c`）で、最初の項がリテラル
   - この場合、`a or b` に括弧を追加（`(a or b) + c`）

**この処理のポイント**:
- 2つのパターンを認識して、適切な位置に括弧を追加
- `unary`（単項演算子）は除外（`-a or b` のような場合）

### 6.3 編集リクエスト送信の詳細（79-98行目）

**この処理は何をするものか**: 検出された範囲に括弧を追加する編集をエディタに送信します。

**ステップバイステップ解説**:

1. **ステップ1: 結果チェック**（79-81行目）

   ```lua
   if not result then
       return
   end
   ```

   該当する式が見つからなければ終了します。

2. **ステップ2: 編集の作成と送信**（83-98行目）

   ```lua
   proto.awaitRequest('workspace/applyEdit', {
       label = lang.script.COMMAND_REMOVE_SPACE,
       edit = {
           changes = {
               [uri] = {
                   {
                       range   = converter.packRange(state, result.start, result.finish),
                       newText = ('(%s)'):format(text:sub(
                                   guide.positionToOffset(state, result.start + 1),
                                   guide.positionToOffset(state, result.finish)
                                 )),
                   }
               },
           }
       },
   })
   ```

   - 元のテキストを取得し、括弧で囲む
   - `('(%s)'):format(...)` で `(元のテキスト)` という形式にする

**この処理のポイント**:
- テキストの置換ではなく、括弧で囲んだ新しいテキストに置き換え
- `guide.positionToOffset` で位置をオフセットに変換してテキストを抽出

## 7. 処理フロー図

プログラム全体の処理の流れを図で示します。

```mermaid
flowchart TD
    A[コマンド実行] --> B[ファイル情報取得]
    B --> C{ファイル存在?}
    C -->|No| Z[終了]
    C -->|Yes| D[診断範囲を変換]
    D --> E[該当ソースを検索]
    E --> F{or演算子?}
    F -->|No| Z
    F -->|Yes| G{パターン1?}
    G -->|Yes| H[b or 0 に括弧追加]
    G -->|No| I{パターン2?}
    I -->|Yes| J[a or b に括弧追加]
    I -->|No| Z
    H --> K[workspace/applyEdit送信]
    J --> K
    K --> Z
```

**図の説明**:
- **パターン1**: `a + b or 0` -> `a + (b or 0)`
- **パターン2**: `a or b + c` -> `(a or b) + c`

## 8. 外部連携

### 呼び出し関係図

```
[呼び出し元]                    [このプログラム]                    [呼び出し先]
                                     │
code-action.lua ───────────▶ solve.lua ────────────────────▶ proto.awaitRequest()
  (lua.solve)                        │                          (編集適用)
                                     │
                                     └──────────────▶ guide.eachSourceContain()
                                                        (ソース検索)
```

### 呼び出し元（このプログラムを呼び出すもの）

| プログラム名 | 呼び出し契機 | 渡されるデータ |
|------------|------------|--------------|
| code-action.lua | ambiguity-1 診断のクイックフィックス選択時 | { uri, range } |

### 呼び出し先（このプログラムが呼び出すもの）

| プログラム名 | 呼び出し目的 | 渡すデータ | 受け取るデータ |
|------------|------------|----------|--------------|
| files.getText | ファイル内容取得 | URI | テキスト文字列 |
| files.getState | AST取得 | URI | パーサー状態 |
| guide.eachSourceContain | 範囲内ソース検索 | AST, 開始位置, コールバック | 該当ソース |
| proto.awaitRequest | 編集適用 | リクエスト名, 編集データ | 応答 |

## 9. エラー処理

このプログラムで発生する可能性があるエラーと、その対処方法を説明します。

### エラー一覧

| エラー種別 | 発生条件 | 対処内容 | 該当行 |
|----------|---------|---------|--------|
| ファイルなし | ファイルが読み込まれていない | 何もせず終了 | 35-37 |
| 該当なし | パターンに一致する式がない | 何もせず終了 | 79-81 |

### エラー発生時の動作

このプログラムは例外を投げません。該当する式が見つからない場合は静かに終了します。

## 10. このプログラムを読むときのポイント

このプログラムを理解するためのアドバイスです。

### 推奨する読み方

1. **まず opMap と literalMap を理解**: どの演算子とリテラルが対象か
2. **次に2つのパターンを理解**: どのような式に括弧を追加するか
3. **最後に編集処理を理解**: どのように括弧を挿入するか

### 注意すべきポイント

- `source[1]` と `source[2]` は二項演算式の左辺と右辺を表します
- `source[1][2]` は左辺の式の右項を表します（例: `a + b` の `b`）
- `first.type ~= 'unary'` で単項演算子（`-a` など）を除外しています

### 関連して読むべきファイル

| ファイル | パス | 関連性 |
|---------|------|--------|
| ambiguity-1.lua | `script/core/diagnostics/ambiguity-1.lua` | この問題を検出する診断 |
| code-action.lua | `script/core/code-action.lua` | このコマンドを呼び出すコードアクション |

## 11. 備考

- 「ambiguity-1」は演算子の優先順位の曖昧さを示す診断コードです
- このプログラムは特定のパターンのみを処理し、全ての曖昧な式を対象とするわけではありません
- label が `COMMAND_REMOVE_SPACE` になっていますが、これは再利用された可能性があります（本来は `COMMAND_ADD_BRACKETS` などが適切）
