restore(lang/compiler): bring back lang/src/compiler from e917d400; add Hako index canaries and docs; implement Rust-side index operator (Array/Map get/set) with Fail‑Fast diagnostics

- restore: lang/src/compiler/** (parser/emit/builder/pipeline_v2) from e917d400
- docs: docs/development/selfhosting/index-operator-hako.md
- smokes(hako): tools/smokes/v2/profiles/quick/core/index_operator_hako.sh (opt-in)
- smokes(vm): adjust index_operator_vm.sh for semicolon gate + stable error text
- rust/parser: allow IndexExpr and assignment LHS=Index; postfix parse LBRACK chain
- rust/builder: lower arr/map index to BoxCall get/set; annotate array/map literals; Fail‑Fast for unsupported types
- CURRENT_TASK: mark Rust side done; add Hako tasks checklist

Note: files disappeared likely due to branch FF to a lineage without lang/src/compiler; no explicit delete commit found. Added anchor checks and suggested CI guard in follow-up.
This commit is contained in:
nyash-codex
2025-10-31 20:18:39 +09:00
parent 86fd03afe8
commit 5e3d9e7ae4
86 changed files with 6214 additions and 20 deletions

View File

@ -0,0 +1,60 @@
Pipeline V2 — BoxFirst Extract→Emit
Scope
- Selfhost compilerの emitonly 経路Stage1 JSON → MIR(JSON v0/v1))を、箱の責務で明確化する。
- Parser/Resolver/Runtime には影響しない。既定挙動は不変devフラグ/引数でのみ起動)。
Modules責務
- compare_extract_box.hako
- 目的: Stage1 JSON から Compare(lhs, rhs, op) を堅牢に抽出(整数のみ)。
- API:
- extract_return_compare_ints(ast_json) -> {cmp,lhs,rhs} | null
- extract_if_compare_ints(ast_json) -> {cmp,lhs,rhs} | null
- 失敗時: null呼び出し側でフォールバック
- emit_compare_box.hako
- 目的: Compare の MIR(JSON v0) 生成。
- API:
- emit_compare_ret(lhs,rhs,cmp,trace) -> JSON v0compare→ret
- emit_compare_cfg3(lhs,rhs,cmp,materialize,trace) -> JSON v0branch/jump/ret; materialize=2でcopy材化想定
- 失敗時: なし(入力は抽出済み前提)。
- pipeline.hakoflow PipelineV2
- 役割: Extract系→Emit系の配線。Call/Method/New/Compare/If(Compare) を段階的に対応。
- フラグ:
- prefer_cfg=0: Returnonlycompare→ret
- prefer_cfg=1: CFGbranch/jump/ret
- prefer_cfg=2: CFG+材化(将来の copy after PHI を想定; 現状は等価分岐)
- trace: 1で最小トレース[trace]を出力。既定は0静音
I/O最小仕様
- 入力: Stage1 JSONReturn/If/Call/Method/New の最小形)。負数/空白は RegexFlow で吸収。
- Call/Method/New の `args` 配列は `{"type":"Int","value":…}` のみを許容する。NamedArg/DefaultArg/VarArg など Int 以外が混在した場合は Null を返し、呼び出し側で FailFastStage0/Resolver 側で脱糖すること)。
- 出力: MIR(JSON v0)。将来 v1(MirCall) への直出力は lower_stage1_to_mir_v1 を併設dev用途
FailFast & Fallback
- 抽出箱は見つからない場合 null を返す。pipeline は legacy extractorStage1ExtractFlowでフォールバックする。
- 既定ONは変えないdev引数でのみ有効
Stage GuardStage2 / Stage3
- Stage2: Call/Method/New 向けの emit 手前ガード。Stage1 で弾いた NamedArg / DefaultArg / VarArg などが混入した場合は Null で FailFast し、呼び出し側に返す。
- Stage3: MIR(JSON) 生成器。Stage2 の整形結果のみを受理し、`PipelineV2.lower_stage1_to_mir` が null を返した場合は Emit を実行しない。
- 代表スモーク
- Stage1 ガード: `selfhost_pipeline_v2_stage1_invalid_args_fail_vm.sh` / `..._named_default_fail_vm.sh` / `..._vararg_fail_vm.sh`
- Stage2/3 正常系: `selfhost_pipeline_v2_call_exec_vm.sh`, `selfhost_pipeline_v2_method_exec_vm.sh`, `selfhost_pipeline_v2_newbox_exec_vm.sh`
Testing
- quick/selfhost に compare/binop/call/method/new の代表スモークがある。Compare系は Returnonly と CFG をそれぞれ確認。
- MiniVMapps/selfhost/vm/boxes/mir_vm_min.nyashは最小仕様。算術/比較/CFGのみのスモークで品質を担保。
Notes
- 追加の Extract 箱Call/Method/Newを段階導入し、Stage1ExtractFlow の責務を縮小する計画。
- trace は既定OFF。--emit-trace 指定時のみ出力する。CI/quick は既定で静音。
WASM 開発ラインとの取り込み方針(注意)
- wasm-development ブランチは独立開発ライン。Selfhost 側には以下のみ注意して取り込む。
- 共有仕様MIR JSON 形状、PHI invariants、v1/v0 変換ポリシ)に関するドキュメントの同期。
- Python LLVM ハーネスの仕様更新点(値配線/PHI 正規化)を docs に反映し、実装取り込みは最小・可逆。
- Selfhost本フォルダの箱 API/入出力は変更せず、adapter で吸収MirJsonV1Adapter など)。
- 実装取り込みは小粒・局所・既定OFFのフラグ配下。quick が緑のままになる粒度で実施。
- 互換チェックは quick/integration の代表スモークで行い、重い検証は wasm ライン側で継続。

View File

@ -0,0 +1,33 @@
UsingResolverBox — PreResolve Using Declarations
Scope
- Manage alias→path and alias→namespace name maps for compiler pipeline (P2A).
- Consume lightweight JSON emitted by parser (UsingCollectorBox or ParserBox.add_using).
- Provide stable getters for later NamespaceBox (P2B) and Emit boxes.
Responsibilities
- Input forms:
- Path using: [{"name":"Alias","path":"apps/..../file.hako"}, ...]
- Namespace using: [{"name":"selfhost.vm.mir_min"}, ...] or [{"name":"Alias"}] when ParserBox.add_using(ns, alias) is used.
- Output/context:
- alias_paths: Alias → file path
- alias_names: Alias → namespace name (best effort; Alias itself when only ns is known)
- modules_map: NsName → file path (provided by caller; no file IO here)
NonResponsibilities
- Reading hako.toml or filesystem.
- Runtime using resolution. This is compileronly preresolution.
API (box methods)
- load_usings_json(usings_json)
- load_modules_json(modules_json)
- add_ns(alias, ns_name) / add_module(ns_name, path) / add_path(alias, path)
- resolve_path_alias(alias) -> path | null
- resolve_namespace_alias(alias) -> ns_name | null
- resolve_module_path_from_alias(alias) -> path | null
- to_context_json() -> {alias_paths,namespaces,modules}
Notes
- When UsingCollectorBox is used, namespace entries contain the full name in "name" and no alias. In that case alias_names[alias] will use the same string; callers may still override via add_ns(alias, ns).
- Keep this box pure and sideeffect free for easy testing.

View File

@ -0,0 +1,28 @@
// alias_preflight_box.hako — AliasPreflightBox
// Responsibility: Early check for using-alias heads in Stage1 names.
// Input: raw dotted label (e.g., "alias.something.method"), UsingResolverBox instance.
// Behavior: If dotted, verify alias head is known; otherwise print a stable one-line error and return 0.
// NonResponsibility: Namespace normalization or emit.
using "lang/src/compiler/pipeline_v2/using_resolver_box.hako" as UsingResolverBox
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
static box AliasPreflightBox {
// Returns 1 if OK, 0 if alias head is unresolved (prints error).
check_head(raw_label, r_state) {
if raw_label == null { return 1 }
if r_state == null { return 1 }
local s = "" + raw_label
local dot = RegexFlow.find_from(s, ".", 0)
if dot < 0 { return 1 }
local head = s.substring(0, dot)
if head == null || head == "" { return 1 }
if UsingResolverBox.resolve_namespace_alias(r_state, head) == null {
print("[ERROR] Unresolved using alias: " + head)
return 0
}
return 1
}
}
static box AliasPreflightMain { main(args) { return 0 } }

View File

@ -0,0 +1,11 @@
// BackendBox — backend tag holder (no execution in Phase 15.7)
box BackendBox {
name
birth(n) { if n == null { n = "vm" } me.name = n return 0 }
get_name() { return me.name }
// Stub execute: reserved for future; returns 0 for now
execute(_mir_json) { return 0 }
}
static box BackendStub { main(args) { return 0 } }

View File

@ -0,0 +1,12 @@
// CallExtractBox — Stage1 JSON から Return(Call name(args...)) を抽出(整数引数のみ)
// Delegation to Stage1IntArgsExtractBox (unified implementation)
using "lang/src/compiler/pipeline_v2/stage1_int_args_extract_box.hako" as Unified
static box CallExtractBox {
// Returns { name: String, args: [Int,...] } or null
extract_return_call_ints(ast_json) {
return Unified.extract_return_call_ints(ast_json)
}
}
static box CallExtractStub { main(args) { return 0 } }

View File

@ -0,0 +1,119 @@
// CompareExtractBox — Stage1 JSON から Compare(lhs,rhs,op) を堅牢に抽出(整数のみ)
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box CompareExtractBox {
// --- internal helpers (range & safe find) ---
_find_brace_range(s, around_pos) {
// Find object range that contains around_pos: search '{' backwards, then match '}' by depth
if around_pos < 0 { return null }
local start = around_pos
loop (start >= 0) {
local ch = s.substring(start, start+1)
if ch == "{" { break }
start = start - 1
}
if start < 0 { return null }
local i = start
local depth = 0
loop(true) {
local ch2 = s.substring(i, i+1)
if ch2 == "" { break }
if ch2 == "{" { depth = depth + 1 } else { if ch2 == "}" { depth = depth - 1 } }
if depth == 0 { return { "from": start, "to": i + 1 } }
i = i + 1
}
return null
}
_find_in_range(s, needle, start, endp) {
local p = RegexFlow.find_from(s, needle, start)
if p < 0 { return -1 }
if p >= endp { return -1 }
return p
}
// Returns MapBox {"cmp": String, "lhs": Int, "rhs": Int } or null when not found
extract_return_compare_ints(ast_json) {
if ast_json == null { return null }
// Find Return then Compare (whitespace耐性は RegexFlow に委譲)
local rq = RegexFlow.find_from(ast_json, "\"type\":\"Return\"", 0)
if rq < 0 { return null }
local cq = RegexFlow.find_from(ast_json, "\"type\":\"Compare\"", rq)
if cq < 0 { return null }
// op
local opk_pos = RegexFlow.find_from(ast_json, "\"op\":\"", cq)
if opk_pos < 0 { return null }
local opk_end = RegexFlow.find_from(ast_json, "\"", opk_pos + 6)
if opk_end < 0 { return null }
local cmp = ast_json.substring(opk_pos + 6, opk_end)
// lhs/rhs → 各 value の digits を抽出
local lhsp = RegexFlow.find_from(ast_json, "\"lhs\"", cq)
local rhsp = RegexFlow.find_from(ast_json, "\"rhs\"", cq)
if lhsp < 0 || rhsp < 0 { return null }
local lv = -1
local rv = -1
{
local vpos = RegexFlow.find_from(ast_json, "\"value\":", lhsp)
if vpos >= 0 {
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { lv = RegexFlow.to_int(ds) }
}
}
{
local vpos2 = RegexFlow.find_from(ast_json, "\"value\":", rhsp)
if vpos2 >= 0 {
local ds2 = RegexFlow.digits_from(ast_json, vpos2 + 8)
if ds2 != "" { rv = RegexFlow.to_int(ds2) }
}
}
if lv < 0 || rv < 0 { return null }
// Pack cmp/lhs/rhs into ArrayBox [cmp, lhs, rhs]
return [lv, rv, cmp]
}
// If(cond=Compare(lhs,rhs,op), then=[Return(Int 1)], else=[Return(Int 0)]) → MapBox {cmp,lhs,rhs} or null
extract_if_compare_ints(ast_json) {
if ast_json == null { return null }
// Find If first
local ip = RegexFlow.find_from(ast_json, "\"type\":\"If\"", 0)
if ip < 0 { return null }
// Find Compare after If
local cq = RegexFlow.find_from(ast_json, "\"type\":\"Compare\"", ip)
if cq < 0 { return null }
// Restrict to the Compare object range to avoid accidental matches
local range = me._find_brace_range(ast_json, cq)
if range == null { return null }
local rs = BoxHelpers.map_get(range, "from")
local re = BoxHelpers.map_get(range, "to")
// TEMP debug
print("DBG:cmp-range=" + ast_json.substring(rs, re))
// op
local opk_pos = me._find_in_range(ast_json, "\"op\":\"", rs, re)
if opk_pos < 0 { return null }
local opv_start = opk_pos + 6
local opk_end = me._find_in_range(ast_json, "\"", opv_start, re)
if opk_end < 0 { return null }
local cmp = ast_json.substring(opv_start, opk_end)
print("DBG:cmp-op=" + cmp)
// lhs
local lhsp = me._find_in_range(ast_json, "\"lhs\"", rs, re)
if lhsp < 0 { return null }
local vpos = me._find_in_range(ast_json, "\"value\":", lhsp, re)
if vpos < 0 { return null }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds == "" { return null }
local lv = RegexFlow.to_int(ds)
print("DBG:lhs=" + (""+lv))
// rhs
local rhsp = me._find_in_range(ast_json, "\"rhs\"", rs, re)
if rhsp < 0 { return null }
local vpos2 = me._find_in_range(ast_json, "\"value\":", rhsp, re)
if vpos2 < 0 { return null }
local ds2 = RegexFlow.digits_from(ast_json, vpos2 + 8)
if ds2 == "" { return null }
local rv = RegexFlow.to_int(ds2)
print("DBG:rhs=" + (""+rv))
return [lv, rv, cmp]
}
}
static box CompareExtractStub { main(args) { return 0 } }

View File

@ -0,0 +1,33 @@
// EmitBinopBox — binop の最小 MIR(JSON v0) 生成
using "lang/src/shared/json/mir_builder_min.hako" as MirJsonBuilderMin
static box EmitBinopBox {
_map_binop(opk) {
if opk == "+" { return "Add" }
if opk == "-" { return "Sub" }
if opk == "*" { return "Mul" }
if opk == "/" { return "Div" }
if opk == "%" { return "Mod" }
return opk
}
emit_binop(lhs, rhs, opk) { return EmitBinopBox.emit_binop2(lhs, rhs, opk, 0) }
emit_binop2(lhs, rhs, opk, trace) {
// dev-trace (minimal, env-gate想定)
if trace == 1 { print("[emit] binop lhs=" + lhs + " rhs=" + rhs + " op=" + opk) }
local kind = EmitBinopBox._map_binop(opk)
local b = MirJsonBuilderMin.make()
|> MirJsonBuilderMin.start_module()
|> MirJsonBuilderMin.start_function("main")
|> MirJsonBuilderMin.start_block(0)
|> MirJsonBuilderMin.add_const(1, lhs)
|> MirJsonBuilderMin.add_const(2, rhs)
|> MirJsonBuilderMin.add_binop(kind, 1, 2, 3)
|> MirJsonBuilderMin.add_ret(3)
|> MirJsonBuilderMin.end_all()
return MirJsonBuilderMin.to_string(b)
}
}
static box EmitBinopStub { main(args) { return 0 } }

View File

@ -0,0 +1,89 @@
// EmitCallBox — Return(Call name(int_args...)) を MIR(JSON v0) に最小変換
// 仕様: 各引数を const i64 として材化し、call を発行、dst を ret する。
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using lang.compiler.emit.common.header_emit as HeaderEmitBox
using "lang/src/shared/mir/mir_schema_box.hako" as MirSchemaBox
static box EmitCallBox {
_to_str(n) {
local v = n
if v == 0 { return "0" }
if v < 0 { return "-" + EmitCallBox._to_str(0 - v) }
local out = ""; local digits = "0123456789"
loop (v > 0) {
local d = v % 10
local ch = digits.substring(d, d+1)
out = ch + out
v = v / 10
}
return out
}
_quote(s) {
if s == null { return "\"\"" }
local out = ""; local i = 0; local n = s.size()
loop (i < n) {
local ch = call("String.substring/2", s, i, i+1)
if ch == "\\" { out = out + "\\\\" }
else { if ch == "\"" { out = out + "\\\"" } else {
if ch == "\n" { out = out + "\\n" } else {
if ch == "\r" { out = out + "\\r" } else {
if ch == "\t" { out = out + "\\t" } else { out = out + ch }
}
}
}}
i = i + 1
}
return "\"" + out + "\""
}
emit_call_int_args(name, args) {
name = match name { null => "", _ => name }
args = match args { null => [], _ => args }
// JSON v0 shape (HeaderEmitBox contract): {functions:[{name,params,blocks:[{id,instructions}]}]}
// Materialize immediate int args: r1..rN; mir_call Extern(name)(r1..rN)->rK; ret rK
local s = "" + args
local pos = 0
local n = 0
// Build instruction JSON (string) pieces (plugins OFFでも動くよう配列を使わない)
local body = "["
local first = 1
// const r1..rN
loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else {
local vid = 1 + n
local vv = RegexFlow.to_int(ds)
if first == 1 { first = 0 } else { body = body + "," }
body = body + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + EmitCallBox._to_str(vid) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + EmitCallBox._to_str(vv) + "}}"
n = n + 1
pos = pos + ds.size()
}
if pos >= s.size() { break }
}
local dst = n + 1
// mir_call (Extern)
{
// args JSON: [1,2,...]
local args_s = "["; { local i=0; loop(i<n) { if i>0 { args_s = args_s + "," } args_s = args_s + EmitCallBox._to_str(1+i); i=i+1 } } args_s = args_s + "]"
local name_q = EmitCallBox._quote("" + name)
local call_json = "{\\\"op\\\":\\\"mir_call\\\",\\\"dst\\\":" + EmitCallBox._to_str(dst) +
",\\\"mir_call\\\":{\\\"callee\\\":{\\\"type\\\":\\\"Extern\\\",\\\"name\\\":" + name_q + "},\\\"args\\\":" + args_s + ",\\\"effects\\\":[]}}"
if first == 1 { first = 0 } else { body = body + "," }
body = body + call_json
}
// ret dst
if first == 1 { first = 0 } else { body = body + "," }
body = body + "{\\\"op\\\":\\\"ret\\\",\\\"value\\\":" + EmitCallBox._to_str(dst) + "}"
body = body + "]"
local module_json = "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":[{\"id\":0,\"instructions\":" + body + "}]}]}"
return module_json
}
// JSON v1 (MirCall) emission — shape equivalentCallEmitBox + HeaderEmitBox
emit_call_int_args_v1(name, args) {
// v1 path is shape-equivalent for now — delegate to v0 builder above
return EmitCallBox.emit_call_int_args(name, args)
}
}
static box EmitCallStub { main(args) { return 0 } }

View File

@ -0,0 +1,84 @@
// EmitCompareBox — compare/branch/jump/ret の最小 MIR(JSON v0) 生成string直組み
using "lang/src/compiler/pipeline_v2/local_ssa_box.hako" as LocalSSABox
using lang.compiler.emit.common.mir_emit as MirEmitBox
using lang.compiler.emit.common.header_emit as HeaderEmitBox
static box EmitCompareBox {
_to_str(n) {
local v = n
if v == 0 { return "0" }
if v < 0 { return "-" + EmitCompareBox._to_str(0 - v) }
local out = ""; local digits = "0123456789"
loop (v > 0) { local d = v % 10; local ch = digits.substring(d, d+1); out = ch + out; v = v / 10 }
return out
}
_quote(s) {
if s == null { return "\"\"" }
local out = ""; local i = 0; local n = s.size()
loop (i < n) {
local ch = call("String.substring/2", s, i, i+1)
if ch == "\\" { out = out + "\\\\" }
else { if ch == "\"" { out = out + "\\\"" } else {
if ch == "\n" { out = out + "\\n" } else {
if ch == "\r" { out = out + "\\r" } else {
if ch == "\t" { out = out + "\\t" } else { out = out + ch }
}
}
}}
i = i + 1
}
return "\"" + out + "\""
}
// Return-only variant (prefer_cfg = 0): emit compare → ret directly
emit_compare(lhs, rhs, cmp) { return EmitCompareBox.emit_compare_ret(lhs, rhs, cmp, 0) }
emit_compare_ret(lhs, rhs, cmp, trace) {
// dev-trace最小
if trace == 1 { print("[emit] compare-ret lhs=" + lhs + " rhs=" + rhs + " cmp=" + cmp) }
// entry: const lhs/rhs; compare→r3; ret r3string直組み
local lhs_s = EmitCompareBox._to_str(lhs)
local rhs_s = EmitCompareBox._to_str(rhs)
local cmp_q = EmitCompareBox._quote("" + cmp)
local body = "[" +
"{\\\"op\\\":\\\"const\\\",\\\"dst\\\":1,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + lhs_s + "}}," +
"{\\\"op\\\":\\\"const\\\",\\\"dst\\\":2,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + rhs_s + "}}," +
"{\\\"op\\\":\\\"compare\\\",\\\"cmp\\\":" + cmp_q + ",\\\"lhs\\\":1,\\\"rhs\\\":2,\\\"dst\\\":3}," +
"{\\\"op\\\":\\\"ret\\\",\\\"value\\\":3}" +
"]"
return "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":[{\"id\":0,\"instructions\":" + body + "}]}]}"
}
emit_compare_cfg(lhs, rhs, cmp) { return EmitCompareBox.emit_compare_cfg2(lhs, rhs, cmp, 0) }
emit_compare_cfg2(lhs, rhs, cmp, materialize) { return EmitCompareBox.emit_compare_cfg3(lhs, rhs, cmp, materialize, 0) }
emit_compare_cfg3(lhs, rhs, cmp, materialize, trace) {
if trace == 1 { print("[emit] compare lhs=" + lhs + " rhs=" + rhs + " cmp=" + cmp + " mat=" + materialize) }
// normalize cmp via match
cmp = match cmp { null => "Gt", "" => "Gt", _ => cmp }
// string直組み
local lhs_s = EmitCompareBox._to_str(lhs)
local rhs_s = EmitCompareBox._to_str(rhs)
local cmp_q = EmitCompareBox._quote("" + cmp)
// Block 0 instructions
local b0 = "[" +
"{\\\"op\\\":\\\"const\\\",\\\"dst\\\":1,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + lhs_s + "}}," +
"{\\\"op\\\":\\\"const\\\",\\\"dst\\\":2,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + rhs_s + "}}," +
"{\\\"op\\\":\\\"compare\\\",\\\"cmp\\\":" + cmp_q + ",\\\"lhs\\\":1,\\\"rhs\\\":2,\\\"dst\\\":3}"
if materialize == 1 { b0 = b0 + ",{\\\"op\\\":\\\"copy\\\",\\\"dst\\\":4,\\\"src\\\":3}" }
// branch uses key "else" (not else_id) to match reader
b0 = b0 + ",{\\\"op\\\":\\\"branch\\\",\\\"cond\\\":3,\\\"then\\\":1,\\\"else\\\":2}]"
// Block 1 (then): set r6=1; jump -> 3
local b1 = "[{\"op\":\"const\",\"dst\":6,\"value\":{\"type\":\"i64\",\"value\":1}},{\"op\":\"jump\",\"target\":3}]"
// Block 2 (else): set r6=0; jump -> 3
local b2 = "[{\"op\":\"const\",\"dst\":6,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"jump\",\"target\":3}]"
// Block 3 (merge): ret r6
local b3 = "[{\"op\":\"ret\",\"value\":6}]"
local blocks = "[{\"id\":0,\"instructions\":" + b0 + "},{\"id\":1,\"instructions\":" + b1 + "},{\"id\":2,\"instructions\":" + b2 + "},{\"id\":3,\"instructions\":" + b3 + "}]"
return "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":" + blocks + "}]}"
}
}
static box EmitCompareStub { main(args) { return 0 } }

View File

@ -0,0 +1,84 @@
// EmitMethodBox — Return(Method recv, method, args[int...]) → MIR(JSON v0)
// 最小形: const recv→r1; 各引数を r2..rN; boxcall(method, recv=r1, args=r2..) → rK; ret rK
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using lang.compiler.emit.common.header_emit as HeaderEmitBox
using "lang/src/shared/mir/mir_schema_box.hako" as MirSchemaBox
static box EmitMethodBox {
_to_str(n) {
local v = n
if v == 0 { return "0" }
if v < 0 { return "-" + EmitMethodBox._to_str(0 - v) }
local out = ""; local digits = "0123456789"
loop (v > 0) { local d = v % 10; local ch = digits.substring(d, d+1); out = ch + out; v = v / 10 }
return out
}
_quote(s) {
if s == null { return "\"\"" }
local out = ""; local i = 0; local n = s.size()
loop (i < n) {
local ch = call("String.substring/2", s, i, i+1)
if ch == "\\" { out = out + "\\\\" }
else { if ch == "\"" { out = out + "\\\"" } else {
if ch == "\n" { out = out + "\\n" } else {
if ch == "\r" { out = out + "\\r" } else {
if ch == "\t" { out = out + "\\t" } else { out = out + ch }
}
}
}}
i = i + 1
}
return "\"" + out + "\""
}
emit_method_int_args(method, recv_val, args) {
method = match method { null => "", _ => method }
args = match args { null => [], _ => args }
// Shape: const recv->r1; const args r2..rN; mir_call Method(method, r1, r2..)->rK; ret rK
local s = "" + args
local pos = 0
local n = 0
// Build instruction JSON (string) pieces配列を使わない
local body = "["
local first = 1
// recv first
body = body + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":1,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + EmitMethodBox._to_str(recv_val) + "}}"
first = 0
// materialize args r2..r(n+1)
loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else {
local vid = 2 + n
local vv = RegexFlow.to_int(ds)
body = body + "," + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + EmitMethodBox._to_str(vid) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + EmitMethodBox._to_str(vv) + "}}"
n = n + 1
pos = pos + ds.size()
}
if pos >= s.size() { break }
}
local dst = n + 2
// mir_call (Method)
{
// args JSON: [2,3,...]
local args_s = "["; { local i=0; loop(i<n) { if i>0 { args_s = args_s + "," } args_s = args_s + EmitMethodBox._to_str(2+i); i=i+1 } } args_s = args_s + "]"
local method_q = EmitMethodBox._quote("" + method)
// receiver id is 1
local call_json = "{\\\"op\\\":\\\"mir_call\\\",\\\"dst\\\":" + EmitMethodBox._to_str(dst) +
",\\\"mir_call\\\":{\\\"callee\\\":{\\\"type\\\":\\\"Method\\\",\\\"method\\\":" + method_q + ",\\\"receiver\\\":1},\\\"args\\\":" + args_s + ",\\\"effects\\\":[]}}"
body = body + "," + call_json
}
// ret dst
body = body + "," + "{\\\"op\\\":\\\"ret\\\",\\\"value\\\":" + EmitMethodBox._to_str(dst) + "}"
body = body + "]"
local module_json = "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":[{\"id\":0,\"instructions\":" + body + "}]}]}"
return module_json
}
// JSON v1 (MirCall) emission — shape equivalentCallEmitBox + HeaderEmitBox
emit_method_int_args_v1(method, recv_val, args) {
// v1 path is shape-equivalent for now — delegate to v0 builder above
return EmitMethodBox.emit_method_int_args(method, recv_val, args)
}
}
static box EmitMethodStub { main(args) { return 0 } }

View File

@ -0,0 +1,111 @@
using "lang/src/shared/mir/json_emit_box.hako" as JsonEmitBox
// Shared MIR helpers (P1/P2)
using "lang/src/shared/mir/mir_schema_box.hako" as MirSchema
using "lang/src/shared/mir/block_builder_box.hako" as BlockBuilder
using "lang/src/compiler/pipeline_v2/local_ssa_box.hako" as LocalSSABox
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
flow EmitMirFlow {
_int(n) { return "" + n }
emit_return_int(v) {
local mod_full = BlockBuilder.const_ret(v)
return JsonEmitBox.to_json(mod_full)
}
_map_binop(opk) {
if opk == "+" { return "Add" }
if opk == "-" { return "Sub" }
if opk == "*" { return "Mul" }
if opk == "/" { return "Div" }
if opk == "%" { return "Mod" }
return opk
}
emit_binop(lhs, rhs, opk) {
local kind = EmitMirFlow._map_binop(opk)
local mod_full = BlockBuilder.binop(lhs, rhs, kind)
return JsonEmitBox.to_json(mod_full)
}
emit_compare_cfg(lhs, rhs, cmp) {
return EmitMirFlow.emit_compare_cfg2(lhs, rhs, cmp, 0)
}
// emit_compare_cfg2: materialize=1 なら cond を copy して branch する
emit_compare_cfg2(lhs, rhs, cmp, materialize) {
local mod_full = BlockBuilder.compare_branch(lhs, rhs, cmp)
if materialize != 0 {
// functions[0].blocks[0].instructions に cond=3 材化(copy 5<-3) を挿入
local fns = BoxHelpers.map_get(mod_full, "functions")
local fns_len = BoxHelpers.array_len(fns)
if fns_len > 0 {
local blocks = BoxHelpers.map_get(BoxHelpers.array_get(fns, 0), "blocks")
local blocks_len = BoxHelpers.array_len(blocks)
if blocks_len > 0 {
local insts = BoxHelpers.map_get(BoxHelpers.array_get(blocks, 0), "instructions")
if insts != null { LocalSSABox.ensure_after_last_def_copy(insts, 3, 5) }
}
}
}
return JsonEmitBox.to_json(mod_full)
}
// emit_loop_counter: while (i < limit) { i = i + 1 } ; return i
// Minimal loop CFG without PHI by updating the same dst id for 'i'.
// Blocks:
// 0: const i=0 -> r1; const limit -> r2; const one=1 -> r4; jump 1
// 1: compare lt r1 r2 -> r3; branch r3 then:2 else:3
// 2: binop add r1 r4 -> r1; jump 1
// 3: ret r1
emit_loop_counter(limit) {
local mod_full = BlockBuilder.loop_counter(limit)
return JsonEmitBox.to_json(mod_full)
}
// P4: emit extern call for op_eq via shared BlockBuilder (immediate values)
// Shape: const 1=lhs; const 2=rhs; mir_call Extern(op_eq)(1,2)->r3; ret r3
emit_op_eq(lhs, rhs) {
local vals = new ArrayBox()
vals.push(lhs)
vals.push(rhs)
local mod_full = BlockBuilder.extern_call_ival_ret("nyrt.ops.op_eq", vals, "nyrt.ops.op_eq")
return JsonEmitBox.to_json(mod_full)
}
// P4: generic extern/global/method/constructor emitters (immediate values)
emit_extern_call(name, arg_vals) {
local mod_full = BlockBuilder.extern_call_ival_ret(name, arg_vals, name)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
emit_global_call(name, arg_vals) {
local mod_full = BlockBuilder.global_call_ival_ret(name, arg_vals, name)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
emit_method_call(method, recv_val, arg_vals) {
local mod_full = BlockBuilder.method_call_ival_ret(method, recv_val, arg_vals, method)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
emit_constructor(box_type, arg_vals) {
local mod_full = BlockBuilder.constructor_call_ival_ret(box_type, arg_vals, box_type)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
// P5: ctor(ArrayBox)→size()→ret via shared builder
emit_array_ctor_then_size() {
local mod_full = BlockBuilder.ctor_then_size_ret()
return JsonEmitBox.to_json(mod_full)
}
// Internal: LocalSSA materialize last ret value with a copy
_ssa_materialize_last_result(mod_full) {
// Delegate to LocalSSABox for structural materialization policy.
LocalSSABox.ensure_materialize_last_ret(mod_full)
}
}

View File

@ -0,0 +1,110 @@
using "lang/src/shared/mir/json_emit_box.hako" as JsonEmitBox
// Shared MIR helpers (P1)
using "lang/src/shared/mir/mir_schema_box.hako" as MirSchema
using "lang/src/shared/mir/block_builder_box.hako" as BlockBuilder
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
using "lang/src/compiler/pipeline_v2/local_ssa_box.hako" as LocalSSABox
flow EmitMirFlowMap {
_array_len(arr) { return BoxHelpers.array_len(arr) }
_array_get(arr, idx) { return BoxHelpers.array_get(arr, idx) }
_map_get(obj, key) { return BoxHelpers.map_get(obj, key) }
_i(n) { return n }
_empty_arr() { return new ArrayBox() }
_instr_const(dst, val) {
local v = {type: "i64", value: EmitMirFlowMap._i(val)}
return {op: "const", dst: EmitMirFlowMap._i(dst), value: v}
}
_instr_ret(val) {
return {op: "ret", value: EmitMirFlowMap._i(val)}
}
_instr_binop(kind, lhs, rhs, dst) {
return {op: "binop", op_kind: kind, lhs: EmitMirFlowMap._i(lhs), rhs: EmitMirFlowMap._i(rhs), dst: EmitMirFlowMap._i(dst)}
}
_instr_compare(cmp, lhs, rhs, dst) {
return {op: "compare", cmp: cmp, lhs: EmitMirFlowMap._i(lhs), rhs: EmitMirFlowMap._i(rhs), dst: EmitMirFlowMap._i(dst)}
}
_instr_branch(cond, then_id, else_id) {
return {op: "branch", cond: EmitMirFlowMap._i(cond), then: EmitMirFlowMap._i(then_id), "else": EmitMirFlowMap._i(else_id)}
}
_instr_jump(target) {
return {op: "jump", target: EmitMirFlowMap._i(target)}
}
_block(id, insts) {
return {id: EmitMirFlowMap._i(id), instructions: insts}
}
_single_function_main(blocks) {
local f = {name: "main", params: [], blocks: blocks}
local fns = [f]
return {functions: fns}
}
emit_return_int(v) {
// Route via shared BlockBuilder (P1). Keep output shape identical to legacy by
// returning only the { functions: [...] } wrapper.
local mod_full = BlockBuilder.const_ret(v)
return JsonEmitBox.to_json(mod_full)
}
emit_binop(lhs, rhs, opk) {
// Shared builder (P2 prep)
local mod_full = BlockBuilder.binop(lhs, rhs, opk)
return JsonEmitBox.to_json(mod_full)
}
// materialize: 0/1 (builder parity)
emit_compare_cfg2(lhs, rhs, cmp, materialize) {
local mod_full = BlockBuilder.compare_branch(lhs, rhs, cmp)
if materialize != 0 {
local fns = me._map_get(mod_full, "functions")
local fns_len = me._array_len(fns)
if fns_len > 0 {
local blocks = me._map_get(me._array_get(fns, 0), "blocks")
local blocks_len = me._array_len(blocks)
if blocks_len > 0 {
local insts = me._map_get(me._array_get(blocks, 0), "instructions")
if insts != null { LocalSSABox.ensure_after_last_def_copy(insts, 3, 5) }
}
}
}
return JsonEmitBox.to_json(mod_full)
}
emit_loop_counter(limit) {
// Shared builder (P2 prep)
local mod_full = BlockBuilder.loop_counter(limit)
return JsonEmitBox.to_json(mod_full)
}
// P4: generic extern/global/method/constructor emitters (immediate values)
emit_extern_call(name, arg_vals) {
local mod_full = BlockBuilder.extern_call_ival_ret(name, arg_vals, name)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
emit_global_call(name, arg_vals) {
local mod_full = BlockBuilder.global_call_ival_ret(name, arg_vals, name)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
emit_method_call(method, recv_val, arg_vals) {
local mod_full = BlockBuilder.method_call_ival_ret(method, recv_val, arg_vals, method)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
emit_constructor(box_type, arg_vals) {
local mod_full = BlockBuilder.constructor_call_ival_ret(box_type, arg_vals, box_type)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
// P5: ctor(ArrayBox)→size()→ret via shared builder
emit_array_ctor_then_size() {
local mod_full = BlockBuilder.ctor_then_size_ret()
return JsonEmitBox.to_json(mod_full)
}
}

View File

@ -0,0 +1,36 @@
// EmitNewBoxBox — Return(New class, args[int...]) → MIR(JSON v0)
// 最小形: 各引数を r1..rN; newbox(class, args=r1..rN) → rK; ret rK
using "lang/src/shared/json/mir_builder_min.hako" as MirJsonBuilderMin
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using "lang/src/compiler/pipeline_v2/stage1_args_parser_box.hako" as Stage1ArgsParserBox
using "lang/src/shared/mir/json_emit_box.hako" as JsonEmitBox
using "lang/src/shared/mir/block_builder_box.hako" as BlockBuilder
using "lang/src/compiler/pipeline_v2/local_ssa_box.hako" as LocalSSABox
static box EmitNewBoxBox {
emit_newbox_int_args(class_name, args) {
class_name = match class_name { null => "", _ => class_name }
args = match args { null => [], _ => args }
// ArgsParserBox 正規化 → BlockBuilder 直結
local vals = Stage1ArgsParserBox.parse_ints(args)
if vals == null { return null }
local mod_full = BlockBuilder.constructor_call_ival_ret(class_name, vals, class_name)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
// JSON v1 (MirCall) emission — experimental, shape-only
emit_newbox_int_args_v1(class_name, args) {
class_name = match class_name { null => "", _ => class_name }
args = match args { null => [], _ => args }
// 同形出力shared builder に一本化)
local vals = Stage1ArgsParserBox.parse_ints(args)
if vals == null { return null }
local mod_full = BlockBuilder.constructor_call_ival_ret(class_name, vals, class_name)
LocalSSABox.ensure_materialize_last_ret(mod_full)
return JsonEmitBox.to_json(mod_full)
}
}
static box EmitNewBoxStub { main(args) { return 0 } }

View File

@ -0,0 +1,14 @@
// EmitReturnBox — return(Int) の最小 MIR(JSON v0) 生成(依存最小・文字列直組み)
static box EmitReturnBox {
emit_return_int(v) { return EmitReturnBox.emit_return_int2(v, 0) }
emit_return_int2(v, trace) {
if trace == 1 { print("[emit] return v=" + v) }
local sv = "" + v
// Minimal JSON v0: const→ret
return "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + sv + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
}
}
static box EmitReturnStub { main(args) { return 0 } }

View File

@ -0,0 +1,49 @@
// ExecutionPipelineBox — Orchestrate Parser → Emit (emit-only; no execution)
using lang.compiler.parser.box as ParserBoxMod
// Parser dependencies (Phase 2 refactoring: hierarchical structure)
using lang.compiler.parser.scan.parser_string_utils_box
using lang.compiler.parser.scan.parser_ident_scan_box
using lang.compiler.parser.scan.parser_number_scan_box
using lang.compiler.parser.scan.parser_string_scan_box
using lang.compiler.parser.using.using_collector_box
using lang.compiler.parser.expr.parser_expr_box
using lang.compiler.parser.expr.parser_peek_box
using lang.compiler.parser.expr.parser_literal_box
using lang.compiler.parser.stmt.parser_stmt_box
using lang.compiler.parser.stmt.parser_control_box
using lang.compiler.parser.stmt.parser_exception_box
using lang.compiler.stage1.json_program_box
using lang.compiler.stage1.emitter_box as EmitterBoxMod
using "lang/src/compiler/pipeline_v2/backend_box.hako" as BackendBoxMod
box ExecutionPipelineBox {
backend_name
backend
birth(name) {
// Optional backend tag (no execution here)
if name == null { name = "vm" }
me.backend_name = name
me.backend = new BackendBox(name)
return 0
}
// Run with source text; stage3_flag=1 enables Stage3 acceptance in parser
run_source(src, stage3_flag) {
if src == null { src = "return 0" }
if stage3_flag == null { stage3_flag = 0 }
// Parse
local p = new ParserBox()
if stage3_flag == 1 { p.stage3_enable(1) }
p.extract_usings(src)
local usings = p.get_usings_json()
local ast = p.parse_program2(src)
// Emit Stage1 JSON with meta.usings
local json = EmitterBox.emit_program(ast, usings)
if json == null || json.size() == 0 { return 1 }
print(json)
return 0
}
}
static box ExecutionPipelineStub { main(args) { return 0 } }

View File

@ -0,0 +1,26 @@
// flow_entry.hako — Pipeline v2 entry boxemit-only
// Guard: This box performs no execution. Returns MIR(JSON) as text.
using "lang/src/compiler/pipeline_v2/pipeline.hako" as PipelineV2
static box FlowEntryBox {
// Emit legacy v0 JSONcall/boxcall/newbox。最小入力: Stage1 JSON 文字列
emit_v0_from_ast(ast_json, prefer_cfg) {
return PipelineV2.lower_stage1_to_mir(ast_json, prefer_cfg)
}
// Emit v0 with using context (alias/module maps) — prefer this when names need resolution
emit_v0_from_ast_with_usings(ast_json, prefer_cfg, usings_json, modules_json) {
print("[DEBUG FlowEntry] emit_v0_from_ast_with_usings called")
local result = PipelineV2.lower_stage1_to_mir_with_usings(ast_json, prefer_cfg, usings_json, modules_json)
print("[DEBUG FlowEntry] result=" + result)
return result
}
// Emit v1 → v0 互換 JSONunified mir_call を一旦生成して適応)。自己ホスト実行向け
emit_v1_compat_from_ast(ast_json, prefer_cfg) {
return PipelineV2.lower_stage1_to_mir_v1_compat(ast_json, prefer_cfg)
}
// No-op entry箱ガード用
main(args) { return 0 }
}

View File

@ -0,0 +1,16 @@
// HeaderEmitBox — emit-only helper for minimal Stage-1 JSON header
// Responsibility
// - Provide a single place to emit the minimal JSON v0 header used by
// quiet child pipelines (e.g., --min-json). No parsing or lowering here.
// Input/Output
// - Input: none
// - Output: prints a single JSON line: {"version":0,"kind":"Program"}
// Non-goals
// - This box does not attempt to inject body or metadata; callers may compose.
static box HeaderEmitBox {
emit_min_json_header() {
print("{\"version\":0,\"kind\":\"Program\"}")
return 0
}
}

View File

@ -0,0 +1,37 @@
// json_minify_box.hako — strip insignificant whitespace from JSON text
// Note: preserves characters inside string literals, including escaped quotes
static box JsonMinifyBox {
minify(text) {
if text == null { return null }
local s = "" + text
local out = ""
local i = 0
local n = s.size()
local in_str = 0
loop(i < n) {
local ch = s.substring(i, i+1)
if in_str == 1 {
if ch == "\\" {
out = out + ch
i = i + 1
if i < n { out = out + s.substring(i, i+1) }
} else {
out = out + ch
if ch == "\"" { in_str = 0 }
}
} else {
if ch == "\"" { in_str = 1 out = out + ch }
else {
// skip whitespace outside strings
if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" { }
else { out = out + ch }
}
}
i = i + 1
}
return out
}
}
static box JsonMinifyStub { main(args) { return 0 } }

View File

@ -0,0 +1,188 @@
// LocalSSABox — 材化materialize/Copy 挿入の最小ポリシーを集約
// Phase 15.7: 最小実装。将来 PHI/Call 前の規約をここに集約して拡張する。
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box LocalSSABox {
_maybe_unwrap_instructions(insts) {
if insts == null { return null }
local repr = "" + insts
if repr.indexOf("MapBox(") == 0 {
local inner = BoxHelpers.map_get(insts, "instructions")
if inner != null { return inner }
}
return insts
}
// 汎用: copy 命令を instsArrayBox of Map末尾に追加
add_copy(insts, dst, src) {
if insts == null { return 1 }
insts = me._maybe_unwrap_instructions(insts)
if insts == null { return 1 }
call("ArrayBox.push/2", insts, { op:"copy", dst: dst, src: src })
return 0
}
// PHI直後の材化最小: いまは add_copy と同じ。将来 PHI 群スキップを実装
ensure_after_phis_copy(insts, src, dst) {
if insts == null { return 1 }
insts = me._maybe_unwrap_instructions(insts)
if insts == null { return 1 }
if BoxHelpers.is_array(insts) == 0 {
return me.add_copy(insts, dst, src)
}
local n = BoxHelpers.array_len(insts)
local i = 0
loop (i < n) {
local ins = BoxHelpers.array_get(insts, i)
if ins == null { break }
local op = BoxHelpers.map_get(ins, "op")
if op == null || op != "phi" { break }
i = i + 1
}
local insert_at = i // phi 直後
local node = { op:"copy", dst: dst, src: src }
if insert_at >= n {
call("ArrayBox.push/2", insts, node)
return 0
}
if n > 0 {
call("ArrayBox.push/2", insts, BoxHelpers.array_get(insts, n - 1))
local j = n - 1
loop (j >= insert_at) {
call("ArrayBox.set/3", insts, j + 1, BoxHelpers.array_get(insts, j))
j = j - 1
}
call("ArrayBox.set/3", insts, insert_at, node)
return 0
}
call("ArrayBox.push/2", insts, node)
return 0
}
// 新規: 定義直後に copy を挿入(安全・最小)
ensure_after_last_def_copy(insts, src, dst) {
if insts == null { return 1 }
if BoxHelpers.is_array(insts) == 0 { return 1 }
local n = BoxHelpers.array_len(insts)
local insert_at = n
// Find the first terminator index (ret/branch/jump/throw) to ensure block ends with a terminator
local term_at = n
{
local i = 0
loop(i < n) {
local ins = BoxHelpers.array_get(insts, i)
if ins != null {
local op = BoxHelpers.map_get(ins, "op")
if op == "ret" || op == "branch" || op == "jump" || op == "throw" { term_at = i break }
}
i = i + 1
}
}
// 探索: 最後に dst=src を定義した位置
{
local i = 0
loop (i < n) {
local ins = BoxHelpers.array_get(insts, i)
if ins != null {
print("[LS] checking def ins=" + ("" + ins))
local d_raw = BoxHelpers.map_get(ins, "dst")
print("[LS] dst raw=" + ("" + d_raw))
local d = BoxHelpers.value_i64(d_raw)
if d != null && d == src { insert_at = i + 1 }
}
i = i + 1
}
}
// Do not cross terminator: insert before the first terminator if present
if insert_at > term_at { insert_at = term_at }
local node = { op:"copy", dst: dst, src: src }
if insert_at >= n { call("ArrayBox.push/2", insts, node) return 0 }
// 1つ末尾に空きを作る末尾要素を複製して押し出す
if n > 0 {
call("ArrayBox.push/2", insts, BoxHelpers.array_get(insts, n - 1))
local j = n - 1
loop (j >= insert_at) {
call("ArrayBox.set/3", insts, j + 1, BoxHelpers.array_get(insts, j))
j = j - 1
}
call("ArrayBox.set/3", insts, insert_at, node)
return 0
}
call("ArrayBox.push/2", insts, node)
return 0
}
// calls の最小材化: 最後の ret が参照する値 src を探し、src の直後に copy(dst=src+1) を挿入する。
// mod_full: { functions: [ { blocks: [ { instructions: [...] } ] } ] }
ensure_materialize_last_ret(mod_full) {
if mod_full == null { return 1 }
local fns = BoxHelpers.map_get(mod_full, "functions")
if fns == null || BoxHelpers.array_len(fns) == 0 { return 1 }
local blocks = BoxHelpers.map_get(BoxHelpers.array_get(fns, 0), "blocks")
if blocks == null || BoxHelpers.array_len(blocks) == 0 { return 1 }
local insts = BoxHelpers.map_get(BoxHelpers.array_get(blocks, 0), "instructions")
if insts == null || BoxHelpers.array_len(insts) == 0 { return 1 }
// ret の引数を探索
local n = BoxHelpers.array_len(insts)
local src = null
{
local i = 0
loop(i < n) {
local ins = BoxHelpers.array_get(insts, i)
if ins != null {
local op = BoxHelpers.map_get(ins, "op")
if op == "ret" {
src = BoxHelpers.value_i64(BoxHelpers.map_get(ins, "value"))
}
}
i = i + 1
}
}
if src == null { return 1 }
return me.ensure_after_last_def_copy(insts, src, src + 1)
}
// ensure_cond: 最初の branch の cond を検出し、その定義直後に copy を1つ挿入最小
// - 形状期待: いずれかの block.instructions に { op:"branch", cond:<id>, ... }
ensure_cond(mod_full) {
if mod_full == null { return 1 }
local fns = BoxHelpers.map_get(mod_full, "functions")
if fns == null { return 1 }
local bn = BoxHelpers.array_len(fns)
if bn == 0 { return 1 }
local blocks = BoxHelpers.map_get(BoxHelpers.array_get(fns, 0), "blocks")
if blocks == null || BoxHelpers.array_len(blocks) == 0 { return 1 }
// 探索: 各ブロックの instructions から最初の branch を見つけ、そのブロックで材化
local bi = 0
local block_count = BoxHelpers.array_len(blocks)
loop (bi < block_count) {
local insts = BoxHelpers.map_get(BoxHelpers.array_get(blocks, bi), "instructions")
local insts_len = BoxHelpers.array_len(insts)
if insts_len > 0 {
local n = insts_len
local cond = null
{
local i = 0
loop(i < n) {
local ins = BoxHelpers.array_get(insts, i)
if ins != null {
local op = BoxHelpers.map_get(ins, "op")
if op == "branch" {
cond = BoxHelpers.value_i64(BoxHelpers.map_get(ins, "cond"))
break
}
}
i = i + 1
}
}
if cond != null { return me.ensure_after_last_def_copy(insts, cond, cond + 2) }
}
bi = bi + 1
}
return 1
}
}
static box LocalSSAStub { main(args) { return 0 } }

View File

@ -0,0 +1,43 @@
// map_helpers_box.hako — Pipeline用の軽量ヘルパMapBoxの型付き取得
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box MapHelpersBox {
_raw_get(m, key) {
return BoxHelpers.map_get(m, key)
}
_to_i64(v) { return StringHelpers.to_i64(v) }
get_str(m, key) {
local v = me._raw_get(m, key)
if v == null { return "" }
if BoxHelpers.is_map(v) == 1 {
local inner = BoxHelpers.map_get(v, "value")
if inner != null { return "" + inner }
}
return "" + v
}
get_i64(m, key) {
local v = me._raw_get(m, key)
return BoxHelpers.value_i64(v)
}
expect_str(m, key) { return me.get_str(m, key) }
expect_i64(m, key) { return me.get_i64(m, key) }
opt_str(m, key, def) {
local s = me.get_str(m, key)
if s == null || s == "" { return def }
return s
}
opt_i64(m, key, def) {
local n = me.get_i64(m, key)
if n == null { return def }
return n
}
}
static box MapHelpersStub { main(args) { return 0 } }

View File

@ -0,0 +1,12 @@
// MethodExtractBox — Stage1 JSON から Return(Method recv, method, args) を抽出(整数引数のみ; recvは無視
// Delegation to Stage1IntArgsExtractBox (unified implementation)
using "lang/src/compiler/pipeline_v2/stage1_int_args_extract_box.hako" as Unified
static box MethodExtractBox {
// Returns { method: String, args: [Int,...] } or null
extract_return_method_ints(ast_json) {
return Unified.extract_return_method_ints(ast_json)
}
}
static box MethodExtractStub { main(args) { return 0 } }

View File

@ -0,0 +1,35 @@
// MirBuilderBox — minimal Ny→MIR(JSON v0) lowering entry (dev)
// Phase 15.7: kept optional; pipeline.v2 uses Emit flow directly.
using "lang/src/compiler/pipeline_v2/stage1_extract_flow.hako" as Stage1ExtractFlow
using "lang/src/compiler/pipeline_v2/emit_return_box.hako" as EmitReturnBox
using "lang/src/compiler/pipeline_v2/emit_binop_box.hako" as EmitBinopBox
using "lang/src/compiler/pipeline_v2/emit_compare_box.hako" as EmitCompareBox
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
box MirBuilderBox {
// Placeholder for optimizer toggle
optimize_flag
birth() { me.optimize_flag = 0 return 0 }
set_optimize(v) { if v == null { v = 0 } me.optimize_flag = v return 0 }
// Accept Stage1 AST JSON and emit minimal MIR(JSON v0)
build(ast_json) {
if ast_json == null { return EmitReturnBox.emit_return_int2(0, 0) }
// If(cond=Compare) → CFG (branch/jump/ret)
if call("String.indexOf/2", ast_json, "\"type\":\"If\"") >= 0 {
local ic = Stage1ExtractFlow.extract_if_compare(ast_json)
if ic != null { return EmitCompareBox.emit_compare_cfg3(BoxHelpers.map_get(ic, "lhs"), BoxHelpers.map_get(ic, "rhs"), BoxHelpers.map_get(ic, "cmp"), 0, 0) }
}
// Return(Compare)
local c = Stage1ExtractFlow.extract_return_compare(ast_json)
if c != null { return EmitCompareBox.emit_compare_cfg3(BoxHelpers.map_get(c, "lhs"), BoxHelpers.map_get(c, "rhs"), BoxHelpers.map_get(c, "cmp"), 0, 0) }
// Return(BinOp)
local b = Stage1ExtractFlow.extract_return_binop(ast_json)
if b != null { return EmitBinopBox.emit_binop2(BoxHelpers.map_get(b, "lhs"), BoxHelpers.map_get(b, "rhs"), BoxHelpers.map_get(b, "kind"), 0) }
// Fallback: Return(Int)
local v = Stage1ExtractFlow.extract_return_int(ast_json)
return EmitReturnBox.emit_return_int2(v, 0)
}
}
static box MirBuilderStub { main(args) { return 0 } }

View File

@ -0,0 +1,91 @@
// MirCallBox — JSON v1 unified call emitters薄い箱
// 目的: v1mir_callを第一級に扱う最小APIを集中させる。実行は含まないemit-only
using "lang/src/shared/json/mir_builder_min.hako" as MirJsonBuilderMin
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using lang.compiler.emit.common.mir_emit as MirEmitBox
using lang.compiler.emit.common.call_emit as CallEmitBox
using lang.compiler.emit.common.header_emit as HeaderEmitBox
using "lang/src/shared/mir/json_emit_box.hako" as JsonEmitBox
static box MirCallBox {
// Global(name, args:int[])
emit_call_v1(name, args) {
name = match name { null => "", _ => name }
args = match args { null => [], _ => args }
local s = "" + args
local pos = 0
local n = 0
local insts = []
// materialize const args r1..rN
loop (true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(1 + n, RegexFlow.to_int(ds))) n = n + 1 pos = pos + ds.size() }
if pos >= s.size() { break }
}
local dst = n + 1
// args 1..n
local arg_ids = new ArrayBox()
{ local i=0 loop(i<n) { arg_ids.push(1+i) i=i+1 } }
insts.push(CallEmitBox.make_mir_call_global(name, arg_ids, dst))
insts.push(MirEmitBox.make_ret(dst))
local blocks = [HeaderEmitBox.make_block(0, insts)]
local fns = [HeaderEmitBox.make_function_main(blocks)]
return JsonEmitBox.to_json(HeaderEmitBox.make_module_with_functions(fns))
}
// Method(method, recv:int, args:int[])
emit_method_v1(method, recv_val, args) {
method = match method { null => "", _ => method }
args = match args { null => [], _ => args }
local s = "" + args
local pos = 0
local n = 0
local insts = []
insts.push(MirEmitBox.make_const(1, recv_val))
// materialize args r2..r(n+1)
{ local i = 0 loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(2 + i, RegexFlow.to_int(ds))) i = i + 1 n = i pos = pos + ds.size() }
if pos >= s.size() { break }
}
}
local dst = n + 2
// args 2..(n+1)
local arg_ids = new ArrayBox()
{ local i=0 loop(i<n) { arg_ids.push(2+i) i=i+1 } }
insts.push(CallEmitBox.make_mir_call_method(method, 1, arg_ids, dst))
insts.push(MirEmitBox.make_ret(dst))
local blocks = [HeaderEmitBox.make_block(0, insts)]
local fns = [HeaderEmitBox.make_function_main(blocks)]
return JsonEmitBox.to_json(HeaderEmitBox.make_module_with_functions(fns))
}
// Constructor(class, args:int[])
emit_newbox_v1(class_name, args) {
class_name = match class_name { null => "", _ => class_name }
args = match args { null => [], _ => args }
local s = "" + args
local pos = 0
local n = 0
local insts = []
// materialize args r1..rN
{ local i = 0 loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(1 + i, RegexFlow.to_int(ds))) i = i + 1 n = i pos = pos + ds.size() }
if pos >= s.size() { break }
}
}
local dst = n + 1
// args 1..n
local arg_ids = new ArrayBox()
{ local i=0 loop(i<n) { arg_ids.push(1+i) i=i+1 } }
insts.push(CallEmitBox.make_mir_call_constructor(class_name, arg_ids, dst))
insts.push(MirEmitBox.make_ret(dst))
local blocks = [HeaderEmitBox.make_block(0, insts)]
local fns = [HeaderEmitBox.make_function_main(blocks)]
return JsonEmitBox.to_json(HeaderEmitBox.make_module_with_functions(fns))
}
}
static box MirCallStub { main(args) { return 0 } }

View File

@ -0,0 +1,29 @@
// name_resolve_box.hako — PipelineNameResolveBox
// Responsibility: normalize raw global call names with using-context
// - primary: NamespaceBox.normalize_global_name(raw, resolver)
// - fallback: unique tail match via UsingResolverBox.guess_namespace_from_tail
using "lang/src/compiler/pipeline_v2/using_resolver_box.hako" as UsingResolverBox
using "lang/src/compiler/pipeline_v2/namespace_box.hako" as NamespaceBox
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
static box PipelineNameResolveBox {
// Returns fully-qualified name or null
normalize_call_name(raw_name, r_state) {
if raw_name == null { return null }
// Try normalizer first
local fq = NamespaceBox.normalize_global_name(raw_name, r_state)
if fq != null { return fq }
// Try head.tail composition with modules-based guess (unique only)
local dot2 = RegexFlow.find_from(raw_name, ".", 0)
if dot2 < 0 { return null }
local head2 = raw_name.substring(0, dot2)
local tail2 = raw_name.substring(dot2 + 1, raw_name.size())
local ns2 = UsingResolverBox.resolve_namespace_alias(r_state, head2)
if ns2 == null { ns2 = UsingResolverBox.guess_namespace_from_tail(r_state, head2) }
if ns2 == null { return null }
return ns2 + "." + tail2
}
}
static box PipelineNameResolveMain { main(args) { return 0 } }

View File

@ -0,0 +1,61 @@
// NamespaceBox — Resolve alias-based names to fully qualified module functions
// Responsibility:
// - Canonicalize global call names like "Timer.now_ms" → "selfhost.core.timer.now_ms"
// - Uses UsingResolverBox context (alias→ns) provided by caller
// NonResponsibility:
// - IO / TOML 読み込み、Runner 側の using 解決、実行
using "lang/src/compiler/pipeline_v2/using_resolver_box.hako" as UsingResolver
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
static box NamespaceBox {
// Normalize global function name using resolver context
// Behavior:
// - If name has a dot and its head matches an alias in resolver, replace head with ns
// - Otherwise return the original name
normalize_global_name(name, resolver_state) {
if name == null { return "" }
local s = "" + name
// find first '.' (head separator)
local pos = RegexFlow.find_from(s, ".", 0)
if pos < 0 { return s }
local head = s.substring(0, pos)
local tail = s.substring(pos + 1, s.size())
if resolver_state == null { return s }
local ns = UsingResolver.resolve_namespace_alias(resolver_state, head)
if ns == null {
// Strict: alias used but not declared
print("[ERROR] Unresolved using alias: " + head)
return null
}
return ns + "." + tail
}
// Normalize class name similarly (e.g., Alias.Box → Ns.Box)
normalize_class_name(class_name, resolver_state) {
if class_name == null { return "" }
local s = "" + class_name
local pos = RegexFlow.find_from(s, ".", 0)
if pos < 0 {
// Single identifier; if it's an alias, return ns (leave as-is otherwise)
if resolver_state == null { return s }
local ns = UsingResolver.resolve_namespace_alias(resolver_state, s)
if ns != null { return ns }
// If a bare alias is used but not declared, treat as error as well
// (keeps behavior consistent with global name normalization)
print("[ERROR] Unresolved using alias: " + s)
return null
}
local head = s.substring(0, pos)
local tail = s.substring(pos + 1, s.size())
if resolver_state == null { return s }
local ns2 = UsingResolver.resolve_namespace_alias(resolver_state, head)
if ns2 == null {
print("[ERROR] Unresolved using alias: " + head)
return null
}
return ns2 + "." + tail
}
}
static box NamespaceBoxStub { main(args) { return 0 } }

View File

@ -0,0 +1,12 @@
// NewExtractBox — Stage1 JSON から Return(New class(args...)) を抽出(整数引数のみ)
// Delegation to Stage1IntArgsExtractBox (unified implementation)
using "lang/src/compiler/pipeline_v2/stage1_int_args_extract_box.hako" as Unified
static box NewExtractBox {
// Returns { class: String, args: [Int,...] } or null
extract_return_new_ints(ast_json) {
return Unified.extract_return_new_ints(ast_json)
}
}
static box NewExtractStub { main(args) { return 0 } }

View File

@ -0,0 +1,90 @@
// normalizer_box.hako — Minimal normalizer for Compare maps (cmp/lhs/rhs)
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box NormalizerBox {
// Normalize cmp/lhs/rhs into MapBox with String keys
normalize_cmp(raw) {
if raw == null { return null }
// TEMP debug
if BoxHelpers.is_map(raw) == 1 && BoxHelpers.map_get(raw, "cmp") != null { print("DBG:N.cmp_in=1") } else { print("DBG:N.cmp_in=0") }
local out = new MapBox()
// cmp
local cmp = "" + BoxHelpers.map_get(raw, "cmp")
if cmp == null || cmp == "" { return null }
BoxHelpers.map_set(out, "cmp", cmp)
// lhs/rhs (delegate to StringHelpers)
local lhs = StringHelpers.to_i64(BoxHelpers.map_get(raw, "lhs"))
local rhs = StringHelpers.to_i64(BoxHelpers.map_get(raw, "rhs"))
BoxHelpers.map_set(out, "lhs", lhs)
BoxHelpers.map_set(out, "rhs", rhs)
if BoxHelpers.map_get(out, "cmp") != null { print("DBG:N.cmp_out=1") } else { print("DBG:N.cmp_out=0") }
return out
}
// Normalize Call: { name:String, args:[Int...] } → MapBox("name","args:Array<i64>")
normalize_call_ints(raw) {
if raw == null { return null }
local out = new MapBox()
local name = "" + BoxHelpers.map_get(raw, "name")
if name == null || name == "" { return null }
BoxHelpers.map_set(out, "name", name)
// args: accept array-like; fallback to empty
local arr = new ArrayBox()
local src = BoxHelpers.map_get(raw, "args")
if src != null {
local n = BoxHelpers.array_len(src)
local i = 0
loop (i < n) {
arr.push(StringHelpers.to_i64(BoxHelpers.array_get(src, i)))
i = i + 1
}
}
BoxHelpers.map_set(out, "args", arr)
return out
}
// Normalize Method: { method:String, args:[Int...] } → MapBox("method","args:Array<i64>")
normalize_method_ints(raw) {
if raw == null { return null }
local out = new MapBox()
local m = "" + BoxHelpers.map_get(raw, "method")
if m == null || m == "" { return null }
BoxHelpers.map_set(out, "method", m)
local arr = new ArrayBox()
local src = BoxHelpers.map_get(raw, "args")
if src != null {
local n = BoxHelpers.array_len(src)
local i = 0
loop (i < n) {
arr.push(StringHelpers.to_i64(BoxHelpers.array_get(src, i)))
i = i + 1
}
}
BoxHelpers.map_set(out, "args", arr)
return out
}
// Normalize New: { class:String, args:[Int...] } → MapBox("class","args:Array<i64>")
normalize_new_ints(raw) {
if raw == null { return null }
local out = new MapBox()
local c = "" + BoxHelpers.map_get(raw, "class")
if c == null || c == "" { return null }
BoxHelpers.map_set(out, "class", c)
local arr = new ArrayBox()
local src = BoxHelpers.map_get(raw, "args")
if src != null {
local n = BoxHelpers.array_len(src)
local i = 0
loop (i < n) {
arr.push(StringHelpers.to_i64(BoxHelpers.array_get(src, i)))
i = i + 1
}
}
BoxHelpers.map_set(out, "args", arr)
return out
}
}
static box NormalizerStub { main(args) { return 0 } }

View File

@ -0,0 +1,558 @@
using "lang/src/compiler/pipeline_v2/stage1_extract_flow.hako" as Stage1ExtractFlow
using "lang/src/compiler/pipeline_v2/emit_return_box.hako" as EmitReturnBox
using "lang/src/compiler/pipeline_v2/emit_binop_box.hako" as EmitBinopBox
using "lang/src/compiler/pipeline_v2/emit_compare_box.hako" as EmitCompareBox
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using lang.compiler.builder.ssa.local as LocalSSA
using "lang/src/compiler/pipeline_v2/emit_call_box.hako" as EmitCallBox
using "lang/src/compiler/pipeline_v2/emit_method_box.hako" as EmitMethodBox
using "lang/src/compiler/pipeline_v2/emit_newbox_box.hako" as EmitNewBoxBox
using "lang/src/compiler/pipeline_v2/mir_call_box.hako" as MirCallBox
using "lang/src/shared/json/mir_v1_adapter.hako" as MirJsonV1Adapter
using "lang/src/compiler/pipeline_v2/compare_extract_box.hako" as CompareExtractBox
using "lang/src/compiler/pipeline_v2/normalizer_box.hako" as NormalizerBox
using "lang/src/compiler/pipeline_v2/map_helpers_box.hako" as MapHelpersBox
using "lang/src/compiler/pipeline_v2/call_extract_box.hako" as CallExtractBox
using "lang/src/compiler/pipeline_v2/method_extract_box.hako" as MethodExtractBox
using "lang/src/compiler/pipeline_v2/new_extract_box.hako" as NewExtractBox
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
using "lang/src/compiler/pipeline_v2/using_resolver_box.hako" as UsingResolverBox
using "lang/src/compiler/pipeline_v2/namespace_box.hako" as NamespaceBox
using "lang/src/compiler/pipeline_v2/signature_verifier_box.hako" as SignatureVerifierBox
using "lang/src/compiler/pipeline_v2/stage1_json_scanner_box.hako" as Stage1JsonScannerBox
using "lang/src/compiler/pipeline_v2/stage1_name_args_normalizer_box.hako" as NameArgsNormBox
using "lang/src/compiler/pipeline_v2/alias_preflight_box.hako" as AliasPreflightBox
using "lang/src/compiler/pipeline_v2/stage1_args_parser_box.hako" as Stage1ArgsParserBox
using "lang/src/compiler/pipeline_v2/pipeline_helpers_box.hako" as PipelineHelpersBox
flow PipelineV2 {
lower_stage1_to_mir(ast_json, prefer_cfg) {
// Backward-compatible entry (trace=0)
return PipelineV2.lower_stage1_to_mir_trace(ast_json, prefer_cfg, 0)
}
// Experimental: emit JSON v1 (MirCall) directly (no adapter). Shape-only focus.
lower_stage1_to_mir_v1(ast_json, prefer_cfg) {
if ast_json == null { return EmitReturnBox.emit_return_int2(0, 0) }
local r = UsingResolverBox.state_new()
// Call (prefer extract box)
{
local kq = RegexFlow.find_from(ast_json, "\"type\":\"Call\"", 0)
if kq >= 0 {
// Strict preflight (scanner): read name and enforce using alias resolution
{
local scan = Stage1JsonScannerBox.extract_name_args(ast_json, kq)
if scan != null {
if AliasPreflightBox.check_head(scan.get("name"), r) != 1 { return null }
}
}
local kc = CallExtractBox.extract_return_call_ints(ast_json)
if kc != null {
// Normalize to fix key/types at entry
local kn = NormalizerBox.normalize_call_ints(kc)
if kn == null { return null }
if SignatureVerifierBox.verify_call_name_arity(kn.get("name"), kn.get("args")) != 1 { return null }
local out_call = EmitCallBox.emit_call_int_args_v1(kn.get("name"), kn.get("args"))
if out_call == null { return null }
return out_call
}
// Tolerant scanner fallback → v1 emit (raw; no resolver context here)
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pair = Stage1JsonScannerBox.extract_name_args(ast_json, pos_body)
if pair != null {
local out_scan = EmitCallBox.emit_call_int_args_v1(pair.get("name"), pair.get("args_text"))
if out_scan == null { return null }
return out_scan
}
}
// Fallback legacy
local k = Stage1ExtractFlow.extract_return_call(ast_json)
if k != null {
local kn2 = NormalizerBox.normalize_call_ints(k)
if kn2 == null { return null }
if SignatureVerifierBox.verify_call_name_arity(kn2.get("name"), kn2.get("args")) != 1 { return null }
local out_legacy = EmitCallBox.emit_call_int_args_v1(kn2.get("name"), kn2.get("args"))
if out_legacy == null { return null }
return out_legacy
}
}
}
// Method (recv:0 placeholder; prefer extract box)
{
local mq = RegexFlow.find_from(ast_json, "\"type\":\"Method\"", 0)
if mq >= 0 {
local mb = MethodExtractBox.extract_return_method_ints(ast_json)
if mb != null {
local mn = NormalizerBox.normalize_method_ints(mb)
if mn == null { return null }
local out_method = EmitMethodBox.emit_method_int_args_v1(mn.get("method"), 0, mn.get("args"))
if out_method == null { return null }
return out_method
}
// Tolerant scanner fallback (method/args only)
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pair = Stage1JsonScannerBox.extract_label_args(ast_json, "method", pos_body)
if pair != null {
local out_method_scan = EmitMethodBox.emit_method_int_args_v1(pair.get("label"), 0, pair.get("args_text"))
if out_method_scan == null { return null }
return out_method_scan
}
}
local m = Stage1ExtractFlow.extract_return_method(ast_json)
if m != null {
local mn2 = NormalizerBox.normalize_method_ints(m)
if mn2 == null { return null }
local out_method_legacy = EmitMethodBox.emit_method_int_args_v1(mn2.get("method"), 0, mn2.get("args"))
if out_method_legacy == null { return null }
return out_method_legacy
}
}
}
// New (prefer extract box)
{
local nq = RegexFlow.find_from(ast_json, "\"type\":\"New\"", 0)
if nq >= 0 {
local nb = NewExtractBox.extract_return_new_ints(ast_json)
if nb != null {
local nn = NormalizerBox.normalize_new_ints(nb)
if nn == null { return null }
local out_new = EmitNewBoxBox.emit_newbox_int_args_v1(nn.get("class"), nn.get("args"))
if out_new == null { return null }
return out_new
}
// Tolerant scanner fallback (class/args only)
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pair = Stage1JsonScannerBox.extract_label_args(ast_json, "class", pos_body)
if pair != null {
local out_new_scan = EmitNewBoxBox.emit_newbox_int_args_v1(pair.get("label"), pair.get("args_text"))
if out_new_scan == null { return null }
return out_new_scan
}
}
local n = Stage1ExtractFlow.extract_return_new(ast_json)
if n != null {
local nn2 = NormalizerBox.normalize_new_ints(n)
if nn2 == null { return null }
local out_new_legacy = EmitNewBoxBox.emit_newbox_int_args_v1(nn2.get("class"), nn2.get("args"))
if out_new_legacy == null { return null }
return out_new_legacy
}
}
}
// Fallback to v0 path for remaining cases
return PipelineV2.lower_stage1_to_mir(ast_json, prefer_cfg)
}
// Experimental helper: emit v1 then downgrade to v0 for MiniVM exec
lower_stage1_to_mir_v1_compat(ast_json, prefer_cfg) {
local j1 = PipelineV2.lower_stage1_to_mir_v1(ast_json, prefer_cfg)
return MirJsonV1Adapter.to_v0(j1)
}
lower_stage1_to_mir_trace(ast_json, prefer_cfg, trace) {
local prefer = PipelineHelpersBox.to_i64(prefer_cfg)
if ast_json == null { return EmitReturnBox.emit_return_int2(0, trace) }
if trace == 1 { print("[pipe] prefer_cfg=" + prefer_cfg + " prefer=" + prefer) }
local r = UsingResolverBox.state_new()
// Early fast-path: any Compare in program (Int-only)
{
local cq0 = RegexFlow.find_from(ast_json, "\"type\":\"Compare\"", 0)
if cq0 >= 0 {
local opk_pos0 = RegexFlow.find_from(ast_json, "\"op\":\"", cq0)
local cmp0 = ""
if opk_pos0 >= 0 {
local opk_end0 = RegexFlow.find_from(ast_json, "\"", opk_pos0 + 6)
if opk_end0 >= 0 { cmp0 = ast_json.substring(opk_pos0 + 6, opk_end0) }
}
local lhsp0 = RegexFlow.find_from(ast_json, "\"lhs\"", cq0)
local rhsp0 = RegexFlow.find_from(ast_json, "\"rhs\"", cq0)
if lhsp0 >= 0 && rhsp0 >= 0 {
local lv0 = 0
local rv0 = 0
local vpos0 = RegexFlow.find_from(ast_json, "\"value\":", lhsp0)
if vpos0 >= 0 {
local ds0 = RegexFlow.digits_from(ast_json, vpos0 + 8)
if ds0 != "" { lv0 = RegexFlow.to_int(ds0) }
}
local vpos02 = RegexFlow.find_from(ast_json, "\"value\":", rhsp0)
if vpos02 >= 0 {
local ds02 = RegexFlow.digits_from(ast_json, vpos02 + 8)
if ds02 != "" { rv0 = RegexFlow.to_int(ds02) }
}
if cmp0 == null || cmp0 == "" { cmp0 = "Gt" }
if prefer >= 1 {
local mat0 = 0
if prefer >= 2 { mat0 = 1 }
if trace == 1 { print("[pipe] any-fast cfg cmp=" + cmp0 + " lhs=" + (""+lv0) + " rhs=" + (""+rv0) + " mat=" + (""+mat0)) }
local jfast0 = EmitCompareBox.emit_compare_cfg3(lv0, rv0, cmp0, mat0, trace)
return LocalSSA.ensure_cond(jfast0)
} else {
if trace == 1 { print("[pipe] any-fast ret cmp=" + cmp0 + " lhs=" + (""+lv0) + " rhs=" + (""+rv0)) }
return EmitCompareBox.emit_compare_ret(lv0, rv0, cmp0, trace)
}
}
}
}
// If(...) pattern (prefer CFG form)
{
local iq = RegexFlow.find_from(ast_json, "\"type\":\"If\"", 0)
if iq >= 0 {
// Fast-path: directly parse Compare within If for primitives (Int-only)
{
local cv = PipelineHelpersBox.parse_compare_values(ast_json, iq)
if cv != null {
local mat = 1
local lfast = cv.get(0)
local rfast = cv.get(1)
local cmpfast = cv.get(2)
if cmpfast == null || cmpfast == "" { cmpfast = "Gt" }
if trace == 1 { print("[pipe] if-fast cmp=" + cmpfast + " lhs=" + (""+lfast) + " rhs=" + (""+rfast) + " mat=" + (""+mat)) }
local jfast = EmitCompareBox.emit_compare_cfg3(lfast, rfast, cmpfast, mat, trace)
return LocalSSA.ensure_cond(jfast)
}
}
// Prefer robust extractor box
local icb = CompareExtractBox.extract_if_compare_ints(ast_json)
if icb != null {
local mat = 0
if prefer >= 2 { mat = 1 }
local l = icb.get(0)
local r = icb.get(1)
local c = icb.get(2)
local j0 = EmitCompareBox.emit_compare_cfg3(l, r, c, mat, trace)
return LocalSSA.ensure_cond(j0)
}
// Fallback legacy extractor
local ic = Stage1ExtractFlow.extract_if_compare(ast_json)
if ic != null {
local mat = 0
if prefer >= 2 { mat = 1 }
local l2 = ic.get("lhs") + 0
local r2 = ic.get("rhs") + 0
local c2 = "" + ic.get("cmp")
if c2 == null || c2 == "" { c2 = "Gt" }
local j0 = EmitCompareBox.emit_compare_cfg3(l2, r2, c2, mat, trace)
return LocalSSA.ensure_cond(j0)
}
}
}
// Next Call (Return(Call ...)) — prefer extract box
{
local kq = RegexFlow.find_from(ast_json, "\"type\":\"Call\"", 0)
if kq >= 0 {
// Strict preflight via tolerant scanner: read raw name and enforce using alias resolution
{
local scan0 = Stage1JsonScannerBox.extract_name_args(ast_json, kq)
if scan0 != null {
if AliasPreflightBox.check_head(scan0.get("name"), r) != 1 { return null }
}
}
local kc = CallExtractBox.extract_return_call_ints(ast_json)
if kc != null {
local kn = NormalizerBox.normalize_call_ints(kc)
if kn == null { return null }
if SignatureVerifierBox.verify_call_name_arity(kn.get("name"), kn.get("args")) != 1 { return null }
local j4 = EmitCallBox.emit_call_int_args(kn.get("name"), kn.get("args"))
if j4 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j4))
}
// Fallback: scanner → normalizer → emit
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pair = Stage1JsonScannerBox.extract_name_args(ast_json, pos_body)
if pair != null {
local norm = NameArgsNormBox.normalize_call(pair, r)
if norm != null {
local j4b = EmitCallBox.emit_call_int_args(norm.get("name"), norm.get("args_text"))
if j4b == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j4b))
}
}
}
local k = Stage1ExtractFlow.extract_return_call(ast_json)
if k != null {
local kn2 = NormalizerBox.normalize_call_ints(k)
if kn2 == null { return null }
if SignatureVerifierBox.verify_call_name_arity(kn2.get("name"), kn2.get("args")) != 1 { return null }
local j4 = EmitCallBox.emit_call_int_args(kn2.get("name"), kn2.get("args"))
if j4 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j4))
}
// (scanner fallback handled above)
}
}
// Next Method (Return(Method ...)) — prefer extract box; recv is 0 (placeholder)
{
local mq = RegexFlow.find_from(ast_json, "\"type\":\"Method\"", 0)
if mq >= 0 {
local mb = MethodExtractBox.extract_return_method_ints(ast_json)
if mb != null {
local mn = NormalizerBox.normalize_method_ints(mb)
if mn == null { return null }
// Compile-time arity check (best-effort by method name)
if SignatureVerifierBox.verify_from_args(mn.get("method"), mn.get("args")) != 1 { return null }
local j5 = EmitMethodBox.emit_method_int_args(mn.get("method"), 0, mn.get("args"))
if j5 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j5))
}
// Lightweight Stage1 scanner as tolerant fallback (method/args only)
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pair = Stage1JsonScannerBox.extract_label_args(ast_json, "method", pos_body)
if pair != null {
local raw2 = pair.get("label")
local args2 = pair.get("args_text")
local fq2 = NamespaceBox.normalize_global_name(raw2, r)
if fq2 != null {
if SignatureVerifierBox.verify_from_args(fq2, args2) == 1 {
local j5b = EmitMethodBox.emit_method_int_args(fq2, 0, args2)
if j5b == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j5b))
}
}
}
}
local m = Stage1ExtractFlow.extract_return_method(ast_json)
if m != null {
local mn2 = NormalizerBox.normalize_method_ints(m)
if mn2 == null { return null }
if SignatureVerifierBox.verify_from_args(mn2.get("method"), mn2.get("args")) != 1 { return null }
local j5 = EmitMethodBox.emit_method_int_args(mn2.get("method"), 0, mn2.get("args"))
if j5 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j5))
}
}
}
// Next New (Return(New ...)) — prefer extract box
{
local nq = RegexFlow.find_from(ast_json, "\"type\":\"New\"", 0)
if nq >= 0 {
local nb = NewExtractBox.extract_return_new_ints(ast_json)
if nb != null {
local nn = NormalizerBox.normalize_new_ints(nb)
if nn == null { return null }
local j6 = EmitNewBoxBox.emit_newbox_int_args(nn.get("class"), nn.get("args"))
if j6 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j6))
}
local n = Stage1ExtractFlow.extract_return_new(ast_json)
if n != null {
local nn2 = NormalizerBox.normalize_new_ints(n)
if nn2 == null { return null }
local j6 = EmitNewBoxBox.emit_newbox_int_args(nn2.get("class"), nn2.get("args"))
if j6 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j6))
}
// Tolerant scanner for New: class/args only
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pairN = Stage1JsonScannerBox.extract_label_args(ast_json, "class", pos_body)
if pairN != null {
local rawC = pairN.get("label")
local argsN = pairN.get("args_text")
// No namespace resolution here (non-using path): emit directly
local j6b = EmitNewBoxBox.emit_newbox_int_args(rawC, argsN)
if j6b == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j6b))
}
}
}
}
// Try Compare first (avoid mis-detection by 'op' field inside Compare)
// Guard by raw scan to avoid calling methods on null-like values
{
local cq = RegexFlow.find_from(ast_json, "\"type\":\"Compare\"", 0)
if cq >= 0 {
// Prefer robust extractor box (returns ArrayBox [lv, rv, cmp])
local ce = CompareExtractBox.extract_return_compare_ints(ast_json)
if ce != null {
local final_lhs = ce.get(0) // ArrayBox: lhs at index 0
local final_rhs = ce.get(1) // ArrayBox: rhs at index 1
local cmp = ce.get(2) // ArrayBox: cmp at index 2
if trace == 1 { print("[trace] compare via box lhs=" + final_lhs + " rhs=" + final_rhs + " cmp=" + cmp) }
if prefer_cfg >= 1 {
local mat = 0
if prefer >= 2 { mat = 1 }
// Direct emit (no MapBox/Normalizer needed - values already normalized)
local j1 = EmitCompareBox.emit_compare_cfg3(final_lhs, final_rhs, cmp, mat, trace)
return LocalSSA.ensure_cond(j1)
} else {
// Direct emit (no MapBox/Normalizer needed - values already normalized)
local j0 = EmitCompareBox.emit_compare_ret(final_lhs, final_rhs, cmp, trace)
return LocalSSA.ensure_cond(j0)
}
}
// Fallback to legacy extractor (should rarely be used)
local c = Stage1ExtractFlow.extract_return_compare(ast_json)
if c != null {
local lhs = c.get("lhs")
local rhs = c.get("rhs")
local cmp2 = c.get("cmp")
if trace == 1 { print("[trace] compare via legacy lhs=" + lhs + " rhs=" + rhs + " cmp=" + cmp2) }
if prefer_cfg >= 1 {
local mat = 0
if prefer >= 2 { mat = 1 }
local raw3 = {cmp: cmp2, lhs: lhs, rhs: rhs}
local nn3 = NormalizerBox.normalize_cmp(raw3)
if nn3 == null { return null }
local j1 = EmitCompareBox.emit_compare_cfg3(nn3.get("lhs"), nn3.get("rhs"), nn3.get("cmp"), mat, trace)
return LocalSSA.ensure_cond(j1)
} else {
local raw4 = {cmp: cmp2, lhs: lhs, rhs: rhs}
local nn4 = NormalizerBox.normalize_cmp(raw4)
if nn4 == null { return null }
local j0 = EmitCompareBox.emit_compare_ret(nn4.get("lhs"), nn4.get("rhs"), nn4.get("cmp"), trace)
return LocalSSA.ensure_cond(j0)
}
}
}
}
// Then BinOp (guard by raw scan)
{
local bq = RegexFlow.find_from(ast_json, "\"type\":\"BinOp\"", 0)
if bq >= 0 {
local b = Stage1ExtractFlow.extract_return_binop(ast_json)
local lhs = b.get("lhs")
local rhs = b.get("rhs")
local kind = b.get("kind")
local j2 = EmitBinopBox.emit_binop2(lhs, rhs, kind, trace)
return LocalSSA.ensure_cond(j2)
}
}
// Fallback Return(Int)
local v = Stage1ExtractFlow.extract_return_int(ast_json)
local j3 = EmitReturnBox.emit_return_int2(v, trace)
return LocalSSA.ensure_cond(j3)
}
// Overload: resolve names via UsingResolverBox before emit
lower_stage1_to_mir_with_usings(ast_json, prefer_cfg, usings_json, modules_json) {
print("[DEBUG] === lower_stage1_to_mir_with_usings ENTRY ===")
print("[DEBUG] ast_json length=" + ast_json.length())
print("[DEBUG] usings_json=" + usings_json)
print("[DEBUG] modules_json=" + modules_json)
if ast_json == null { return PipelineV2.lower_stage1_to_mir(ast_json, prefer_cfg) }
// Build resolver context
local r = UsingResolverBox.state_new()
if usings_json != null { UsingResolverBox.load_usings_json(r, usings_json) }
if modules_json != null { UsingResolverBox.load_modules_json(r, modules_json) }
// Upgrade alias→namespace mapping now that modules are loaded (ModuleFirst)
UsingResolverBox.upgrade_aliases(r)
print("[DEBUG] upgrade_aliases complete")
// Prefer Call/Method/New branches with name normalization; otherwise fallback to default
{
local kq = RegexFlow.find_from(ast_json, "\"type\":\"Call\"", 0)
if kq >= 0 {
// Strict preflight via tolerant scanner: read raw name and enforce using alias resolution
{
local scan0 = Stage1JsonScannerBox.extract_name_args(ast_json, kq)
if scan0 != null {
local raw_head = scan0.get("name")
print("[DEBUG] preflight raw_head=" + raw_head)
local dot = RegexFlow.find_from(raw_head, ".", 0)
if dot >= 0 {
local head = raw_head.substring(0, dot)
local resolved = UsingResolverBox.resolve_namespace_alias(r, head)
print("[DEBUG] preflight head=" + head + " resolved=" + resolved)
if resolved == null {
print("[ERROR] Unresolved using alias: " + head)
return null
}
}
}
}
// Prefer lightweight Stage1 scanner firstplugins OFFでも安全
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local pair = Stage1JsonScannerBox.extract_name_args(ast_json, pos_body)
if pair != null {
local raw2 = pair.get("name")
local args2 = pair.get("args_text")
local fq2 = NamespaceBox.normalize_global_name(raw2, r)
if fq2 != null {
local j4b = EmitCallBox.emit_call_int_args(fq2, args2)
if j4b == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j4b))
}
}
}
// Structured extract配列/Map依存。plugins ON時の静的パス
local kc = CallExtractBox.extract_return_call_ints(ast_json)
if kc != null {
local kn = NormalizerBox.normalize_call_ints(kc)
if kn == null {
print("[DEBUG] normalize_call_ints returned null")
return null
}
local raw_name = kn.get("name")
print("[DEBUG] raw_name before normalize=" + raw_name)
if SignatureVerifierBox.verify_call_name_arity(raw_name, kn.get("args")) != 1 {
print("[DEBUG] verify_call_name_arity failed for " + raw_name)
return null
}
local fq_name = NamespaceBox.normalize_global_name(raw_name, r)
print("[DEBUG] fq_name after normalize=" + fq_name)
if fq_name == null {
print("[DEBUG] normalize_global_name returned null for raw_name=" + raw_name)
return null
}
local j4 = EmitCallBox.emit_call_int_args(fq_name, kn.get("args"))
if j4 == null {
print("[DEBUG] emit_call_int_args returned null")
return null
}
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j4))
}
}
}
{
local nq = RegexFlow.find_from(ast_json, "\"type\":\"New\"", 0)
if nq >= 0 {
local nb = NewExtractBox.extract_return_new_ints(ast_json)
if nb != null {
local nn = NormalizerBox.normalize_new_ints(nb)
if nn == null { return null }
local raw_c = nn.get("class")
local fq_c = NamespaceBox.normalize_class_name(raw_c, r)
if fq_c == null { return null }
local j6 = EmitNewBoxBox.emit_newbox_int_args(fq_c, nn.get("args"))
if j6 == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j6))
}
// Scanner fallback: class + args → normalizer → emit
{
local pos_body = Stage1JsonScannerBox.find_body_start(ast_json)
if pos_body < 0 { pos_body = 0 }
local nscan = Stage1JsonScannerBox.extract_label_args(ast_json, "class", pos_body)
if nscan != null {
local normN = NameArgsNormBox.normalize_class(nscan, r)
if normN != null {
local j6b = EmitNewBoxBox.emit_newbox_int_args(normN.get("class"), normN.get("args_text"))
if j6b == null { return null }
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j6b))
}
}
}
}
}
// Fallback to default lowering (includes Compare/Method and other forms)
return PipelineV2.lower_stage1_to_mir(ast_json, prefer_cfg)
}
}

View File

@ -0,0 +1,15 @@
// pipeline_emit_box.hako — PipelineEmitBox
// Responsibility: small wrappers that emit MIR(JSON v0) and apply LocalSSA ensures
using "lang/src/compiler/pipeline_v2/emit_call_box.hako" as EmitCallBox
using "lang/src/compiler/pipeline_v2/local_ssa_box.hako" as LocalSSABox
static box PipelineEmitBox {
// Emit Call(name, int-args) → JSON v0, wrapped with LocalSSA ensures
emit_call_int_args_v0(name, args) {
local j = EmitCallBox.emit_call_int_args(name, args)
return LocalSSABox.ensure_calls(LocalSSABox.ensure_cond(j))
}
}
static box PipelineEmitMain { main(args) { return 0 } }

View File

@ -0,0 +1,65 @@
// pipeline_helpers_box.hako — Pipeline解析ヘルパの独立Box
// 責務: JSON/数値解析の共通ユーティリティ
// Extracted from PipelineV2 flow (556行 → 506行、-50行削減)
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box PipelineHelpersBox {
// Parse integer at specific position (whitespace-aware, sign-aware)
parse_int_at(s, idx) {
if s == null { return null }
local i = idx
loop(true) {
local ch = s.substring(i, i+1)
if ch == "" { return null }
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { i = i + 1 continue }
break
}
local neg = 0
local ch = s.substring(i, i+1)
if ch == "-" { neg = 1 i = i + 1 }
local acc = 0
local matched = 0
loop(true) {
ch = s.substring(i, i+1)
if ch == "" { break }
local digit_idx = "0123456789".indexOf(ch)
if digit_idx >= 0 {
matched = 1
acc = acc * 10 + digit_idx
i = i + 1
} else { break }
}
if matched == 0 { return null }
if neg == 1 { acc = 0 - acc }
return acc
}
// Parse integer after prefix string (search + extract pattern)
parse_int_after_prefix(s, prefix, search_pos) {
local p = RegexFlow.find_from(s, prefix, search_pos)
if p < 0 { return null }
local res = PipelineHelpersBox.parse_int_at(s, p + prefix.size())
return res
}
// Parse Compare values from Stage-1 JSON (returns [lhs, rhs, cmp])
parse_compare_values(ast_json, start_pos) {
if ast_json == null { return null }
local op_pos = RegexFlow.find_from(ast_json, "\"op\":\"", start_pos)
if op_pos < 0 { return null }
local cmp_start = op_pos + 6
local cmp_end = RegexFlow.find_from(ast_json, "\"", cmp_start)
if cmp_end < 0 { return null }
local cmp = ast_json.substring(cmp_start, cmp_end)
local lhs_val = PipelineHelpersBox.parse_int_after_prefix(ast_json, "\"lhs\":{\"type\":\"Int\",\"value\":", start_pos)
if lhs_val == null { return null }
local rhs_val = PipelineHelpersBox.parse_int_after_prefix(ast_json, "\"rhs\":{\"type\":\"Int\",\"value\":", start_pos)
if rhs_val == null { return null }
return [lhs_val, rhs_val, cmp]
}
// Delegate to StringHelpers for type conversion
to_i64(v) { return StringHelpers.to_i64(v) }
}

View File

@ -0,0 +1,31 @@
// readonly_map_view.hako — 読み取り専用ビュー(誤更新防止用)
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box ReadOnlyMapView {
_m
of(m) {
local v = new ReadOnlyMapView()
v._m = m
return v
}
has(key) { return call("MapBox.has/2", me._m, key) }
get(key) { return BoxHelpers.map_get(me._m, key) }
set(key, val) {
print("[ReadOnlyMapView] set forbidden: key=" + (""+key))
return 0
}
delete(key) {
print("[ReadOnlyMapView] delete forbidden: key=" + (""+key))
return 0
}
clear() {
print("[ReadOnlyMapView] clear forbidden")
return 0
}
}
static box ReadOnlyMapViewStub { main(args) { return 0 } }

View File

@ -0,0 +1,95 @@
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
flow RegexFlow {
// Minimal regex-like helpers focused on readability and determinism.
// Supported primitives: digits (\d+), identifier ([A-Za-z_][A-Za-z0-9_]*),
// literal substring find, read_digits from position.
// Read optional whitespace + optional '-' + digits
digits_from(s, pos) {
if s == null { return "" }
if pos < 0 { pos = 0 }
local out = ""
local i = pos
// skip ASCII whitespaces
loop(true) {
local ch0 = s.substring(i, i+1)
if ch0 == "" { break }
if ch0 == " " || ch0 == "\t" || ch0 == "\n" || ch0 == "\r" { i = i + 1 continue }
break
}
// optional minus sign
local ch = s.substring(i, i+1)
if ch == "-" { out = out + ch i = i + 1 }
// digits
loop(true) {
ch = s.substring(i, i+1)
if ch == "" { break }
if ch >= "0" && ch <= "9" { out = out + ch i = i + 1 } else { break }
}
return out
}
ident_from(s, pos) {
// [A-Za-z_][A-Za-z0-9_]*
if s == null { return "" }
if pos < 0 { pos = 0 }
local out = ""
local i = pos
local ch = s.substring(i, i+1)
if !((ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z") || ch == "_") { return "" }
out = out + ch
i = i + 1
loop(true) {
ch = s.substring(i, i+1)
if ch == "" { break }
if ((ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z") || (ch >= "0" && ch <= "9") || ch == "_") {
out = out + ch
i = i + 1
} else { break }
}
return out
}
find_from(s, needle, pos) {
if s == null { return -1 }
if needle == null { return -1 }
if pos < 0 { pos = 0 }
local n = s.size()
local m = needle.size()
if m == 0 { return pos }
local i = pos
local limit = n - m
loop(i <= limit) {
local seg = s.substring(i, i+m)
if seg == needle { return i }
i = i + 1
}
return -1
}
last_index_of(s, needle) {
// Delegate to StringHelpers (more efficient backward search)
return StringHelpers.last_index_of(s, needle)
}
to_int(digits) {
if digits == null { return 0 }
local n = digits.size()
if n == 0 { return 0 }
local i = 0
local neg = 0
if digits.substring(0,1) == "-" { neg = 1 i = 1 }
local acc = 0
loop(i < n) {
local ch = digits.substring(i, i+1)
if ch < "0" || ch > "9" { break }
local d = "0123456789".indexOf(ch)
if d < 0 { break }
acc = acc * 10 + d
i = i + 1
}
if neg == 1 { return 0 - acc }
return acc
}
}

View File

@ -0,0 +1,111 @@
// signature_verifier_box.hako — SignatureVerifierBox
// Responsibility: compile-time arity checks for common built-in methods.
// Notes:
// - Stage1 JSON at this phase does not carry receiver type, so we verify
// by method name only for known methods where arity is uniform across boxes
// (e.g., indexOf=1 on String/Array, push=1 on Array, etc.).
// - Unknown methods are allowed (return 1) to avoid over-blocking during bring-up.
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
// Method registry is optional; allow unknowns by design in bring-up.
static box SignatureVerifierBox {
// Return 1 if (method, argc) is acceptable; 0 otherwise.
verify_method(method, argc) {
if method == null { return 1 }
// Registry check skipped (optional). Unknown names are allowed during bring-up.
// Fallback legacy (kept for compatibility)
local m = "" + method
local a = ("" + argc) + ""
// Stringlike
if m == "len" && a == "0" { return 1 }
if m == "length" && a == "0" { return 1 }
if m == "substring" && a == "2" { return 1 }
if m == "concat" && a == "1" { return 1 }
if m == "indexOf" && a == "1" { return 1 }
if m == "replace" && a == "2" { return 1 }
if m == "trim" && a == "0" { return 1 }
if m == "toUpper" && a == "0" { return 1 }
if m == "toLower" && a == "0" { return 1 }
// Arraylike
if m == "get" && a == "1" { return 1 }
if m == "set" && a == "2" { return 1 }
if m == "push" && a == "1" { return 1 }
if m == "pop" && a == "0" { return 1 }
if m == "clear" && a == "0" { return 1 }
if m == "contains" && a == "1" { return 1 }
if m == "join" && a == "1" { return 1 }
if m == "slice" && a == "2" { return 1 }
// Maplike
if m == "size" && a == "0" { return 1 }
if m == "has" && a == "1" { return 1 }
if m == "delete" && a == "1" { return 1 }
if m == "remove" && a == "1" { return 1 }
if m == "keys" && a == "0" { return 1 }
if m == "values" && a == "0" { return 1 }
if m == "toJSON" && a == "0" { return 1 }
// Unknown method: allow (return 1) for now
return 1
}
// Count integer args from Stage1 args JSON (shape: [ {"type":"Int","value":N}, ... ])
_count_int_args(args) {
if args == null { return 0 }
local s = "" + args
local pos = 0
local n = 0
loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" {
pos = pos + 1
} else {
n = n + 1
// advance to the end of this digit run
local p2 = RegexFlow.find_from(s, ds, pos)
if p2 < 0 { pos = pos + ds.size() } else { pos = p2 + ds.size() }
}
if pos >= s.size() { break }
}
return n
}
// Convenience: verify from args JSON directly; prints an error on mismatch.
// Returns 1 if OK, 0 if mismatch.
verify_from_args(method, args) {
local argc = SignatureVerifierBox._count_int_args(args)
local ok = SignatureVerifierBox.verify_method(method, argc)
if ok != 1 {
print("[ERROR] No matching method by arity: " + method + "/" + (""+argc))
return 0
}
return 1
}
// Name-aware call verifier for global functions like "StringBox.indexOf".
// If name pattern matches a known built-in box, validate by registry; otherwise allow.
verify_call_name_arity(name, args) {
if name == null { return 1 }
local argc = SignatureVerifierBox._count_int_args(args)
local s = "" + name
// Split at last '.' for method name
local last = RegexFlow.last_index_of(s, ".")
if last < 0 { return 1 }
local method = s.substring(last + 1, s.size())
// Determine class token just before method (penultimate segment)
local head_all = s.substring(0, last)
local prev = RegexFlow.last_index_of(head_all, ".")
local head = head_all
if prev >= 0 {
head = head_all.substring(prev + 1, head_all.size())
}
// Normalize head to Box name
local bxname = head
if head == "String" { bxname = "StringBox" }
if head == "Array" { bxname = "ArrayBox" }
if head == "Map" { bxname = "MapBox" }
if bxname == "StringBox" || bxname == "ArrayBox" || bxname == "MapBox" { return 1 }
return 1
}
}
static box SignatureVerifierMain { main(args) { return 0 } }

View File

@ -0,0 +1,67 @@
// stage1_args_parser_box.hako — Stage1ArgsParserBox
// Responsibility: Parse Stage1 args JSON text into integers (MVP: Int only).
// NonResponsibility: MIR emit or namespace.
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
static box Stage1ArgsParserBox {
// Count Int occurrences in Stage1 args JSON text.
count_ints(args_text) {
if args_text == null { return 0 }
local s = "" + args_text
local pos = 0
local n = 0
loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { n = n + 1 pos = pos + ds.size() }
if pos >= s.size() { break }
}
return n
}
// Parse ints from args_text and return ArrayBox [i64,...].
parse_ints(args_text) {
if args_text == null { return [] }
local s = "" + args_text
if me._only_int_nodes(s) != 1 { return null }
local pos = 0
local out = []
loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" {
pos = pos + 1
} else {
out.push(RegexFlow.to_int(ds))
// advance to end of this token to avoid re-matching
local p2 = RegexFlow.find_from(s, ds, pos)
if p2 < 0 { pos = pos + ds.size() } else { pos = p2 + ds.size() }
}
if pos >= s.size() { break }
}
return out
}
_only_int_nodes(s) {
if s == null { return 1 }
local lb = JsonCursorBox.find_from(s, "[", 0)
if lb < 0 { return 1 }
local rb = JsonCursorBox.seek_array_end(s, lb)
if rb < 0 { rb = s.size() }
local chk = RegexFlow.find_from(s, "\"type\":\"", lb)
if chk < 0 || chk >= rb { return 1 }
local pos = chk
loop(true) {
if pos < 0 || pos >= rb { break }
local ty_start = pos + 8
local ty_end = RegexFlow.find_from(s, "\"", ty_start)
if ty_end < 0 || ty_end >= rb { return 0 }
local ty = s.substring(ty_start, ty_end)
if ty != "Int" { return 0 }
pos = RegexFlow.find_from(s, "\"type\":\"", ty_end + 1)
if pos < 0 || pos >= rb { break }
}
return 1
}
}
static box Stage1ArgsParserMain { main(args) { return 0 } }

View File

@ -0,0 +1,209 @@
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
flow Stage1ExtractFlow {
// Extract minimal info from Stage1 JSON (Return Int / BinOp / Compare)
_idx(s, needle) { return RegexFlow.find_from(s, needle, 0) }
_idx_from(s, needle, pos) { return RegexFlow.find_from(s, needle, pos) }
extract_return_int(ast_json) {
if ast_json == null { return 0 }
local ret_key = "\"type\":\"Return\""
local p = RegexFlow.last_index_of(ast_json, ret_key)
if p < 0 { p = Stage1ExtractFlow._idx(ast_json, ret_key) }
if p < 0 { return 0 }
// whitespace tolerant: find 'type:"Int"' then subsequent 'value:'
local tpos = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Int\"", p)
if tpos < 0 { return 0 }
local vpos = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", tpos)
if vpos < 0 { return 0 }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
return RegexFlow.to_int(ds)
}
extract_return_binop(ast_json) {
// Return(BinOp) → {kind, lhs, rhs} or null
if ast_json == null { return null }
local p = Stage1ExtractFlow._idx(ast_json, "\"type\":\"Return\"")
if p < 0 { return null }
local q = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"BinOp\"", p)
if q < 0 { return null }
local opk_pos = Stage1ExtractFlow._idx_from(ast_json, "\"op\":\"", q)
if opk_pos < 0 { return null }
local opk_end = Stage1ExtractFlow._idx_from(ast_json, "\"", opk_pos + 6)
if opk_end < 0 { return null }
local opk = ast_json.substring(opk_pos + 6, opk_end)
// lhs/rhs int (whitespace tolerant)
local lhsp = Stage1ExtractFlow._idx_from(ast_json, "\"lhs\"", q)
local rhsp = Stage1ExtractFlow._idx_from(ast_json, "\"rhs\"", q)
if lhsp < 0 || rhsp < 0 { return null }
local lval = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", lhsp)
local rval = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", rhsp)
if lval < 0 || rval < 0 { return null }
local lhs_ds = RegexFlow.digits_from(ast_json, lval + 8)
local rhs_ds = RegexFlow.digits_from(ast_json, rval + 8)
if lhs_ds == "" || rhs_ds == "" { return null }
local lhs = RegexFlow.to_int(lhs_ds)
local rhs = RegexFlow.to_int(rhs_ds)
local kind = opk
return { kind: kind, lhs: lhs, rhs: rhs }
}
extract_return_compare(ast_json) {
// Return(Compare) → {cmp, lhs, rhs} or null
if ast_json == null { return null }
local p = Stage1ExtractFlow._idx(ast_json, "\"type\":\"Return\"")
if p < 0 { return null }
local q = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Compare\"", p)
if q < 0 { return null }
local opk_pos = Stage1ExtractFlow._idx_from(ast_json, "\"op\":\"", q)
if opk_pos < 0 { return null }
local opk_end = Stage1ExtractFlow._idx_from(ast_json, "\"", opk_pos + 6)
if opk_end < 0 { return null }
local opk = ast_json.substring(opk_pos + 6, opk_end)
// lhs/rhs int (whitespace tolerant)
local lhsp = Stage1ExtractFlow._idx_from(ast_json, "\"lhs\"", q)
local rhsp = Stage1ExtractFlow._idx_from(ast_json, "\"rhs\"", q)
if lhsp < 0 || rhsp < 0 { return null }
local lval = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", lhsp)
local rval = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", rhsp)
if lval < 0 || rval < 0 { return null }
local lhs_ds = RegexFlow.digits_from(ast_json, lval + 8)
local rhs_ds = RegexFlow.digits_from(ast_json, rval + 8)
if lhs_ds == "" || rhs_ds == "" { return null }
local lhs = RegexFlow.to_int(lhs_ds)
local rhs = RegexFlow.to_int(rhs_ds)
return { cmp: opk, lhs: lhs, rhs: rhs }
}
// If(cond=Compare(lhs,rhs,op), then=[Return(Int 1)], else=[Return(Int 0)]) → {cmp, lhs, rhs} or null
extract_if_compare(ast_json) {
if ast_json == null { return null }
// Find If node first (tolerant to whitespace)
local ip = RegexFlow.find_from(ast_json, "\"type\":\"If\"", 0)
if ip < 0 { return null }
// Find Compare under/after If
local q = RegexFlow.find_from(ast_json, "\"type\":\"Compare\"", ip)
if q < 0 { return null }
// Extract op
local opk_pos = RegexFlow.find_from(ast_json, "\"op\":\"", q)
if opk_pos < 0 { return null }
local opk_end = RegexFlow.find_from(ast_json, "\"", opk_pos + 6)
if opk_end < 0 { return null }
local opk = ast_json.substring(opk_pos + 6, opk_end)
// Extract lhs/rhs integer values
local lhsp = RegexFlow.find_from(ast_json, "\"lhs\"", q)
local rhsp = RegexFlow.find_from(ast_json, "\"rhs\"", q)
if lhsp < 0 || rhsp < 0 { return null }
local lval = RegexFlow.find_from(ast_json, "\"value\":", lhsp)
local rval = RegexFlow.find_from(ast_json, "\"value\":", rhsp)
if lval < 0 || rval < 0 { return null }
local lhs_ds = RegexFlow.digits_from(ast_json, lval + 8)
local rhs_ds = RegexFlow.digits_from(ast_json, rval + 8)
if lhs_ds == "" || rhs_ds == "" { return null }
local lhs = RegexFlow.to_int(lhs_ds)
local rhs = RegexFlow.to_int(rhs_ds)
return { cmp: opk, lhs: lhs, rhs: rhs }
}
// Return(Method recv, method, args) → {method, args:[int,...]} or null
extract_return_method(ast_json) {
if ast_json == null { return null }
local p = Stage1ExtractFlow._idx(ast_json, "\"type\":\"Return\"")
if p < 0 { return null }
local q = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Method\"", p)
if q < 0 { return null }
if Stage1IntArgsExtractBox.validate_only_int_args(ast_json, q) != 1 { return null }
// method name
local mk = Stage1ExtractFlow._idx_from(ast_json, "\"method\":\"", q)
if mk < 0 { return null }
local mk_end = Stage1ExtractFlow._idx_from(ast_json, "\"", mk + 10)
if mk_end < 0 { return null }
local mname = ast_json.substring(mk + 10, mk_end)
// args (integers only; recv は無視)
local ak = Stage1ExtractFlow._idx_from(ast_json, "\"args\":[", q)
local args = []
if ak >= 0 {
local rb = Stage1ExtractFlow._idx_from(ast_json, "]", ak)
if rb < 0 { rb = ast_json.size() }
local i = ak
loop(true) {
local tpos = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Int\"", i)
if tpos < 0 || tpos >= rb { break }
local vpos = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", tpos)
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { args.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
}
}
return { method: mname, args: args }
}
// Return(New class(args)) → {class, args:[int,...]} or null
extract_return_new(ast_json) {
if ast_json == null { return null }
local p = Stage1ExtractFlow._idx(ast_json, "\"type\":\"Return\"")
if p < 0 { return null }
local q = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"New\"", p)
if q < 0 { return null }
local ck = Stage1ExtractFlow._idx_from(ast_json, "\"class\":\"", q)
if ck < 0 { return null }
local ck_end = Stage1ExtractFlow._idx_from(ast_json, "\"", ck + 9)
if ck_end < 0 { return null }
local cname = ast_json.substring(ck + 9, ck_end)
if Stage1IntArgsExtractBox.validate_only_int_args(ast_json, q) != 1 { return null }
// args integers only
local ak = Stage1ExtractFlow._idx_from(ast_json, "\"args\":[", q)
local args = []
if ak >= 0 {
local rb = Stage1ExtractFlow._idx_from(ast_json, "]", ak)
if rb < 0 { rb = ast_json.size() }
local i = ak
loop(true) {
local tpos = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Int\"", i)
if tpos < 0 || tpos >= rb { break }
local vpos = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", tpos)
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { args.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
}
}
return { class: cname, args: args }
}
// Return(Call name(args...)) → {name, args:[int,...]} or null
extract_return_call(ast_json) {
if ast_json == null { return null }
// Return → Call 検出
local p = Stage1ExtractFlow._idx(ast_json, "\"type\":\"Return\"")
if p < 0 { return null }
local q = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Call\"", p)
if q < 0 { return null }
if Stage1IntArgsExtractBox.validate_only_int_args(ast_json, q) != 1 { return null }
// name 抽出
local nk = Stage1ExtractFlow._idx_from(ast_json, "\"name\":\"", q)
if nk < 0 { return null }
local nk_end = Stage1ExtractFlow._idx_from(ast_json, "\"", nk + 8)
if nk_end < 0 { return null }
local name = ast_json.substring(nk + 8, nk_end)
// args 抽出(整数に限定): args 配列内の Int ノードのみを拾う
local ak = Stage1ExtractFlow._idx_from(ast_json, "\"args\":[", q)
if ak < 0 { return { name: name, args: [] } }
local rb = Stage1ExtractFlow._idx_from(ast_json, "]", ak)
if rb < 0 { rb = ast_json.size() }
local args = []
local i = ak
loop(true) {
local tpos = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Int\"", i)
if tpos < 0 || tpos >= rb { break }
local vpos = Stage1ExtractFlow._idx_from(ast_json, "\"value\":", tpos)
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { args.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
}
return { name: name, args: args }
}
}

View File

@ -0,0 +1,127 @@
// Stage1IntArgsExtractBox — 統合Extract: Return(Call/Method/New) から名前と整数引数を抽出
// Responsibility: 3つの類似ExtractBoxを統合call_extract/method_extract/new_extract
// Notes: 85%重複コードを共通化、args抽出ロジックを一元管理
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
static box Stage1IntArgsExtractBox {
// ===== 汎用Extract内部実装 =====
extract_return_expr_ints(ast_json, expr_type, name_key, name_offset) {
if ast_json == null { return null }
// Step 1: Return 探索
local rq = RegexFlow.find_from(ast_json, "\"type\":\"Return\"", 0)
if rq < 0 { return null }
// Step 2: Expr type 探索Call/Method/New
local q = RegexFlow.find_from(ast_json, "\"type\":\"" + expr_type + "\"", rq)
if q < 0 { return null }
// Step 3: 名前抽出
local nk = RegexFlow.find_from(ast_json, name_key, q)
if nk < 0 { return null }
local nk_end = RegexFlow.find_from(ast_json, "\"", nk + name_offset)
if nk_end < 0 { return null }
local name = ast_json.substring(nk + name_offset, nk_end)
// Step 4: args抽出Int only、共通ロジック
local args = me._extract_int_args(ast_json, q)
return { name: name, args: args }
}
// ===== 内部ヘルパーInt引数抽出共通化 =====
_extract_int_args(ast_json, start_pos) {
local ak = RegexFlow.find_from(ast_json, "\"args\":[", start_pos)
local vals = []
if ak < 0 { return vals }
// bracket-aware end detection
local lb = RegexFlow.find_from(ast_json, "[", ak)
local rb = ast_json.size()
if lb >= 0 {
local i2 = lb + 1
local depth = 1
loop(true) {
local ch = ast_json.substring(i2, i2+1)
if ch == "" { break }
if ch == "[" { depth = depth + 1 } else { if ch == "]" { depth = depth - 1 } }
if depth == 0 { rb = i2 break }
i2 = i2 + 1
}
if me._ensure_only_int_types(ast_json, lb, rb) != 1 { return null }
}
// scan ints within ak..rb
local i = ak
loop(true) {
local tpos = RegexFlow.find_from(ast_json, "\"type\":\"Int\"", i)
if tpos < 0 || tpos >= rb { break }
local vpos = RegexFlow.find_from(ast_json, "\"value\":", tpos)
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { vals.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
}
return vals
}
_ensure_only_int_types(ast_json, array_start, array_end) {
local check = array_start
loop(true) {
local tpos = RegexFlow.find_from(ast_json, "\"type\":\"", check)
if tpos < 0 || tpos >= array_end { break }
local ty_start = tpos + 8
local ty_end = RegexFlow.find_from(ast_json, "\"", ty_start)
if ty_end < 0 || ty_end >= array_end { return 0 }
local ty = ast_json.substring(ty_start, ty_end)
if ty != "Int" {
return 0
}
check = ty_end + 1
}
return 1
}
validate_only_int_args(ast_json, expr_pos) {
local ak = RegexFlow.find_from(ast_json, "\"args\":[", expr_pos)
if ak < 0 { return 1 }
local lb = RegexFlow.find_from(ast_json, "[", ak)
if lb < 0 { return 1 }
local rb = ast_json.size()
local i2 = lb + 1
local depth = 1
loop(true) {
local ch = ast_json.substring(i2, i2+1)
if ch == "" { break }
if ch == "[" { depth = depth + 1 } else { if ch == "]" { depth = depth - 1 } }
if depth == 0 { rb = i2 break }
i2 = i2 + 1
}
return me._ensure_only_int_types(ast_json, lb, rb)
}
// ===== 公開API後方互換 =====
extract_return_call_ints(ast_json) {
return me.extract_return_expr_ints(ast_json, "Call", "\"name\":\"", 8)
}
extract_return_method_ints(ast_json) {
local result = me.extract_return_expr_ints(ast_json, "Method", "\"method\":\"", 10)
if result == null { return null }
// method key for backward compatibility
return { method: result.name, args: result.args }
}
extract_return_new_ints(ast_json) {
local result = me.extract_return_expr_ints(ast_json, "New", "\"class\":\"", 9)
if result == null { return null }
// class key for backward compatibility
return { class: result.name, args: result.args }
}
}
static box Stage1IntArgsExtractStub { main(args) { return 0 } }

View File

@ -0,0 +1,99 @@
// stage1_json_scanner_box.hako — Stage1JsonScannerBox
// Responsibility: Provide common scans for Stage1 JSON strings used by PipelineV2
// - body anchor detection
// - key search (plain/escaped/": " tolerant)
// - value start after key
// - name/args basic extraction
// NonResponsibility: MIR emit, namespace resolution, IO
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box Stage1JsonScannerBox {
// Find the start position of Program.body array key
find_body_start(ast_json) {
if ast_json == null { return -1 }
local s = "" + ast_json
local pos = JsonCursorBox.find_key_dual(s, "\"body\":[", "\\\"body\\\":[", 0)
return pos
}
// Tolerant key finder: exact/escaped/colon+space
find_key(ast_json, key, start_pos) {
if ast_json == null { return -1 }
local s = "" + ast_json
local plain = "\"" + key + "\":\""
local escaped = "\\\"" + key + "\\\":\\\""
local p1 = JsonCursorBox.find_key_dual(s, plain, escaped, start_pos)
if p1 >= 0 { return p1 }
if plain.size() >= 2 {
local head = plain.substring(0, plain.size() - 1)
local last = plain.substring(plain.size() - 1, plain.size())
local spaced = head + " " + last
local p2 = JsonCursorBox.find_from(s, spaced, start_pos)
if p2 >= 0 { return p2 }
// Escaped + spaced: tolerate JSON embedded as string with colon-space
if escaped.size() >= 2 {
local ehead = escaped.substring(0, escaped.size() - 1)
local elast = escaped.substring(escaped.size() - 1, escaped.size())
local espaced = ehead + " " + elast
local p3 = JsonCursorBox.find_from(s, espaced, start_pos)
if p3 >= 0 { return p3 }
}
}
return -1
}
// Compute the index of first char of a JSON string value following a key at key_pos
value_start_after_key_pos(s, key_pos) {
if s == null { return -1 }
local i = key_pos
local n = s.size()
loop(i < n) {
local ch = s.substring(i,i+1)
if ch == ":" { i = i + 1 break }
i = i + 1
}
loop(i < n) {
local ch2 = s.substring(i,i+1)
if ch2 == " " || ch2 == "\t" { i = i + 1 continue }
break
}
if i < n {
local ch0 = s.substring(i, i+1)
if ch0 == "\\" { if i + 1 < n && s.substring(i+1,i+2) == "\"" { return i + 2 } else { return i } }
if ch0 == "\"" { return i + 1 } else { return i }
}
return -1
}
// Extract label (generic) and args slice starting at/after start_pos; returns map or null
extract_label_args(ast_json, label_key, start_pos) {
if ast_json == null { return null }
local s = "" + ast_json
local npos = me.find_key(s, label_key, start_pos)
local apos = me.find_key(s, "args", start_pos)
if npos < 0 || apos < 0 { return null }
local vstart = me.value_start_after_key_pos(s, npos)
if vstart < 0 { return null }
local nend = JsonCursorBox.find_from(s, "\"", vstart)
if nend <= vstart { return null }
local label = s.substring(vstart, nend)
local lb = JsonCursorBox.find_from(s, "[", apos)
local rb = s.size()
if lb >= 0 {
// Use JsonCursorBox for escape-aware array end seeking
local rb_result = JsonCursorBox.seek_array_end(s, lb)
if rb_result >= lb { rb = rb_result }
}
local args_text = s.substring(apos, rb)
return map({ label: label, args_text: args_text, label_pos: npos, args_pos: apos, label_key: label_key })
}
// Backward compatible helper for Call (label_key = "name")
extract_name_args(ast_json, start_pos) {
local m = me.extract_label_args(ast_json, "name", start_pos)
if m == null { return null }
call("MapBox.set/3", m, "name", BoxHelpers.map_get(m, "label"))
return m
}
}
static box Stage1JsonScannerMain { main(args){ return 0 } }

View File

@ -0,0 +1,60 @@
// stage1_name_args_normalizer_box.hako — Stage1NameArgsNormalizerBox
// Responsibility: Normalize (label, args_text) from Stage1 scanner into names ready for emit.
// - Call: name normalization via UsingResolverBox/NamespaceBox, arity sanity via SignatureVerifierBox
// - Method: same as Call but for method label
// - New: class normalization via UsingResolverBox/NamespaceBox
// NonResponsibility: MIR emit or IO
using "lang/src/compiler/pipeline_v2/namespace_box.hako" as NamespaceBox
using "lang/src/compiler/pipeline_v2/signature_verifier_box.hako" as SignatureVerifierBox
using "lang/src/compiler/pipeline_v2/alias_preflight_box.hako" as AliasPreflightBox
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box Stage1NameArgsNormalizerBox {
_label_or_name(scan) {
if scan == null { return null }
local n = BoxHelpers.map_get(scan, "name")
if n != null { return n }
return BoxHelpers.map_get(scan, "label")
}
// Returns { name, args_text } or null
normalize_call(scan, r) {
if scan == null { return null }
local raw = me._label_or_name(scan)
if raw == null || raw == "" { return null }
if AliasPreflightBox.check_head(raw, r) != 1 { return null }
local fq = NamespaceBox.normalize_global_name(raw, r)
if fq == null { return null }
local args = BoxHelpers.map_get(scan, "args_text")
// Best-effort arity check for globals
if SignatureVerifierBox.verify_call_name_arity(fq, args) != 1 { return null }
return { name: fq, args_text: args }
}
// Returns { method, args_text } or null
normalize_method(scan, r) {
if scan == null { return null }
local raw = me._label_or_name(scan)
if raw == null || raw == "" { return null }
if AliasPreflightBox.check_head(raw, r) != 1 { return null }
local fq = NamespaceBox.normalize_global_name(raw, r)
if fq == null { return null }
local args = BoxHelpers.map_get(scan, "args_text")
if SignatureVerifierBox.verify_from_args(fq, args) != 1 { return null }
return { method: fq, args_text: args }
}
// Returns { class, args_text } or null
normalize_class(scan, r) {
if scan == null { return null }
local raw = me._label_or_name(scan)
if raw == null || raw == "" { return null }
// class names are resolved through class resolver (no alias head check needed but harmless)
local fq = NamespaceBox.normalize_class_name(raw, r)
if fq == null { return null }
return { class: fq, args_text: BoxHelpers.map_get(scan, "args_text") }
}
}
static box Stage1NameArgsNormalizerMain { main(args) { return 0 } }

View File

@ -0,0 +1,37 @@
// terminator_guard_box.hako — TerminatorGuardBox
// Responsibility: Provide a thin, unified guard to prevent emit after terminator.
// Notes: Operates on an instructions ArrayBox of Map nodes with key "op".
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
static box TerminatorGuardBox {
// Return 1 if the given instructions array ends with ret/throw
block_terminated_insts(insts) {
if insts == null { return 0 }
local n = BoxHelpers.array_len(insts)
if n <= 0 { return 0 }
local last = BoxHelpers.array_get(insts, n - 1)
if last == null { return 0 }
local op = BoxHelpers.map_get(last, "op")
if op == null { return 0 }
if op == "ret" || op == "throw" { return 1 }
return 0
}
// Unified message; kept stable for tests
print_error(opname) {
if opname == null { opname = "op" }
print("[ERROR] TerminatorGuard: emit after terminator forbidden (attempt=" + opname + ")")
}
// Check before emit; returns 1 if allowed, 0 if blocked (and prints message)
guard_before_emit_insts(insts, opname) {
if me.block_terminated_insts(insts) == 1 {
me.print_error(opname)
return 0
}
return 1
}
}
static box TerminatorGuardStub { main(args) { return 0 } }

View File

@ -0,0 +1,70 @@
// UsingResolverBox — static, stateful resolver helpersインスタンス禁止・VM互換
// State layout (Map): {
// alias_paths: Map, alias_names: Map, alias_keys: Array,
// modules_map: Map, modules_keys: Array
// }
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
static box UsingResolverBox {
// Lightweight state as String: holds modules_json only
state_new() { return "" }
load_usings_json(state, usings_json) { return state }
load_modules_json(state, mod_json) { return ("" + mod_json) }
resolve_path_alias(state, alias) { return null }
resolve_namespace_alias(state, alias) {
if alias == null { return null }
local s = "" + state
// Prefer unique tail match by last segment
local i = 0
local start = 0
local found = null
loop(true) {
local kpos = RegexFlow.find_from(s, "\"", start)
if kpos < 0 { break }
local kend = RegexFlow.find_from(s, "\"", kpos + 1)
if kend < 0 { break }
local key = s.substring(kpos + 1, kend)
local dot = RegexFlow.last_index_of(key, ".")
local last = key
if dot >= 0 { last = key.substring(dot + 1, key.size()) }
if last == alias {
if found == null { found = key } else { return null }
} else {
// first-letter case-insensitive match
if last.size() == alias.size() && last.size() > 0 {
local l0 = last.substring(0,1)
local a0 = alias.substring(0,1)
local restl = last.substring(1, last.size())
local resta = alias.substring(1, alias.size())
if restl == resta {
local U = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; local L = "abcdefghijklmnopqrstuvwxyz"
local idxL = L.indexOf(l0); local idxU = U.indexOf(l0)
if (idxL >= 0 && U.substring(idxL, idxL+1) == a0) || (idxU >= 0 && L.substring(idxU, idxU+1) == a0) {
if found == null { found = key } else { return null }
}
}
}
}
start = kend + 1
}
return found
}
resolve_module_path_from_alias(state, alias) { return null }
guess_namespace_from_tail(state, tail) { return me.resolve_namespace_alias(state, tail) }
upgrade_aliases(state) { return 0 }
to_context_json(state) { return "{}" }
map_to_json(m) { return "{}" }
}
static box UsingResolverBoxMain { main(args) { return 0 } }