# 機能設計書 32-正規表現

## 概要

本ドキュメントは、Julia Base ライブラリにおける正規表現機能の設計を記述する。`Regex` 型（`r""` リテラル）による正規表現マッチング・キャプチャ・`eachmatch` を提供し、PCRE2 ライブラリをバックエンドとして使用する。

### 本機能の処理概要

本機能は、PCRE2（Perl Compatible Regular Expressions 2）ライブラリを基盤として、文字列に対する正規表現パターンマッチング、キャプチャグループの抽出、反復マッチング、および正規表現ベースの置換を提供する。

**業務上の目的・背景**：正規表現はテキスト処理における最も強力なパターンマッチング手法であり、ログ解析、データバリデーション、テキスト変換、パース処理など広範な場面で不可欠である。Julia において高性能な正規表現処理を標準機能として提供することで、外部パッケージへの依存なくテキスト処理を実現可能とする。

**機能の利用シーン**：文字列からの構造化データ抽出（日付、メールアドレス等）、入力バリデーション、文字列置換（キャプチャグループ参照付き）、テキスト中のパターン検索・カウント。

**主要な処理内容**：
1. `Regex` 型の構築とPCRE2パターンのコンパイル（JITコンパイル含む）
2. `match` による最初のマッチの検出と `RegexMatch` オブジェクトの返却
3. `eachmatch` による全マッチの反復取得（オーバーラップ対応）
4. `occursin` による存在チェック
5. `startswith` / `endswith` の Regex 対応（アンカー付きマッチ）
6. `findnext` / `findfirst` による位置検索
7. `count` によるマッチ回数カウント
8. `SubstitutionString`（`s""` リテラル）によるキャプチャグループ参照置換
9. Regex の連結（`*`）と繰り返し（`^`）による正規表現の合成

**関連システム・外部連携**：PCRE2 ライブラリ（PCRE2_jll 経由で提供）。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | REPL | 主画面 | 対話的な正規表現操作 |

## 機能種別

文字列パターンマッチング・テキスト処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pattern | AbstractString | Yes | 正規表現パターン文字列 | PCRE2 構文に準拠 |
| flags | AbstractString | No | フラグ文字列（i/m/s/x/a） | 不正フラグで ArgumentError |
| compile_options | UInt32 | No | コンパイルオプションビットマスク | PCRE.COMPILE_MASK で検証 |
| match_options | UInt32 | No | マッチオプションビットマスク | PCRE.EXECUTE_MASK で検証 |
| s | AbstractString | Yes | マッチ対象文字列 | String/SubString{String} のみ |
| idx | Integer | No | 検索開始インデックス | デフォルトは firstindex(s) |
| overlap | Bool | No | オーバーラップマッチ許可 | デフォルトは false |

### 入力データソース

関数の引数として直接受け取る。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| RegexMatch | RegexMatch{S} | マッチ結果（match, captures, offset, offsets, regex） |
| マッチ有無 | Bool | occursin/startswith/endswith の結果 |
| マッチ位置 | UnitRange{Int} / Nothing | findnext/findfirst の結果 |
| マッチ回数 | Int | count の結果 |
| イテレータ | RegexMatchIterator | eachmatch の結果 |

### 出力先

関数の戻り値として呼び出し元に返却する。

## 処理フロー

### 処理シーケンス

```
1. Regex(pattern, flags) の構築
   └─ フラグ文字列を compile_options/match_options に変換
   └─ compile() で PCRE.compile + PCRE.jit_compile を実行
   └─ finalizer で PCRE.free_re を登録
2. match(re, str, idx)
   └─ compile(re) でコンパイル確認
   └─ PCRE.exec_r_data で実行、マッチデータ取得
   └─ ovec_ptr からオフセットベクトルを読み取り
   └─ SubString でマッチ文字列・キャプチャを構築
   └─ RegexMatch オブジェクトを返却
3. eachmatch(re, str; overlap)
   └─ RegexMatchIterator を構築
   └─ iterate で match を繰り返し呼び出し
   └─ オーバーラップ対応: nextind(str, offset) or offset + ncodeunits(match)
4. replace(str, re => subst)
   └─ RegexAndMatchData でマッチデータを事前確保
   └─ findnext(::RegexAndMatchData, str, i) で位置検索
   └─ _replace(io, ::SubstitutionString, str, r, re) でキャプチャ参照置換
```

### フローチャート

