# 機能設計書 149-split

## 概要

本ドキュメントは、Hack Standard Library (HSL) の `Regex\split` 関数の機能設計を定義する。正規表現パターンをデリミタとして文字列を分割する関数である。

### 本機能の処理概要

**業務上の目的・背景**：テキスト処理において、複雑なパターンを区切り文字として文字列を分割する必要がある場面がある。本機能は、PCREパターンをデリミタとして使用し、文字列を複数のチャンクに分割する。`Str\split`との違いは、固定文字列ではなく正規表現パターンで分割できる点である。

**機能の利用シーン**：ログのパース（複数の区切り文字に対応）、CSVの解析（引用符内のカンマを無視）、自然言語処理（複数の句読点で分割）など、複雑な区切りパターンが必要な場面で利用される。

**主要な処理内容**：
1. マッチが見つかるたびに、前のマッチからの部分を結果に追加
2. 制限数に達したら残りの文字列を最後の要素として追加
3. 全ての分割結果をvecとして返却

**関連システム・外部連携**：PCREライブラリに依存する正規表現エンジンを使用。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトはUIを持たないライブラリ |

## 機能種別

データ処理 / 文字列操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $haystack | string | Yes | 分割対象の文字列 | なし |
| $delimiter | Pattern<Match> | Yes | 区切りとなるPCRE正規表現パターン | PCREパターンとして有効であること |
| $limit | ?int | No | 最大分割数（デフォルト: null=無制限） | 2以上であること |

### 入力データソース

関数呼び出し時の引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | vec<string> | 分割された文字列のvec |

### 出力先

関数の戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. 制限値の検証
   └─ limitがnullの場合は無限大に設定
   └─ limitが2未満の場合は例外
2. 初期化
   └─ 結果vec、オフセット、マッチ終了位置、カウント
3. 最初のマッチを検索
4. ループ開始（マッチあり && カウント < 制限）
   └─ 前回マッチから今回マッチまでの部分を結果に追加
   └─ マッチ長を計算
   └─ 空マッチ処理
   └─ カウント増加
   └─ 次のマッチを検索
5. 残りの文字列を結果に追加
6. 結果vecを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{limit 有効?}
    B -->|No| C[limit = INF]
    B -->|limit < 2| D[例外をスロー]
    B -->|Yes| E[初期化]
    C --> E
    E --> F[マッチを検索]
    F --> G{マッチあり && count < limit?}
    G -->|No| H[残りを結果に追加]
    G -->|Yes| I[マッチ前の部分を結果に追加]
    I --> J[マッチ長を計算]
    J --> K{空マッチ?}
    K -->|Yes| L[offset++]
    K -->|No| M[offset = match_end]
    L --> N{offset > 文字列長?}
    N -->|Yes| H
    N -->|No| O[count++]
    M --> O
    O --> P[次のマッチを検索]
    P --> G
    H --> Q[結果を返却]
    D --> R[終了]
    Q --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-149-1 | 制限検証 | limitは2以上でなければならない | limitが指定された場合 |
| BR-149-2 | 残り文字列 | 制限に達した場合、残りは最後の要素に含まれる | 常時 |
| BR-149-3 | マッチなし | デリミタにマッチしない場合、元の文字列のみのvecを返す | 常時 |
| BR-149-4 | 空マッチ処理 | 空マッチの場合、1文字ずつ分割される | デリミタが空マッチの場合 |

### 計算ロジック

- 分割部分: `Str\slice($haystack, $match_end, $offset - $match_end)`
- 残り文字列: `Str\slice($haystack, $match_end)`

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

該当なし（データベース操作を行わない純粋関数）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvariantException | limitが2未満 | limitを2以上に設定 |
| PREG_* | Regex\Exception | PCREエラー | first_matchと同様 |

### リトライ仕様

該当なし

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

該当なし（データベース操作を行わない）

## パフォーマンス要件

- 時間計算量: O(n * m) - nは文字列長、mはマッチ数
- 空間計算量: O(n) - 分割結果を格納

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

- ReDoS（正規表現DoS）攻撃に注意
- `[]`: 純粋コンテキストで実行可能

## 備考

- 固定文字列での分割には`Str\split()`を使用
- limitが1の場合は元の文字列のみのvecを返すべきだが、現実装では例外

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | regex.php | `src/regex/regex.php` | Pattern<Match>型、?int型のlimitを確認 |

**読解のコツ**: limitが`null`の場合は無制限分割、整数の場合はその数に制限される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | regex.php | `src/regex/regex.php` | split関数の定義（187-223行目） |

**主要処理フロー**:
1. **192-194行目**: limitがnullの場合、`\INF`を設定
2. **195行目**: `invariant`でlimitが2以上であることを検証
3. **196行目**: `Str\length($haystack)` で文字列長を取得
4. **197-200行目**: 結果vec、オフセット、マッチ終了位置、カウントを初期化
5. **201行目**: 最初のマッチを検索
6. **202行目**: `while ($match && $count < $limit)` でループ
7. **204行目**: マッチ前の部分を結果に追加
8. **205-217行目**: マッチ長計算、位置更新、空マッチ処理
9. **218行目**: カウント増加
10. **219行目**: 次のマッチを検索
11. **221行目**: 残りの文字列を結果に追加
12. **222行目**: 結果を返却

#### Step 3: 制限処理を理解する

**重要なロジック（202行目）**:
```hack
while ($match && $count < $limit) {
```

countは1から開始し、分割するたびに増加。limit=2の場合、1回分割して2つの要素を生成。

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

```
Regex\split($haystack, $delimiter, $limit)
    |
    +-- invariant($limit > 1)
    |       └─ 制限値の検証
    |
    +-- Str\length($haystack)
    |
    +-- _Private\regex_match($haystack, $delimiter, inout $offset)
    |       └─ 繰り返しマッチング
    |
    +-- while ループ
            |
            +-- Str\slice()
            |       └─ 分割部分を抽出
            |
            +-- 空マッチ処理
```

### データフロー図

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

$haystack ─────┐
               |
$delimiter ────┼──> whileループ ───> 繰り返しマッチング
               |         │
$limit ────────┘         v
                    分割結果を蓄積
                         │
                         v
                    vec<string> ────────> 戻り値
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| regex.php | `src/regex/regex.php` | ソース | split関数の公開インターフェース |
| private.php | `src/regex/private.php` | ソース | regex_match内部実装 |
| RegexTest.php | `tests/regex/RegexTest.php` | テスト | split関数のテストケース |
