Phase 25.1b: Step2完了(FuncBodyBasicLowerBox導入)

Step2実装内容:
- FuncBodyBasicLowerBox導入(defs専用下請けモジュール)
- _try_lower_local_if_return実装(Local+単純if)
- _inline_local_ints実装(軽い正規化)
- minimal lowers統合(Return/BinOp/IfCompare/MethodArray系)

Fail-Fast体制確立:
- MirBuilderBox: defs_onlyでも必ずタグ出力
- [builder/selfhost-first:unsupported:defs_only]
- [builder/selfhost-first:unsupported:no_match]

Phase構造整備:
- Phase 25.1b README新設(Step0-3計画)
- Phase 25.2b README新設(次期計画)
- UsingResolverBox追加(using system対応準備)

スモークテスト:
- stage1_launcher_program_to_mir_canary_vm.sh追加

Next: Step3 LoopForm対応

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-15 22:32:13 +09:00
parent 6856922374
commit 7ca7f646de
31 changed files with 1670 additions and 323 deletions

View File

@ -10,9 +10,10 @@
// - Recommended: Dev/CI は wrappertools/hakorune_emit_mir.sh経由で Program→MIR を行い、失敗時は GateC に自動委譲する。
// Note: sh_core must be in [modules] for parser dependencies to resolve
using "hako.compiler.entry.bundle_resolver" as BundleResolver
using hako.compiler.entry.bundle_resolver as BundleResolver
using lang.compiler.parser.box as ParserBox
using lang.compiler.entry.func_scanner as FuncScannerBox
using lang.compiler.entry.using_resolver as Stage1UsingResolverBox
// Note: Runner resolves entry as Main.main by default.
// Provide static box Main with method main(args) as the entry point.
@ -499,6 +500,16 @@ static box Main {
body_src = merged_prefix + body_src
}
{
local apply_flag = env.get("HAKO_STAGEB_APPLY_USINGS")
if apply_flag == null || ("" + apply_flag) != "0" {
local resolved_prefix = Stage1UsingResolverBox.resolve_for_source(body_src)
if resolved_prefix != null && resolved_prefix != "" {
body_src = resolved_prefix + "\n" + body_src
}
}
}
// 5) Normalize body: trim leading/trailing whitespaces/newlines
{
local s = body_src
@ -523,7 +534,7 @@ static box Main {
// 既定で MIR 直出力は行わない(重い経路を避け、一行出力を保証)。
local ast_json = p.parse_program2(body_src)
// 6.5) Dev-toggle: scan for function definitions (box Main { method <name>(...) {...} })
// 6.5) Dev-toggle: scan for function definitions (static box { method <name>(...) {...} })
// Toggle: HAKO_STAGEB_FUNC_SCAN=1
// Policy: conservative minimal scanner for Phase 21.6 call canary (no nesting, basic only)
// Scope: method <name>(params) { ... } outside of main (same box Main)
@ -532,8 +543,8 @@ static box Main {
{
local func_scan = env.get("HAKO_STAGEB_FUNC_SCAN")
if func_scan != null && ("" + func_scan) == "1" {
// Use FuncScannerBox to extract method definitions
local methods = FuncScannerBox.scan_functions(src, "Main")
// Use FuncScannerBox to extract method definitions from all boxes
local methods = FuncScannerBox.scan_all_boxes(src)
// Build defs JSON array
if methods.length() > 0 {

View File

@ -7,16 +7,96 @@
using lang.compiler.parser.box as ParserBox
static box FuncScannerBox {
// Scan source for method definitions (excluding main)
// Returns ArrayBox of method definitions
// Scan source for method definitions (excluding Main.main)
method scan_functions(source, box_name) {
return me._scan_methods(source, box_name, 1, 0)
}
// Scan all static box definitions and collect their methods.
method scan_all_boxes(source) {
local defs = new ArrayBox()
local s = "" + source
local n = s.length()
local i = 0
local in_str = 0
local esc = 0
local in_line = 0
local in_block = 0
loop(i < n) {
local ch = s.substring(i, i + 1)
if in_line == 1 {
if ch == "\n" { in_line = 0 }
i = i + 1
continue
}
if in_block == 1 {
if ch == "*" && i + 1 < n && s.substring(i + 1, i + 2) == "/" {
in_block = 0
i = i + 2
continue
}
i = i + 1
continue
}
if in_str == 1 {
if esc == 1 { esc = 0 i = i + 1 continue }
if ch == "\\" { esc = 1 i = i + 1 continue }
if ch == "\"" { in_str = 0 i = i + 1 continue }
i = i + 1
continue
}
if ch == "\"" { in_str = 1 i = i + 1 continue }
if ch == "/" && i + 1 < n {
local ch2 = s.substring(i + 1, i + 2)
if ch2 == "/" { in_line = 1 i = i + 2 continue }
if ch2 == "*" { in_block = 1 i = i + 2 continue }
}
if s.substring(i, i + 3) == "box" && me._kw_boundary_before(s, i) == 1 && me._kw_boundary_after(s, i + 3) == 1 {
local cursor = i + 3
cursor = me._skip_whitespace(s, cursor)
local name_start = cursor
loop(cursor < n) {
local ch_name = s.substring(cursor, cursor + 1)
if me._is_ident_char(ch_name) == 1 { cursor = cursor + 1 } else { break }
}
local box_name = s.substring(name_start, cursor)
if box_name == "" {
i = cursor
continue
}
cursor = me._skip_whitespace(s, cursor)
if cursor >= n || s.substring(cursor, cursor + 1) != "{" {
i = cursor
continue
}
local close_idx = me._find_matching_brace(s, cursor)
if close_idx < 0 { break }
local body = s.substring(cursor + 1, close_idx)
local skip_main = 0
if box_name == "Main" { skip_main = 1 }
local include_me = 0
if box_name != "Main" { include_me = 1 }
local defs_box = me._scan_methods(body, box_name, skip_main, include_me)
me._append_defs(defs, defs_box)
i = close_idx + 1
continue
}
i = i + 1
}
return defs
}
method _scan_methods(source, box_name, skip_main, include_me) {
local methods = new ArrayBox()
local s = "" + source
local n = s.length()
local i = 0
loop(i < n) {
// Search for "method " pattern
// Search for "method "
local k = -1
{
local pat = "method "
@ -28,7 +108,7 @@ static box FuncScannerBox {
}
}
if k < 0 { break }
i = k + 7 // skip "method "
i = k + 7
// Extract method name (alphanumeric until '(')
local name_start = i
@ -44,8 +124,7 @@ static box FuncScannerBox {
if name_end < 0 { break }
local method_name = s.substring(name_start, name_end)
// Skip main (already extracted as body)
if method_name == "main" { i = name_end continue }
if skip_main == 1 && box_name == "Main" && method_name == "main" { i = name_end continue }
// Find '(' after name
local lparen = name_end
@ -75,6 +154,21 @@ static box FuncScannerBox {
// Extract params (minimal: comma-separated names)
local params_str = s.substring(lparen + 1, rparen)
local params = me._parse_params(params_str)
if include_me == 1 {
local first = ""
if params.length() > 0 { first = "" + params.get(0) }
if first != "me" {
local merged = new ArrayBox()
merged.push("me")
local pi2 = 0
local pn2 = params.length()
loop(pi2 < pn2) {
merged.push(params.get(pi2))
pi2 = pi2 + 1
}
params = merged
}
}
// Find opening '{' after ')'
local lbrace = -1
@ -130,10 +224,7 @@ static box FuncScannerBox {
// Extract method body (inside braces)
local method_body = s.substring(lbrace + 1, rbrace)
// Strip comments from method body
method_body = me._strip_comments(method_body)
// Trim method body
method_body = me._trim(method_body)
// Parse method body to JSON (statement list)
@ -144,7 +235,6 @@ static box FuncScannerBox {
body_json = p.parse_program2(method_body)
}
// Store method definition
if body_json != null && body_json != "" {
local def = new MapBox()
def.set("name", method_name)
@ -159,6 +249,101 @@ static box FuncScannerBox {
return methods
}
method _append_defs(dst, defs_box) {
if defs_box == null { return }
local i = 0
loop(i < defs_box.length()) {
dst.push(defs_box.get(i))
i = i + 1
}
}
method _skip_whitespace(s, idx) {
local i = idx
local n = s.length()
loop(i < n) {
local ch = s.substring(i, i + 1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { i = i + 1 } else { break }
}
return i
}
method _kw_boundary_before(s, idx) {
if idx <= 0 { return 1 }
local ch = s.substring(idx - 1, idx)
if me._is_ident_char(ch) == 1 { return 0 }
return 1
}
method _kw_boundary_after(s, idx) {
if idx >= s.length() { return 1 }
local ch = s.substring(idx, idx + 1)
if me._is_ident_char(ch) == 1 { return 0 }
return 1
}
method _is_ident_char(ch) {
if ch == null || ch == "" { return 0 }
if ch >= "0" && ch <= "9" { return 1 }
if ch >= "A" && ch <= "Z" { return 1 }
if ch >= "a" && ch <= "z" { return 1 }
if ch == "_" { return 1 }
return 0
}
method _find_matching_brace(s, open_idx) {
local n = s.length()
local i = open_idx
local depth = 0
local in_str = 0
local esc = 0
local in_line = 0
local in_block = 0
loop(i < n) {
local ch = s.substring(i, i + 1)
if in_line == 1 {
if ch == "\n" { in_line = 0 }
i = i + 1
continue
}
if in_block == 1 {
if ch == "*" && i + 1 < n && s.substring(i + 1, i + 2) == "/" {
in_block = 0
i = i + 2
continue
}
i = i + 1
continue
}
if in_str == 1 {
if esc == 1 { esc = 0 i = i + 1 continue }
if ch == "\\" { esc = 1 i = i + 1 continue }
if ch == "\"" { in_str = 0 i = i + 1 continue }
i = i + 1
continue
}
if ch == "\"" { in_str = 1 i = i + 1 continue }
if ch == "/" && i + 1 < n {
local ch2 = s.substring(i + 1, i + 2)
if ch2 == "/" { in_line = 1 i = i + 2 continue }
if ch2 == "*" { in_block = 1 i = i + 2 continue }
}
if ch == "{" {
depth = depth + 1
i = i + 1
continue
}
if ch == "}" {
depth = depth - 1
i = i + 1
if depth == 0 { return i - 1 }
continue
}
i = i + 1
}
return -1
}
// Helper: parse comma-separated parameter names
method _parse_params(params_str) {
local params = new ArrayBox()
@ -202,7 +387,6 @@ static box FuncScannerBox {
local esc = 0
local in_line = 0
local in_block = 0
loop(i < n) {
local ch = s.substring(i, i + 1)
if in_line == 1 {
@ -223,7 +407,6 @@ static box FuncScannerBox {
i = i + 1
continue
}
// Not in string/comment
if ch == "\"" { out = out + ch in_str = 1 i = i + 1 continue }
if ch == "/" && i + 1 < n {
local ch2 = s.substring(i + 1, i + 2)
@ -233,29 +416,24 @@ static box FuncScannerBox {
out = out + ch
i = i + 1
}
return out
}
// Helper: trim whitespace from string
// Helper: trim whitespace
method _trim(s) {
if s == null { return "" }
local str = "" + s
local n = str.length()
local b = 0
// left trim (space, tab, CR, LF)
loop(b < n) {
local ch = str.substring(b, b + 1)
if ch == " " || ch == "\t" || ch == "\r" || ch == "\n" { b = b + 1 } else { break }
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { b = b + 1 } else { break }
}
// right trim
local e = n
loop(e > b) {
local ch = str.substring(e - 1, e)
if ch == " " || ch == "\t" || ch == "\r" || ch == "\n" { e = e - 1 } else { break }
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break }
}
if e > b { return str.substring(b, e) }
return ""
}

View File

@ -0,0 +1,150 @@
// 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.
//
// 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 {
resolve_for_source(src) {
if src == null { return "" }
local apply_flag = env.get("HAKO_STAGEB_APPLY_USINGS")
if apply_flag != null && ("" + apply_flag) == "0" { return "" }
local entries = me._collect_using_entries(src)
if entries == null || entries.length() == 0 { return "" }
local modules = me._build_module_map()
local seen = new MapBox()
local prefix = ""
local i = 0
local n = entries.length()
loop(i < n) {
local entry = entries.get(i)
local name = "" + entry.get("name")
if name == "" { i = i + 1 continue }
local path = entry.get("path")
if path == null || ("" + path) == "" {
path = me._lookup_module_path(modules, name)
} else {
path = "" + path
}
if path == null || path == "" { i = i + 1 continue }
local abs_path = me._abs_path(path)
if abs_path == null || abs_path == "" { i = i + 1 continue }
if seen.get(abs_path) == "1" { i = i + 1 continue }
local code = me._read_file(abs_path)
if code == null { i = i + 1 continue }
seen.set(abs_path, "1")
prefix = prefix + "\n" + code + "\n"
i = i + 1
}
return prefix
}
// Collect entries from UsingCollectorBox JSON output.
_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) {
local name_idx = JsonFragBox.index_of_from(json, "\"name\":\"", pos)
if name_idx < 0 { break }
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)
pos = obj_end + 1
}
return out
}
_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
loop(true) {
local next = ParserCommonUtilsBox.index_of(str, start, delim)
local seg = ""
if next >= 0 { seg = str.substring(start, next) } else { seg = str.substring(start, str.length()) }
me._push_module_entry(map, seg)
if next < 0 { break }
start = next + delim.length()
}
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 }
@fb = new FileBox()
@ok = fb.open(path, "r")
if ok != 1 { return null }
@content = fb.read()
fb.close()
return content
}
}
static box Stage1UsingResolverBoxMain { main(args) { return 0 } }

View File

@ -41,6 +41,7 @@ entry.func_scanner = "entry/func_scanner.hako"
entry.compiler = "entry/compiler.hako"
entry.compiler_stageb = "entry/compiler_stageb.hako"
entry.bundle_resolver = "entry/bundle_resolver.hako"
entry.using_resolver = "entry/using_resolver_box.hako"
[dependencies]
"selfhost.shared" = "^1.0.0"

View File

@ -20,6 +20,24 @@ using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
using lang.mir.builder.func_lowering as FuncLoweringBox
static box MirBuilderBox {
method _log_builder_fail(reason) {
local tag = "[builder/selfhost-first:unsupported:" + reason + "]"
print(tag)
if env.get("HAKO_SELFHOST_TRACE") == "1" {
print("[builder/selfhost-first:trace] fail_reason=" + reason)
}
}
// Helper to inject optional function defs + normalization
method _norm_if_apply(m, func_defs_mir) {
if m == null { return null }
local result = FuncLoweringBox.inject_funcs(m, func_defs_mir)
if env.get("HAKO_MIR_BUILDER_METHODIZE") == "1" {
result = FuncLoweringBox.methodize_calls_in_mir(result)
}
local nv = env.get("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE")
if nv != null && ("" + nv) == "1" { return NormBox.normalize_all(result) }
return result
}
// Availability probe (for canaries)
is_available() {
// For now, availability means delegate toggle is present
@ -59,20 +77,6 @@ static box MirBuilderBox {
func_defs_mir = FuncLoweringBox.lower_func_defs(s, s)
}
}
// Helper: optional normalization (dev toggle, default OFF) + func injection
local norm_if = function(m) {
if m == null { return null }
// Inject function definitions if available
local result = FuncLoweringBox.inject_funcs(m, func_defs_mir)
// Optional methodize (dev): rewrite legacy Call -> unified mir_call(Method)
if env.get("HAKO_MIR_BUILDER_METHODIZE") == "1" {
result = FuncLoweringBox.methodize_calls_in_mir(result)
}
// Optional normalize (dev)
local nv = env.get("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE")
if nv != null && ("" + nv) == "1" { return NormBox.normalize_all(result) }
return result
}
// Internal path既定ON — const(int)+ret, binop+ret ほか、registry 優先の lowering
// Disable with: HAKO_MIR_BUILDER_INTERNAL=0
{
@ -88,7 +92,7 @@ static box MirBuilderBox {
"{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"compare\",\"operation\":\"<\",\"lhs\":1,\"rhs\":2,\"dst\":3},{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
"{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," +
"{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}"
return norm_if(mir)
return me._norm_if_apply(mir, func_defs_mir)
}
}
// Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1,
@ -138,7 +142,7 @@ static box MirBuilderBox {
"{\\\"op\\\":\\\"mir_call\\\",\\\"dst\\\":4,\\\"mir_call\\\":{\\\"callee\\\":{\\\"type\\\":\\\"Method\\\",\\\"method\\\":\\\"" + method + "\\\",\\\"receiver\\\":1},\\\"args\\\":[" + args_text + "],\\\"effects\\\":[]}}," +
"{\\\"op\\\":\\\"ret\\\",\\\"value\\\":4}]}]}]}"
print("[mirbuilder/registry:return.method.arraymap]")
return norm_if(mir)
return me._norm_if_apply(mir, func_defs_mir)
}
// Registry list汎用
using "hako.mir.builder.pattern_registry" as PatternRegistryBox
@ -166,23 +170,23 @@ static box MirBuilderBox {
local i = 0; local n = names.length()
loop(i < n) {
local nm = "" + names.get(i)
if nm == "if.compare.intint" { local out = LowerIfCompareBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "if.compare.fold.binints" { local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "if.compare.fold.varint" { local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "if.compare.varint" { local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "if.compare.varvar" { local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.method.arraymap" { local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.method.string.length" { local out = LowerReturnMethodStringLengthBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.loop.strlen.sum" { local out = LowerReturnLoopStrlenSumBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.var.local" { local out = LowerReturnVarLocalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.string" { local out = LowerReturnStringBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.float" { local out = LowerReturnFloatBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.bool" { local out = LowerReturnBoolBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.logical" { local out = LowerReturnLogicalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.binop.varint" { local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.binop.varvar" { local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.binop.intint" { local out = LowerReturnBinOpBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "return.int" { local out = LowerReturnIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return norm_if(out) } }
if nm == "if.compare.intint" { local out = LowerIfCompareBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "if.compare.fold.binints" { local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "if.compare.fold.varint" { local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "if.compare.varint" { local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "if.compare.varvar" { local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.method.arraymap" { local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.method.string.length" { local out = LowerReturnMethodStringLengthBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.loop.strlen.sum" { local out = LowerReturnLoopStrlenSumBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.var.local" { local out = LowerReturnVarLocalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.string" { local out = LowerReturnStringBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.float" { local out = LowerReturnFloatBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.bool" { local out = LowerReturnBoolBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.logical" { local out = LowerReturnLogicalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.binop.varint" { local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.binop.varvar" { local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.binop.intint" { local out = LowerReturnBinOpBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
if nm == "return.int" { local out = LowerReturnIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
i = i + 1
}
// Fall-through to chain if none matched
@ -203,53 +207,53 @@ static box MirBuilderBox {
using "hako.mir.builder.internal.lower_loop_count_param" as LowerLoopCountParamBox
using "hako.mir.builder.internal.lower_loop_simple" as LowerLoopSimpleBox
// Prefer New(Constructor) minimal first to avoid unresolved nested lowers in inline runs
{ local out_newc = LowerNewboxConstructorBox.try_lower(s); if out_newc != null { return norm_if(out_newc) } }
{ local out_arr_size = LowerMethodArraySizeBox.try_lower(s); if out_arr_size != null { return norm_if(out_arr_size) } }
{ local out_arr_push = LowerMethodArrayPushBox.try_lower(s); if out_arr_push != null { return norm_if(out_arr_push) } }
{ local out_arr_gs = LowerMethodArrayGetSetBox.try_lower(s); if out_arr_gs != null { return norm_if(out_arr_gs) } }
{ local out_map_size = LowerMethodMapSizeBox.try_lower(s); if out_map_size != null { return norm_if(out_map_size) } }
{ local out_map_gs = LowerMethodMapGetSetBox.try_lower(s); if out_map_gs != null { return norm_if(out_map_gs) } }
{ local out_ls = LowerLoadStoreLocalBox.try_lower(s); if out_ls != null { return norm_if(out_ls) } }
{ local out_toc = LowerTypeOpCheckBox.try_lower(s); if out_toc != null { return norm_if(out_toc) } }
{ local out_tca = LowerTypeOpCastBox.try_lower(s); if out_tca != null { return norm_if(out_tca) } }
{ local out_newc = LowerNewboxConstructorBox.try_lower(s); if out_newc != null { return me._norm_if_apply(out_newc, func_defs_mir) } }
{ local out_arr_size = LowerMethodArraySizeBox.try_lower(s); if out_arr_size != null { return me._norm_if_apply(out_arr_size, func_defs_mir) } }
{ local out_arr_push = LowerMethodArrayPushBox.try_lower(s); if out_arr_push != null { return me._norm_if_apply(out_arr_push, func_defs_mir) } }
{ local out_arr_gs = LowerMethodArrayGetSetBox.try_lower(s); if out_arr_gs != null { return me._norm_if_apply(out_arr_gs, func_defs_mir) } }
{ local out_map_size = LowerMethodMapSizeBox.try_lower(s); if out_map_size != null { return me._norm_if_apply(out_map_size, func_defs_mir) } }
{ local out_map_gs = LowerMethodMapGetSetBox.try_lower(s); if out_map_gs != null { return me._norm_if_apply(out_map_gs, func_defs_mir) } }
{ local out_ls = LowerLoadStoreLocalBox.try_lower(s); if out_ls != null { return me._norm_if_apply(out_ls, func_defs_mir) } }
{ local out_toc = LowerTypeOpCheckBox.try_lower(s); if out_toc != null { return me._norm_if_apply(out_toc, func_defs_mir) } }
{ local out_tca = LowerTypeOpCastBox.try_lower(s); if out_tca != null { return me._norm_if_apply(out_tca, func_defs_mir) } }
// Loop lowers (sum_bc/continue/break normalization)
// Allow skipping heavy loop lowers on plugin-less hosts: HAKO_MIR_BUILDER_SKIP_LOOPS=1
{
local skip_loops = env.get("HAKO_MIR_BUILDER_SKIP_LOOPS")
local do_loops = (skip_loops == null) || (("" + skip_loops) != "1")
if do_loops == 1 {
{ local out_loop2 = LowerLoopSumBcBox.try_lower(s); if out_loop2 != null { return norm_if(out_loop2) } }
{ local out_loop2 = LowerLoopSumBcBox.try_lower(s); if out_loop2 != null { return me._norm_if_apply(out_loop2, func_defs_mir) } }
}
}
{ local out_if2b = LowerIfNestedBox.try_lower(s); if out_if2b != null { return norm_if(out_if2b) } }
{ local out_if2 = LowerIfThenElseFollowingReturnBox.try_lower(s); if out_if2 != null { return norm_if(out_if2) } }
{ local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { return norm_if(out_if) } }
{ local out_ifb = LowerIfCompareFoldBinIntsBox.try_lower(s); if out_ifb != null { return norm_if(out_ifb) } }
{ local out_ifbv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_ifbv != null { return norm_if(out_ifbv) } }
{ local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { return norm_if(out_ifvi) } }
{ local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { return norm_if(out_ifvv) } }
{ local out_if2b = LowerIfNestedBox.try_lower(s); if out_if2b != null { return me._norm_if_apply(out_if2b, func_defs_mir) } }
{ local out_if2 = LowerIfThenElseFollowingReturnBox.try_lower(s); if out_if2 != null { return me._norm_if_apply(out_if2, func_defs_mir) } }
{ local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { return me._norm_if_apply(out_if, func_defs_mir) } }
{ local out_ifb = LowerIfCompareFoldBinIntsBox.try_lower(s); if out_ifb != null { return me._norm_if_apply(out_ifb, func_defs_mir) } }
{ local out_ifbv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_ifbv != null { return me._norm_if_apply(out_ifbv, func_defs_mir) } }
{ local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { return me._norm_if_apply(out_ifvi, func_defs_mir) } }
{ local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { return me._norm_if_apply(out_ifvv, func_defs_mir) } }
{
local skip_loops2 = env.get("HAKO_MIR_BUILDER_SKIP_LOOPS")
local do_loops2 = (skip_loops2 == null) || (("" + skip_loops2) != "1")
if do_loops2 == 1 {
{ local out_loopp = LowerLoopCountParamBox.try_lower(s); if out_loopp != null { return norm_if(out_loopp) } }
{ local out_loop = LowerLoopSimpleBox.try_lower(s); if out_loop != null { return norm_if(out_loop) } }
{ local out_loopp = LowerLoopCountParamBox.try_lower(s); if out_loopp != null { return me._norm_if_apply(out_loopp, func_defs_mir) } }
{ local out_loop = LowerLoopSimpleBox.try_lower(s); if out_loop != null { return me._norm_if_apply(out_loop, func_defs_mir) } }
}
}
{ local out_var = LowerReturnVarLocalBox.try_lower(s); if out_var != null { return norm_if(out_var) } }
{ local out_str = LowerReturnStringBox.try_lower(s); if out_str != null { return norm_if(out_str) } }
{ local out_f = LowerReturnFloatBox.try_lower(s); if out_f != null { return norm_if(out_f) } }
{ local out_log = LowerReturnLogicalBox.try_lower(s); if out_log != null { return norm_if(out_log) } }
{ local out_meth = LowerReturnMethodArrayMapBox.try_lower(s); if out_meth != null { return norm_if(out_meth) } }
{ local out_meth_s = LowerReturnMethodStringLengthBox.try_lower(s); if out_meth_s != null { return norm_if(out_meth_s) } }
{ local out_sum = LowerReturnLoopStrlenSumBox.try_lower(s); if out_sum != null { return norm_if(out_sum) } }
{ local out_bool = LowerReturnBoolBox.try_lower(s); if out_bool != null { return norm_if(out_bool) } }
{ local out_bvi = LowerReturnBinOpVarIntBox.try_lower(s); if out_bvi != null { return norm_if(out_bvi) } }
{ local out_bvv = LowerReturnBinOpVarVarBox.try_lower(s); if out_bvv != null { return norm_if(out_bvv) } }
{ local out_bin = LowerReturnBinOpBox.try_lower(s); if out_bin != null { return norm_if(out_bin) } }
{ local out_var = LowerReturnVarLocalBox.try_lower(s); if out_var != null { return me._norm_if_apply(out_var, func_defs_mir) } }
{ local out_str = LowerReturnStringBox.try_lower(s); if out_str != null { return me._norm_if_apply(out_str, func_defs_mir) } }
{ local out_f = LowerReturnFloatBox.try_lower(s); if out_f != null { return me._norm_if_apply(out_f, func_defs_mir) } }
{ local out_log = LowerReturnLogicalBox.try_lower(s); if out_log != null { return me._norm_if_apply(out_log, func_defs_mir) } }
{ local out_meth = LowerReturnMethodArrayMapBox.try_lower(s); if out_meth != null { return me._norm_if_apply(out_meth, func_defs_mir) } }
{ local out_meth_s = LowerReturnMethodStringLengthBox.try_lower(s); if out_meth_s != null { return me._norm_if_apply(out_meth_s, func_defs_mir) } }
{ local out_sum = LowerReturnLoopStrlenSumBox.try_lower(s); if out_sum != null { return me._norm_if_apply(out_sum, func_defs_mir) } }
{ local out_bool = LowerReturnBoolBox.try_lower(s); if out_bool != null { return me._norm_if_apply(out_bool, func_defs_mir) } }
{ local out_bvi = LowerReturnBinOpVarIntBox.try_lower(s); if out_bvi != null { return me._norm_if_apply(out_bvi, func_defs_mir) } }
{ local out_bvv = LowerReturnBinOpVarVarBox.try_lower(s); if out_bvv != null { return me._norm_if_apply(out_bvv, func_defs_mir) } }
{ local out_bin = LowerReturnBinOpBox.try_lower(s); if out_bin != null { return me._norm_if_apply(out_bin, func_defs_mir) } }
{
local out_int = LowerReturnIntBox.try_lower(s)
if out_int != null { return norm_if(out_int) }
if out_int != null { return me._norm_if_apply(out_int, func_defs_mir) }
}
// Find Return marker (or If)
// Case (If with Compare + Return(Int)/Return(Int) in branches)
@ -341,7 +345,7 @@ static box MirBuilderBox {
"{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
"{\"id\":1,\"instructions\":[{\"op\":\"const\",\"dst\":4,\"value\":{\"type\":\"i64\",\"value\":" + then_val + "}},{\"op\":\"ret\",\"value\":4}]}," +
"{\"id\":2,\"instructions\":[{\"op\":\"const\",\"dst\":5,\"value\":{\"type\":\"i64\",\"value\":" + else_val + "}},{\"op\":\"ret\",\"value\":5}]}]}]}"
return norm_if(mir_if)
return me._norm_if_apply(mir_if, func_defs_mir)
}
}
}
@ -350,7 +354,7 @@ static box MirBuilderBox {
// NewBox(Constructor) minimal
{
local out_new = LowerNewboxConstructorBox.try_lower(s)
if out_new != null { return norm_if(out_new) }
if out_new != null { return me._norm_if_apply(out_new, func_defs_mir) }
}
// Fallback cases below: Return(Binary) and Return(Int)
local k_ret = JsonFragBox.index_of_from(s, "\"type\":\"Return\"", 0)
@ -392,7 +396,7 @@ static box MirBuilderBox {
"{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val + "}}," +
"{\"op\":\"binop\",\"operation\":\"" + op + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
"{\"op\":\"ret\",\"value\":3}]}]}]}"
return norm_if(mir_bin)
return me._norm_if_apply(mir_bin, func_defs_mir)
}
}
}
@ -408,7 +412,7 @@ static box MirBuilderBox {
if num != null {
if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" { print("[mirbuilder/fallback:Return(Int) val=" + num + "]") }
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + num + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
return norm_if(mir)
return me._norm_if_apply(mir, func_defs_mir)
}
}
} else {
@ -421,11 +425,9 @@ static box MirBuilderBox {
// Unsupported internal shape → try defs fallback (dev: HAKO_MIR_BUILDER_FUNCS=1)
print("[mirbuilder/internal/unsupported] only Return(Int|Binary(Int,Int)) supported in internal mode")
if func_defs_mir != null && func_defs_mir != "" {
// Assemble a minimal module from lowered defs
local defs = func_defs_mir
if defs.substring(0,1) == "," { defs = defs.substring(1, defs.length()) }
local mod = "{\\\"functions\\\":[" + defs + "]}"
return norm_if(mod)
me._log_builder_fail("defs_only")
} else {
me._log_builder_fail("no_match")
}
return null
}
@ -436,7 +438,7 @@ static box MirBuilderBox {
// Call host provider via extern: env.mirbuilder.emit(program_json)
local args = new ArrayBox(); args.push(program_json)
local ret = hostbridge.extern_invoke("env.mirbuilder", "emit", args)
return norm_if(ret)
return me._norm_if_apply(ret, func_defs_mir)
}
// Provider not wired → FailFast tag
print("[mirbuilder/delegate/missing] no provider; enable HAKO_MIR_BUILDER_DELEGATE=1")

View File

@ -16,30 +16,29 @@ using "hako.mir.builder.internal.lower_if_compare_varint" as LowerIfCompareVarIn
using "hako.mir.builder.internal.lower_if_compare_varvar" as LowerIfCompareVarVarBox
static box MirBuilderBox {
method _norm_json_if_needed(m) {
if m == null { return null }
local nv = env.get("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE")
if nv != null && ("" + nv) == "1" { return NormBox.normalize_all(m) }
return m
}
// Minimal entry
method emit_from_program_json_v0(program_json, opts) {
if program_json == null { print("[mirbuilder/min/input:null]"); return null }
local s = "" + program_json
if !(s.contains("\"version\"")) || !(s.contains("\"kind\"")) { print("[mirbuilder/min/input:invalid]"); return null }
// Small helper: optionally normalize MIR(JSON) via toggle (dev-only, default OFF)
local norm_if = function(m) {
if m == null { return null }
local nv = env.get("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE")
if nv != null && ("" + nv) == "1" { return NormBox.normalize_all(m) }
return m
}
// Try minimal patterns (lightweight only)
{ local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { print("[mirbuilder/min:return.method.arraymap]"); return norm_if(out) } }
{ local out_v = LowerReturnBinOpVarIntBox.try_lower(s); if out_v != null { print("[mirbuilder/min:return.binop.varint]"); return norm_if(out_v) } }
{ local out_b = LowerReturnBinOpBox.try_lower(s); if out_b != null { print("[mirbuilder/min:return.binop.intint]"); return norm_if(out_b) } }
{ local out_bvv = LowerReturnBinOpVarVarBox.try_lower(s); if out_bvv != null { print("[mirbuilder/min:return.binop.varvar]"); return norm_if(out_bvv) } }
{ local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { print("[mirbuilder/min:return.method.arraymap]"); return me._norm_json_if_needed(out) } }
{ local out_v = LowerReturnBinOpVarIntBox.try_lower(s); if out_v != null { print("[mirbuilder/min:return.binop.varint]"); return me._norm_json_if_needed(out_v) } }
{ local out_b = LowerReturnBinOpBox.try_lower(s); if out_b != null { print("[mirbuilder/min:return.binop.intint]"); return me._norm_json_if_needed(out_b) } }
{ local out_bvv = LowerReturnBinOpVarVarBox.try_lower(s); if out_bvv != null { print("[mirbuilder/min:return.binop.varvar]"); return me._norm_json_if_needed(out_bvv) } }
// Compare lowers: prefer fold/var-based before int-int to avoid greedy match
{ local out_if_fv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_if_fv != null { print("[mirbuilder/min:if.compare.fold.varint]"); return norm_if(out_if_fv) } }
{ local out_if_fb = LowerIfCompareFoldBinIntsBox.try_lower(s); if out_if_fb != null { print("[mirbuilder/min:if.compare.fold.binints]"); return norm_if(out_if_fb) } }
{ local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { print("[mirbuilder/min:if.compare.varint]"); return norm_if(out_ifvi) } }
{ local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { print("[mirbuilder/min:if.compare.varvar]"); return norm_if(out_ifvv) } }
{ local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { print("[mirbuilder/min:if.compare.intint]"); return norm_if(out_if) } }
{ local out2 = LowerReturnIntBox.try_lower(s); if out2 != null { print("[mirbuilder/min:return.int]"); return norm_if(out2) } }
{ local out_if_fv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_if_fv != null { print("[mirbuilder/min:if.compare.fold.varint]"); return me._norm_json_if_needed(out_if_fv) } }
{ local out_if_fb = LowerIfCompareFoldBinIntsBox.try_lower(s); if out_if_fb != null { print("[mirbuilder/min:if.compare.fold.binints]"); return me._norm_json_if_needed(out_if_fb) } }
{ local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { print("[mirbuilder/min:if.compare.varint]"); return me._norm_json_if_needed(out_ifvi) } }
{ local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { print("[mirbuilder/min:if.compare.varvar]"); return me._norm_json_if_needed(out_ifvv) } }
{ local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { print("[mirbuilder/min:if.compare.intint]"); return me._norm_json_if_needed(out_if) } }
{ local out2 = LowerReturnIntBox.try_lower(s); if out2 != null { print("[mirbuilder/min:return.int]"); return me._norm_json_if_needed(out2) } }
print("[mirbuilder/min/unsupported]")
return null
}

View File

@ -2,7 +2,7 @@
Responsibility
- Convert StageB Program(JSON v0) into MIR(JSON v0) for VM/LLVM lines.
- Phase 20.34 starts with a delegate implementation to Runner and progressively internalizes ops.
- Keep the boundary/contract stable and FailFast; no silent fallback to stub MIR.
Interface (stable)
- `emit_from_program_json_v0(program_json: String, opts: Map|Null) -> String|Null`
@ -11,12 +11,24 @@ Interface (stable)
Tags (FailFast, stable)
- `[mirbuilder/input/null]` — input is null
- `[mirbuilder/input/invalid]` — header missing (version/kind)
- `[mirbuilder/delegate]` — delegate path selected (Runner `--program-json-to-mir`)
- `[mirbuilder/internal/unsupported] ...` — Program(JSON) shape not yet supported by internal lowers
- `[builder/selfhost-first:unsupported:defs_only]` — only defs を lowering できる状態main なし)のため中止
- `[builder/selfhost-first:unsupported:no_match]` — internal lowers / defs のどちらにもマッチせず中止
- `[builder/funcs:fail:no-main]` — inject_funcs が main を含まない MIR に defs を差し込もうとしたため拒否(`HAKO_MIR_BUILDER_REQUIRE_MAIN=1` 時)
- `[mirbuilder/delegate]` — delegate path selectedRunner/extern provider 経由)
- `[mirbuilder/delegate/missing]` — delegate/provider not wired yet
Toggles (default OFF)
- `HAKO_MIR_BUILDER_DELEGATE=1`: Use Runner `--program-json-to-mir` as a temporary provider
Toggles
- `HAKO_MIR_BUILDER_INTERNAL=0/1` — internal lowers gate既定=1
- `HAKO_MIR_BUILDER_REGISTRY=0/1` — pattern registry gate既定=1
- `HAKO_MIR_BUILDER_DELEGATE=1` — use Runner/extern provider (`env.mirbuilder.emit`) 経由で Program→MIR
- `HAKO_MIR_BUILDER_FUNCS=1` — enable defs lowering via `FuncLoweringBox.lower_func_defs`
- `HAKO_MIR_BUILDER_METHODIZE=1` — enable call→mir_call(Method) rewrite after MIR 生成
- `HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1` — apply JsonFrag normalizer to selfhost/provider output
- `HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG=1` — devonly: minimal loop MIR を強制生成(テスト用)
- `HAKO_MIR_BUILDER_REQUIRE_MAIN=1` — inject_funcs で `"name":"main"` を持たない MIR に defs を追加するのを禁止(既定=0
Notes
- BoxFirst policy: boundary/contract first, then implementation. Keep tags stable; no silent fallback.
- Large payloads: implementation may stream/json-scan later; initial version is stringbased.
- BoxFirst policy: define the interface and tags first, then evolve implementation behind the same contract.
- Large payloads: implementation is currently string/JsonFragbased; later phases may stream or segment JSON, but I/F は維持する。
- Phase 25.1b では `FuncLoweringBox` との連携を拡張し、「main + defs」構造を前提とした multifunction MIR の土台を整えるdefsonly モジュールは FailFast

View File

@ -0,0 +1,344 @@
// FuncBodyBasicLowerBox — reuse minimal lowers for defs (Local/If/Return only)
// Scope: functions composed of Local / Assign / If / Return statements (no Loop)
// Policy: delegate to existing pattern lowers (ReturnInt, IfCompare, etc.) and
// rebind the resulting MIR JSON to the target box/method name + params.
using selfhost.shared.json.utils.json_frag as JsonFragBox
using "hako.mir.builder.internal.lower_return_int" as LowerReturnIntBox
using "hako.mir.builder.internal.lower_return_binop" as LowerReturnBinOpBox
using "hako.mir.builder.internal.lower_return_binop_varint" as LowerReturnBinOpVarIntBox
using "hako.mir.builder.internal.lower_return_binop_varvar" as LowerReturnBinOpVarVarBox
using "hako.mir.builder.internal.lower_return_method_array_map" as LowerReturnMethodArrayMapBox
using "hako.mir.builder.internal.lower_if_compare" as LowerIfCompareBox
using "hako.mir.builder.internal.lower_if_compare_fold_binints" as LowerIfCompareFoldBinIntsBox
using "hako.mir.builder.internal.lower_if_compare_fold_varint" as LowerIfCompareFoldVarIntBox
using "hako.mir.builder.internal.lower_if_compare_varint" as LowerIfCompareVarIntBox
using "hako.mir.builder.internal.lower_if_compare_varvar" as LowerIfCompareVarVarBox
static box FuncBodyBasicLowerBox {
lower(func_name, box_name, params_arr, body_json) {
if body_json == null { return null }
local s = "" + body_json
if !(s.contains("\"version\"")) || !(s.contains("\"kind\"")) { return null }
local lowered = me._try_lower_local_if_return(func_name, box_name, params_arr, s)
if lowered != null { return lowered }
s = me._inline_local_ints(s)
// Try minimal lowers (same order as MirBuilderMinBox, restricted set)
{ local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.method.arraymap]") } }
{ local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.varint]") } }
{ local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.varvar]") } }
{ local out = LowerReturnBinOpBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.intint]") } }
{ local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:if.compare.fold.varint]") } }
{ local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:if.compare.fold.binints]") } }
{ local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:if.compare.varint]") } }
{ local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:if.compare.varvar]") } }
{ local out = LowerIfCompareBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:if.compare.intint]") } }
{ local out = LowerReturnIntBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.int]") } }
if s.contains("\"type\":\"Loop\"") && env.get("HAKO_SELFHOST_TRACE") == "1" {
print("[builder/funcs:unsupported:loop]")
}
return null
}
method _rebind(mir_json, func_name, box_name, params_arr, tag) {
if mir_json == null { return null }
local s = "" + mir_json
local target = me._build_func_name(box_name, func_name, params_arr)
if target == null { return null }
local name_idx = JsonFragBox.index_of_from(s, "\"name\":\"", 0)
if name_idx < 0 { return null }
local name_end = JsonFragBox.index_of_from(s, "\"", name_idx + 8)
if name_end < 0 { return null }
s = s.substring(0, name_idx) + "\"name\":\"" + target + "\"" + s.substring(name_end + 1, s.length())
local params_json = me._build_params_json(params_arr)
local params_idx = JsonFragBox.index_of_from(s, "\"params\":", 0)
if params_idx >= 0 {
local bracket_start = JsonFragBox.index_of_from(s, "[", params_idx)
if bracket_start >= 0 {
local bracket_end = me._find_matching_bracket(s, bracket_start)
if bracket_end >= bracket_start {
s = s.substring(0, params_idx) + "\"params\":" + params_json + s.substring(bracket_end + 1, s.length())
}
}
}
if env.get("HAKO_SELFHOST_TRACE") == "1" {
print(tag + " -> " + target)
}
return s
}
method _build_func_name(box_name, func_name, params_arr) {
if box_name == null || func_name == null { return null }
local arity = 0
if params_arr != null { arity = JsonFragBox._str_to_int("" + params_arr.length()) }
local suffix = "/" + arity
return ("" + box_name) + "." + ("" + func_name) + suffix
}
method _build_params_json(params_arr) {
if params_arr == null { return "[]" }
local params_json = "["
local i = 0
local n = params_arr.length()
loop(i < n) {
if i > 0 { params_json = params_json + "," }
params_json = params_json + "\\\"" + ("" + params_arr.get(i)) + "\\\""
i = i + 1
}
params_json = params_json + "]"
return params_json
}
method _find_matching_bracket(text, start_idx) {
local s = "" + text
local n = s.length()
local depth = 0
local in_str = 0
local esc = 0
local i = start_idx
loop(i < n) {
local ch = s.substring(i, i + 1)
if in_str == 1 {
if esc == 1 { esc = 0 i = i + 1 continue }
if ch == "\\" { esc = 1 i = i + 1 continue }
if ch == "\"" { in_str = 0 i = i + 1 continue }
i = i + 1
continue
}
if ch == "\"" { in_str = 1 i = i + 1 continue }
if ch == "[" { depth = depth + 1 i = i + 1 continue }
if ch == "]" {
depth = depth - 1
if depth == 0 { return i }
i = i + 1
continue
}
i = i + 1
}
return -1
}
method _inline_local_ints(body_json) {
local s = "" + body_json
local locals = new ArrayBox()
local search = 0
loop(search < s.length()) {
local loc_idx = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", search)
if loc_idx < 0 { break }
local name_idx = JsonFragBox.index_of_from(s, "\"name\":", loc_idx)
if name_idx < 0 { search = loc_idx + 1 continue }
local name = JsonFragBox.read_string_after(s, name_idx + 7)
if name == null || name == "" { search = loc_idx + 1 continue }
local expr_idx = JsonFragBox.index_of_from(s, "\"expr\":{", loc_idx)
if expr_idx < 0 { search = loc_idx + 1 continue }
local type_idx = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", expr_idx)
if type_idx < 0 { search = loc_idx + 1 continue }
local val_idx = JsonFragBox.index_of_from(s, "\"value\":", type_idx)
if val_idx < 0 { search = loc_idx + 1 continue }
local val = JsonFragBox.read_int_after(s, val_idx + 8)
if val == null { search = loc_idx + 1 continue }
local info = new MapBox()
info.set("name", name)
info.set("value", JsonFragBox._str_to_int("" + val))
locals.push(info)
search = loc_idx + 1
}
local i = 0
local n = locals.length()
loop(i < n) {
local info = locals.get(i)
local lname = "" + info.get("name")
local lval = "" + info.get("value")
local pattern = "{\"type\":\"Var\",\"name\":\"" + lname + "\"}"
local replacement = "{\"type\":\"Int\",\"value\":" + lval + "}"
loop(true) {
local idx = JsonFragBox.index_of_from(s, pattern, 0)
if idx < 0 { break }
s = s.substring(0, idx) + replacement + s.substring(idx + pattern.length(), s.length())
}
i = i + 1
}
return s
}
method _try_lower_local_if_return(func_name, box_name, params_arr, body_json) {
local local_info = me._extract_first_local(body_json)
if local_info == null { return null }
local if_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"If\"", 0)
if if_idx < 0 { return null }
local cond = me._extract_compare(body_json, if_idx)
if cond == null { return null }
if cond.get("lhs_kind") != "var" || cond.get("lhs_name") != local_info.get("name") { return null }
if cond.get("rhs_kind") != "int" { return null }
local then_ret = me._extract_then_return(body_json, if_idx)
if then_ret == null { return null }
local tail_ret = me._extract_tail_return(body_json, if_idx)
if tail_ret == null { return null }
return me._emit_local_if(func_name, box_name, params_arr, local_info, cond, then_ret, tail_ret)
}
method _extract_first_local(body_json) {
local idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Local\"", 0)
if idx < 0 { return null }
local name_idx = JsonFragBox.index_of_from(body_json, "\"name\":", idx)
if name_idx < 0 { return null }
local name = JsonFragBox.read_string_after(body_json, name_idx + 7)
if name == null || name == "" { return null }
local expr_idx = JsonFragBox.index_of_from(body_json, "\"expr\":{", idx)
if expr_idx < 0 { return null }
local type_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Int\"", expr_idx)
if type_idx < 0 { return null }
local val_idx = JsonFragBox.index_of_from(body_json, "\"value\":", type_idx)
if val_idx < 0 { return null }
local val = JsonFragBox.read_int_after(body_json, val_idx + 8)
if val == null { return null }
local info = new MapBox()
info.set("name", name)
info.set("value", JsonFragBox._str_to_int("" + val))
return info
}
method _extract_compare(body_json, if_idx) {
local cond_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Compare\"", if_idx)
if cond_idx < 0 { return null }
local op_idx = JsonFragBox.index_of_from(body_json, "\"op\":", cond_idx)
if op_idx < 0 { return null }
local op = JsonFragBox.read_string_after(body_json, op_idx + 5)
if op == null { return null }
if !(op == "<" || op == ">" || op == "<=" || op == ">=" || op == "==" || op == "!=") { return null }
local lhs_info = me._extract_operand(body_json, "\"lhs\":{", cond_idx)
local rhs_info = me._extract_operand(body_json, "\"rhs\":{", cond_idx)
if lhs_info == null || rhs_info == null { return null }
local info = new MapBox()
info.set("op", op)
info.set("lhs_kind", lhs_info.get("kind"))
info.set("lhs_name", lhs_info.get("name"))
info.set("lhs_value", lhs_info.get("value"))
info.set("rhs_kind", rhs_info.get("kind"))
info.set("rhs_name", rhs_info.get("name"))
info.set("rhs_value", rhs_info.get("value"))
return info
}
method _extract_operand(body_json, key, cond_idx) {
local idx = JsonFragBox.index_of_from(body_json, key, cond_idx)
if idx < 0 { return null }
local kind = null
local name = null
local value = null
local type_var = JsonFragBox.index_of_from(body_json, "\"type\":\"Var\"", idx)
local type_int = JsonFragBox.index_of_from(body_json, "\"type\":\"Int\"", idx)
if type_var >= 0 && (type_int < 0 || type_var < type_int) {
kind = "var"
local name_idx = JsonFragBox.index_of_from(body_json, "\"name\":", type_var)
if name_idx < 0 { return null }
name = JsonFragBox.read_string_after(body_json, name_idx + 7)
if name == null { return null }
} else if type_int >= 0 {
kind = "int"
local val_idx = JsonFragBox.index_of_from(body_json, "\"value\":", type_int)
if val_idx < 0 { return null }
value = JsonFragBox.read_int_after(body_json, val_idx + 8)
if value == null { return null }
value = JsonFragBox._str_to_int("" + value)
} else {
return null
}
local info = new MapBox()
info.set("kind", kind)
info.set("name", name)
info.set("value", value)
return info
}
method _extract_then_return(body_json, if_idx) {
local then_idx = JsonFragBox.index_of_from(body_json, "\"then\":", if_idx)
if then_idx < 0 { return null }
local ret_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Return\"", then_idx)
if ret_idx < 0 { return null }
return me._extract_return_expr(body_json, ret_idx)
}
method _extract_tail_return(body_json, if_idx) {
local ret_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Return\"", if_idx + 1)
if ret_idx < 0 { return null }
local ret_next = JsonFragBox.index_of_from(body_json, "\"type\":\"Return\"", ret_idx + 1)
if ret_next >= 0 { ret_idx = ret_next }
return me._extract_return_expr(body_json, ret_idx)
}
method _extract_return_expr(body_json, ret_idx) {
local var_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Var\"", ret_idx)
local int_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Int\"", ret_idx)
if var_idx >= 0 && (int_idx < 0 || var_idx < int_idx) {
local name_idx = JsonFragBox.index_of_from(body_json, "\"name\":", var_idx)
if name_idx < 0 { return null }
local name = JsonFragBox.read_string_after(body_json, name_idx + 7)
if name == null { return null }
local info = new MapBox()
info.set("kind", "var")
info.set("name", name)
return info
}
if int_idx >= 0 {
local val_idx = JsonFragBox.index_of_from(body_json, "\"value\":", int_idx)
if val_idx < 0 { return null }
local val = JsonFragBox.read_int_after(body_json, val_idx + 8)
if val == null { return null }
local info = new MapBox()
info.set("kind", "int")
info.set("value", JsonFragBox._str_to_int("" + val))
return info
}
return null
}
method _emit_local_if(func_name, box_name, params_arr, local_info, cond, then_ret, tail_ret) {
local target = me._build_func_name(box_name, func_name, params_arr)
local params_json = me._build_params_json(params_arr)
local local_val = local_info.get("value")
local rhs_val = cond.get("rhs_value")
local op = "" + cond.get("op")
local then_instr = ""
if then_ret.get("kind") == "var" && then_ret.get("name") == local_info.get("name") {
then_instr = "{\"op\":\"ret\",\"value\":1}"
} else if then_ret.get("kind") == "int" {
then_instr = "{\"op\":\"const\",\"dst\":4,\"value\":{\"type\":\"i64\",\"value\":" + then_ret.get("value") + "}},{\"op\":\"ret\",\"value\":4}"
} else {
return null
}
local else_instr = ""
if tail_ret.get("kind") == "var" && tail_ret.get("name") == local_info.get("name") {
else_instr = "{\"op\":\"ret\",\"value\":1}"
} else if tail_ret.get("kind") == "int" {
local else_reg = 4
if then_ret.get("kind") == "int" { else_reg = 5 }
else_instr = "{\"op\":\"const\",\"dst\":" + else_reg + ",\"value\":{\"type\":\"i64\",\"value\":" + tail_ret.get("value") + "}},{\"op\":\"ret\",\"value\":" + else_reg + "}"
} else {
return null
}
local blocks = "{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + local_val + "}}," +
"{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val + "}}," +
"{\"op\":\"compare\",\"operation\":\"" + op + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
"{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
"{\"id\":1,\"instructions\":[" + then_instr + "]}," +
"{\"id\":2,\"instructions\":[" + else_instr + "]}"
if env.get("HAKO_SELFHOST_TRACE") == "1" {
print("[funcs/basic:local_if] -> " + target)
}
return "{\"functions\":[{\"name\":\"" + target + "\",\"params\":" + params_json + ",\"locals\":[],\"blocks\":[" + blocks + "]}]}"
}
}

View File

@ -5,6 +5,7 @@
// Output: Additional MIR functions + resolved Call targets
using selfhost.shared.json.utils.json_frag as JsonFragBox
using lang.mir.builder.func_body.basic_lower_box as FuncBodyBasicLowerBox
static box FuncLoweringBox {
// Lower function definitions to MIR
@ -14,6 +15,11 @@ static box FuncLoweringBox {
local s = "" + program_json
local func_defs_mir = ""
local trace_funcs = 0
{
local trace_env = env.get("HAKO_SELFHOST_TRACE")
if trace_env != null && ("" + trace_env) == "1" { trace_funcs = 1 }
}
// Check for "defs" key in Program JSON
local defs_idx = JsonFragBox.index_of_from(s, "\"defs\":", 0)
@ -201,6 +207,8 @@ static box FuncLoweringBox {
local mir_func = me._lower_func_body(func_name, box_name, params_arr, body_json, func_map)
if mir_func != null && mir_func != "" {
func_jsons.push(mir_func)
} else if trace_funcs == 1 {
print("[builder/funcs:skip] " + box_name + "." + func_name + " body unsupported")
}
}
}
@ -308,6 +316,11 @@ static box FuncLoweringBox {
method _lower_func_body(func_name, box_name, params_arr, body_json, func_map) {
local body_str = "" + body_json
{
local basic = FuncBodyBasicLowerBox.lower(func_name, box_name, params_arr, body_str)
if basic != null { return basic }
}
// Check for Return statement
local ret_idx = JsonFragBox.index_of_from(body_str, "\"type\":\"Return\"", 0)
if ret_idx < 0 { return null }
@ -645,6 +658,21 @@ static box FuncLoweringBox {
local funcs_idx = JsonFragBox.index_of_from(mir_str, "\"functions\":[", 0)
if funcs_idx < 0 { return mir_json }
// Optional: require presence of a main function (guarded by env)
{
local req = env.get("HAKO_MIR_BUILDER_REQUIRE_MAIN")
if req != null && ("" + req) == "1" {
// Minimal check: look for a function with name "main"
local main_idx = JsonFragBox.index_of_from(mir_str, "\"name\":\"main\"", funcs_idx)
if main_idx < 0 {
if env.get("HAKO_SELFHOST_TRACE") == "1" {
print("[builder/funcs:fail:no-main]")
}
return mir_json
}
}
}
// Find first function's closing }
local first_func_start = funcs_idx + 13 // skip "functions":[
local brace_depth = 0

View File

@ -12,8 +12,8 @@ static box BuilderConfigBox {
}
// Trace controls
trace_enabled() { return self._is_on("HAKO_MIR_BUILDER_TRACE") }
debug_enabled() { return self._is_on("HAKO_MIR_BUILDER_DEBUG") || self._is_on("NYASH_CLI_VERBOSE") }
trace_enabled() { return me._is_on("HAKO_MIR_BUILDER_TRACE") }
debug_enabled() { return me._is_on("HAKO_MIR_BUILDER_DEBUG") || me._is_on("NYASH_CLI_VERBOSE") }
// Registry and internal lowers
internal_on() {
@ -29,10 +29,10 @@ static box BuilderConfigBox {
registry_only() { return env.get("HAKO_MIR_BUILDER_REGISTRY_ONLY") }
// Loop/JsonFrag related
loop_jsonfrag_on() { return self._is_on("HAKO_MIR_BUILDER_LOOP_JSONFRAG") }
loop_force_jsonfrag_on() { return self._is_on("HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG") }
jsonfrag_normalize_on() { return self._is_on("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE") }
skip_loops_on() { return self._is_on("HAKO_MIR_BUILDER_SKIP_LOOPS") }
loop_jsonfrag_on() { return me._is_on("HAKO_MIR_BUILDER_LOOP_JSONFRAG") }
loop_force_jsonfrag_on() { return me._is_on("HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG") }
jsonfrag_normalize_on() { return me._is_on("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE") }
skip_loops_on() { return me._is_on("HAKO_MIR_BUILDER_SKIP_LOOPS") }
// Return-mode for loop adapter: "string" (default) | "map"
loop_adapter_return_mode() {

View File

@ -47,8 +47,8 @@ static box LoopOptsBox {
if BuilderConfigBox.jsonfrag_normalize_on() == 1 { mir = JsonFragNormalizerBox.normalize_all(mir) }
local mode = BuilderConfigBox.loop_adapter_return_mode()
if mode == "map" {
local m = self.new_map()
m = self.put(m, "mir", mir)
local m = me.new_map()
m = me.put(m, "mir", mir)
return m
}
return mir
@ -74,8 +74,8 @@ static box LoopOptsBox {
// Return mode: default string, optionally wrap into Map
local mode = BuilderConfigBox.loop_adapter_return_mode()
if mode == "map" {
local m = self.new_map()
m = self.put(m, "mir", mir)
local m = me.new_map()
m = me.put(m, "mir", mir)
return m
}
return mir

View File

@ -20,26 +20,26 @@ static box LowerLoopCountParamBox {
local varname = LoopScanBox.find_loop_var_name(s, k_cmp)
if varname == null { return null }
// Local <varname> = (Int init | Var initName)
// Local <varname> = (Int start_value | Var startName)
local k_local_i = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", 0)
if k_local_i < 0 { return null }
if JsonFragBox.index_of_from(s, "\"name\":\"" + varname + "\"", k_local_i) < 0 { return null }
local init = null
local start_value = null
{
local k_init_int = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", k_local_i)
local k_loop_next = JsonFragBox.index_of_from(s, "\"type\":\"Loop\"", k_local_i)
if k_init_int >= 0 && (k_loop_next < 0 || k_init_int < k_loop_next) {
local kv_i = JsonFragBox.index_of_from(s, "\"value\":", k_init_int); if kv_i >= 0 { init = JsonFragBox.read_int_after(s, kv_i + 8) }
local kv_i = JsonFragBox.index_of_from(s, "\"value\":", k_init_int); if kv_i >= 0 { start_value = JsonFragBox.read_int_after(s, kv_i + 8) }
} else {
local k_init_var = JsonFragBox.index_of_from(s, "\"type\":\"Var\"", k_local_i)
if k_init_var >= 0 && (k_loop_next < 0 || k_init_var < k_loop_next) {
local kn_i = JsonFragBox.index_of_from(s, "\"name\":\"", k_init_var); if kn_i < 0 { return null }
local vname = JsonFragBox.read_string_after(s, kn_i)
if vname != null { init = PatternUtilBox.find_local_int_before(s, vname, k_local_i) }
if vname != null { start_value = PatternUtilBox.find_local_int_before(s, vname, k_local_i) }
}
}
}
if init == null { return null }
if start_value == null { return null }
// Loop Compare normalize: accept < / <= / > / >= with Var(varname) on either side
// op: accept '<'/'<=' with i on lhs; '>'/'>=' with i on lhs (descending); swapped '>'/'>=' with i on rhs (ascending)
local k_op = JsonFragBox.index_of_from(s, "\"op\":", k_cmp); if k_op < 0 { return null }
@ -140,7 +140,7 @@ static box LowerLoopCountParamBox {
// Adapter集中JsonFrag化の踏み台
local opts = LoopOptsBox.new_map()
opts = LoopOptsBox.put(opts, "mode", "count")
opts = LoopOptsBox.put(opts, "init", init)
opts = LoopOptsBox.put(opts, "init", start_value)
opts = LoopOptsBox.put(opts, "limit", limit)
opts = LoopOptsBox.put(opts, "step", step)
opts = LoopOptsBox.put(opts, "cmp", cmp)

View File

@ -8,6 +8,7 @@ builder.func_lowering = "builder/func_lowering.hako"
builder.MirBuilderBox = "builder/MirBuilderBox.hako"
builder.MirBuilderMinBox = "builder/MirBuilderMinBox.hako"
builder.pattern_registry = "builder/pattern_registry.hako"
builder.func_body.basic_lower_box = "builder/func_body/basic_lower_box.hako"
# MIR builder internal modules
builder.internal.prog_scan_box = "builder/internal/prog_scan_box.hako"

View File

@ -29,13 +29,13 @@ static box HakoCli {
@cmd = "" + cmd_raw
if cmd == "run" {
return self.cmd_run(args)
return me.cmd_run(args)
} else if cmd == "build" {
return self.cmd_build(args)
return me.cmd_build(args)
} else if cmd == "emit" {
return self.cmd_emit(args)
return me.cmd_emit(args)
} else if cmd == "check" {
return self.cmd_check(args)
return me.cmd_check(args)
}
print("[hakorune] unknown command: " + cmd)
@ -63,7 +63,7 @@ static box HakoCli {
@sub_raw = args.get(1)
@sub = "" + sub_raw
if sub == "exe" {
return self.cmd_build_exe(args)
return me.cmd_build_exe(args)
}
print("[hakorune] build: unknown subcommand: " + sub)
@ -86,7 +86,7 @@ static box HakoCli {
@source_path = null
@i = 2
while i < argc {
loop(i < argc) {
@arg_raw = args.get(i)
@arg = "" + arg_raw
if arg == "-o" || arg == "--out" {
@ -116,66 +116,53 @@ static box HakoCli {
return 91
}
// Read Hako source
@fb = new FileBox()
@ok = fb.open(source_path, "r")
if ok != 1 {
print("[hakorune] build exe: failed to open source " + source_path)
return 91
}
@src = fb.read()
fb.close()
if src == null || src == "" {
print("[hakorune] build exe: source is empty")
return 91
}
local tag = "[hakorune] build exe"
local src = me._read_file(tag, source_path)
if src == null { return 91 }
// .hako → Program(JSON v0)
@prog = BuildBox.emit_program_json_v0(src, null)
local prog = BuildBox.emit_program_json_v0(src, null)
if prog == null {
print("[hakorune] build exe: BuildBox returned null")
return 91
}
@ps = "" + prog
local ps = "" + prog
if ps.indexOf("\"version\":0") < 0 || ps.indexOf("\"kind\":\"Program\"") < 0 {
print("[hakorune] build exe: unexpected Program(JSON) output (missing version/kind)")
return 91
}
// Program(JSON v0) → MIR(JSON)
@mir = MirBuilderBox.emit_from_program_json_v0(ps, null)
local mir = MirBuilderBox.emit_from_program_json_v0(ps, null)
if mir == null {
print("[hakorune] build exe: MirBuilderBox returned null")
return 91
}
@ms = "" + mir
local ms = "" + mir
// MIR(JSON) → object via env.codegen.emit_object
@emit_args = new ArrayBox()
local emit_args = new ArrayBox()
emit_args.push(ms)
@obj = hostbridge.extern_invoke("env.codegen", "emit_object", emit_args)
local obj = hostbridge.extern_invoke("env.codegen", "emit_object", emit_args)
if obj == null || ("" + obj) == "" {
print("[hakorune] build exe: env.codegen.emit_object failed")
return 91
}
@obj_path = "" + obj
local obj_path = "" + obj
// object → EXE via env.codegen.link_object
@link_args = new ArrayBox()
local link_args = new ArrayBox()
link_args.push(obj_path)
if out_path != null && out_path != "" {
link_args.push(out_path)
}
@exe = hostbridge.extern_invoke("env.codegen", "link_object", link_args)
local exe = hostbridge.extern_invoke("env.codegen", "link_object", link_args)
if exe == null || ("" + exe) == "" {
print("[hakorune] build exe: env.codegen.link_object failed")
return 91
}
@exe_path = "" + exe
local exe_path = "" + exe
if quiet != 1 {
print("[hakorune] build exe: " + exe_path)
}
return 0
}
@ -192,9 +179,9 @@ static box HakoCli {
@sub_raw = args.get(1)
@sub = "" + sub_raw
if sub == "mir-json" {
return self.cmd_emit_mir_json(args)
return me.cmd_emit_mir_json(args)
} else if sub == "program-json" {
return self.cmd_emit_program_json(args)
return me.cmd_emit_program_json(args)
}
print("[hakorune] emit: unknown subcommand: " + sub)
@ -212,20 +199,20 @@ static box HakoCli {
return 92
}
@path = null
@out_path = null
@quiet = 0
local path = null
local out_path = null
local quiet = 0
@i = 2
while i < argc {
@arg_raw = args.get(i)
@arg = "" + arg_raw
local i = 2
loop(i < argc) {
local arg_raw = args.get(i)
local arg = "" + arg_raw
if arg == "-o" || arg == "--out" {
if i + 1 >= argc {
print("[hakorune] emit program-json: -o/--out requires a path")
return 92
}
@op_raw = args.get(i + 1)
local op_raw = args.get(i + 1)
out_path = "" + op_raw
i = i + 2
} else if arg == "--quiet" {
@ -247,46 +234,26 @@ static box HakoCli {
return 92
}
// Read Hako source from file
@fb = new FileBox()
@ok = fb.open(path, "r")
if ok != 1 {
print("[hakorune] emit program-json: failed to open " + path)
return 92
}
@src = fb.read()
fb.close()
if src == null || src == "" {
print("[hakorune] emit program-json: source is empty")
return 92
}
local tag = "[hakorune] emit program-json"
local src = me._read_file(tag, path)
if src == null { return 92 }
// Compile to Program(JSON v0) via BuildBox
@prog = BuildBox.emit_program_json_v0(src, null)
local prog = BuildBox.emit_program_json_v0(src, null)
if prog == null {
print("[hakorune] emit program-json: BuildBox returned null")
return 92
}
// Minimal validation: require Program(version 0)
@ps = "" + prog
local ps = "" + prog
if ps.indexOf("\"version\":0") < 0 || ps.indexOf("\"kind\":\"Program\"") < 0 {
print("[hakorune] emit program-json: unexpected output (missing version/kind)")
return 92
}
if out_path != null && out_path != "" {
@out_fb = new FileBox()
@ok2 = out_fb.open(out_path, "w")
if ok2 != 1 {
print("[hakorune] emit program-json: failed to open output " + out_path)
if me._write_file(tag, out_path, ps, quiet) != 1 {
return 92
}
out_fb.write(ps)
out_fb.close()
if quiet != 1 {
print("[hakorune] emit program-json: written " + out_path)
}
} else {
print(ps)
}
@ -298,23 +265,23 @@ static box HakoCli {
// - Program(JSON v0) から MIR(JSON) を生成する経路に加えて、
// .hako ソースから直接 Program(JSON v0)→MIR(JSON) まで進める経路もサポートする。
method cmd_emit_mir_json(args){
@argc = 0
local argc = 0
if args { argc = args.size() }
@prog_path = null
@source_path = null
@out_path = null
@quiet = 0
@i = 2
while i < argc {
@arg_raw = args.get(i)
@arg = "" + arg_raw
local prog_path = null
local source_path = null
local out_path = null
local quiet = 0
local i = 2
loop(i < argc) {
local arg_raw = args.get(i)
local arg = "" + arg_raw
if arg == "--from-program-json" {
if i + 1 >= argc {
print("[hakorune] emit mir-json: --from-program-json requires a path")
return 92
}
@p_raw = args.get(i + 1)
local p_raw = args.get(i + 1)
prog_path = "" + p_raw
i = i + 2
} else if arg == "-o" || arg == "--out" {
@ -322,14 +289,13 @@ static box HakoCli {
print("[hakorune] emit mir-json: -o/--out requires a path")
return 92
}
@op_raw = args.get(i + 1)
local op_raw = args.get(i + 1)
out_path = "" + op_raw
i = i + 2
} else if arg == "--quiet" {
quiet = 1
i = i + 1
} else {
// Interpret as <source.hako> when not already set
if source_path == null {
source_path = arg
i = i + 1
@ -340,54 +306,32 @@ static box HakoCli {
}
}
// Prevent ambiguous usage
if prog_path != null && prog_path != "" && source_path != null && source_path != "" {
print("[hakorune] emit mir-json: specify either --from-program-json or <source.hako>, not both")
return 92
}
@prog_json = null
if (prog_path == null || prog_path == "") && (source_path == null || source_path == "") {
print("[hakorune] emit mir-json: require --from-program-json <file> or <source.hako>")
return 92
}
local tag = "[hakorune] emit mir-json"
local prog_json = null
if prog_path != null && prog_path != "" {
// Read Program(JSON v0) from file
@fb = new FileBox()
@ok = fb.open(prog_path, "r")
if ok != 1 {
print("[hakorune] emit mir-json: failed to open " + prog_path)
return 92
}
@p = fb.read()
fb.close()
if p == null || p == "" {
print("[hakorune] emit mir-json: program JSON is empty")
return 92
}
prog_json = p
local prog_text = me._read_file(tag, prog_path)
if prog_text == null { return 92 }
prog_json = prog_text
} else {
// Expect .hako source path
if source_path == null || source_path == "" {
print("[hakorune] emit mir-json: require --from-program-json <file> or <source.hako>")
return 92
}
@fb2 = new FileBox()
@ok2 = fb2.open(source_path, "r")
if ok2 != 1 {
print("[hakorune] emit mir-json: failed to open source " + source_path)
return 92
}
@src = fb2.read()
fb2.close()
if src == null || src == "" {
print("[hakorune] emit mir-json: source is empty")
return 92
}
// Compile to Program(JSON v0) via BuildBox
@prog = BuildBox.emit_program_json_v0(src, null)
local src = me._read_file(tag, source_path)
if src == null { return 92 }
local prog = BuildBox.emit_program_json_v0(src, null)
if prog == null {
print("[hakorune] emit mir-json: BuildBox returned null")
return 92
}
@ps = "" + prog
local ps = "" + prog
if ps.indexOf("\"version\":0") < 0 || ps.indexOf("\"kind\":\"Program\"") < 0 {
print("[hakorune] emit mir-json: unexpected Program(JSON) output (missing version/kind)")
return 92
@ -395,27 +339,17 @@ static box HakoCli {
prog_json = ps
}
// Convert Program(JSON v0) → MIR(JSON) via MirBuilderBox
@mir = MirBuilderBox.emit_from_program_json_v0(prog_json, null)
local mir = MirBuilderBox.emit_from_program_json_v0(prog_json, null)
if mir == null {
print("[hakorune] emit mir-json: MirBuilderBox returned null")
return 92
}
// Success: emit MIR(JSON) to stdout or file
@ms = "" + mir
local ms = "" + mir
if out_path != null && out_path != "" {
@out_fb = new FileBox()
@ok3 = out_fb.open(out_path, "w")
if ok3 != 1 {
print("[hakorune] emit mir-json: failed to open output " + out_path)
if me._write_file(tag, out_path, ms, quiet) != 1 {
return 92
}
out_fb.write(ms)
out_fb.close()
if quiet != 1 {
print("[hakorune] emit mir-json: written " + out_path)
}
} else {
print(ms)
}
@ -423,18 +357,56 @@ static box HakoCli {
return 0
}
// hakorune check <entry.hako>
// - Reserved for future static checks (syntax/using/subset).
method cmd_check(args){
print("[hakorune] check: not implemented yet")
return 93
}
// Helpers for file I/O (Phase 25.1a: consolidate StageB friendly patterns)
method _read_file(tag, path){
if path == null || path == "" {
print(tag + ": source path is required")
return null
}
local fb = new FileBox()
local ok = fb.open(path, "r")
if ok != 1 {
print(tag + ": failed to open " + path)
return null
}
local content = fb.read()
fb.close()
if content == null || content == "" {
print(tag + ": source is empty")
return null
}
return "" + content
}
method _write_file(tag, path, content, quiet){
if path == null || path == "" { return 1 }
local fb = new FileBox()
local ok = fb.open(path, "w")
if ok != 1 {
print(tag + ": failed to open output " + path)
return 0
}
fb.write(content)
fb.close()
if quiet != 1 {
print(tag + ": written " + path)
}
return 1
}
}
static box Main {
// Main entrypoint for Stage1 hakorune CLI.
// args: raw argv array (excluding executable name).
main(args){
method main(args){
@cli = new HakoCli()
return cli.run(args)
}

View File

@ -12,7 +12,7 @@ static box LoopFormBox {
// i = i + 1;
// }
// Builds LoopForm structure with Header/Latch PHI + Exit PHI (continue/break aware)
loop_counter(limit, skip_value, break_value) {
method loop_counter(limit, skip_value, break_value) {
if skip_value == null { skip_value = 2 }
if break_value == null { break_value = limit }
@ -109,7 +109,7 @@ static box LoopFormBox {
// body: r12 = i + step, jump latch
// latch: jump header (PHI incoming from body)
// exit: ret r10
loop_count(limit) {
method loop_count(limit) {
// Preheader
local pre = new ArrayBox()
pre.push(MirSchemaBox.inst_const(1, 0))
@ -148,10 +148,10 @@ static box LoopFormBox {
// loop_count_param — counting loop with init/step parameters
// Returns final i value, starting from init, incremented by step while i < limit
loop_count_param(init, limit, step) {
method build_loop_count_param(start_value, limit, step) {
// Preheader
local pre = new ArrayBox()
pre.push(MirSchemaBox.inst_const(1, init))
pre.push(MirSchemaBox.inst_const(1, start_value))
pre.push(MirSchemaBox.inst_const(2, limit))
pre.push(MirSchemaBox.inst_const(3, step))
pre.push(MirSchemaBox.inst_jump(1))
@ -185,13 +185,13 @@ static box LoopFormBox {
// Extended param variant: allow custom compare op ("Lt"/"Le"/"Gt"/"Ge") via opts
// and negative step (handled by passing negative string for step)
loop_count_param_ex(init, limit, step, cmp) {
method build_loop_count_param_ex(start_value, limit, step, cmp) {
local cmpop = "" + cmp
if cmpop == null || cmpop == "" { cmpop = "Lt" }
// Preheader
local pre = new ArrayBox()
pre.push(MirSchemaBox.inst_const(1, init))
pre.push(MirSchemaBox.inst_const(1, start_value))
pre.push(MirSchemaBox.inst_const(2, limit))
pre.push(MirSchemaBox.inst_const(3, step))
pre.push(MirSchemaBox.inst_jump(1))
@ -227,18 +227,18 @@ static box LoopFormBox {
// mode:
// - "count" : counting loop that returns final i (uses loop_count)
// - "sum_bc" : sum with break/continue sentinels (uses loop_counter)
build(mode, limit, skip_value, break_value) {
method build(mode, limit, skip_value, break_value) {
local m = "" + mode
if m == "count" {
return me.loop_count(limit)
}
if m == "count_param" {
// Here, skip_value carries init and break_value carries step (temporary param slots)
local init = skip_value
local start_value = skip_value
local step = break_value
if init == null { init = 0 }
if start_value == null { start_value = 0 }
if step == null { step = 1 }
return me.loop_count_param(init, limit, step)
return me.build_loop_count_param(start_value, limit, step)
}
if m == "sum_bc" {
if skip_value == null { skip_value = 2 }
@ -250,20 +250,20 @@ static box LoopFormBox {
}
// Map-based builder: build2({ mode, init, limit, step, skip, break })
build2(opts) {
method build2(opts) {
if opts == null { return null }
local mode = "" + opts.get("mode")
local init = opts.get("init")
local start_value = opts.get("init")
local limit = opts.get("limit")
local step = opts.get("step")
local skip_v = opts.get("skip")
local break_v = opts.get("break")
if mode == "count" {
if init == null { init = 0 }
if start_value == null { start_value = 0 }
if step == null { step = 1 }
local cmp = opts.get("cmp") // optional: "Lt"/"Le"/"Gt"/"Ge"
if cmp == null { cmp = "Lt" }
return me.loop_count_param_ex(init, limit, step, cmp)
return me.build_loop_count_param_ex(start_value, limit, step, cmp)
}
if mode == "sum_bc" { return me.loop_counter(limit, skip_v, break_v) }
print("[loopform/unsupported-mode] " + mode)