Phase 21.6 solidification: chain green (return/binop/loop/call); add Phase 21.7 normalization plan (methodize static boxes). Update CURRENT_TASK.md and docs.

This commit is contained in:
nyash-codex
2025-11-11 22:35:45 +09:00
parent 52b62c5772
commit 9e2fa1e36e
19 changed files with 1309 additions and 35 deletions

View File

@ -12,6 +12,7 @@
using sh_core as StringHelpers // Required: ParserStringUtilsBox depends on this (using chain unresolved)
using "hako.compiler.entry.bundle_resolver" as BundleResolver
using lang.compiler.parser.box as ParserBox
using lang.compiler.entry.func_scanner as FuncScannerBox
// Note: Runner resolves entry as Main.main by default.
// Provide static box Main with method main(args) as the entry point.
@ -339,6 +340,66 @@ static box Main {
// Bridge(JSON v0) が Program v0 を受け取り MIR に lowering するため、ここでは AST(JSON v0) を出力する。
// 既定で MIR 直出力は行わない(重い経路を避け、一行出力を保証)。
local ast_json = p.parse_program2(body_src)
// 6.5) Dev-toggle: scan for function definitions (box Main { 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)
// Output: inject "defs":[{"name":"<name>","params":[...],"body":[...], "box":"Main"}] to Program JSON
local defs_json = ""
{
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")
// Build defs JSON array
if methods.length() > 0 {
defs_json = ",\"defs\":["
local mi = 0
local mn = methods.length()
loop(mi < mn) {
local def = methods.get(mi)
local mname = "" + def.get("name")
local mparams = def.get("params")
local mbody = "" + def.get("body_json")
local mbox = "" + def.get("box")
// Build params array JSON
local params_arr = "["
local pi = 0
local pn = mparams.length()
loop(pi < pn) {
if pi > 0 { params_arr = params_arr + "," }
params_arr = params_arr + "\"" + ("" + mparams.get(pi)) + "\""
pi = pi + 1
}
params_arr = params_arr + "]"
if mi > 0 { defs_json = defs_json + "," }
defs_json = defs_json + "{\"name\":\"" + mname + "\",\"params\":" + params_arr + ",\"body\":" + mbody + ",\"box\":\"" + mbox + "\"}"
mi = mi + 1
}
defs_json = defs_json + "]"
}
}
}
// 7) Inject defs into Program JSON if available
if defs_json != "" && defs_json.length() > 0 {
// Insert defs before closing } of Program JSON
local ajson = "" + ast_json
local close_pos = -1
{
local j = ajson.length() - 1
loop(j >= 0) {
if ajson.substring(j, j + 1) == "}" { close_pos = j break }
j = j - 1
}
}
if close_pos >= 0 {
ast_json = ajson.substring(0, close_pos) + defs_json + ajson.substring(close_pos, ajson.length())
}
}
print(ast_json)
return 0
}

View File

@ -0,0 +1,262 @@
// 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 <name>(params) { ... } outside of main (same box Main)
// Output: [{"name":"<name>","params":[...],"body_json":"<Program 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 ""
}
}

View File

@ -35,6 +35,12 @@ builder.ssa.cond_inserter = "builder/ssa/cond_inserter.hako"
builder.rewrite.special = "builder/rewrite/special.hako"
builder.rewrite.known = "builder/rewrite/known.hako"
# Entry point modules (Phase 15 compiler infrastructure)
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"
[dependencies]
"selfhost.shared" = "^1.0.0"
"selfhost.vm" = "^1.0.0"