docs(phi): Phase 25.1 - LoopForm v2 コメント整備 + ケース表完成

-  [LoopForm] タグで統一コメント追加
  - src/mir/loop_builder.rs
    - header-cond: Case A/B分岐説明
    - exit-break path / continue-backedge path
    - exit PHI for Case A/B
  - src/mir/phi_core/loop_snapshot_merge.rs
    - Case A/B分岐: header ∈ exit_preds判定ロジック
  - src/mir/phi_core/exit_phi_builder.rs
    - LoopForm Process ステップバイステップ説明

-  UsingCollectorBox Region+next_i化
  - lang/src/compiler/parser/using/using_collector_box.hako
    - 全ループをLoopForm v2形式に統一
    - next_i, next_j, next_k, next_t パターン導入
    - SSA安全化(未定義変数撲滅)

-  LoopForm v2 ケース表完成
  - docs/development/architecture/loops/loopform_ssot.md
    - Case A/B/C/D の完全な表
    - テスト対応マッピング
    - 実装ファイル対応表

🎯 成果: LoopForm v2の「形」をソース・テスト・ドキュメントで完全固定
This commit is contained in:
nyash-codex
2025-11-21 06:22:21 +09:00
parent 51359574d9
commit baf028a94f
5 changed files with 442 additions and 170 deletions

View File

@ -11,28 +11,47 @@ using lang.compiler.parser.scan.parser_common_utils_box as ParserCommonUtilsBox
static box UsingCollectorBox {
// Public API: collect line-based using declarations to JSON array string
// Refactored to LoopForm v2 / Region+next_i pattern (Phase 25.1)
collect(src) {
if src == null { return "[]" }
local n = src.length()
local i = 0
local first = 1
local out = "["
// Main line-scanning loop: Region+next_i形
loop(i < n) {
// line slice [i, j)
local next_i = i
// Find line boundaries [i, j) - Region+next_j形
local j = i
loop(j < n && src.substring(j, j+1) != "\n") { j = j + 1 }
loop(j < n && src.substring(j, j+1) != "\n") {
local next_j = j + 1
j = next_j
}
local line = src.substring(i, j)
// trim left spaces/tabs
// Trim left spaces/tabs - Region+next_k形
local k = 0
loop(k < line.length() && (line.substring(k,k+1) == " " || line.substring(k,k+1) == "\t")) { k = k + 1 }
loop(k < line.length() && (line.substring(k,k+1) == " " || line.substring(k,k+1) == "\t")) {
local next_k = k + 1
k = next_k
}
// Process if line starts with "using "
if ParserCommonUtilsBox.starts_with(line, k, "using ") == 1 {
local rest = ParserCommonUtilsBox.trim(line.substring(k + 6, line.length()))
// split on ' as '
// Split on ' as ' - initialize all variables before conditional use
local as_pos = ParserCommonUtilsBox.index_of(rest, 0, " as ")
local target = rest
local alias = null
if as_pos >= 0 { target = ParserCommonUtilsBox.trim(rest.substring(0, as_pos)) alias = ParserCommonUtilsBox.trim(rest.substring(as_pos + 4, rest.length())) }
// path or namespace
if as_pos >= 0 {
target = ParserCommonUtilsBox.trim(rest.substring(0, as_pos))
alias = ParserCommonUtilsBox.trim(rest.substring(as_pos + 4, rest.length()))
}
// Determine if target is a path or namespace
local is_path = 0
if target.length() > 0 {
if ParserCommonUtilsBox.starts_with(target, 0, ParserCommonUtilsBox.dq()) == 1 { is_path = 1 }
@ -41,38 +60,71 @@ static box UsingCollectorBox {
if target.length() >= 5 && ParserCommonUtilsBox.starts_with(target, target.length()-5, ".hako") == 1 { is_path = 1 }
if target.length() >= 6 && ParserCommonUtilsBox.starts_with(target, target.length()-6, ".hako") == 1 { is_path = 1 }
}
// Initialize name and path before conditional blocks
local name = ""
local path = null
if is_path == 1 {
// strip quotes
// Strip quotes if present - use temp var to avoid SSA issues
local clean_target = target
if ParserCommonUtilsBox.starts_with(target, 0, ParserCommonUtilsBox.dq()) == 1 {
target = target.substring(1, target.length())
if target.length() > 0 && target.substring(target.length()-1, target.length()) == ParserCommonUtilsBox.dq() { target = target.substring(0, target.length()-1) }
local temp1 = target.substring(1, target.length())
clean_target = temp1
if temp1.length() > 0 && temp1.substring(temp1.length()-1, temp1.length()) == ParserCommonUtilsBox.dq() {
clean_target = temp1.substring(0, temp1.length()-1)
}
}
path = target
if alias != null { name = alias } else {
// basename
local p = target
path = clean_target
// Determine name from alias or basename
if alias != null {
name = alias
} else {
// Extract basename: Region+next_t形 - use temp vars to avoid SSA issues
local p = clean_target
local idx = -1
local t = 0
loop(t < p.length()) { if p.substring(t,t+1) == "/" { idx = t } t = t + 1 }
if idx >= 0 { p = p.substring(idx+1, p.length()) }
// strip extension
if p.length() > 5 && ParserCommonUtilsBox.starts_with(p, p.length()-5, ".hako") == 1 { p = p.substring(0, p.length()-5) }
else { if p.length() > 6 && ParserCommonUtilsBox.starts_with(p, p.length()-6, ".hako") == 1 { p = p.substring(0, p.length()-6) } }
name = p
loop(t < p.length()) {
local next_t = t + 1
if p.substring(t,t+1) == "/" { idx = t }
t = next_t
}
// Extract substring only if idx found
local p2 = p
if idx >= 0 { p2 = p.substring(idx+1, p.length()) }
// Strip extension - use temp vars
local p3 = p2
if p2.length() > 5 && ParserCommonUtilsBox.starts_with(p2, p2.length()-5, ".hako") == 1 {
p3 = p2.substring(0, p2.length()-5)
} else if p2.length() > 6 && ParserCommonUtilsBox.starts_with(p2, p2.length()-6, ".hako") == 1 {
p3 = p2.substring(0, p2.length()-6)
}
name = p3
}
} else {
name = target
}
// append entry
if first == 0 { out = out + "," } else { first = 0 }
// Append entry to output
if first == 0 {
out = out + ","
} else {
first = 0
}
out = out + "{" + ParserCommonUtilsBox.dq() + "name" + ParserCommonUtilsBox.dq() + ":" + ParserCommonUtilsBox.dq() + ParserCommonUtilsBox.esc_json(name) + ParserCommonUtilsBox.dq()
if path != null { out = out + "," + ParserCommonUtilsBox.dq() + "path" + ParserCommonUtilsBox.dq() + ":" + ParserCommonUtilsBox.dq() + ParserCommonUtilsBox.esc_json(path) + ParserCommonUtilsBox.dq() }
if path != null {
out = out + "," + ParserCommonUtilsBox.dq() + "path" + ParserCommonUtilsBox.dq() + ":" + ParserCommonUtilsBox.dq() + ParserCommonUtilsBox.esc_json(path) + ParserCommonUtilsBox.dq()
}
out = out + "}"
}
i = j + 1
next_i = j + 1
i = next_i
}
out = out + "]"
return out
}