```mermaid
flowchart TD
    A[開始: match] --> B[compile でパターンコンパイル]
    B --> C[PCRE.exec_r_data 実行]
    C --> D{マッチ成功?}
    D -->|No| E[PCRE.free_match_data & return nothing]
    D -->|Yes| F[ovec_ptr からオフセット取得]
    F --> G[SubString でマッチ文字列構築]
    G --> H[キャプチャグループを Vector で構築]
    H --> I[RegexMatch オブジェクト生成]
    I --> J[PCRE.free_match_data]
    J --> K[return RegexMatch]
    E --> L[終了]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-32-01 | デフォルトオプション | UTF + MATCH_INVALID_UTF + ALT_BSUX + UCP がデフォルト | Regex() コンストラクタ |
| BR-32-02 | ASCII モード | フラグ 'a' で UTF/UCP を無効化し、バイト列として処理 | r"..."a |
| BR-32-03 | スレッドセーフコンパイル | PCRE_COMPILE_LOCK が定義されていればロック下でコンパイル | compile() |
| BR-32-04 | マッチ対象制限 | match は String と SubString{String} のみサポート | match(::Regex, ::AbstractString) |
| BR-32-05 | startswith アンカー | startswith(s, r) は PCRE.ANCHORED オプションを使用 | startswith(::AbstractString, ::Regex) |
| BR-32-06 | endswith アンカー | endswith(s, r) は PCRE.ENDANCHORED オプションを使用 | endswith(::AbstractString, ::Regex) |
| BR-32-07 | Regex 結合 | 異なる imsx 以外のオプションを持つ Regex の結合は不可 | *(::Regex, ::Regex) |

### 計算ロジック

- RegexMatch のキャプチャ: `ovec_ptr` から `2i+1`, `2i+2` で各キャプチャの開始・終了バイトオフセットを取得
- PCRE.UNSET のキャプチャは `nothing` として格納

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

### 操作別データベース影響一覧

該当なし。

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | 不正な正規表現フラグ（i/m/s/x/a 以外） | 正しいフラグを指定 |
| - | ArgumentError | 不正なコンパイルオプション | PCRE.COMPILE_MASK 内の値を使用 |
| - | ArgumentError | 不正なマッチオプション | PCRE.EXECUTE_MASK 内の値を使用 |
| - | ArgumentError | String/SubString{String} 以外への match | String(s) で変換してから使用 |
| - | PCRE エラー | 不正な正規表現パターン | パターン構文を修正 |
| - | error | SubstitutionString で存在しないグループ名を参照 | 正しいグループ名を使用 |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- PCRE2 の JIT コンパイルにより、繰り返しマッチングの高速化を実現
- `RegexAndMatchData` 構造体により、`replace` 時のマッチデータ割り当てを 1 回に削減
- `compile()` はパターンが既にコンパイル済み（`regex != C_NULL`）の場合は即座に返却

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

- 正規表現パターンによる ReDoS（Regular Expression Denial of Service）攻撃のリスクがある。ユーザー入力をパターンとして使用する場合は注意が必要
- `MATCH_INVALID_UTF` フラグにより、不正な UTF-8 シーケンスに対しても安全にマッチングを実行可能

## 備考

- `Regex` は `mutable struct` であり、遅延コンパイル（初回使用時にコンパイル）をサポートする
- `r""` マクロリテラルはエスケープ処理が不要（raw string）
- `s""` マクロリテラルで `SubstitutionString` を構築し、`\1`, `\g<name>` でキャプチャグループを参照可能
- `AnnotatedString` への対応メソッドも提供されている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | regex.jl | `base/regex.jl` | Regex 構造体（23-46行目）: pattern, compile_options, match_options, regex フィールド |
| 1-2 | regex.jl | `base/regex.jl` | RegexMatch 構造体（226-232行目）: match, captures, offset, offsets, regex フィールド |
| 1-3 | regex.jl | `base/regex.jl` | SubstitutionString 構造体（600-602行目）: string フィールド |
| 1-4 | regex.jl | `base/regex.jl` | RegexAndMatchData 構造体（635-639行目）: re, match_data フィールド |

**読解のコツ**: `Regex` は `mutable struct` であり、`regex` フィールド（`Ptr{Cvoid}`）は遅延コンパイルにより初回使用時に設定される。`finalizer` で PCRE リソースを解放する点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | regex.jl | `base/regex.jl` | Regex コンストラクタ（48-67行目）: フラグ文字列からオプションへの変換 |
| 2-2 | regex.jl | `base/regex.jl` | @r_str マクロ（123行目）: r"..." リテラルの展開 |
| 2-3 | regex.jl | `base/regex.jl` | compile 関数（70-89行目）: PCRE.compile + jit_compile |

**主要処理フロー**:
1. **48行目**: `Regex(pattern, flags)` でフラグ文字を `compile_options` ビットマスクに変換
2. **52-57行目**: 'a' フラグで UCP/UTF/MATCH_INVALID_UTF を無効化
3. **70-89行目**: `compile()` で `PCRE.compile` + `PCRE.jit_compile` を実行、スレッドセーフ対応

#### Step 3: match の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | regex.jl | `base/regex.jl` | match 関数（430-449行目）: PCRE.exec_r_data によるマッチ実行 |
| 3-2 | regex.jl | `base/regex.jl` | RegexMatch の構築（441-446行目）: ovec からキャプチャ抽出 |

**主要処理フロー**:
- **434行目**: `PCRE.exec_r_data(re.regex, str, idx-1, opts)` でマッチ実行
- **439行目**: `div(PCRE.ovec_length(data), 2) - 1` でキャプチャグループ数算出
- **441行目**: `unsafe_load(p, 1)+1` でマッチ開始位置取得（0-indexed から 1-indexed へ変換）
- **442行目**: `PCRE.UNSET` のキャプチャは `nothing` に変換

#### Step 4: eachmatch と置換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | regex.jl | `base/regex.jl` | RegexMatchIterator（730-742行目）: イテレータ構造体 |
| 4-2 | regex.jl | `base/regex.jl` | RegexMatchIterator.iterate（744-772行目）: オーバーラップ対応の反復 |
| 4-3 | regex.jl | `base/regex.jl` | _replace(SubstitutionString)（670-728行目）: キャプチャ参照置換 |

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

```
r"pattern"flags
    |
    +-- @r_str(pattern, flags) -> Regex(pattern, flags)
            |
            +-- compile(re)
                    +-- PCRE.compile(pattern, compile_options)
                    +-- PCRE.jit_compile(regex)

