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:
60
lang/src/compiler/pipeline_v2/README.md
Normal file
60
lang/src/compiler/pipeline_v2/README.md
Normal file
@ -0,0 +1,60 @@
|
||||
Pipeline V2 — Box‑First Extract→Emit
|
||||
|
||||
Scope
|
||||
- Selfhost compilerの emit‑only 経路(Stage‑1 JSON → MIR(JSON v0/v1))を、箱の責務で明確化する。
|
||||
- Parser/Resolver/Runtime には影響しない。既定挙動は不変(devフラグ/引数でのみ起動)。
|
||||
|
||||
Modules(責務)
|
||||
- compare_extract_box.hako
|
||||
- 目的: Stage‑1 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 v0(compare→ret)
|
||||
- emit_compare_cfg3(lhs,rhs,cmp,materialize,trace) -> JSON v0(branch/jump/ret; materialize=2でcopy材化想定)
|
||||
- 失敗時: なし(入力は抽出済み前提)。
|
||||
|
||||
- pipeline.hako(flow PipelineV2)
|
||||
- 役割: Extract系→Emit系の配線。Call/Method/New/Compare/If(Compare) を段階的に対応。
|
||||
- フラグ:
|
||||
- prefer_cfg=0: Return‑only(compare→ret)
|
||||
- prefer_cfg=1: CFG(branch/jump/ret)
|
||||
- prefer_cfg=2: CFG+材化(将来の copy after PHI を想定; 現状は等価分岐)
|
||||
- trace: 1で最小トレース([trace])を出力。既定は0(静音)。
|
||||
|
||||
I/O(最小仕様)
|
||||
- 入力: Stage‑1 JSON(Return/If/Call/Method/New の最小形)。負数/空白は RegexFlow で吸収。
|
||||
- Call/Method/New の `args` 配列は `{"type":"Int","value":…}` のみを許容する。NamedArg/DefaultArg/VarArg など Int 以外が混在した場合は Null を返し、呼び出し側で Fail‑Fast(Stage‑0/Resolver 側で脱糖すること)。
|
||||
- 出力: MIR(JSON v0)。将来 v1(MirCall) への直出力は lower_stage1_to_mir_v1 を併設(dev用途)。
|
||||
|
||||
Fail‑Fast & Fallback
|
||||
- 抽出箱は見つからない場合 null を返す。pipeline は legacy extractor(Stage1ExtractFlow)でフォールバックする。
|
||||
- 既定ONは変えない(dev引数でのみ有効)。
|
||||
|
||||
Stage Guard(Stage‑2 / Stage‑3)
|
||||
- Stage‑2: Call/Method/New 向けの emit 手前ガード。Stage‑1 で弾いた NamedArg / DefaultArg / VarArg などが混入した場合は Null で Fail‑Fast し、呼び出し側に返す。
|
||||
- Stage‑3: MIR(JSON) 生成器。Stage‑2 の整形結果のみを受理し、`PipelineV2.lower_stage1_to_mir` が null を返した場合は Emit を実行しない。
|
||||
- 代表スモーク
|
||||
- Stage‑1 ガード: `selfhost_pipeline_v2_stage1_invalid_args_fail_vm.sh` / `..._named_default_fail_vm.sh` / `..._vararg_fail_vm.sh`
|
||||
- Stage‑2/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系は Return‑only と CFG をそれぞれ確認。
|
||||
- Mini‑VM(apps/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 ライン側で継続。
|
||||
33
lang/src/compiler/pipeline_v2/README_using_resolver.md
Normal file
33
lang/src/compiler/pipeline_v2/README_using_resolver.md
Normal file
@ -0,0 +1,33 @@
|
||||
UsingResolverBox — Pre‑Resolve Using Declarations
|
||||
|
||||
Scope
|
||||
- Manage alias→path and alias→namespace name maps for compiler pipeline (P2‑A).
|
||||
- Consume lightweight JSON emitted by parser (UsingCollectorBox or ParserBox.add_using).
|
||||
- Provide stable getters for later NamespaceBox (P2‑B) 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)
|
||||
|
||||
Non‑Responsibilities
|
||||
- Reading hako.toml or filesystem.
|
||||
- Runtime using resolution. This is compiler‑only pre‑resolution.
|
||||
|
||||
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 side‑effect free for easy testing.
|
||||
|
||||
28
lang/src/compiler/pipeline_v2/alias_preflight_box.hako
Normal file
28
lang/src/compiler/pipeline_v2/alias_preflight_box.hako
Normal file
@ -0,0 +1,28 @@
|
||||
// alias_preflight_box.hako — AliasPreflightBox
|
||||
// Responsibility: Early check for using-alias heads in Stage‑1 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.
|
||||
// Non‑Responsibility: 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 } }
|
||||
11
lang/src/compiler/pipeline_v2/backend_box.hako
Normal file
11
lang/src/compiler/pipeline_v2/backend_box.hako
Normal 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 } }
|
||||
|
||||
12
lang/src/compiler/pipeline_v2/call_extract_box.hako
Normal file
12
lang/src/compiler/pipeline_v2/call_extract_box.hako
Normal file
@ -0,0 +1,12 @@
|
||||
// CallExtractBox — Stage‑1 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 } }
|
||||
119
lang/src/compiler/pipeline_v2/compare_extract_box.hako
Normal file
119
lang/src/compiler/pipeline_v2/compare_extract_box.hako
Normal file
@ -0,0 +1,119 @@
|
||||
// CompareExtractBox — Stage‑1 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 } }
|
||||
33
lang/src/compiler/pipeline_v2/emit_binop_box.hako
Normal file
33
lang/src/compiler/pipeline_v2/emit_binop_box.hako
Normal 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 } }
|
||||
89
lang/src/compiler/pipeline_v2/emit_call_box.hako
Normal file
89
lang/src/compiler/pipeline_v2/emit_call_box.hako
Normal 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 equivalent(CallEmitBox + 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 } }
|
||||
84
lang/src/compiler/pipeline_v2/emit_compare_box.hako
Normal file
84
lang/src/compiler/pipeline_v2/emit_compare_box.hako
Normal 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 r3(string直組み)
|
||||
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 } }
|
||||
84
lang/src/compiler/pipeline_v2/emit_method_box.hako
Normal file
84
lang/src/compiler/pipeline_v2/emit_method_box.hako
Normal 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 equivalent(CallEmitBox + 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 } }
|
||||
111
lang/src/compiler/pipeline_v2/emit_mir_flow.hako
Normal file
111
lang/src/compiler/pipeline_v2/emit_mir_flow.hako
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
110
lang/src/compiler/pipeline_v2/emit_mir_flow_map.hako
Normal file
110
lang/src/compiler/pipeline_v2/emit_mir_flow_map.hako
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
36
lang/src/compiler/pipeline_v2/emit_newbox_box.hako
Normal file
36
lang/src/compiler/pipeline_v2/emit_newbox_box.hako
Normal 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 } }
|
||||
14
lang/src/compiler/pipeline_v2/emit_return_box.hako
Normal file
14
lang/src/compiler/pipeline_v2/emit_return_box.hako
Normal 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 } }
|
||||
49
lang/src/compiler/pipeline_v2/execution_pipeline_box.hako
Normal file
49
lang/src/compiler/pipeline_v2/execution_pipeline_box.hako
Normal 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 Stage‑3 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 Stage‑1 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 } }
|
||||
26
lang/src/compiler/pipeline_v2/flow_entry.hako
Normal file
26
lang/src/compiler/pipeline_v2/flow_entry.hako
Normal file
@ -0,0 +1,26 @@
|
||||
// flow_entry.hako — Pipeline v2 entry box(emit-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 JSON(call/boxcall/newbox)。最小入力: Stage‑1 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 互換 JSON(unified 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 }
|
||||
}
|
||||
16
lang/src/compiler/pipeline_v2/header_emit_box.hako
Normal file
16
lang/src/compiler/pipeline_v2/header_emit_box.hako
Normal 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
|
||||
}
|
||||
}
|
||||
37
lang/src/compiler/pipeline_v2/json_minify_box.hako
Normal file
37
lang/src/compiler/pipeline_v2/json_minify_box.hako
Normal 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 } }
|
||||
188
lang/src/compiler/pipeline_v2/local_ssa_box.hako
Normal file
188
lang/src/compiler/pipeline_v2/local_ssa_box.hako
Normal 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 命令を insts(ArrayBox 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 } }
|
||||
43
lang/src/compiler/pipeline_v2/map_helpers_box.hako
Normal file
43
lang/src/compiler/pipeline_v2/map_helpers_box.hako
Normal 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 } }
|
||||
12
lang/src/compiler/pipeline_v2/method_extract_box.hako
Normal file
12
lang/src/compiler/pipeline_v2/method_extract_box.hako
Normal file
@ -0,0 +1,12 @@
|
||||
// MethodExtractBox — Stage‑1 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 } }
|
||||
35
lang/src/compiler/pipeline_v2/mir_builder_box.hako
Normal file
35
lang/src/compiler/pipeline_v2/mir_builder_box.hako
Normal 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 Stage‑1 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 } }
|
||||
91
lang/src/compiler/pipeline_v2/mir_call_box.hako
Normal file
91
lang/src/compiler/pipeline_v2/mir_call_box.hako
Normal file
@ -0,0 +1,91 @@
|
||||
// MirCallBox — JSON v1 unified call emitters(薄い箱)
|
||||
// 目的: v1(mir_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 } }
|
||||
29
lang/src/compiler/pipeline_v2/name_resolve_box.hako
Normal file
29
lang/src/compiler/pipeline_v2/name_resolve_box.hako
Normal 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 } }
|
||||
61
lang/src/compiler/pipeline_v2/namespace_box.hako
Normal file
61
lang/src/compiler/pipeline_v2/namespace_box.hako
Normal 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
|
||||
// Non‑Responsibility:
|
||||
// - 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 } }
|
||||
12
lang/src/compiler/pipeline_v2/new_extract_box.hako
Normal file
12
lang/src/compiler/pipeline_v2/new_extract_box.hako
Normal file
@ -0,0 +1,12 @@
|
||||
// NewExtractBox — Stage‑1 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 } }
|
||||
90
lang/src/compiler/pipeline_v2/normalizer_box.hako
Normal file
90
lang/src/compiler/pipeline_v2/normalizer_box.hako
Normal 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 } }
|
||||
558
lang/src/compiler/pipeline_v2/pipeline.hako
Normal file
558
lang/src/compiler/pipeline_v2/pipeline.hako
Normal 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 Mini‑VM 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 (Module‑First)
|
||||
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 first(plugins 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)
|
||||
}
|
||||
|
||||
}
|
||||
15
lang/src/compiler/pipeline_v2/pipeline_emit_box.hako
Normal file
15
lang/src/compiler/pipeline_v2/pipeline_emit_box.hako
Normal 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 } }
|
||||
65
lang/src/compiler/pipeline_v2/pipeline_helpers_box.hako
Normal file
65
lang/src/compiler/pipeline_v2/pipeline_helpers_box.hako
Normal 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) }
|
||||
}
|
||||
31
lang/src/compiler/pipeline_v2/readonly_map_view.hako
Normal file
31
lang/src/compiler/pipeline_v2/readonly_map_view.hako
Normal 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 } }
|
||||
95
lang/src/compiler/pipeline_v2/regex_flow.hako
Normal file
95
lang/src/compiler/pipeline_v2/regex_flow.hako
Normal 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
|
||||
}
|
||||
}
|
||||
111
lang/src/compiler/pipeline_v2/signature_verifier_box.hako
Normal file
111
lang/src/compiler/pipeline_v2/signature_verifier_box.hako
Normal file
@ -0,0 +1,111 @@
|
||||
// signature_verifier_box.hako — SignatureVerifierBox
|
||||
// Responsibility: compile-time arity checks for common built-in methods.
|
||||
// Notes:
|
||||
// - Stage‑1 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) + ""
|
||||
// String‑like
|
||||
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 }
|
||||
// Array‑like
|
||||
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 }
|
||||
// Map‑like
|
||||
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 Stage‑1 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 } }
|
||||
67
lang/src/compiler/pipeline_v2/stage1_args_parser_box.hako
Normal file
67
lang/src/compiler/pipeline_v2/stage1_args_parser_box.hako
Normal file
@ -0,0 +1,67 @@
|
||||
// stage1_args_parser_box.hako — Stage1ArgsParserBox
|
||||
// Responsibility: Parse Stage‑1 args JSON text into integers (MVP: Int only).
|
||||
// Non‑Responsibility: 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 Stage‑1 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 } }
|
||||
209
lang/src/compiler/pipeline_v2/stage1_extract_flow.hako
Normal file
209
lang/src/compiler/pipeline_v2/stage1_extract_flow.hako
Normal file
@ -0,0 +1,209 @@
|
||||
using "lang/src/compiler/pipeline_v2/regex_flow.hako" as RegexFlow
|
||||
|
||||
flow Stage1ExtractFlow {
|
||||
// Extract minimal info from Stage‑1 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 }
|
||||
}
|
||||
}
|
||||
127
lang/src/compiler/pipeline_v2/stage1_int_args_extract_box.hako
Normal file
127
lang/src/compiler/pipeline_v2/stage1_int_args_extract_box.hako
Normal 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 } }
|
||||
99
lang/src/compiler/pipeline_v2/stage1_json_scanner_box.hako
Normal file
99
lang/src/compiler/pipeline_v2/stage1_json_scanner_box.hako
Normal file
@ -0,0 +1,99 @@
|
||||
// stage1_json_scanner_box.hako — Stage1JsonScannerBox
|
||||
// Responsibility: Provide common scans for Stage‑1 JSON strings used by PipelineV2
|
||||
// - body anchor detection
|
||||
// - key search (plain/escaped/": " tolerant)
|
||||
// - value start after key
|
||||
// - name/args basic extraction
|
||||
// Non‑Responsibility: 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 } }
|
||||
@ -0,0 +1,60 @@
|
||||
// stage1_name_args_normalizer_box.hako — Stage1NameArgsNormalizerBox
|
||||
// Responsibility: Normalize (label, args_text) from Stage‑1 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
|
||||
// Non‑Responsibility: 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 } }
|
||||
37
lang/src/compiler/pipeline_v2/terminator_guard_box.hako
Normal file
37
lang/src/compiler/pipeline_v2/terminator_guard_box.hako
Normal 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 } }
|
||||
70
lang/src/compiler/pipeline_v2/using_resolver_box.hako
Normal file
70
lang/src/compiler/pipeline_v2/using_resolver_box.hako
Normal 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 } }
|
||||
Reference in New Issue
Block a user