2025-12-21 03:21:22 +09:00
|
|
|
|
Status: ✅ P0 Complete
|
|
|
|
|
|
Scope: `StringUtils.is_integer/1`(nested-if + loop)を JoinIR で受理して `--profile quick` を進める。
|
2025-12-21 00:29:55 +09:00
|
|
|
|
Related:
|
|
|
|
|
|
- Now: `docs/development/current/main/10-Now.md`
|
|
|
|
|
|
- Phase 258: `docs/development/current/main/phases/phase-258/README.md`
|
|
|
|
|
|
- Design goal: `docs/development/current/main/design/join-explicit-cfg-construction.md`
|
|
|
|
|
|
|
|
|
|
|
|
# Phase 259: `StringUtils.is_integer/1` (nested-if + loop)
|
|
|
|
|
|
|
2025-12-21 03:21:22 +09:00
|
|
|
|
## P0 Result (2025-12-21)
|
|
|
|
|
|
|
|
|
|
|
|
- **is_integer/1**: Pattern8 で認識・実行成功 ✅
|
|
|
|
|
|
- **VM smoke test**: `[PASS] phase259_p0_is_integer_vm` ✅
|
|
|
|
|
|
- **Exit code**: 7(is_integer("123") == true)
|
|
|
|
|
|
- **json_lint_vm**: まだ FAIL(別問題: nested-loop with break / Pattern2)
|
|
|
|
|
|
|
|
|
|
|
|
### Key Fixes Applied
|
|
|
|
|
|
|
|
|
|
|
|
1. `expr_result = Some(join_exit_value)` - Pattern7 style で明示設定
|
|
|
|
|
|
2. `loop_var_name = Some(parts.loop_var.clone())` - merge_entry_block 選択用
|
|
|
|
|
|
3. `loop_invariants = [(me, me_host), (s, s_host)]` - PHI-free 不変量パラメータ
|
|
|
|
|
|
4. `skipped_entry_redirects` - k_exit のスキップ時ブロック参照リダイレクト
|
|
|
|
|
|
|
2025-12-21 00:29:55 +09:00
|
|
|
|
## Current Status (SSOT)
|
|
|
|
|
|
|
2025-12-21 03:21:22 +09:00
|
|
|
|
- ✅ is_integer/1 は Pattern8 で解決
|
|
|
|
|
|
- ❌ json_lint_vm は別の nested-loop with break パターンで失敗中(Phase 260+)
|
|
|
|
|
|
|
|
|
|
|
|
### Next FAIL (Phase 260+)
|
|
|
|
|
|
|
|
|
|
|
|
- **Function**: `Main.main/0` in `apps/examples/json_lint/main.hako`
|
|
|
|
|
|
- **Error**: `[cf_loop/pattern2] Failed to extract break condition from loop body`
|
|
|
|
|
|
- **Pattern**: Nested loop(外側 `loop(i < cases.length())` 内で内側 `loop(j < valid.length())` + break)
|
|
|
|
|
|
- **AST Structure**:
|
|
|
|
|
|
```
|
|
|
|
|
|
loop(i < cases.length()) {
|
|
|
|
|
|
local s = ...
|
|
|
|
|
|
local ok = 0
|
|
|
|
|
|
local j = 0
|
|
|
|
|
|
loop(j < valid.length()) { // ← 内側ループ
|
|
|
|
|
|
if (s == valid.get(j)) {
|
|
|
|
|
|
ok = 1
|
|
|
|
|
|
break // ← Pattern2 が抽出失敗
|
|
|
|
|
|
}
|
|
|
|
|
|
j = j + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ok == 1) { print("OK") } else { print("ERROR") }
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- **Reproduce**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./target/release/hakorune --backend vm apps/examples/json_lint/main.hako
|
|
|
|
|
|
```
|
2025-12-21 00:29:55 +09:00
|
|
|
|
- Shape summary(ログ由来):
|
|
|
|
|
|
- prelude: nested-if to compute `start` (handles leading `"-"`)
|
|
|
|
|
|
- loop: `loop(i < s.length()) { if not this.is_digit(s.substring(i, i+1)) { return false } i = i + 1 }`
|
|
|
|
|
|
- post: `return true`
|
|
|
|
|
|
- caps: `If,Loop,NestedIf,Return`
|
|
|
|
|
|
|
|
|
|
|
|
## Goal
|
|
|
|
|
|
|
|
|
|
|
|
- `StringUtils.is_integer/1` を JoinIR で受理し、quick の first FAIL を次へ進める
|
|
|
|
|
|
|
|
|
|
|
|
## Proposed Approach (P0)
|
|
|
|
|
|
|
2025-12-21 02:40:07 +09:00
|
|
|
|
**P0 Design Decision: Pattern8(新規)採用**
|
2025-12-21 00:29:55 +09:00
|
|
|
|
|
2025-12-21 02:40:07 +09:00
|
|
|
|
### Why Pattern8?
|
|
|
|
|
|
|
|
|
|
|
|
Pattern6(index_of系)は "見つける" scan(返り値: 整数 i or -1)で、is_integer は "全部検証する" predicate scan(返り値: 真偽値 true/false)。役割が異なるため、Pattern8 として分離した。
|
|
|
|
|
|
|
|
|
|
|
|
### Pattern8 vs Pattern6
|
|
|
|
|
|
|
|
|
|
|
|
| | Pattern6 (index_of系) | Pattern8 (is_integer系) |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 役割 | "見つける" scan | "全部検証する" predicate scan |
|
|
|
|
|
|
| Match形 | `substring(...) == needle` | `not predicate(ch)` → early exit |
|
|
|
|
|
|
| 返り値 | Integer (i or -1) | Boolean (true/false) |
|
|
|
|
|
|
| Exit PHI | `i`(ループ状態変数) | `ret_bool`(検証結果) |
|
|
|
|
|
|
| Carriers | [i] (LoopState) | [] (empty, expr_result のみ) |
|
|
|
|
|
|
|
|
|
|
|
|
### JoinIR Contract
|
|
|
|
|
|
|
|
|
|
|
|
- **jump_args_layout**: ExprResultPlusCarriers(carriers=0)
|
|
|
|
|
|
- **expr_result**: Some(join_exit_value) - ret_bool from k_exit (pipeline handling)
|
|
|
|
|
|
- **exit_bindings**: Empty(carriers なし)
|
|
|
|
|
|
- **SSOT**: `join_inputs = entry_func.params.clone()`
|
|
|
|
|
|
- **Me receiver**: Passed as param [i, me, s] (by-name 禁止)
|
|
|
|
|
|
|
|
|
|
|
|
### 受理形(P0固定)
|
|
|
|
|
|
|
|
|
|
|
|
```nyash
|
|
|
|
|
|
loop(i < s.length()) {
|
|
|
|
|
|
if not this.is_digit(s.substring(i, i + 1)) {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- prelude の start 計算は許可(ただし i_init = start で渡す)
|
|
|
|
|
|
- predicate は Me method call(this.is_digit)のみ
|
|
|
|
|
|
- step は 1 固定
|