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:
@ -10,9 +10,10 @@
|
||||
// - Recommended: Dev/CI は wrapper(tools/hakorune_emit_mir.sh)経由で Program→MIR を行い、失敗時は Gate‑C に自動委譲する。
|
||||
|
||||
// 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 {
|
||||
|
||||
@ -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 ""
|
||||
}
|
||||
|
||||
150
lang/src/compiler/entry/using_resolver_box.hako
Normal file
150
lang/src/compiler/entry/using_resolver_box.hako
Normal file
@ -0,0 +1,150 @@
|
||||
// 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.
|
||||
//
|
||||
// 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 } }
|
||||
@ -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"
|
||||
|
||||
@ -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 → Fail‑Fast tag
|
||||
print("[mirbuilder/delegate/missing] no provider; enable HAKO_MIR_BUILDER_DELEGATE=1")
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Responsibility
|
||||
- Convert Stage‑B 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 Fail‑Fast; 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 (Fail‑Fast, 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 selected(Runner/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` — dev‑only: minimal loop MIR を強制生成(テスト用)
|
||||
- `HAKO_MIR_BUILDER_REQUIRE_MAIN=1` — inject_funcs で `"name":"main"` を持たない MIR に defs を追加するのを禁止(既定=0)
|
||||
|
||||
Notes
|
||||
- Box‑First policy: boundary/contract first, then implementation. Keep tags stable; no silent fallback.
|
||||
- Large payloads: implementation may stream/json-scan later; initial version is string‑based.
|
||||
- Box‑First policy: define the interface and tags first, then evolve implementation behind the same contract.
|
||||
- Large payloads: implementation is currently string/JsonFrag‑based; later phases may stream or segment JSON, but I/F は維持する。
|
||||
- Phase 25.1b では `FuncLoweringBox` との連携を拡張し、「main + defs」構造を前提とした multi‑function MIR の土台を整える(defs‑only モジュールは Fail‑Fast)。
|
||||
|
||||
344
lang/src/mir/builder/func_body/basic_lower_box.hako
Normal file
344
lang/src/mir/builder/func_body/basic_lower_box.hako
Normal 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 + "]}]}"
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 Stage‑B 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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user