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 } }
|
||||
Reference in New Issue
Block a user