- 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.
231 lines
8.5 KiB
Plaintext
231 lines
8.5 KiB
Plaintext
// Stage1UsingResolverBox — resolve `using` statements for Stage‑1 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 Stage‑B.
|
||
// 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 Stage‑1 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 は空のまま返す(Stage‑B 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 } }
|