match(re, str, idx)
    |
    +-- compile(re)
    +-- PCRE.exec_r_data(regex, str, idx-1, opts)
    |       +-- [成功] ovec_ptr(data) -> オフセット取得
    |       +-- [失敗] free_match_data & return nothing
    +-- SubString(str, start, end) -> マッチ文字列
    +-- Vector{Union{Nothing,SubString}} -> キャプチャ
    +-- RegexMatch(match, captures, offset, offsets, re)

eachmatch(re, str; overlap)
    |
    +-- RegexMatchIterator(re, str, overlap)
            +-- iterate -> match(re, str, offset, opts)
                    +-- [overlap=true] nextind(str, offset)
                    +-- [overlap=false] offset + ncodeunits(match)

replace(str, re => subst::SubstitutionString)
    |
    +-- _pat_replacer(re) -> RegexAndMatchData(re)
    |       +-- compile(re)
    |       +-- PCRE.create_match_data(regex)
    +-- _replace(io, subst, str, r, re::RegexAndMatchData)
            +-- unescape_string(subst.string)
            +-- _write_capture(io, group, str, r, re)
                    +-- PCRE.substring_copy_bynumber
```

### データフロー図

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

pattern: String -------> Regex(pattern, flags) -------> Regex 構造体
  + flags: String            |
                             +-> PCRE.compile -> Ptr{Cvoid}
                             +-> PCRE.jit_compile

Regex + str -------> match(re, str, idx) -------> RegexMatch / nothing
                         |
                         +-> PCRE.exec_r_data -> matched, data
                         +-> ovec_ptr -> offsets
                         +-> SubString -> captures

Regex + str -------> eachmatch(re, str) -------> RegexMatchIterator
                         |                           |
                         +-> iterate -------> RegexMatch (各マッチ)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| regex.jl | `base/regex.jl` | ソース | Regex/RegexMatch/SubstitutionString の定義と主要操作 |
| pcre.jl | `base/pcre.jl` | ソース | PCRE2 ライブラリの Julia ラッパー関数群 |
| util.jl | `base/strings/util.jl` | ソース | replace 関数の共通実装（_replace_init, _replace_finish 等） |
| search.jl | `base/strings/search.jl` | ソース | findnext/findprev の基盤関数 |
| PCRE2_jll | `stdlib/PCRE2_jll/` | JLL | PCRE2 バイナリライブラリの提供 |
