# 機能設計書 29-数値パーサ

## 概要

本ドキュメントは、mrubyランタイムにおける数値パーサ機能の設計を記述する。文字列から整数および浮動小数点数を解析する低レベルのパーサ関数群を提供する。

### 本機能の処理概要

数値パーサ機能は、文字列表現から数値（整数・浮動小数点数）を解析してC言語の数値型に変換するユーティリティ関数群である。コンパイラ（パーサ・コード生成）やString#to_f等の文字列変換で使用される。

**業務上の目的・背景**：Rubyスクリプト中の数値リテラル解析や、String#to_i / String#to_fなどの文字列→数値変換において、プラットフォーム非依存の高精度な数値パーサが必要である。標準Cライブラリのstrtol/strtodに依存せず、mruby独自の実装により組み込み環境での移植性を確保する。

**機能の利用シーン**：コンパイラでの数値リテラル解析、String#to_iによる整数変換、String#to_fによる浮動小数点数変換、sprintfでの数値フォーマット。

**主要な処理内容**：
1. mrb_read_int: 10進数文字列からmrb_int（整数）への変換（オーバーフロー検出付き）
2. mrb_read_float: 文字列からdouble（浮動小数点数）への変換（整数部・小数部・指数部対応）
3. mrb_int_read: mrb_read_intの旧API互換ラッパー（非推奨）
4. mrb_float_read: mrb_read_floatの旧API互換ラッパー（非推奨）

**関連システム・外部連携**：コンパイラ（parse.y / codegen.c）、文字列処理（string.c）、sprintf（mruby-sprintf）、packモジュール（mruby-pack）。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | C API関数として内部的に利用される |

## 機能種別

ユーティリティ（文字列→数値変換）

## 入力仕様

### 入力パラメータ

#### mrb_read_int

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| p | const char* | Yes | 解析開始位置の文字列ポインタ | - |
| e | const char* | No | 文字列終端位置（NULLの場合は'\0'まで） | - |
| endp | char** | No | 解析終了位置の出力（NULLの場合は書き込まない） | - |
| np | mrb_int* | Yes | 結果の整数値を格納する変数へのポインタ | - |

#### mrb_read_float

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| str | const char* | Yes | 解析対象の文字列 | - |
| endp | char** | No | 解析終了位置の出力（NULLの場合は書き込まない） | - |
| fp | double* | Yes | 結果の浮動小数点値を格納する変数へのポインタ | - |

### 入力データソース

- コンパイラからの数値リテラル文字列
- Rubyスクリプトからの文字列（String#to_i, String#to_f経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値（mrb_read_int） | mrb_bool | TRUE: 成功、FALSE: オーバーフロー |
| np（mrb_read_int） | mrb_int | 解析された整数値 |
| 戻り値（mrb_read_float） | mrb_bool | TRUE: 成功、FALSE: 数値なし |
| fp（mrb_read_float） | double | 解析された浮動小数点値 |
| endp | char* | 解析が停止した文字列位置 |

### 出力先

呼び出し元へのポインタ経由での返却

## 処理フロー

### 処理シーケンス

```
1. mrb_read_int(p, e, endp, np)
   ├─ while文でISIGIT判定を繰り返し
   │   ├─ n = n * 10 + digit（オーバーフロー検出付き）
   │   └─ オーバーフロー時: FALSE返却
   ├─ endp設定
   └─ TRUE返却

2. mrb_read_float(str, endp, fp)
   ├─ 空白スキップ
   ├─ 符号処理（+/-）
   ├─ 整数部解析（uint64_t精度、オーバーフロー検出）
   ├─ 小数部解析（最大17桁精度）
   ├─ 指数部解析（e/E + 符号 + 数値）
   ├─ mrb_pow10()でべき乗適用
   └─ 符号適用・結果返却

3. mrb_int_read(p, e, endp) [旧API]
   └─ mrb_read_int呼び出し
      ├─ 失敗時: errno=ERANGE, endp=NULL, MRB_INT_MAX返却
      └─ 成功時: n返却

4. mrb_float_read(str, endp) [旧API]
   └─ mrb_read_float呼び出し
      ├─ 失敗時: errno=ERANGE
      └─ d返却
```

### フローチャート

