// 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) // Returns ArrayBox of method definitions method scan_functions(source, box_name) { local methods = new ArrayBox() local s = "" + source local n = s.length() local i = 0 loop(i < n) { // Search for "method " pattern 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 // skip "method " // 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) // Skip main (already extracted as body) if 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) // 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) // 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) local body_json = null if method_body.length() > 0 { local p = new ParserBox() p.stage3_enable(1) 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) def.set("params", params) def.set("body_json", body_json) def.set("box", box_name) methods.push(def) } i = rbrace } return methods } // 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) { 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 } // 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) 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 from string method _trim(s) { 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 } } // 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 e > b { return str.substring(b, e) } return "" } }