// FuncScannerBox — Function definition scanner for Stage-B compiler // Policy: Extract method definitions from Hako source (conservative, minimal) // Toggle: HAKO_STAGEB_FUNC_SCAN=1 // Scope: method (params) { ... } outside of main (same box Main) // Output: [{"name":"","params":[...],"body_json":"","box":"Main"}] using lang.compiler.parser.box as ParserBox static box FuncScannerBox { // 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) { // source が null の場合はスキャン対象がないので空配列を返すよ。 if source == null { return new ArrayBox() } 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 " local k = -1 { local pat = "method " local m = pat.length() local j = i loop(j + m <= n) { if s.substring(j, j + m) == pat { k = j break } j = j + 1 } } if k < 0 { break } i = k + 7 // Extract method name (alphanumeric until '(') local name_start = i local name_end = -1 { local j = i loop(j < n) { local ch = s.substring(j, j + 1) if ch == "(" { name_end = j break } j = j + 1 } } if name_end < 0 { break } local method_name = s.substring(name_start, name_end) if skip_main == 1 && box_name == "Main" && method_name == "main" { i = name_end continue } // Find '(' after name local lparen = name_end // Find matching ')' for params (skip strings) local rparen = -1 { local j = lparen + 1 local in_str = 0 local esc = 0 loop(j < n) { local ch = s.substring(j, j + 1) if in_str == 1 { if esc == 1 { esc = 0 j = j + 1 continue } if ch == "\\" { esc = 1 j = j + 1 continue } if ch == "\"" { in_str = 0 j = j + 1 continue } j = j + 1 continue } if ch == "\"" { in_str = 1 j = j + 1 continue } if ch == ")" { rparen = j break } j = j + 1 } } if rparen < 0 { break } // 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 { local j = rparen + 1 local in_str = 0 local esc = 0 loop(j < n) { local ch = s.substring(j, j + 1) if in_str == 1 { if esc == 1 { esc = 0 j = j + 1 continue } if ch == "\\" { esc = 1 j = j + 1 continue } if ch == "\"" { in_str = 0 j = j + 1 continue } j = j + 1 continue } if ch == "\"" { in_str = 1 j = j + 1 continue } if ch == "{" { lbrace = j break } j = j + 1 } } if lbrace < 0 { break } // Find matching '}' (balanced) local rbrace = -1 { local depth = 0 local j = lbrace local in_str = 0 local esc = 0 loop(j < n) { local ch = s.substring(j, j + 1) if in_str == 1 { if esc == 1 { esc = 0 j = j + 1 continue } if ch == "\\" { esc = 1 j = j + 1 continue } if ch == "\"" { in_str = 0 j = j + 1 continue } j = j + 1 continue } if ch == "\"" { in_str = 1 j = j + 1 continue } if ch == "{" { depth = depth + 1 j = j + 1 continue } if ch == "}" { depth = depth - 1 j = j + 1 if depth == 0 { rbrace = j - 1 break } continue } j = j + 1 } } if rbrace < 0 { break } // Extract method body (inside braces) local method_body = s.substring(lbrace + 1, rbrace) method_body = me._strip_comments(method_body) method_body = me._trim(method_body) // Parse method body to JSON (statement list) local body_json = null if method_body.length() > 0 { local p = new ParserBox() p.stage3_enable(1) body_json = p.parse_program2(method_body) } if body_json != null && body_json != "" { local def = new MapBox() def.set("name", method_name) def.set("params", params) def.set("body_json", body_json) def.set("box", box_name) methods.push(def) } i = rbrace } 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() local pstr = "" + params_str local pn = pstr.length() local pstart = 0 loop(pstart < pn) { // Skip whitespace loop(pstart < pn) { local ch = pstr.substring(pstart, pstart + 1) if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { pstart = pstart + 1 } else { break } } if pstart >= pn { break } // Find next comma or end local pend = pstart loop(pend < pn) { local ch = pstr.substring(pend, pend + 1) if ch == "," { break } pend = pend + 1 } // Extract param name (trim) local pname = pstr.substring(pstart, pend) pname = me._trim(pname) if pname.length() > 0 { params.push(pname) } pstart = pend + 1 } return params } // Helper: strip comments from source method _strip_comments(source) { // source が null の場合はそのまま空文字として扱う(コメント除去する対象がないだけ) if source == null { return "" } local s = "" + source local out = "" local i = 0 local n = s.length() 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 out = out + ch } 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 { out = out + ch esc = 0 i = i + 1 continue } if ch == "\\" { out = out + ch esc = 1 i = i + 1 continue } if ch == "\"" { out = out + ch in_str = 0 i = i + 1 continue } out = out + ch i = i + 1 continue } 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) if ch2 == "/" { in_line = 1 i = i + 2 continue } if ch2 == "*" { in_block = 1 i = i + 2 continue } } out = out + ch i = i + 1 } return out } // Helper: trim whitespace method _trim(s) { if s == null { return "" } local str = "" + s local n = str.length() local b = 0 loop(b < n) { local ch = str.substring(b, b + 1) if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { b = b + 1 } else { break } } local e = n loop(e > b) { local ch = str.substring(e - 1, e) if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break } } if e > b { return str.substring(b, e) } return "" } }