Files
hakorune/lang/src/compiler/entry/using_resolver_box.hako
nyash-codex 476c72bab8 refactor(phase27.13): Replace @ notation with local variables in UsingResolverBox
- Replace 3 instances of @ notation in _read_file method:
  - @fb → local fb
  - @ok → local ok
  - @content → local content

Result:
-  Build success: cargo build --release (warnings only)
-  Tests: 2/2 existing tests pass (type_sanity, empty_module_returns_none)
- ⚠️ auto_lowering test still blocked by 'using' keyword parser error (separate issue)

Phase 27.13 JoinIR lowering implementation is complete.
Parser fixes needed for full test enablement.
2025-11-24 03:33:11 +09:00

231 lines
8.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Stage1UsingResolverBox — resolve `using` statements for Stage1 pipelines
// Responsibilities:
// - Collect line-based using declarations via UsingCollectorBox.
// - Resolve namespace targets against `nyash.toml` `[modules]` entries (fed via env).
// - Read the referenced .hako files and return a concatenated prefix for StageB.
// Structure tags:
// [Region] JSON/ArrayBox/MapBox ループはすべて Region+next_i 形に統一して SSA を安定化。
// [LoopForm] carrier=pinned を明示し、continue/break を next_i で合流させる。
//
// Env requirements:
// - HAKO_STAGEB_MODULES_LIST: `name=path` entries joined by "|||".
// - NYASH_ROOT: absolute path to repo root (for resolving relative paths).
using lang.compiler.parser.using.using_collector_box as UsingCollectorBox
using lang.compiler.parser.scan.parser_common_utils_box as ParserCommonUtilsBox
using selfhost.shared.json.utils.json_frag as JsonFragBox
static box Stage1UsingResolverBox {
// Entrypoint: collect `using` entries and stitch a prefix.
// - HAKO_STAGEB_APPLY_USINGS=0 → prefix ""defs/body には何も足さない)
// - Depth guard prevents recursive self-calls from cascading.
// [Region] entries 走査は pos/next_pos で一意に前進。pinned: entries/seen, carrier: i。
resolve_for_source(src) {
// Shallow recursion guard to prevent accidental self-recursion in Stage1 using resolver.
{
local depth = env.get("HAKO_STAGEB_USING_DEPTH")
if depth != null && ("" + depth) != "0" {
print("[stageb/recursion] Stage1UsingResolverBox.resolve_for_source recursion detected")
return ""
}
env.set("HAKO_STAGEB_USING_DEPTH", "1")
}
local prefix = ""
if src != null {
local apply_flag = env.get("HAKO_STAGEB_APPLY_USINGS")
// apply_flag==0 なら prefix は空のまま返すStageB defs/body に変更なし)
if apply_flag == null || ("" + apply_flag) != "0" {
local entries = me._collect_using_entries(src)
if entries != null && entries.length() > 0 {
local modules = me._build_module_map()
local seen = new MapBox()
local i = 0
local n = entries.length()
loop(i < n) {
// Region+next_i 形で多経路を末尾合流させるSSA/PHI 安定化)
local next_i = i + 1
local entry = entries.get(i)
local name = "" + entry.get("name")
// 早期スキップ条件をまとめて evaluate
local should_emit = 1
if name == "" { should_emit = 0 }
// path 解決(明示 path 優先、無ければ modules マップ)
local path = null
if should_emit == 1 {
path = entry.get("path")
if path == null || ("" + path) == "" {
path = me._lookup_module_path(modules, name)
} else {
path = "" + path
}
if path == null || path == "" { should_emit = 0 }
}
// abs path 化
local abs_path = null
if should_emit == 1 {
abs_path = me._abs_path(path)
if abs_path == null || abs_path == "" { should_emit = 0 }
}
// 重複 guard
if should_emit == 1 {
if seen.get(abs_path) == "1" { should_emit = 0 }
}
// ファイル読み込み
if should_emit == 1 {
local code = me._read_file(abs_path)
if code == null { should_emit = 0 }
if should_emit == 1 {
seen.set(abs_path, "1")
prefix = prefix + "\n" + code + "\n"
}
}
i = next_i
}
}
}
}
// Clear depth guard before returning適用なしでも guard を戻す)
env.set("HAKO_STAGEB_USING_DEPTH", "0")
return prefix
}
// Program(JSON v0) に対する using 解決IOなし、SSOT委譲
// - modules_json: nyash.toml [modules] 相当の JSON
// - using_entries_json: UsingCollectorBox.collect の生出力
// - ctx: resolve_ssot_box 向けのヒントmodules/cwd/using_paths など)
resolve_for_program_json(program_json, modules_json, using_entries_json, ctx) {
using hako.using.resolve.ssot as UsingResolveSSOTBox
// MVP: IO せずに SSOT が shape を整えるだけ。現状は透過返し。
local resolved = UsingResolveSSOTBox.resolve_modules(modules_json, using_entries_json, ctx)
// 返すものは modules_json をそのまま(今は純粋パス)。将来 prefix/string を付与する。
return resolved
}
// Collect entries from UsingCollectorBox JSON output.
// Responsibility: JSON を配列にパースするだけpath 解決やファイル読み込みは呼び元)。
// [Region] JSON スキャンは pos/next_pos を明示し、early-exit も next_pos=n で一本化。
_collect_using_entries(src) {
local json = UsingCollectorBox.collect(src)
if json == null || json == "" || json == "[]" { return null }
local out = new ArrayBox()
local pos = 0
local n = json.length()
loop(pos < n) {
// Region+next_pos 形break を next_pos=n で表現)
local next_pos = n
local name_idx = JsonFragBox.index_of_from(json, "\"name\":\"", pos)
if name_idx < 0 {
next_pos = n
} else {
local name = JsonFragBox.read_string_after(json, name_idx + 7)
local obj_end = JsonFragBox.index_of_from(json, "}", name_idx)
if obj_end < 0 { obj_end = n }
local path = null
local path_idx = JsonFragBox.index_of_from(json, "\"path\":\"", name_idx)
if path_idx >= 0 && path_idx < obj_end {
path = JsonFragBox.read_string_after(json, path_idx + 7)
}
local entry = new MapBox()
entry.set("name", name)
if path != null { entry.set("path", path) }
out.push(entry)
next_pos = obj_end + 1
}
pos = next_pos
}
return out
}
// Build modules map from env `HAKO_STAGEB_MODULES_LIST` ("name=path" joined by "|||").
// Responsibility: env を MapBox に積むだけ。存在しない/空なら空 Map を返す。
// [Region] delim 走査も start/next_start で一本化し、continue/break を排除した形に固定。
_build_module_map() {
local map = new MapBox()
local raw = env.get("HAKO_STAGEB_MODULES_LIST")
if raw == null { return map }
local str = "" + raw
if str == "" { return map }
local delim = "|||"
local start = 0
local cont = 1
loop(cont == 1) {
local next_start = str.length()
local next = ParserCommonUtilsBox.index_of(str, start, delim)
local seg = ""
if next >= 0 {
seg = str.substring(start, next)
next_start = next + delim.length()
} else {
seg = str.substring(start, str.length())
cont = 0
}
me._push_module_entry(map, seg)
start = next_start
}
return map
}
_push_module_entry(map, seg) {
if seg == null { return }
local line = ParserCommonUtilsBox.trim("" + seg)
if line == "" { return }
local eq_idx = ParserCommonUtilsBox.index_of(line, 0, "=")
if eq_idx < 0 { return }
local key = ParserCommonUtilsBox.trim(line.substring(0, eq_idx))
local val = ParserCommonUtilsBox.trim(line.substring(eq_idx + 1, line.length()))
if key == "" || val == "" { return }
map.set(key, val)
}
_lookup_module_path(map, name) {
if map == null { return null }
local val = map.get(name)
if val != null { return "" + val }
return null
}
_abs_path(path) {
if path == null { return null }
local p = "" + path
if p == "" { return null }
if ParserCommonUtilsBox.starts_with(p, 0, "/") == 1 { return p }
local root = env.get("NYASH_ROOT")
if root == null || root == "" { return p }
local base = "" + root
if base == "" { return p }
if base.substring(base.length() - 1, base.length()) == "/" { return base + p }
return base + "/" + p
}
_read_file(path) {
if path == null { return null }
local fb = new FileBox()
local ok = fb.open(path, "r")
if ok != 1 {
// Dev note: keep quiet by default; enable verbose by setting HAKO_STAGEB_USING_DEBUG=1.
local dbg = env.get("HAKO_STAGEB_USING_DEBUG")
if dbg != null && ("" + dbg) == "1" {
print("[stageb/using_resolver] failed to open file: " + path)
}
return null
}
local content = fb.read()
fb.close()
return content
}
}
static box Stage1UsingResolverBoxMain { main(args) { return 0 } }