```mermaid
flowchart TD
    A[文字列入力] --> B{関数種別}
    B -->|mrb_read_int| C[ISDIGIT判定ループ]
    C --> D{オーバーフロー?}
    D -->|Yes| E[FALSE返却]
    D -->|No| F[n = n*10 + digit]
    F --> C
    C -->|ループ終了| G[endp設定・TRUE返却]

    B -->|mrb_read_float| H[空白スキップ]
    H --> I[符号処理]
    I --> J[整数部解析 uint64_t]
    J --> K{小数点?}
    K -->|Yes| L[小数部解析 最大17桁]
    K -->|No| M{数字あり?}
    L --> M
    M -->|No| N[FALSE返却]
    M -->|Yes| O[double合成]
    O --> P{指数部 e/E?}
    P -->|Yes| Q[指数解析・mrb_pow10適用]
    P -->|No| R[符号適用]
    Q --> R
    R --> S[TRUE返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 10進数限定 | mrb_read_intは10進数のみ対応（基数指定なし） | mrb_read_int |
| BR-02 | オーバーフロー安全 | mrb_int_mul_overflow/mrb_int_add_overflowで検出 | mrb_read_int |
| BR-03 | 精度制限 | 小数部は最大17桁まで解析（uint64_tオーバーフロー防止） | mrb_read_float |
| BR-04 | 指数上限 | 指数値が10000を超えない範囲でクランプ | mrb_read_float |
| BR-05 | pow10テーブル | 正の指数は1e0〜1e308、負の指数は1e0〜1e-323のルックアップテーブル | mrb_read_float |
| BR-06 | 旧API互換 | mrb_int_read/mrb_float_readはerrnoを設定する旧仕様 | readnum.c |
| BR-07 | MRB_NO_FLOAT | MRB_NO_FLOAT定義時はmrb_read_float/mrb_float_readは無効 | コンパイル時 |

### 計算ロジック

| 計算名 | 計算式 | 説明 |
|--------|--------|------|
| 整数累積 | n = n * 10 + (*p - '0') | オーバーフロー検出付き加算 |
| 小数部合成 | res += frac_part * mrb_pow10(-frac_digits) | 小数部をべき乗で正規化 |
| 指数適用 | res *= mrb_pow10(e) | 指数部のべき乗を乗算 |

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 整数オーバーフロー | n * 10 + digitがmrb_intの範囲超過 | FALSEを返却 |
| - | 数値なし | 入力文字列に数字が含まれない | FALSEを返却、endpを入力先頭に設定 |
| - | 指数なし | 'e'の後に数字がない | 指数部を無視してdone |
| ERANGE | レガシーオーバーフロー | mrb_int_readで変換失敗 | errno=ERANGE、MRB_INT_MAX返却 |

### リトライ仕様

リトライは不要。

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

特になし。

## パフォーマンス要件

- mrb_read_floatはpow10ルックアップテーブル（合計632エントリ）を使用し、計算精度とパフォーマンスを両立
- 整数部はuint64_t精度で累積し、オーバーフロー時のみ浮動小数点にフォールバック
- mrb_read_intはインライン可能な小さいループで高速

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

- 整数オーバーフローはmrb_int_mul_overflow/mrb_int_add_overflowマクロで安全に検出
- 浮動小数点パーサは指数値を10000以下にクランプし、整数オーバーフローを防止
- 小数部の桁数を17桁に制限し、uint64_tオーバーフローを防止

## 備考

- mrb_int_read/mrb_float_readは非推奨（obsolete）であり、新しいコードではmrb_read_int/mrb_read_floatを使用すべき
- pow10テーブルはpow10_positive（309エントリ、1e0〜1e308）とpow10_negative（324エントリ、1e0〜1e-323）で構成される
- mrb_read_floatはstrtod(3)のERANGEチェックを意図的に省略している（コメントアウトされたコード参照）

---

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

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

### 推奨読解順序

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

pow10ルックアップテーブルがmrb_read_floatの精度とパフォーマンスの鍵である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | readfloat.c | `src/readfloat.c` | pow10_positive配列（10-42行目）: 1e0〜1e308の309エントリ |
| 1-2 | readfloat.c | `src/readfloat.c` | pow10_negative配列（44-78行目）: 1e0〜1e-323の324エントリ |
| 1-3 | value.h | `include/mruby/value.h` | API宣言（106-112行目）: mrb_read_int, mrb_read_float, 旧API宣言 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | readint.c | `src/readint.c` | mrb_read_int（11-27行目）: 10進整数パーサ全体 |
| 2-2 | readfloat.c | `src/readfloat.c` | mrb_read_float（112-226行目）: 浮動小数点パーサ全体 |
| 2-3 | readnum.c | `src/readnum.c` | mrb_int_read（15-27行目）: 旧APIラッパー |
| 2-4 | readnum.c | `src/readnum.c` | mrb_float_read（33-42行目）: 旧APIラッパー |

**mrb_read_intの主要処理フロー**:
1. **16行目**: ISIGITでループ、各桁を処理
2. **18-19行目**: mrb_int_mul_overflow/mrb_int_add_overflowでオーバーフロー検出
3. **20行目**: オーバーフロー時にFALSEを即座に返却
4. **24-26行目**: endp設定と結果返却

**mrb_read_floatの主要処理フロー**:
1. **125行目**: 空白スキップ
2. **128-129行目**: 符号（+/-）処理
3. **132-142行目**: 整数部をuint64_tで累積、オーバーフロー検出
4. **145-156行目**: 小数部を最大17桁まで解析
5. **159-163行目**: 数字なしの場合FALSEを返却
6. **166-176行目**: 整数部と小数部をdoubleに合成
7. **179-202行目**: 指数部（e/E）の解析とmrb_pow10による適用
8. **205行目**: 符号の適用

#### Step 3: 内部ヘルパー関数

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | readfloat.c | `src/readfloat.c` | mrb_pow10（83-99行目）: 指数からdouble値へのルックアップ |

**mrb_pow10の処理**:
1. **86-89行目**: 正の指数 - pow10_positiveテーブルから取得、範囲外はHUGE_VAL
2. **93-97行目**: 負の指数 - pow10_negativeテーブルから取得、範囲外は0.0

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

```
mrb_read_int (readint.c:11)
    │
    ├─ mrb_int_mul_overflow (マクロ - numeric.h)
    └─ mrb_int_add_overflow (マクロ - numeric.h)

mrb_read_float (readfloat.c:112)
    │
    └─ mrb_pow10 (readfloat.c:83)
           │
           ├─ pow10_positive[] テーブル参照
           └─ pow10_negative[] テーブル参照

mrb_int_read (readnum.c:15) [旧API]
    │
    └─ mrb_read_int (readint.c:11)

mrb_float_read (readnum.c:33) [旧API]
    │
    └─ mrb_read_float (readfloat.c:112)

--- 呼び出し元 ---
parse.y (コンパイラ)
    └─ mrb_read_int / mrb_read_float
codegen.c (コード生成)
    └─ mrb_read_int
string.c (String#to_f等)
    └─ mrb_read_float
sprintf.c (sprintf)
    └─ mrb_read_int
```

### データフロー図

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

"12345" (文字列)    ───▶ mrb_read_int()          ───▶ mrb_int n = 12345
                           │                             mrb_bool = TRUE
                           └─ overflow検出        ───▶ mrb_bool = FALSE

"3.14e+2" (文字列)  ───▶ mrb_read_float()        ───▶ double fp = 314.0
                           │                             mrb_bool = TRUE
                           ├─ int_part=3 (uint64_t)
                           ├─ frac_part=14, digits=2
                           ├─ 合成: 3.0 + 14*1e-2 = 3.14
                           ├─ 指数: e=2, pow10(2)=100
                           └─ 結果: 3.14 * 100 = 314.0
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| readint.c | `src/readint.c` | ソース | mrb_read_int: 10進整数パーサ |
| readfloat.c | `src/readfloat.c` | ソース | mrb_read_float: 浮動小数点パーサ、pow10テーブル |
| readnum.c | `src/readnum.c` | ソース | mrb_int_read/mrb_float_read: 旧API互換ラッパー |
| value.h | `include/mruby/value.h` | ヘッダ | API関数の宣言（106-112行目） |
| numeric.h | `include/mruby/numeric.h` | ヘッダ | mrb_int_mul_overflow等のオーバーフロー検出マクロ |
| string.c | `src/string.c` | ソース | mrb_read_floatの呼び出し元（String#to_f） |
| parse.y | `mrbgems/mruby-compiler/core/parse.y` | ソース | コンパイラからの呼び出し元 |
| codegen.c | `mrbgems/mruby-compiler/core/codegen.c` | ソース | コード生成からの呼び出し元 |
| sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | ソース | sprintfからの呼び出し元 |
