Phase 20.34: expand MirBuilder internal library with comprehensive lowering boxes; add pattern registry and program scanning infrastructure; implement internal lowerers for if/loop/return patterns; add dev tools and comprehensive canary tests; update VM boxes and host providers for internal delegation; wire phase2034 test suite with 30+ canary scripts covering internal lowering scenarios
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
@ -908,13 +908,92 @@ Toggles(既定OFF)
|
||||
- HAKO_CORE_DIRECT=1 / HAKO_CORE_DIRECT_INPROC=1 — MIR(JSON) を Core 直行で実行
|
||||
- SMOKES_ENABLE_SELFHOST=1 — EXE カナリアを有効化
|
||||
|
||||
Near‑term Tasks
|
||||
- docs: phase‑20.34(README/PLAN/CHECKLIST)を追加(本ファイル含む)
|
||||
- scaffold: MirBuilderBox.hako / LLVMEmitBox.hako の最小 I/F(未対応は Fail‑Fast)
|
||||
- smokes: Program→MIR(return/binop/if)、.o 生成(SKIP許容)、EXE(opt‑in)
|
||||
- provider: plugin spec ドキュメント(ny-llvmc/llvmlite どちらも裏側として許容)
|
||||
Near‑term Tasks(進捗)
|
||||
- docs: phase‑20.34(README/PLAN/CHECKLIST)を追加 — DONE
|
||||
- scaffold: MirBuilderBox.hako / LLVMEmitBox.hako の最小 I/F — DONE(タグ・トグル固定)
|
||||
- provider: Host providers を extern 経由で接続 — DONE
|
||||
- env.mirbuilder.emit: Program(JSON v0) → MIR(JSON v0)
|
||||
- env.codegen.emit_object: MIR(JSON v0) → ny-llvmc → .o
|
||||
- hostbridge.extern_invoke(name, method, args?): VM 側で env.mirbuilder/env.codegen へ橋渡し(ArrayBox 先頭要素を文字列化)
|
||||
- smokes: phase2034 カナリア追加 — DONE(MirBuilder=PASS、LLVMEmit=ny-llvmc不在/未解決はSKIP)
|
||||
- next: LLVMCodegenBox(Hako ABI プラグインBox)実装&hako.toml登録 → extern TTL 撤退
|
||||
- next: MirBuilderBox 内蔵実装(const/binop/ret → compare/branch/jump/phi)を段階導入(委譲縮小)
|
||||
|
||||
Acceptance
|
||||
- quick: 新規カナリア PASS(SKIPはポリシー通り)
|
||||
- integration: 既存緑維持、ノイズ増加なし
|
||||
- docs: Env/TTL/Fail‑Fast を明文化
|
||||
|
||||
Update — 2025‑11‑03 (VM extern wiring + canary fix)
|
||||
- Rust VM へ extern 受け口を配線し、delegate→core カナリアを安定化。
|
||||
- handlers/externals.rs: ExternCall("env.mirbuilder","emit"), ("env.codegen","emit_object"), ("hostbridge","extern_invoke") を実装。
|
||||
- handlers/calls.rs: legacy/global 解決形("hostbridge.extern_invoke" / "…/3")もブリッジ。
|
||||
- カナリア更新: phase2034/mirbuilder_varvar_delegate_core_canary_vm.sh は JSON 抽出を jq 検証に変更(整形出力を許容)。→ PASS
|
||||
|
||||
Action Plan — A→B→C(docs first, code later)
|
||||
A) LLVM provider 明確化(docs+運用)
|
||||
- 方針: LLVMEmit は provider‑first。ny-llvmc あり: 実行、なし: 明示タグで SKIP。
|
||||
- トグル: `HAKO_LLVM_EMIT_PROVIDER=ny-llvmc|llvmlite`(既定OFF)。ny-llvmc 選択時は `env.codegen.emit_object` を呼び出す。
|
||||
- 受理条件: ny-llvmc 不在時に `[llvmemit/ny-llvmc/not-found]` が安定出力。存在時は `.o` を生成しパスを返す。
|
||||
- Docs 整理: CURRENT_TASK(本節)に運用とタグ、トグルの最小セットを記載(完了)。README/phase docs は次ステップで同期。
|
||||
|
||||
B) MirBuilder 内蔵化(第一段・委譲縮小)
|
||||
- 範囲: Program(JSON v0) → MIR(JSON v0) の最小命令(const/binop/ret)を MirBuilderBox 内で直接生成。
|
||||
- ガード: 既定は挙動不変(委譲)。`HAKO_MIR_BUILDER_DELEGATE=1` で明示的に委譲経路を使用。
|
||||
- タグ不変: 入力不正は `[mirbuilder/input/invalid]`、委譲未配線は `[mirbuilder/delegate/missing]` を維持。
|
||||
- 受理条件: canary(return/binop/if)で MIR(JSON) の `functions/blocks` キーを検出し PASS。差分は局所・可逆。
|
||||
|
||||
C) Canary 運用(昇格ポリシー)
|
||||
- MirBuilder canary: quick 既定への昇格候補(現状 PASS)。導線は `tools/smokes/v2/profiles/quick/core/phase2034/mirbuilder_canary_vm.sh`。
|
||||
- LLVMEmit canary: ny-llvmc 検出時のみ PASS、それ以外は `[llvmemit/ny-llvmc/not-found]` で SKIP 継続。
|
||||
- Gate: 昇格は「A 完了+B 第一段 PASS」が条件。ny-llvmc 依存の昇格は行わない(SKIP 運用を維持)。
|
||||
|
||||
Env Quick Reference(20.34 関連)
|
||||
- `HAKO_MIR_BUILDER_DELEGATE=1` — Program→MIR を Runner へ委譲
|
||||
- `HAKO_MIR_BUILDER_INTERNAL=1` — 内蔵の最小変換(Return(Int)→const+ret)を有効化(委譲縮小の第一段)
|
||||
- `HAKO_LLVM_EMIT_PROVIDER=ny-llvmc|llvmlite` — LLVM emit provider 選択
|
||||
- `NYASH_GATE_C_CORE=1`(互換: `HAKO_GATE_C_CORE=1`)— MIR(JSON) を Core 直行で検証
|
||||
|
||||
Next Tasks (Phase 20.34 wrap‑up → 20.35)
|
||||
- P0 (now)
|
||||
- Run canaries with preinclude=ON + inline=OFF to lock structure:
|
||||
- tools/smokes/v2/profiles/quick/core/string_vm_api_canary_vm.sh
|
||||
- phase2034/mirbuilder_internal_return_logical_varvar_{lower,via_builder}_canary_vm.sh
|
||||
- VM include error message: add guidance (using+alias recommended; NYASH_PREINCLUDE=1 for dev)
|
||||
- P1 (20.34)
|
||||
- Finish include→using+alias migration (remaining lowers/boxes if any)
|
||||
- Add CI static check: forbid `include "…"` in `lang/src/**`
|
||||
- Docs: include=deprecated / using+alias=primary / preinclude=dev‑only
|
||||
- P2 (20.35)
|
||||
- Remove preinclude path from runner; archive tools/dev/hako_preinclude.sh
|
||||
- Flip primary verifier to Hakorune VM (HAKO_VERIFY_PRIMARY=hakovm) once phase2034 canaries green
|
||||
- Optional: switch MirBuilder to registry path after parity check
|
||||
|
||||
Checklist (A→B→C — 2025‑11‑03)
|
||||
- A) LLVM provider 明確化: DONE(env `HAKO_LLVM_EMIT_PROVIDER`、タグ: `[llvmemit/provider/missing]` ほか)
|
||||
- B) MirBuilder 内蔵化(1st): IN‑PROGRESS(Return/Int・Binary(Int,Int)・Var/Int・Var/Var 等の lowers は内蔵パスで起動、追加の JsonFragBox/PatternUtilBox 適用は段階導入中)
|
||||
- C) Canary 運用: PASS(mirbuilder_canary_vm / binop/varint、logical/mixed)。var/var core 直行の emit 検証は追跡中(非既定/opt‑in)。
|
||||
|
||||
Next (small, structure‑first)
|
||||
- JsonFragBox adoption: 残存の手書きスキャンを置換(if/compare varint/varvar、method args の int 系)。
|
||||
- PatternUtilBox rollout: logical/return/binop 系の重複逆引きを順次委譲。
|
||||
- MirBuilder registry: `HAKO_MIR_BUILDER_REGISTRY=1` で parity を継続確認しつつ、呼び順の一本化を検討。
|
||||
- VM mir_call: `size/len/push/get/set` のタグ強化と最小状態(recv_id)設計を切り出し。既定は STUB タグ維持。
|
||||
- Verify primary: `HAKO_VERIFY_PRIMARY=hakovm` 経路の file‑read 受け口を追加し、Core fallback を段階縮小。
|
||||
Update — 2025-11-03(Phase 20.34 — P1/P2 sweep, JsonFrag/PatternUtil 統一)
|
||||
- P1(手書きスキャン撤廃): 完了
|
||||
- internal lowers の数値/真偽/文字列抽出を JsonFragBox に統一。
|
||||
- Var→Local 逆引きを PatternUtilBox に統一(map_cmp も共通化)。
|
||||
- 対象(代表): lower_if_compare_* / lower_return_binop_* / lower_return_var_local 他。
|
||||
- P2(loop_form 系 include → using): 完了
|
||||
- lower_loop_simple/count_param/sum_bc から include を撤廃、using のみへ統一。
|
||||
- カナリア/スモーク
|
||||
- internal lowers 構造系は緑(var/var logical lower 直行も PASS)。
|
||||
- MirBuilder→Core 直行系は emit 成功、rc 検証は当面 Hakorune VM primary(HAKO_VERIFY_PRIMARY=hakovm)で実施。
|
||||
- ランナー/テスト安定化
|
||||
- test_runner 未割当変数バグ修正(prefile)。
|
||||
- emit 出力の JSON 抽出に jq を導入(先頭ノイズ除去)。
|
||||
|
||||
Notes
|
||||
- 仕様不変・既定挙動不変(トグルは既定OFF)。
|
||||
- include は lang内の一部(compiler_stageb/core_bridge)のみ残存(別PRで移行)。
|
||||
|
||||
29
docs/updates/phase2034-p1p2-sweep.md
Normal file
29
docs/updates/phase2034-p1p2-sweep.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Phase 20.34 — P1/P2 Sweep (JsonFrag/PatternUtil, loop_form using)
|
||||
|
||||
Status: Completed (2025-11-03)
|
||||
|
||||
Scope
|
||||
- P1: Replace ad‑hoc JSON scans with JsonFragBox and unify reverse lookups via PatternUtilBox.
|
||||
- P2: Remove `include` from loop_form lowers and rely on `using` only.
|
||||
|
||||
Changes
|
||||
- JsonFragBox adoption
|
||||
- Use `read_int_after(text, kv+8)`, `read_bool_after(text, kv+8)`, `read_string_after(text, k+5)` consistently.
|
||||
- Locate keys with `index_of_from(text, "\"key\":", pos)`.
|
||||
- PatternUtilBox adoption
|
||||
- `find_local_int_before(text, name, before_pos)` and `find_local_bool_before(...)` for reverse lookups.
|
||||
- Optional: `map_cmp` for operator mapping (<, >, <=, >=, ==, != → Lt, Gt, ...).
|
||||
- loop_form lowers
|
||||
- `lower_loop_simple/count_param/sum_bc`: remove `include` of loop_form; keep `using selfhost.shared.mir.loopform as LoopFormBox`.
|
||||
|
||||
Verification
|
||||
- Internal lowers canaries: PASS (structure, value paths). Logical Var/Var lower (direct) is green.
|
||||
- Emit→Core rc verification: use `verify_mir_rc` with `HAKO_VERIFY_PRIMARY=hakovm` while Core rc line is being normalized.
|
||||
- Test runner fixes: guard unset `prefile`; JSON extraction via `jq` with leading noise stripping.
|
||||
|
||||
Policy
|
||||
- Behavior invariant (fail‑fast). New toggles default OFF. Minimal, localized diffs only.
|
||||
|
||||
Next
|
||||
- Continue registry migration for MirBuilder (toggle‑guarded).
|
||||
- Migrate remaining non‑internal `include` sites in a separate PR.
|
||||
@ -32,6 +32,288 @@ static box MirBuilderBox {
|
||||
print("[mirbuilder/input/invalid] missing version/kind keys")
|
||||
return null
|
||||
}
|
||||
// Internal minimal path (guarded) — const(int)+ret, or const+const+binop+ret (Phase 20.34 B step)
|
||||
// Toggle: HAKO_MIR_BUILDER_INTERNAL=1
|
||||
{
|
||||
local internal = env.get("HAKO_MIR_BUILDER_INTERNAL")
|
||||
if internal != null && ("" + internal) == "1" {
|
||||
// Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1,
|
||||
// iterate PatternRegistryBox.candidates() and dispatch by name.
|
||||
// NOTE: include not supported in VM, registry disabled for VM execution
|
||||
local use_reg = env.get("HAKO_MIR_BUILDER_REGISTRY")
|
||||
if use_reg != null && ("" + use_reg) == "1" && 0 == 1 {
|
||||
local names = PatternRegistryBox.candidates()
|
||||
local i = 0; local n = names.length()
|
||||
loop(i < n) {
|
||||
local nm = "" + names.get(i)
|
||||
if nm == "if.compare.intint" { local out = LowerIfCompareBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "if.compare.fold.binints" { local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "if.compare.fold.varint" { local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "if.compare.varint" { local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "if.compare.varvar" { local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.method.arraymap" { local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.var.local" { local out = LowerReturnVarLocalBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.string" { local out = LowerReturnStringBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.float" { local out = LowerReturnFloatBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.bool" { local out = LowerReturnBoolBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.logical" { local out = LowerReturnLogicalBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.binop.varint" { local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.binop.varvar" { local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.binop.intint" { local out = LowerReturnBinOpBox.try_lower(s); if out != null { return out } }
|
||||
if nm == "return.int" { local out = LowerReturnIntBox.try_lower(s); if out != null { return out } }
|
||||
i = i + 1
|
||||
}
|
||||
// Fall-through to chain if none matched
|
||||
}
|
||||
// Boxified lowers via using+alias (prefer using over include; VM include unsupported)
|
||||
using "hako.mir.builder.internal.lower_if_then_else_following_return" as LowerIfThenElseFollowingReturnBox
|
||||
using "hako.mir.builder.internal.lower_if_nested" as LowerIfNestedBox
|
||||
using "hako.mir.builder.internal.lower_if_compare" as LowerIfCompareBox
|
||||
using "hako.mir.builder.internal.lower_if_compare_fold_binints" as LowerIfCompareFoldBinIntsBox
|
||||
using "hako.mir.builder.internal.lower_if_compare_fold_varint" as LowerIfCompareFoldVarIntBox
|
||||
using "hako.mir.builder.internal.lower_if_compare_varint" as LowerIfCompareVarIntBox
|
||||
using "hako.mir.builder.internal.lower_if_compare_varvar" as LowerIfCompareVarVarBox
|
||||
using "hako.mir.builder.internal.lower_loop_sum_bc" as LowerLoopSumBcBox
|
||||
using "hako.mir.builder.internal.lower_loop_count_param" as LowerLoopCountParamBox
|
||||
using "hako.mir.builder.internal.lower_loop_simple" as LowerLoopSimpleBox
|
||||
using "hako.mir.builder.internal.lower_return_var_local" as LowerReturnVarLocalBox
|
||||
using "hako.mir.builder.internal.lower_return_string" as LowerReturnStringBox
|
||||
using "hako.mir.builder.internal.lower_return_float" as LowerReturnFloatBox
|
||||
using "hako.mir.builder.internal.lower.logical" as LowerReturnLogicalBox
|
||||
using "hako.mir.builder.internal.lower_return_method_array_map" as LowerReturnMethodArrayMapBox
|
||||
using "hako.mir.builder.internal.lower_return_bool" as LowerReturnBoolBox
|
||||
using "hako.mir.builder.internal.lower_return_binop_varint" as LowerReturnBinOpVarIntBox
|
||||
using "hako.mir.builder.internal.lower_return_binop_varvar" as LowerReturnBinOpVarVarBox
|
||||
using "hako.mir.builder.internal.lower_return_binop" as LowerReturnBinOpBox
|
||||
using "hako.mir.builder.internal.lower_return_int" as LowerReturnIntBox
|
||||
{ local out_if2b = LowerIfNestedBox.try_lower(s); if out_if2b != null { return out_if2b } }
|
||||
{ local out_if2 = LowerIfThenElseFollowingReturnBox.try_lower(s); if out_if2 != null { return out_if2 } }
|
||||
{ local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { return out_if } }
|
||||
{ local out_ifb = LowerIfCompareFoldBinIntsBox.try_lower(s); if out_ifb != null { return out_ifb } }
|
||||
{ local out_ifbv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_ifbv != null { return out_ifbv } }
|
||||
{ local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { return out_ifvi } }
|
||||
{ local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { return out_ifvv } }
|
||||
{ local out_loop2 = LowerLoopSumBcBox.try_lower(s); if out_loop2 != null { return out_loop2 } }
|
||||
{ local out_loopp = LowerLoopCountParamBox.try_lower(s); if out_loopp != null { return out_loopp } }
|
||||
{ local out_loop = LowerLoopSimpleBox.try_lower(s); if out_loop != null { return out_loop } }
|
||||
{ local out_var = LowerReturnVarLocalBox.try_lower(s); if out_var != null { return out_var } }
|
||||
{ local out_str = LowerReturnStringBox.try_lower(s); if out_str != null { return out_str } }
|
||||
{ local out_f = LowerReturnFloatBox.try_lower(s); if out_f != null { return out_f } }
|
||||
{ local out_log = LowerReturnLogicalBox.try_lower(s); if out_log != null { return out_log } }
|
||||
{ local out_meth = LowerReturnMethodArrayMapBox.try_lower(s); if out_meth != null { return out_meth } }
|
||||
{ local out_bool = LowerReturnBoolBox.try_lower(s); if out_bool != null { return out_bool } }
|
||||
{ local out_bvi = LowerReturnBinOpVarIntBox.try_lower(s); if out_bvi != null { return out_bvi } }
|
||||
{ local out_bvv = LowerReturnBinOpVarVarBox.try_lower(s); if out_bvv != null { return out_bvv } }
|
||||
{ local out_bin = LowerReturnBinOpBox.try_lower(s); if out_bin != null { return out_bin } }
|
||||
{
|
||||
local out_int = LowerReturnIntBox.try_lower(s)
|
||||
if out_int != null { return out_int }
|
||||
}
|
||||
// Find Return marker (or If)
|
||||
// Case (If with Compare + Return(Int)/Return(Int) in branches)
|
||||
{
|
||||
local k_if = s.indexOf("\"type\":\"If\"")
|
||||
if k_if >= 0 {
|
||||
// cond: Compare with Int lhs/rhs
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if)
|
||||
if k_cmp >= 0 {
|
||||
// op
|
||||
local k_op2 = s.indexOf("\"op\":", k_cmp)
|
||||
if k_op2 >= 0 {
|
||||
local oi2 = k_op2 + 5; local on2 = s.length()
|
||||
loop(oi2 < on2) { local ch = s.substring(oi2,oi2+1); if ch == "\"" { oi2 = oi2 + 1 break } if ch != " " { break } oi2 = oi2 + 1 }
|
||||
local oj2 = oi2
|
||||
loop(oj2 < on2) { local ch2 = s.substring(oj2,oj2+1); if ch2 == "\"" { break } oj2 = oj2 + 1 }
|
||||
local op2 = s.substring(oi2, oj2)
|
||||
// support <,>,<=,>=,==,!=
|
||||
if !(op2 == "<" || op2 == ">" || op2 == "<=" || op2 == ">=" || op2 == "==" || op2 == "!=") {
|
||||
print("[mirbuilder/internal/unsupported] compare op: " + op2)
|
||||
return null
|
||||
}
|
||||
// lhs Int
|
||||
local lhs_val2 = null
|
||||
{
|
||||
local klhs2 = s.indexOf("\"lhs\":{", k_cmp)
|
||||
if klhs2 >= 0 {
|
||||
local ti = s.indexOf("\"type\":\"Int\"", klhs2)
|
||||
if ti >= 0 {
|
||||
local kv = s.indexOf("\"value\":", ti)
|
||||
if kv >= 0 {
|
||||
local i = kv + 8; local n = s.length();
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i; if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 1 { lhs_val2 = s.substring(i,j) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// rhs Int
|
||||
local rhs_val2 = null
|
||||
{
|
||||
local krhs2 = s.indexOf("\"rhs\":{", k_cmp)
|
||||
if krhs2 >= 0 {
|
||||
local ti = s.indexOf("\"type\":\"Int\"", krhs2)
|
||||
if ti >= 0 {
|
||||
local kv = s.indexOf("\"value\":", ti)
|
||||
if kv >= 0 {
|
||||
local i = kv + 8; local n = s.length();
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i; if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 1 { rhs_val2 = s.substring(i,j) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// then: Return(Int ...)
|
||||
local then_val = null
|
||||
{
|
||||
local kth = s.indexOf("\"then\":", k_if)
|
||||
if kth >= 0 {
|
||||
local rt = s.indexOf("\"type\":\"Return\"", kth)
|
||||
if rt >= 0 {
|
||||
local ti = s.indexOf("\"type\":\"Int\"", rt)
|
||||
if ti >= 0 {
|
||||
local kv = s.indexOf("\"value\":", ti)
|
||||
if kv >= 0 {
|
||||
local i = kv + 8; local n = s.length();
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i; if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 1 { then_val = s.substring(i,j) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// else: Return(Int ...)
|
||||
local else_val = null
|
||||
{
|
||||
local kel = s.indexOf("\"else\":", k_if)
|
||||
if kel >= 0 {
|
||||
local rt = s.indexOf("\"type\":\"Return\"", kel)
|
||||
if rt >= 0 {
|
||||
local ti = s.indexOf("\"type\":\"Int\"", rt)
|
||||
if ti >= 0 {
|
||||
local kv = s.indexOf("\"value\":", ti)
|
||||
if kv >= 0 {
|
||||
local i = kv + 8; local n = s.length();
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i; if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 1 { else_val = s.substring(i,j) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lhs_val2 != null && rhs_val2 != null && then_val != null && else_val != null {
|
||||
// MIR: bb0(const lhs, const rhs, compare op -> v3, branch v3 then bb1 else bb2), bb1(const then, ret), bb2(const else, ret)
|
||||
local mir_if = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[" +
|
||||
"{\"label\":\"bb0\",\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_val2 + "}}," +
|
||||
"{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val2 + "}}," +
|
||||
"{\"op\":\"compare\",\"operation\":\"" + op2 + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
|
||||
"{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
|
||||
"{\"label\":\"bb1\",\"instructions\":[{\"op\":\"const\",\"dst\":4,\"value\":{\"type\":\"i64\",\"value\":" + then_val + "}},{\"op\":\"ret\",\"value\":4}]}," +
|
||||
"{\"label\":\"bb2\",\"instructions\":[{\"op\":\"const\",\"dst\":5,\"value\":{\"type\":\"i64\",\"value\":" + else_val + "}},{\"op\":\"ret\",\"value\":5}]}] }},\"blocks\":3}"
|
||||
return mir_if
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback cases below: Return(Binary) and Return(Int)
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret >= 0 {
|
||||
// Case (Binary): {"type":"Binary","op":"+|-|*|/","lhs":{Int},"rhs":{Int}}
|
||||
local k_bin = s.indexOf("\"type\":\"Binary\"", k_ret)
|
||||
if k_bin >= 0 {
|
||||
// op
|
||||
local k_op = s.indexOf("\"op\":", k_bin)
|
||||
if k_op >= 0 {
|
||||
local oi = k_op + 5; local on = s.length()
|
||||
loop(oi < on) { local ch = s.substring(oi,oi+1); if ch == "\"" { oi = oi + 1 break } if ch != " " { break } oi = oi + 1 }
|
||||
local oj = oi
|
||||
loop(oj < on) { local ch2 = s.substring(oj,oj+1); if ch2 == "\"" { break } oj = oj + 1 }
|
||||
local op = s.substring(oi, oj)
|
||||
if !(op == "+" || op == "-" || op == "*" || op == "/") {
|
||||
print("[mirbuilder/internal/unsupported] binary op: " + op)
|
||||
return null
|
||||
}
|
||||
// lhs Int value
|
||||
local klhs = s.indexOf("\"lhs\":{", k_bin)
|
||||
local lhs_val = null
|
||||
if klhs >= 0 {
|
||||
local ti = s.indexOf("\"type\":\"Int\"", klhs)
|
||||
if ti >= 0 {
|
||||
local kv = s.indexOf("\"value\":", ti)
|
||||
if kv >= 0 {
|
||||
local i = kv + 8; local n = s.length();
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i; if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 1 { lhs_val = s.substring(i,j) }
|
||||
}
|
||||
}
|
||||
}
|
||||
// rhs Int value
|
||||
local krhs = s.indexOf("\"rhs\":{", k_bin)
|
||||
local rhs_val = null
|
||||
if krhs >= 0 {
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", krhs)
|
||||
if ti2 >= 0 {
|
||||
local kv2 = s.indexOf("\"value\":", ti2)
|
||||
if kv2 >= 0 {
|
||||
local i2 = kv2 + 8; local n2 = s.length();
|
||||
loop(i2 < n2) { if s.substring(i2,i2+1) != " " { break } i2 = i2 + 1 }
|
||||
local j2 = i2; if j2 < n2 && s.substring(j2,j2+1) == "-" { j2 = j2 + 1 }
|
||||
local had2 = 0
|
||||
loop(j2 < n2) { local ch3 = s.substring(j2,j2+1); if ch3 >= "0" && ch3 <= "9" { had2 = 1 j2 = j2 + 1 } else { break } }
|
||||
if had2 == 1 { rhs_val = s.substring(i2,j2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
if lhs_val != null && rhs_val != null {
|
||||
local mir_bin = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[{\"label\":\"bb0\",\"instructions\":[" +
|
||||
"{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_val + "}}," +
|
||||
"{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val + "}}," +
|
||||
"{\"op\":\"binop\",\"operation\":\"" + op + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
|
||||
"{\"op\":\"ret\",\"value\":3}]}] }},\"blocks\":1}"
|
||||
return mir_bin
|
||||
}
|
||||
}
|
||||
}
|
||||
// Case (Int): Return(Int N)
|
||||
local k_int = s.indexOf("\"type\":\"Int\"", k_ret)
|
||||
if k_int >= 0 {
|
||||
local k_val = s.indexOf("\"value\":", k_int)
|
||||
if k_val >= 0 {
|
||||
local i = k_val + 8
|
||||
local n = s.length()
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i
|
||||
if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 1 {
|
||||
local num = s.substring(i, j)
|
||||
local mir = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[{\"label\":\"bb0\",\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + num + "}},{\"op\":\"ret\",\"value\":1}] }] }},\"blocks\":1}"
|
||||
return mir
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unsupported internal shape → Fail‑Fast and return null
|
||||
print("[mirbuilder/internal/unsupported] only Return(Int|Binary(Int,Int)) supported in internal mode")
|
||||
return null
|
||||
}
|
||||
}
|
||||
// Delegate-first policy (Phase 20.34 Milestone A)
|
||||
local d = env.get("HAKO_MIR_BUILDER_DELEGATE")
|
||||
if d != null && ("" + d) == "1" {
|
||||
|
||||
22
lang/src/mir/builder/internal/README.md
Normal file
22
lang/src/mir/builder/internal/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# MirBuilder Internal Boxes — Minimal Shapes (Phase 20.34)
|
||||
|
||||
Responsibility
|
||||
- Provide small, readable boxes that lower a very small subset of Program(JSON v0)
|
||||
into MIR(JSON v0) strings without heavy parsing. Fail‑Fast with stable tags.
|
||||
|
||||
Boxes
|
||||
- `prog_scan_box.hako` — tiny helpers for string scanning: find, skip spaces, read quoted/op/int values.
|
||||
- `lower_return_int_box.hako` — Return(Int N) → const(i64 N) + ret.
|
||||
- `lower_return_binop_box.hako` — Return(Binary(Int,Int) op {+,-,*,/}) → const, const, binop, ret.
|
||||
- `lower_if_compare_box.hako` — If(Compare(Int,Int) then Return(Int) else Return(Int)) → compare, branch, ret×2.
|
||||
- `lower_loop_simple_box.hako` — Loop(Compare i<limit) counting loop(i=0..limit)→ LoopFormBox.loop_count に委譲。
|
||||
- `lower_loop_sum_bc_box.hako` — Loop sum + break/continue 哲学(i==break → break / i==skip → continue)→ LoopFormBox.build("sum_bc", ...)。
|
||||
|
||||
Policy
|
||||
- No execution or I/O; emit-only. Keep shapes minimal and obvious. For unsupported inputs, print
|
||||
`[mirbuilder/internal/unsupported] ...` and return null.
|
||||
|
||||
LoopForm param
|
||||
- LoopFormBox.build(mode, limit, skip, break)
|
||||
- mode: "count"(i を返す)/ "sum_bc"(sum の合流を Exit PHI で返す)
|
||||
- limit/skip/break は i64 として扱う。skip/break は null の場合、`skip=2` / `break=limit` を既定とする。
|
||||
66
lang/src/mir/builder/internal/lower_if_compare_box.hako
Normal file
66
lang/src/mir/builder/internal/lower_if_compare_box.hako
Normal file
@ -0,0 +1,66 @@
|
||||
// lower_if_compare_box.hako — If(Compare(Int,Int), then Return(Int), else Return(Int)) → compare+branch+ret×2
|
||||
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerIfCompareBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_if = s.indexOf("\"type\":\"If\"")
|
||||
if k_if < 0 { return null }
|
||||
// cond Compare
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if)
|
||||
if k_cmp < 0 { return null }
|
||||
local k_op = s.indexOf("\"op\":", k_cmp)
|
||||
if k_op < 0 { return null }
|
||||
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
||||
if op == null { return null }
|
||||
if !(op == "<" || op == ">" || op == "<=" || op == ">=" || op == "==" || op == "!=") { return null }
|
||||
// lhs/rhs ints
|
||||
local klhs = s.indexOf("\"lhs\":{", k_cmp)
|
||||
if klhs < 0 { return null }
|
||||
local ti = s.indexOf("\"type\":\"Int\"", klhs)
|
||||
if ti < 0 { return null }
|
||||
local kv_lhs = s.indexOf("\"value\":", ti)
|
||||
if kv_lhs < 0 { return null }
|
||||
local lhs_val = JsonFragBox.read_int_after(s, kv_lhs + 8)
|
||||
if lhs_val == null { return null }
|
||||
local krhs = s.indexOf("\"rhs\":{", k_cmp)
|
||||
if krhs < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", krhs)
|
||||
if ti2 < 0 { return null }
|
||||
local kv_rhs = s.indexOf("\"value\":", ti2)
|
||||
if kv_rhs < 0 { return null }
|
||||
local rhs_val = JsonFragBox.read_int_after(s, kv_rhs + 8)
|
||||
if rhs_val == null { return null }
|
||||
// then/else return ints
|
||||
local kth = s.indexOf("\"then\":", k_if)
|
||||
if kth < 0 { return null }
|
||||
local rt = s.indexOf("\"type\":\"Return\"", kth)
|
||||
if rt < 0 { return null }
|
||||
local ti3 = s.indexOf("\"type\":\"Int\"", rt)
|
||||
if ti3 < 0 { return null }
|
||||
local kv_then = s.indexOf("\"value\":", ti3)
|
||||
if kv_then < 0 { return null }
|
||||
local then_val = JsonFragBox.read_int_after(s, kv_then + 8)
|
||||
if then_val == null { return null }
|
||||
local kel = s.indexOf("\"else\":", k_if)
|
||||
if kel < 0 { return null }
|
||||
local rt2 = s.indexOf("\"type\":\"Return\"", kel)
|
||||
if rt2 < 0 { return null }
|
||||
local ti4 = s.indexOf("\"type\":\"Int\"", rt2)
|
||||
if ti4 < 0 { return null }
|
||||
local kv_else = s.indexOf("\"value\":", ti4)
|
||||
if kv_else < 0 { return null }
|
||||
local else_val = JsonFragBox.read_int_after(s, kv_else + 8)
|
||||
if else_val == null { return null }
|
||||
|
||||
local mir = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[" +
|
||||
"{\"label\":\"bb0\",\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_val + "}}," +
|
||||
"{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val + "}}," +
|
||||
"{\"op\":\"compare\",\"operation\":\"" + op + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
|
||||
"{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
|
||||
"{\"label\":\"bb1\",\"instructions\":[{\"op\":\"const\",\"dst\":4,\"value\":{\"type\":\"i64\",\"value\":" + then_val + "}},{\"op\":\"ret\",\"value\":4}]}," +
|
||||
"{\"label\":\"bb2\",\"instructions\":[{\"op\":\"const\",\"dst\":5,\"value\":{\"type\":\"i64\",\"value\":" + else_val + "}},{\"op\":\"ret\",\"value\":5}]}] }},\"blocks\":3}"
|
||||
return mir
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
// lower_if_compare_fold_binints_box.hako — If(Compare with Binary(Int,Int) on either side) → const-fold and lower
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
using ProgScanBox as Scan
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
|
||||
static box LowerIfCompareFoldBinIntsBox {
|
||||
_map_cmp(op) { if op == "<" { return "Lt" } if op == ">" { return "Gt" } if op == "<=" { return "Le" } if op == ">=" { return "Ge" } if op == "==" { return "Eq" } if op == "!=" { return "Ne" } return null }
|
||||
_fold_bin_ints(s, k_bin_start) {
|
||||
// expects: {"type":"Binary","op":"+|-|*|/","lhs":{"type":"Int","value":L},"rhs":{"type":"Int","value":R}}
|
||||
local kop = s.indexOf("\"op\":\"", k_bin_start); if kop < 0 { return null }
|
||||
local iop = kop + 6; local op = s.substring(iop, iop+1)
|
||||
if !(op == "+" || op == "-" || op == "*" || op == "/") { return null }
|
||||
local kli = s.indexOf("\"type\":\"Int\"", k_bin_start); if kli < 0 { return null }
|
||||
local kvl = s.indexOf("\"value\":", kli); if kvl < 0 { return null }
|
||||
local l = JsonFragBox.read_int_after(s, kvl + 8); if l == null { return null }
|
||||
// rhs int
|
||||
local kri = s.indexOf("\"type\":\"Int\"", kli + 1); if kri < 0 { return null }
|
||||
local kv2 = s.indexOf("\"value\":", kri); if kv2 < 0 { return null }
|
||||
local r = JsonFragBox.read_int_after(s, kv2 + 8); if r == null { return null }
|
||||
// compute
|
||||
local li = 0; local ri = 0; // string to int by simple parse (assume i64 fits)
|
||||
// naive parse via accumulate; rely on toString of concatenation is ok
|
||||
li = 0 + ("" + l); ri = 0 + ("" + r)
|
||||
local res = 0
|
||||
if op == "+" { res = li + ri }
|
||||
else if op == "-" { res = li - ri }
|
||||
else if op == "*" { res = li * ri }
|
||||
else { res = li / ri }
|
||||
return ("" + res)
|
||||
}
|
||||
_resolve_side_int(s, node_pos, if_pos) {
|
||||
// node may be Int, Binary(Int,Int), or Var with Local Int before if_pos
|
||||
if node_pos < 0 { return null }
|
||||
// Int
|
||||
if s.indexOf("\"type\":\"Int\"", node_pos) == node_pos { // best-effort
|
||||
local kv = s.indexOf("\"value\":", node_pos); if kv < 0 { return null }
|
||||
return JsonFragBox.read_int_after(s, kv + 8)
|
||||
}
|
||||
// Binary
|
||||
if s.indexOf("\"type\":\"Binary\"", node_pos) == node_pos {
|
||||
return me._fold_bin_ints(s, node_pos)
|
||||
}
|
||||
// Var(name)
|
||||
if s.indexOf("\"type\":\"Var\"", node_pos) == node_pos {
|
||||
local kn = s.indexOf("\"name\":\"", node_pos); if kn < 0 { return null }
|
||||
local name = JsonFragBox.read_string_after(s, kn + 5); if name == null { return null }
|
||||
// find last matching Local Int before if_pos via util
|
||||
return PatternUtilBox.find_local_int_before(s, name, if_pos)
|
||||
}
|
||||
return null
|
||||
}
|
||||
try_lower(program_json){
|
||||
local s = "" + program_json
|
||||
local k_if = s.indexOf("\"type\":\"If\""); if k_if < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if); if k_cmp < 0 { return null }
|
||||
local op = me._map_cmp(Scan.read_quoted_after_key(s, k_cmp, "op")); if op == null { return null }
|
||||
// locate lhs/rhs node starts (Var/Int/Binary)
|
||||
local klhs = s.indexOf("\"lhs\":{", k_cmp); if klhs < 0 { return null }
|
||||
local krhs = s.indexOf("\"rhs\":{", k_cmp); if krhs < 0 { return null }
|
||||
local lhs_pos = klhs + 6
|
||||
local rhs_pos = krhs + 6
|
||||
local lhs_val = me._resolve_side_int(s, lhs_pos, k_if)
|
||||
local rhs_val = me._resolve_side_int(s, rhs_pos, k_if)
|
||||
if lhs_val == null || rhs_val == null { return null }
|
||||
// then/else Return(Int)
|
||||
local kth = s.indexOf("\"then\":", k_if); if kth < 0 { return null }
|
||||
local rt1 = s.indexOf("\"type\":\"Return\"", kth); if rt1 < 0 { return null }
|
||||
local ti1 = s.indexOf("\"type\":\"Int\"", rt1); if ti1 < 0 { return null }
|
||||
local kv1 = s.indexOf("\"value\":", ti1); if kv1 < 0 { return null }
|
||||
local then_v = JsonFragBox.read_int_after(s, kv1 + 8); if then_v == null { return null }
|
||||
local kel = s.indexOf("\"else\":", k_if); if kel < 0 { return null }
|
||||
local rt2 = s.indexOf("\"type\":\"Return\"", kel); if rt2 < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", rt2); if ti2 < 0 { return null }
|
||||
local kv2b = s.indexOf("\"value\":", ti2); if kv2b < 0 { return null }
|
||||
local else_v = JsonFragBox.read_int_after(s, kv2b + 8); if else_v == null { return null }
|
||||
// Build MIR
|
||||
local b0 = new ArrayBox(); b0.push(MirSchemaBox.inst_const(1, lhs_val)); b0.push(MirSchemaBox.inst_const(2, rhs_val)); b0.push(MirSchemaBox.inst_compare(op,1,2,3)); b0.push(MirSchemaBox.inst_branch(3,1,2))
|
||||
local b1 = new ArrayBox(); b1.push(MirSchemaBox.inst_const(4, then_v)); b1.push(MirSchemaBox.inst_ret(4))
|
||||
local b2 = new ArrayBox(); b2.push(MirSchemaBox.inst_const(5, else_v)); b2.push(MirSchemaBox.inst_ret(5))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0,b0)); blocks.push(MirSchemaBox.block(1,b1)); blocks.push(MirSchemaBox.block(2,b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
// lower_if_compare_fold_varint_box.hako — If(Compare with Binary(Var,Int) or Binary(Int,Var)) → fold using Local Int
|
||||
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerIfCompareFoldVarIntBox {
|
||||
_fold_bin_varint(s, k_bin, if_pos) {
|
||||
// Binary with one Var and one Int; resolve Var via Local and compute result
|
||||
local kop = s.indexOf("\"op\":\"", k_bin); if kop < 0 { return null }
|
||||
local op = JsonFragBox.read_string_after(s, kop + 5)
|
||||
if !(op=="+"||op=="-"||op=="*"||op=="/") { return null }
|
||||
local klv = s.indexOf("\"lhs\":{\"type\":\"Var\"", k_bin)
|
||||
local kli = s.indexOf("\"lhs\":{\"type\":\"Int\"", k_bin)
|
||||
local krv = s.indexOf("\"rhs\":{\"type\":\"Var\"", k_bin)
|
||||
local kri = s.indexOf("\"rhs\":{\"type\":\"Int\"", k_bin)
|
||||
local vval = null; local ival = null
|
||||
if klv >= 0 && kri >= 0 {
|
||||
local kn = s.indexOf("\"name\":", klv); if kn < 0 { return null }
|
||||
local name = JsonFragBox.read_string_after(s, kn + 7)
|
||||
vval = PatternUtilBox.find_local_int_before(s, name, if_pos)
|
||||
local kv = s.indexOf("\"value\":", kri); if kv < 0 { return null }
|
||||
ival = JsonFragBox.read_int_after(s, kv + 8)
|
||||
} else if kli >= 0 && krv >= 0 {
|
||||
local kv2 = s.indexOf("\"value\":", kli); if kv2 < 0 { return null }
|
||||
ival = JsonFragBox.read_int_after(s, kv2 + 8)
|
||||
local kn2 = s.indexOf("\"name\":", krv); if kn2 < 0 { return null }
|
||||
local name2 = JsonFragBox.read_string_after(s, kn2 + 7)
|
||||
vval = PatternUtilBox.find_local_int_before(s, name2, if_pos)
|
||||
}
|
||||
if vval == null || ival == null { return null }
|
||||
local vi = 0 + ("" + vval); local ii2 = 0 + ("" + ival)
|
||||
local res = 0
|
||||
if op=="+" { res = vi + ii2 } else if op=="-" { res = vi - ii2 } else if op=="*" { res = vi * ii2 } else { res = vi / ii2 }
|
||||
return ("" + res)
|
||||
}
|
||||
_resolve_side(s, node_pos, if_pos) {
|
||||
// Try Int
|
||||
if s.indexOf("\"type\":\"Int\"", node_pos) == node_pos {
|
||||
local kv = s.indexOf("\"value\":", node_pos); if kv < 0 { return null }
|
||||
return JsonFragBox.read_int_after(s, kv + 8)
|
||||
}
|
||||
// Binary(Var,Int) or (Int,Var)
|
||||
if s.indexOf("\"type\":\"Binary\"", node_pos) == node_pos {
|
||||
return me._fold_bin_varint(s, node_pos, if_pos)
|
||||
}
|
||||
// Var → Local Int
|
||||
if s.indexOf("\"type\":\"Var\"", node_pos) == node_pos {
|
||||
local kn = s.indexOf("\"name\":", node_pos); if kn < 0 { return null }
|
||||
local name = JsonFragBox.read_string_after(s, kn + 7)
|
||||
return PatternUtilBox.find_local_int_before(s, name, if_pos)
|
||||
}
|
||||
return null
|
||||
}
|
||||
try_lower(program_json){
|
||||
local s = "" + program_json
|
||||
local k_if = s.indexOf("\"type\":\"If\""); if k_if < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if); if k_cmp < 0 { return null }
|
||||
local k_op = s.indexOf("\"op\":", k_cmp); if k_op < 0 { return null }
|
||||
local op_sym = JsonFragBox.read_string_after(s, k_op + 5)
|
||||
local op = PatternUtilBox.map_cmp(op_sym); if op == null { return null }
|
||||
local klhs = s.indexOf("\"lhs\":{", k_cmp); if klhs < 0 { return null }
|
||||
local krhs = s.indexOf("\"rhs\":{", k_cmp); if krhs < 0 { return null }
|
||||
local lhs = me._resolve_side(s, klhs+6, k_if)
|
||||
local rhs = me._resolve_side(s, krhs+6, k_if)
|
||||
if lhs == null || rhs == null { return null }
|
||||
// then/else Return(Int)
|
||||
local kth = s.indexOf("\"then\":", k_if); if kth < 0 { return null }
|
||||
local rt1 = s.indexOf("\"type\":\"Return\"", kth); if rt1 < 0 { return null }
|
||||
local ti1 = s.indexOf("\"type\":\"Int\"", rt1); if ti1 < 0 { return null }
|
||||
local kv1 = s.indexOf("\"value\":", ti1); if kv1 < 0 { return null }
|
||||
local tv = JsonFragBox.read_int_after(s, kv1 + 8); if tv == null { return null }
|
||||
local kel = s.indexOf("\"else\":", k_if); if kel < 0 { return null }
|
||||
local rt2 = s.indexOf("\"type\":\"Return\"", kel); if rt2 < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", rt2); if ti2 < 0 { return null }
|
||||
local kv2b = s.indexOf("\"value\":", ti2); if kv2b < 0 { return null }
|
||||
local ev = JsonFragBox.read_int_after(s, kv2b + 8); if ev == null { return null }
|
||||
// Build
|
||||
local b0=new ArrayBox(); b0.push(MirSchemaBox.inst_const(1,lhs)); b0.push(MirSchemaBox.inst_const(2,rhs)); b0.push(MirSchemaBox.inst_compare(op,1,2,3)); b0.push(MirSchemaBox.inst_branch(3,1,2))
|
||||
local b1=new ArrayBox(); b1.push(MirSchemaBox.inst_const(4,tv)); b1.push(MirSchemaBox.inst_ret(4))
|
||||
local b2=new ArrayBox(); b2.push(MirSchemaBox.inst_const(5,ev)); b2.push(MirSchemaBox.inst_ret(5))
|
||||
local blocks=new ArrayBox(); blocks.push(MirSchemaBox.block(0,b0)); blocks.push(MirSchemaBox.block(1,b1)); blocks.push(MirSchemaBox.block(2,b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
// lower_if_compare_varint_box.hako — If(Compare Var vs Int | Int vs Var) with prior Local Int
|
||||
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerIfCompareVarIntBox {
|
||||
try_lower(program_json){
|
||||
local s = "" + program_json
|
||||
local k_if = s.indexOf("\"type\":\"If\""); if k_if < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if); if k_cmp < 0 { return null }
|
||||
local k_op = s.indexOf("\"op\":", k_cmp); if k_op < 0 { return null }
|
||||
local op_sym = JsonFragBox.read_string_after(s, k_op + 5)
|
||||
local op = PatternUtilBox.map_cmp(op_sym); if op == null { return null }
|
||||
// Var vs Int
|
||||
local klhs_var = s.indexOf("\"lhs\":{\"type\":\"Var\"", k_cmp)
|
||||
local krhs_int = s.indexOf("\"rhs\":{\"type\":\"Int\"", k_cmp)
|
||||
local aval=null; local bval=null
|
||||
if klhs_var >= 0 && krhs_int >= 0 {
|
||||
local k_name = s.indexOf("\"name\":", klhs_var); if k_name < 0 { return null }
|
||||
local name = JsonFragBox.read_string_after(s, k_name + 7)
|
||||
aval = PatternUtilBox.find_local_int_before(s, name, k_if)
|
||||
local ki = s.indexOf("\"type\":\"Int\"", krhs_int)
|
||||
if ki >= 0 {
|
||||
local kv = s.indexOf("\"value\":", ki); if kv < 0 { return null }
|
||||
bval = JsonFragBox.read_int_after(s, kv + 8)
|
||||
}
|
||||
} else {
|
||||
// Int vs Var
|
||||
local klhs_int = s.indexOf("\"lhs\":{\"type\":\"Int\"", k_cmp)
|
||||
local krhs_var = s.indexOf("\"rhs\":{\"type\":\"Var\"", k_cmp)
|
||||
if klhs_int >= 0 && krhs_var >= 0 {
|
||||
local ki2 = s.indexOf("\"type\":\"Int\"", klhs_int)
|
||||
if ki2 >= 0 {
|
||||
local kv2 = s.indexOf("\"value\":", ki2); if kv2 < 0 { return null }
|
||||
aval = JsonFragBox.read_int_after(s, kv2 + 8)
|
||||
}
|
||||
local k_name2 = s.indexOf("\"name\":", krhs_var); if k_name2 < 0 { return null }
|
||||
local name2 = JsonFragBox.read_string_after(s, k_name2 + 7)
|
||||
if name2 != null { bval = PatternUtilBox.find_local_int_before(s, name2, k_if) }
|
||||
}
|
||||
}
|
||||
if aval == null || bval == null { return null }
|
||||
// then/else Return(Int)
|
||||
local kth = s.indexOf("\"then\":", k_if); if kth < 0 { return null }
|
||||
local rt1 = s.indexOf("\"type\":\"Return\"", kth); if rt1 < 0 { return null }
|
||||
local ti1 = s.indexOf("\"type\":\"Int\"", rt1); if ti1 < 0 { return null }
|
||||
local kv_then = s.indexOf("\"value\":", ti1); if kv_then < 0 { return null }
|
||||
local then_v = JsonFragBox.read_int_after(s, kv_then + 8); if then_v == null { return null }
|
||||
local kel = s.indexOf("\"else\":", k_if); if kel < 0 { return null }
|
||||
local rt2 = s.indexOf("\"type\":\"Return\"", kel); if rt2 < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", rt2); if ti2 < 0 { return null }
|
||||
local kv_else = s.indexOf("\"value\":", ti2); if kv_else < 0 { return null }
|
||||
local else_v = JsonFragBox.read_int_after(s, kv_else + 8); if else_v == null { return null }
|
||||
// Build MIR
|
||||
local b0=new ArrayBox(); b0.push(MirSchemaBox.inst_const(1,aval)); b0.push(MirSchemaBox.inst_const(2,bval)); b0.push(MirSchemaBox.inst_compare(op,1,2,3)); b0.push(MirSchemaBox.inst_branch(3,1,2))
|
||||
local b1=new ArrayBox(); b1.push(MirSchemaBox.inst_const(4,then_v)); b1.push(MirSchemaBox.inst_ret(4))
|
||||
local b2=new ArrayBox(); b2.push(MirSchemaBox.inst_const(5,else_v)); b2.push(MirSchemaBox.inst_ret(5))
|
||||
local blocks=new ArrayBox(); blocks.push(MirSchemaBox.block(0,b0)); blocks.push(MirSchemaBox.block(1,b1)); blocks.push(MirSchemaBox.block(2,b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
// lower_if_compare_varvar_box.hako — If(Compare Var vs Var) with prior Local Int bindings
|
||||
// Pattern:
|
||||
// Local a=Int A; Local b=Int B;
|
||||
// If(cond=Compare(op, lhs=Var a, rhs=Var b)) { Return(Int X) } else { Return(Int Y) }
|
||||
// Lowers to const A/B → compare(op) → branch → ret X/Y
|
||||
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerIfCompareVarVarBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_if = s.indexOf("\"type\":\"If\"")
|
||||
if k_if < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if)
|
||||
if k_cmp < 0 { return null }
|
||||
// LHS/RHS Var names
|
||||
local lhs_name = null
|
||||
local klhs = s.indexOf("\"lhs\":{\"type\":\"Var\"", k_cmp)
|
||||
if klhs >= 0 {
|
||||
local k_name_lhs = s.indexOf("\"name\":", klhs); if k_name_lhs < 0 { return null }
|
||||
lhs_name = JsonFragBox.read_string_after(s, k_name_lhs + 7)
|
||||
}
|
||||
local rhs_name = null
|
||||
local krhs = s.indexOf("\"rhs\":{\"type\":\"Var\"", k_cmp)
|
||||
if krhs >= 0 {
|
||||
local k_name_rhs = s.indexOf("\"name\":", krhs); if k_name_rhs < 0 { return null }
|
||||
rhs_name = JsonFragBox.read_string_after(s, k_name_rhs + 7)
|
||||
}
|
||||
if lhs_name == null || rhs_name == null { return null }
|
||||
// Resolve locals
|
||||
local aval = PatternUtilBox.find_local_int_before(s, lhs_name, k_if)
|
||||
local bval = PatternUtilBox.find_local_int_before(s, rhs_name, k_if)
|
||||
if aval == null || bval == null { return null }
|
||||
// op map
|
||||
local k_op = s.indexOf("\"op\":", k_cmp); if k_op < 0 { return null }
|
||||
local sym = JsonFragBox.read_string_after(s, k_op + 5)
|
||||
if sym == null { return null }
|
||||
local op = PatternUtilBox.map_cmp(sym)
|
||||
if op == null { return null }
|
||||
// then/else Return(Int)
|
||||
local kth = s.indexOf("\"then\":", k_if); if kth < 0 { return null }
|
||||
local rt1 = s.indexOf("\"type\":\"Return\"", kth); if rt1 < 0 { return null }
|
||||
local ti1 = s.indexOf("\"type\":\"Int\"", rt1); if ti1 < 0 { return null }
|
||||
local kv_then = s.indexOf("\"value\":", ti1); if kv_then < 0 { return null }
|
||||
local then_val = JsonFragBox.read_int_after(s, kv_then + 8); if then_val == null { return null }
|
||||
local kel = s.indexOf("\"else\":", k_if); if kel < 0 { return null }
|
||||
local rt2 = s.indexOf("\"type\":\"Return\"", kel); if rt2 < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", rt2); if ti2 < 0 { return null }
|
||||
local kv_else = s.indexOf("\"value\":", ti2); if kv_else < 0 { return null }
|
||||
local else_val = JsonFragBox.read_int_after(s, kv_else + 8); if else_val == null { return null }
|
||||
|
||||
// Build MIR
|
||||
local b0 = new ArrayBox()
|
||||
b0.push(MirSchemaBox.inst_const(1, aval))
|
||||
b0.push(MirSchemaBox.inst_const(2, bval))
|
||||
b0.push(MirSchemaBox.inst_compare(op, 1, 2, 3))
|
||||
b0.push(MirSchemaBox.inst_branch(3, 1, 2))
|
||||
local b1 = new ArrayBox(); b1.push(MirSchemaBox.inst_const(4, then_val)); b1.push(MirSchemaBox.inst_ret(4))
|
||||
local b2 = new ArrayBox(); b2.push(MirSchemaBox.inst_const(5, else_val)); b2.push(MirSchemaBox.inst_ret(5))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0)); blocks.push(MirSchemaBox.block(1, b1)); blocks.push(MirSchemaBox.block(2, b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
106
lang/src/mir/builder/internal/lower_if_nested_box.hako
Normal file
106
lang/src/mir/builder/internal/lower_if_nested_box.hako
Normal file
@ -0,0 +1,106 @@
|
||||
// lower_if_nested_box.hako — Nested If lowering
|
||||
// Pattern:
|
||||
// If(cond1=Compare(Int,Int)) {
|
||||
// Return(Int A)
|
||||
// } else {
|
||||
// If(cond2=Compare(Int,Int)) { Return(Int B) } else { Return(Int C) }
|
||||
// }
|
||||
// Lowers to:
|
||||
// bb0: const lhs1,rhs1; compare1 → branch then:bb1 else:bb2
|
||||
// bb1: const A; ret
|
||||
// bb2: const lhs2,rhs2; compare2 → branch then:bb3 else:bb4
|
||||
// bb3: const B; ret
|
||||
// bb4: const C; ret
|
||||
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
|
||||
static box LowerIfNestedBox {
|
||||
_map_cmp(op) {
|
||||
if op == "<" { return "Lt" }
|
||||
if op == ">" { return "Gt" }
|
||||
if op == "<=" { return "Le" }
|
||||
if op == ">=" { return "Ge" }
|
||||
if op == "==" { return "Eq" }
|
||||
if op == "!=" { return "Ne" }
|
||||
return null
|
||||
}
|
||||
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_if1 = s.indexOf("\"type\":\"If\"")
|
||||
if k_if1 < 0 { return null }
|
||||
local k_cmp1 = s.indexOf("\"type\":\"Compare\"", k_if1)
|
||||
if k_cmp1 < 0 { return null }
|
||||
local kop1 = s.indexOf("\"op\":", k_cmp1); if kop1 < 0 { return null }
|
||||
local op1s = JsonFragBox.read_string_after(s, kop1 + 5); if op1s == null { return null }
|
||||
local op1 = me._map_cmp(op1s); if op1 == null { return null }
|
||||
local klhs1 = s.indexOf("\"lhs\":{", k_cmp1); if klhs1 < 0 { return null }
|
||||
local ti11 = s.indexOf("\"type\":\"Int\"", klhs1); if ti11 < 0 { return null }
|
||||
local kv11 = s.indexOf("\"value\":", ti11); if kv11 < 0 { return null }
|
||||
local lhs1 = JsonFragBox.read_int_after(s, kv11 + 8); if lhs1 == null { return null }
|
||||
local krhs1 = s.indexOf("\"rhs\":{", k_cmp1); if krhs1 < 0 { return null }
|
||||
local ti12 = s.indexOf("\"type\":\"Int\"", krhs1); if ti12 < 0 { return null }
|
||||
local kv12 = s.indexOf("\"value\":", ti12); if kv12 < 0 { return null }
|
||||
local rhs1 = JsonFragBox.read_int_after(s, kv12 + 8); if rhs1 == null { return null }
|
||||
// then Return(Int A)
|
||||
local kth = s.indexOf("\"then\":", k_if1); if kth < 0 { return null }
|
||||
local rt1 = s.indexOf("\"type\":\"Return\"", kth); if rt1 < 0 { return null }
|
||||
local ti13 = s.indexOf("\"type\":\"Int\"", rt1); if ti13 < 0 { return null }
|
||||
local kv13 = s.indexOf("\"value\":", ti13); if kv13 < 0 { return null }
|
||||
local aval = JsonFragBox.read_int_after(s, kv13 + 8); if aval == null { return null }
|
||||
// else contains nested If with Return(Int)/Return(Int)
|
||||
local kel = s.indexOf("\"else\":", k_if1); if kel < 0 { return null }
|
||||
local k_if2 = s.indexOf("\"type\":\"If\"", kel); if k_if2 < 0 { return null }
|
||||
local k_cmp2 = s.indexOf("\"type\":\"Compare\"", k_if2); if k_cmp2 < 0 { return null }
|
||||
local kop2 = s.indexOf("\"op\":", k_cmp2); if kop2 < 0 { return null }
|
||||
local op2s = JsonFragBox.read_string_after(s, kop2 + 5); if op2s == null { return null }
|
||||
local op2 = me._map_cmp(op2s); if op2 == null { return null }
|
||||
local klhs2 = s.indexOf("\"lhs\":{", k_cmp2); if klhs2 < 0 { return null }
|
||||
local ti21 = s.indexOf("\"type\":\"Int\"", klhs2); if ti21 < 0 { return null }
|
||||
local kv21 = s.indexOf("\"value\":", ti21); if kv21 < 0 { return null }
|
||||
local lhs2 = JsonFragBox.read_int_after(s, kv21 + 8); if lhs2 == null { return null }
|
||||
local krhs2 = s.indexOf("\"rhs\":{", k_cmp2); if krhs2 < 0 { return null }
|
||||
local ti22 = s.indexOf("\"type\":\"Int\"", krhs2); if ti22 < 0 { return null }
|
||||
local kv22 = s.indexOf("\"value\":", ti22); if kv22 < 0 { return null }
|
||||
local rhs2 = JsonFragBox.read_int_after(s, kv22 + 8); if rhs2 == null { return null }
|
||||
// then Return(Int B)
|
||||
local kth2 = s.indexOf("\"then\":", k_if2); if kth2 < 0 { return null }
|
||||
local rt2 = s.indexOf("\"type\":\"Return\"", kth2); if rt2 < 0 { return null }
|
||||
local ti23 = s.indexOf("\"type\":\"Int\"", rt2); if ti23 < 0 { return null }
|
||||
local kv23 = s.indexOf("\"value\":", ti23); if kv23 < 0 { return null }
|
||||
local bval = JsonFragBox.read_int_after(s, kv23 + 8); if bval == null { return null }
|
||||
// else Return(Int C)
|
||||
local kel2 = s.indexOf("\"else\":", k_if2); if kel2 < 0 { return null }
|
||||
local rt3 = s.indexOf("\"type\":\"Return\"", kel2); if rt3 < 0 { return null }
|
||||
local ti24 = s.indexOf("\"type\":\"Int\"", rt3); if ti24 < 0 { return null }
|
||||
local kv24 = s.indexOf("\"value\":", ti24); if kv24 < 0 { return null }
|
||||
local cval = JsonFragBox.read_int_after(s, kv24 + 8); if cval == null { return null }
|
||||
|
||||
// Build MIR(JSON)
|
||||
local b0 = new ArrayBox()
|
||||
b0.push(MirSchemaBox.inst_const(1, lhs1))
|
||||
b0.push(MirSchemaBox.inst_const(2, rhs1))
|
||||
b0.push(MirSchemaBox.inst_compare(op1, 1, 2, 3))
|
||||
b0.push(MirSchemaBox.inst_branch(3, 1, 2))
|
||||
|
||||
local b1 = new ArrayBox(); b1.push(MirSchemaBox.inst_const(4, aval)); b1.push(MirSchemaBox.inst_ret(4))
|
||||
|
||||
local b2 = new ArrayBox()
|
||||
b2.push(MirSchemaBox.inst_const(5, lhs2))
|
||||
b2.push(MirSchemaBox.inst_const(6, rhs2))
|
||||
b2.push(MirSchemaBox.inst_compare(op2, 5, 6, 7))
|
||||
b2.push(MirSchemaBox.inst_branch(7, 3, 4))
|
||||
|
||||
local b3 = new ArrayBox(); b3.push(MirSchemaBox.inst_const(8, bval)); b3.push(MirSchemaBox.inst_ret(8))
|
||||
local b4 = new ArrayBox(); b4.push(MirSchemaBox.inst_const(9, cval)); b4.push(MirSchemaBox.inst_ret(9))
|
||||
|
||||
local blocks = new ArrayBox()
|
||||
blocks.push(MirSchemaBox.block(0, b0))
|
||||
blocks.push(MirSchemaBox.block(1, b1))
|
||||
blocks.push(MirSchemaBox.block(2, b2))
|
||||
blocks.push(MirSchemaBox.block(3, b3))
|
||||
blocks.push(MirSchemaBox.block(4, b4))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
// lower_if_then_else_following_return_box.hako
|
||||
// Pattern: Program body = [ If(cond=Compare(Int,Int), then=[Return(Int)] (no else)), Return(Int) ]
|
||||
// Lowers to: bb0: const lhs/rhs, compare, branch; bb1: const then, ret; bb2: const else, ret
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
using ProgScanBox as Scan
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerIfThenElseFollowingReturnBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
// If with Compare(Int,Int)
|
||||
local k_if = s.indexOf("\"type\":\"If\"")
|
||||
if k_if < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_if)
|
||||
if k_cmp < 0 { return null }
|
||||
local op = Scan.read_quoted_after_key(s, k_cmp, "op")
|
||||
if op == null { return null }
|
||||
if !(op == "<" || op == ">" || op == "<=" || op == ">=" || op == "==" || op == "!=") { return null }
|
||||
// LHS/RHS ints
|
||||
local klhs = s.indexOf("\"lhs\":{", k_cmp); if klhs < 0 { return null }
|
||||
local ti1 = s.indexOf("\"type\":\"Int\"", klhs); if ti1 < 0 { return null }
|
||||
{ local kv = s.indexOf("\"value\":", ti1); if kv < 0 { return null }; var lhs_val = JsonFragBox.read_int_after(s, kv + 8); if lhs_val == null { return null } }
|
||||
local krhs = s.indexOf("\"rhs\":{", k_cmp); if krhs < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", krhs); if ti2 < 0 { return null }
|
||||
{ local kv2 = s.indexOf("\"value\":", ti2); if kv2 < 0 { return null }; var rhs_val = JsonFragBox.read_int_after(s, kv2 + 8); if rhs_val == null { return null } }
|
||||
// then: Return(Int)
|
||||
local kth = s.indexOf("\"then\":", k_if); if kth < 0 { return null }
|
||||
local rt = s.indexOf("\"type\":\"Return\"", kth); if rt < 0 { return null }
|
||||
local ti3 = s.indexOf("\"type\":\"Int\"", rt); if ti3 < 0 { return null }
|
||||
{ local kv3 = s.indexOf("\"value\":", ti3); if kv3 < 0 { return null }; var then_val = JsonFragBox.read_int_after(s, kv3 + 8); if then_val == null { return null } }
|
||||
// else is omitted → following Return(Int) in Program body
|
||||
local k_after = s.indexOf("\"type\":\"Return\"", k_if + 1); if k_after < 0 { return null }
|
||||
local ti4 = s.indexOf("\"type\":\"Int\"", k_after); if ti4 < 0 { return null }
|
||||
{ local kv4 = s.indexOf("\"value\":", ti4); if kv4 < 0 { return null }; var else_val = JsonFragBox.read_int_after(s, kv4 + 8); if else_val == null { return null } }
|
||||
|
||||
// Build MIR(JSON)
|
||||
local b0 = new ArrayBox()
|
||||
b0.push(MirSchemaBox.inst_const(1, lhs_val))
|
||||
b0.push(MirSchemaBox.inst_const(2, rhs_val))
|
||||
b0.push(MirSchemaBox.inst_compare(op == "<" ? "Lt" : (op == ">" ? "Gt" : (op == "<=" ? "Le" : (op == ">=" ? "Ge" : (op == "==" ? "Eq" : "Ne")))), 1, 2, 3))
|
||||
b0.push(MirSchemaBox.inst_branch(3, 1, 2))
|
||||
|
||||
local b1 = new ArrayBox(); b1.push(MirSchemaBox.inst_const(4, then_val)); b1.push(MirSchemaBox.inst_ret(4))
|
||||
local b2 = new ArrayBox(); b2.push(MirSchemaBox.inst_const(5, else_val)); b2.push(MirSchemaBox.inst_ret(5))
|
||||
|
||||
local blocks = new ArrayBox()
|
||||
blocks.push(MirSchemaBox.block(0, b0))
|
||||
blocks.push(MirSchemaBox.block(1, b1))
|
||||
blocks.push(MirSchemaBox.block(2, b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
// lower_loop_count_param_box.hako — Loop(Compare i<limit) with Local i=init and i+=step → LoopForm.build("count", init, step)
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
using ProgScanBox as Scan
|
||||
using selfhost.shared.mir.loopform as LoopFormBox
|
||||
|
||||
static box LowerLoopCountParamBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
// Local i = Int init
|
||||
local k_local_i = s.indexOf("\"type\":\"Local\"")
|
||||
if k_local_i < 0 { return null }
|
||||
if s.indexOf("\"name\":\"i\"", k_local_i) < 0 { return null }
|
||||
local k_init = s.indexOf("\"type\":\"Int\"", k_local_i)
|
||||
if k_init < 0 { return null }
|
||||
local init = Scan.read_value_int_after(s, k_init)
|
||||
if init == null { return null }
|
||||
// Loop Compare i < Int limit
|
||||
local k_loop = s.indexOf("\"type\":\"Loop\"", k_local_i)
|
||||
if k_loop < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_loop)
|
||||
if k_cmp < 0 { return null }
|
||||
if s.indexOf("\"op\":\"<\"", k_cmp) < 0 { return null }
|
||||
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", k_cmp) < 0 { return null }
|
||||
local k_lim_t = s.indexOf("\"type\":\"Int\"", k_cmp)
|
||||
if k_lim_t < 0 { return null }
|
||||
local limit = Scan.read_value_int_after(s, k_lim_t)
|
||||
if limit == null { return null }
|
||||
// Body increment: Local i = Binary('+', Var i, Int step)
|
||||
local k_body_i = s.indexOf("\"name\":\"i\"", k_loop)
|
||||
if k_body_i < 0 { return null }
|
||||
local k_bop = s.indexOf("\"type\":\"Binary\"", k_body_i)
|
||||
if k_bop < 0 { return null }
|
||||
if s.indexOf("\"op\":\"+\"", k_bop) < 0 { return null }
|
||||
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", k_bop) < 0 { return null }
|
||||
local k_step_t = s.indexOf("\"type\":\"Int\"", k_bop)
|
||||
if k_step_t < 0 { return null }
|
||||
local step = Scan.read_value_int_after(s, k_step_t)
|
||||
if step == null { return null }
|
||||
|
||||
// Build via LoopFormBox (extend build to accept param init/step in future; use loop_count then adjust by init/step)
|
||||
// For now, synthesize by composing loop_count(limit') with pre-increment of i, but since we return i, we can directly emit param loop
|
||||
// Implement dedicated param path in LoopFormBox: loop_count(limit, init, step)
|
||||
if step == 1 && init == 0 { return LoopFormBox.build("count", limit, null, null) }
|
||||
// Fallback to parametric count when available
|
||||
if ("" + step) != "" || ("" + init) != "" {
|
||||
// Call loop_count(limit) is incorrect when init/step differ; prefer loop_count when extension exists.
|
||||
// Use build("count_param", limit, init, step) when mode supported.
|
||||
local out = LoopFormBox.build("count_param", limit, init, step)
|
||||
if out != null { return out }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
38
lang/src/mir/builder/internal/lower_loop_simple_box.hako
Normal file
38
lang/src/mir/builder/internal/lower_loop_simple_box.hako
Normal file
@ -0,0 +1,38 @@
|
||||
// lower_loop_simple_box.hako — Loop(Compare i < N) → counting loop (i from 0 to N) returning i
|
||||
// Notes: minimal scanner that extracts limit N from Program(JSON v0) Loop cond rhs Int.
|
||||
|
||||
using selfhost.shared.mir.loopform as LoopFormBox
|
||||
|
||||
static box LowerLoopSimpleBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
// Find Loop with cond Compare op '<' and rhs Int value
|
||||
local k_loop = s.indexOf("\"type\":\"Loop\"")
|
||||
if k_loop < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_loop)
|
||||
if k_cmp < 0 { return null }
|
||||
// op must be '<'
|
||||
local k_op = s.indexOf("\"op\":\"<\"", k_cmp)
|
||||
if k_op < 0 { return null }
|
||||
// rhs Int value
|
||||
local k_rhs = s.indexOf("\"rhs\":{", k_cmp)
|
||||
if k_rhs < 0 { return null }
|
||||
local k_ti = s.indexOf("\"type\":\"Int\"", k_rhs)
|
||||
if k_ti < 0 { return null }
|
||||
// Scan numeric after "value":
|
||||
local k_v = s.indexOf("\"value\":", k_ti)
|
||||
if k_v < 0 { return null }
|
||||
local i = k_v + 8
|
||||
// skip spaces
|
||||
loop(i < s.length()) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i
|
||||
if j < s.length() && s.substring(j,j+1) == "-" { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < s.length()) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
||||
if had == 0 { return null }
|
||||
local limit = s.substring(i, j)
|
||||
|
||||
// Delegate to shared loop form builder (counting mode)
|
||||
return LoopFormBox.build("count", limit, null, null)
|
||||
}
|
||||
}
|
||||
75
lang/src/mir/builder/internal/lower_loop_sum_bc_box.hako
Normal file
75
lang/src/mir/builder/internal/lower_loop_sum_bc_box.hako
Normal file
@ -0,0 +1,75 @@
|
||||
// lower_loop_sum_bc_box.hako — Loop with Compare(i<limit) and body Break/Continue sentinels → LoopFormBox.loop_counter
|
||||
// Pattern (naive):
|
||||
// Local i=0; Local s=0;
|
||||
// Loop(cond=Compare(op:"<", lhs=Var("i"), rhs=Int limit), body=[
|
||||
// If(cond=Compare(op:"==", lhs=Var("i"), rhs=Int break_value)) then [Break]
|
||||
// If(cond=Compare(op:"==", lhs=Var("i"), rhs=Int skip_value)) then [Continue]
|
||||
// ...
|
||||
// ])
|
||||
// Return Var("s")
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
using ProgScanBox as Scan
|
||||
using selfhost.shared.mir.loopform as LoopFormBox
|
||||
|
||||
static box LowerLoopSumBcBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
// Loop and Compare(i < Int limit)
|
||||
local k_loop = s.indexOf("\"type\":\"Loop\"")
|
||||
if k_loop < 0 { return null }
|
||||
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_loop)
|
||||
if k_cmp < 0 { return null }
|
||||
// op "<"
|
||||
local op = Scan.read_quoted_after_key(s, k_cmp, "op")
|
||||
if op == null || op != "<" { return null }
|
||||
// lhs must mention Var("i"); we check weakly by searching name:"i"
|
||||
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", k_cmp) < 0 { return null }
|
||||
// rhs Int limit
|
||||
local k_rhs = s.indexOf("\"rhs\":{", k_cmp)
|
||||
if k_rhs < 0 { return null }
|
||||
local k_ti = s.indexOf("\"type\":\"Int\"", k_rhs)
|
||||
if k_ti < 0 { return null }
|
||||
local limit = Scan.read_value_int_after(s, k_ti)
|
||||
if limit == null { return null }
|
||||
|
||||
// Break sentinel: If(cond Compare i==X) then Break
|
||||
local break_value = null
|
||||
{
|
||||
local kb = s.indexOf("\"type\":\"Break\"", k_loop)
|
||||
if kb >= 0 {
|
||||
// Find nearest previous Compare and grab rhs Int
|
||||
local kbc = s.lastIndexOf("\"type\":\"Compare\"", kb)
|
||||
if kbc >= 0 {
|
||||
// Ensure op=="==" and lhs Var i
|
||||
local bop = Scan.read_quoted_after_key(s, kbc, "op")
|
||||
if bop != null && bop == "==" && s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", kbc) >= 0 {
|
||||
local kbi = s.indexOf("\"type\":\"Int\"", kbc)
|
||||
if kbi >= 0 { break_value = Scan.read_value_int_after(s, kbi) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Continue sentinel: If(cond Compare i==Y) then Continue
|
||||
local skip_value = null
|
||||
{
|
||||
local kc = s.indexOf("\"type\":\"Continue\"", k_loop)
|
||||
if kc >= 0 {
|
||||
local kcc = s.lastIndexOf("\"type\":\"Compare\"", kc)
|
||||
if kcc >= 0 {
|
||||
local cop = Scan.read_quoted_after_key(s, kcc, "op")
|
||||
if cop != null && cop == "==" && s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", kcc) >= 0 {
|
||||
local kci = s.indexOf("\"type\":\"Int\"", kcc)
|
||||
if kci >= 0 { skip_value = Scan.read_value_int_after(s, kci) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Defaults when not present (LoopFormBox.loop_counter expects non-null ints)
|
||||
if skip_value == null { skip_value = 2 }
|
||||
if break_value == null { break_value = limit }
|
||||
|
||||
return LoopFormBox.build("sum_bc", limit, skip_value, break_value)
|
||||
}
|
||||
}
|
||||
44
lang/src/mir/builder/internal/lower_return_binop_box.hako
Normal file
44
lang/src/mir/builder/internal/lower_return_binop_box.hako
Normal file
@ -0,0 +1,44 @@
|
||||
// lower_return_binop_box.hako — Return(Binary(Int,Int) op {+,-,*,/}) → const+const+binop+ret
|
||||
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerReturnBinOpBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
local k_bin = s.indexOf("\"type\":\"Binary\"", k_ret)
|
||||
if k_bin < 0 { return null }
|
||||
// op
|
||||
local k_op = s.indexOf("\"op\":", k_bin)
|
||||
if k_op < 0 { return null }
|
||||
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
||||
if op == null { return null }
|
||||
if !(op == "+" || op == "-" || op == "*" || op == "/") { return null }
|
||||
// lhs int
|
||||
local klhs = s.indexOf("\"lhs\":{", k_bin)
|
||||
if klhs < 0 { return null }
|
||||
local ti = s.indexOf("\"type\":\"Int\"", klhs)
|
||||
if ti < 0 { return null }
|
||||
local kv_lhs = s.indexOf("\"value\":", ti)
|
||||
if kv_lhs < 0 { return null }
|
||||
local lhs_val = JsonFragBox.read_int_after(s, kv_lhs + 8)
|
||||
if lhs_val == null { return null }
|
||||
// rhs int
|
||||
local krhs = s.indexOf("\"rhs\":{", k_bin)
|
||||
if krhs < 0 { return null }
|
||||
local ti2 = s.indexOf("\"type\":\"Int\"", krhs)
|
||||
if ti2 < 0 { return null }
|
||||
local kv_rhs = s.indexOf("\"value\":", ti2)
|
||||
if kv_rhs < 0 { return null }
|
||||
local rhs_val = JsonFragBox.read_int_after(s, kv_rhs + 8)
|
||||
if rhs_val == null { return null }
|
||||
|
||||
local mir = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[{\"label\":\"bb0\",\"instructions\":[" +
|
||||
"{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_val + "}}," +
|
||||
"{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val + "}}," +
|
||||
"{\"op\":\"binop\",\"operation\":\"" + op + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
|
||||
"{\"op\":\"ret\",\"value\":3}]}] }},\"blocks\":1}"
|
||||
return mir
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
// lower_return_binop_varint_box.hako — Return(Binary Var/Int with prior Local Int)
|
||||
// Patterns:
|
||||
// Local x=Int A; Return(Binary(op, lhs=Var x, rhs=Int B))
|
||||
// Local x=Int A; Return(Binary(op, lhs=Int B, rhs=Var x))
|
||||
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerReturnBinOpVarIntBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
local k_bin = s.indexOf("\"type\":\"Binary\"", k_ret)
|
||||
if k_bin < 0 { return null }
|
||||
local k_op = s.indexOf("\"op\":", k_bin)
|
||||
if k_op < 0 { return null }
|
||||
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
||||
if op == null { return null }
|
||||
if !(op == "+" || op == "-" || op == "*" || op == "/") { return null }
|
||||
// Try Var + Int
|
||||
local klhs_var = s.indexOf("\"lhs\":{\"type\":\"Var\"", k_bin)
|
||||
local krhs_int = s.indexOf("\"rhs\":{\"type\":\"Int\"", k_bin)
|
||||
local var_name = null
|
||||
local rhs_val = null
|
||||
if klhs_var >= 0 && krhs_int >= 0 {
|
||||
local k_name = s.indexOf("\"name\":", klhs_var); if k_name < 0 { return null }
|
||||
var_name = JsonFragBox.read_string_after(s, k_name + 7)
|
||||
local kvi = s.indexOf("\"type\":\"Int\"", krhs_int)
|
||||
if kvi >= 0 {
|
||||
local kv = s.indexOf("\"value\":", kvi); if kv < 0 { return null }
|
||||
rhs_val = JsonFragBox.read_int_after(s, kv + 8)
|
||||
}
|
||||
if var_name != null && rhs_val != null {
|
||||
local var_val = PatternUtilBox.find_local_int_before(s, var_name, k_ret)
|
||||
if var_val != null {
|
||||
local b0 = new ArrayBox()
|
||||
b0.push(MirSchemaBox.inst_const(1, var_val))
|
||||
b0.push(MirSchemaBox.inst_const(2, rhs_val))
|
||||
b0.push(MirSchemaBox.inst_binop(op == "+" ? "Add" : (op == "-" ? "Sub" : (op == "*" ? "Mul" : "Div")), 1, 2, 3))
|
||||
b0.push(MirSchemaBox.inst_ret(3))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Try Int + Var
|
||||
local klhs_int = s.indexOf("\"lhs\":{\"type\":\"Int\"", k_bin)
|
||||
local krhs_var = s.indexOf("\"rhs\":{\"type\":\"Var\"", k_bin)
|
||||
var_name = null
|
||||
local lhs_val = null
|
||||
if klhs_int >= 0 && krhs_var >= 0 {
|
||||
local kvi2 = s.indexOf("\"type\":\"Int\"", klhs_int)
|
||||
if kvi2 >= 0 {
|
||||
local kv2 = s.indexOf("\"value\":", kvi2); if kv2 < 0 { return null }
|
||||
lhs_val = JsonFragBox.read_int_after(s, kv2 + 8)
|
||||
}
|
||||
local k_name2 = s.indexOf("\"name\":", krhs_var); if k_name2 < 0 { return null }
|
||||
var_name = JsonFragBox.read_string_after(s, k_name2 + 7)
|
||||
if lhs_val != null && var_name != null {
|
||||
local var_val2 = PatternUtilBox.find_local_int_before(s, var_name, k_ret)
|
||||
if var_val2 != null {
|
||||
local b1 = new ArrayBox()
|
||||
b1.push(MirSchemaBox.inst_const(1, lhs_val))
|
||||
b1.push(MirSchemaBox.inst_const(2, var_val2))
|
||||
b1.push(MirSchemaBox.inst_binop(op == "+" ? "Add" : (op == "-" ? "Sub" : (op == "*" ? "Mul" : "Div")), 1, 2, 3))
|
||||
b1.push(MirSchemaBox.inst_ret(3))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b1))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
// lower_return_binop_varvar_box.hako — Return(Binary Var vs Var) with prior Local Ints
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
using ProgScanBox as Scan
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
|
||||
static box LowerReturnBinOpVarVarBox {
|
||||
_find_local_int_before(s, name, before_pos) { return PatternUtilBox.find_local_int_before(s, name, before_pos) }
|
||||
try_lower(program_json){
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\""); if k_ret < 0 { return null }
|
||||
local k_bin = s.indexOf("\"type\":\"Binary\"", k_ret); if k_bin < 0 { return null }
|
||||
local op = Scan.read_quoted_after_key(s, k_bin, "op"); if op == null { return null }
|
||||
if !(op == "+" || op == "-" || op == "*" || op == "/") { return null }
|
||||
local klhs = s.indexOf("\"lhs\":{\"type\":\"Var\"", k_bin); if klhs < 0 { return null }
|
||||
local krhs = s.indexOf("\"rhs\":{\"type\":\"Var\"", k_bin); if krhs < 0 { return null }
|
||||
local lname = Scan.read_quoted_after_key(s, klhs, "name"); if lname == null { return null }
|
||||
local rname = Scan.read_quoted_after_key(s, krhs, "name"); if rname == null { return null }
|
||||
local lval = me._find_local_int_before(s, lname, k_ret); if lval == null { return null }
|
||||
local rval = me._find_local_int_before(s, rname, k_ret); if rval == null { return null }
|
||||
local b0=new ArrayBox(); b0.push(MirSchemaBox.inst_const(1,lval)); b0.push(MirSchemaBox.inst_const(2,rval)); b0.push(MirSchemaBox.inst_binop(op == "+" ? "Add" : (op == "-" ? "Sub" : (op == "*" ? "Mul" : "Div")),1,2,3)); b0.push(MirSchemaBox.inst_ret(3))
|
||||
local blocks=new ArrayBox(); blocks.push(MirSchemaBox.block(0,b0));
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
23
lang/src/mir/builder/internal/lower_return_bool_box.hako
Normal file
23
lang/src/mir/builder/internal/lower_return_bool_box.hako
Normal file
@ -0,0 +1,23 @@
|
||||
// lower_return_bool_box.hako — Return(Bool) → const(1/0) + ret
|
||||
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerReturnBoolBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
// find Bool value true/false after Return
|
||||
local k_bool = s.indexOf("\"type\":\"Bool\"", k_ret)
|
||||
if k_bool < 0 { return null }
|
||||
local k_val = s.indexOf("\"value\":", k_bool)
|
||||
if k_val < 0 { return null }
|
||||
local is_true = JsonFragBox.read_bool_after(s, k_val+8)
|
||||
if is_true == null { return null }
|
||||
local v = is_true == 1 ? 1 : 0
|
||||
local b0 = new ArrayBox(); b0.push(MirSchemaBox.inst_const(1, v)); b0.push(MirSchemaBox.inst_ret(1))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
21
lang/src/mir/builder/internal/lower_return_float_box.hako
Normal file
21
lang/src/mir/builder/internal/lower_return_float_box.hako
Normal file
@ -0,0 +1,21 @@
|
||||
// lower_return_float_box.hako — Return(Float) → const(f64) + ret
|
||||
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerReturnFloatBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
local k_float = s.indexOf("\"type\":\"Float\"", k_ret)
|
||||
if k_float < 0 { return null }
|
||||
local k_val = s.indexOf("\"value\":", k_float)
|
||||
if k_val < 0 { return null }
|
||||
local fstr = JsonFragBox.read_float_after(s, k_val+8)
|
||||
if fstr == null { return null }
|
||||
local b0 = new ArrayBox(); b0.push(MirSchemaBox.inst_const_f64(1, fstr)); b0.push(MirSchemaBox.inst_ret(1))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
19
lang/src/mir/builder/internal/lower_return_int_box.hako
Normal file
19
lang/src/mir/builder/internal/lower_return_int_box.hako
Normal file
@ -0,0 +1,19 @@
|
||||
// lower_return_int_box.hako — Return(Int) → const+ret
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
using ProgScanBox as Scan
|
||||
|
||||
static box LowerReturnIntBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
// Expect Int after Return
|
||||
local k_int = s.indexOf("\"type\":\"Int\"", k_ret)
|
||||
if k_int < 0 { return null }
|
||||
local val = Scan.read_value_int_after(s, k_int)
|
||||
if val == null { return null }
|
||||
local mir = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[{\"label\":\"bb0\",\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + val + "}},{\"op\":\"ret\",\"value\":1}] }] }},\"blocks\":1}"
|
||||
return mir
|
||||
}
|
||||
}
|
||||
80
lang/src/mir/builder/internal/lower_return_logical_box.hako
Normal file
80
lang/src/mir/builder/internal/lower_return_logical_box.hako
Normal file
@ -0,0 +1,80 @@
|
||||
// lower_return_logical_box.hako — Return(Logical &&/|| of Bool,Bool) → branch+ret (short-circuit semantics)
|
||||
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
|
||||
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
|
||||
|
||||
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
|
||||
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerReturnLogicalBox {
|
||||
// Removed: _find_local_bool_before (use PatternUtilBox.find_local_bool_before instead)
|
||||
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
local k_log = JsonFragBox.index_of_from(s, "\"type\":\"Logical\"", k_ret)
|
||||
if k_log < 0 { return null }
|
||||
// op: && or || (ProgScanBox is sufficient here)
|
||||
local op = ProgScanBox.read_quoted_after_key(s, k_log, "op")
|
||||
if op == null { return null }
|
||||
if !(op == "&&" || op == "||") { return null }
|
||||
// Resolve lhs to 0/1 (Bool or Var with Local Bool) - robust version
|
||||
local lhs_true = null
|
||||
{
|
||||
local klhs_b = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Bool\"", k_log)
|
||||
if klhs_b >= 0 {
|
||||
local kv1 = JsonFragBox.index_of_from(s, "\"value\":", klhs_b)
|
||||
if kv1 >= 0 {
|
||||
lhs_true = JsonFragBox.read_bool_after(s, kv1 + 8)
|
||||
}
|
||||
} else {
|
||||
local klhs_v = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Var\"", k_log)
|
||||
if klhs_v >= 0 {
|
||||
local name = ProgScanBox.read_quoted_after_key(s, klhs_v, "name")
|
||||
if name != null { lhs_true = PatternUtilBox.find_local_bool_before(s, name, k_log) }
|
||||
}
|
||||
}
|
||||
}
|
||||
if lhs_true == null { return null }
|
||||
// Resolve rhs to 0/1 (Bool or Var) - robust version
|
||||
local rhs_true = null
|
||||
{
|
||||
local krhs_b = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Bool\"", k_log)
|
||||
if krhs_b >= 0 {
|
||||
local kv2 = JsonFragBox.index_of_from(s, "\"value\":", krhs_b)
|
||||
if kv2 >= 0 {
|
||||
rhs_true = JsonFragBox.read_bool_after(s, kv2 + 8)
|
||||
}
|
||||
} else {
|
||||
local krhs_v = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Var\"", k_log)
|
||||
if krhs_v >= 0 {
|
||||
local name2 = ProgScanBox.read_quoted_after_key(s, krhs_v, "name")
|
||||
if name2 != null { rhs_true = PatternUtilBox.find_local_bool_before(s, name2, k_log) }
|
||||
}
|
||||
}
|
||||
}
|
||||
if rhs_true == null { return null }
|
||||
|
||||
// Build MIR: const r1=lhs, rT=1, rF=0; branch on lhs
|
||||
// For &&: if lhs==0 → ret 0; else ret rhs
|
||||
// For ||: if lhs==1 → ret 1; else ret rhs
|
||||
local b0 = new ArrayBox()
|
||||
b0.push(MirSchemaBox.inst_const(1, lhs_true))
|
||||
b0.push(MirSchemaBox.inst_branch(1, 1, 2))
|
||||
|
||||
if op == "&&" {
|
||||
local b1 = new ArrayBox(); b1.push(MirSchemaBox.inst_const(3, rhs_true)); b1.push(MirSchemaBox.inst_ret(3))
|
||||
local b2 = new ArrayBox(); b2.push(MirSchemaBox.inst_const(4, 0)); b2.push(MirSchemaBox.inst_ret(4))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0)); blocks.push(MirSchemaBox.block(1, b1)); blocks.push(MirSchemaBox.block(2, b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
} else {
|
||||
local b1 = new ArrayBox(); b1.push(MirSchemaBox.inst_const(3, 1)); b1.push(MirSchemaBox.inst_ret(3))
|
||||
local b2 = new ArrayBox(); b2.push(MirSchemaBox.inst_const(4, rhs_true)); b2.push(MirSchemaBox.inst_ret(4))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0)); blocks.push(MirSchemaBox.block(1, b1)); blocks.push(MirSchemaBox.block(2, b2))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
// lower_return_method_array_map_box.hako — Return(Method recv Var, method in {size,length,len,get,set,push})
|
||||
// Emits a minimal mir_call(Method) structure (not executed in VM/Core path here).
|
||||
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using "selfhost.shared.json.utils.json_frag" as JsonFragBox
|
||||
|
||||
static box LowerReturnMethodArrayMapBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\""); if k_ret < 0 { return null }
|
||||
local k_m = s.indexOf("\"type\":\"Method\"", k_ret); if k_m < 0 { return null }
|
||||
// receiver must be Var(name)
|
||||
local k_recv = s.indexOf("\"recv\":{\"type\":\"Var\"", k_m); if k_recv < 0 { return null }
|
||||
local method = null
|
||||
{ local km = s.indexOf("\"method\":\"", k_m); if km < 0 { return null } method = JsonFragBox.read_string_after(s, km) }
|
||||
// Allow basic methods
|
||||
if !(method == "size" || method == "length" || method == "len" || method == "get" || method == "set" || method == "push") { return null }
|
||||
// Parse up to two Int args with actual values
|
||||
local args_ids = new ArrayBox()
|
||||
local b0 = new ArrayBox()
|
||||
// Receiver placeholder (Var resolve未実装のため 0 を置く)
|
||||
b0.push(MirSchemaBox.inst_const(1, 0))
|
||||
local k_args = s.indexOf("\"args\":", k_m)
|
||||
local next_id = 2
|
||||
if k_args >= 0 {
|
||||
// first arg: Int or Var(Int)
|
||||
local k_i1 = s.indexOf("\"type\":\"Int\"", k_args)
|
||||
local k_v1 = s.indexOf("\"type\":\"Var\"", k_args)
|
||||
if k_i1 >= 0 && (k_v1 < 0 || k_i1 < k_v1) {
|
||||
// read numeric after value:
|
||||
local k_val1 = s.indexOf("\"value\":", k_i1)
|
||||
if k_val1 >= 0 {
|
||||
local v1 = JsonFragBox.read_int_after(s, k_val1 + 8)
|
||||
if v1 != null { b0.push(MirSchemaBox.inst_const(next_id, v1)) args_ids.push(next_id) next_id = next_id + 1 }
|
||||
}
|
||||
// second arg after first
|
||||
local k_i2 = s.indexOf("\"type\":\"Int\"", k_i1 + 1)
|
||||
local k_v2a = s.indexOf("\"type\":\"Var\"", k_i1 + 1)
|
||||
if k_i2 >= 0 {
|
||||
local k_v2 = s.indexOf("\"value\":", k_i2)
|
||||
if k_v2 >= 0 { local v2 = JsonFragBox.read_int_after(s, k_v2 + 8); if v2 != null { b0.push(MirSchemaBox.inst_const(next_id, v2)) args_ids.push(next_id) next_id = next_id + 1 } }
|
||||
} else if k_v2a >= 0 {
|
||||
// second arg is Var: resolve Local Int value
|
||||
local kn = s.indexOf("\"name\":\"", k_v2a)
|
||||
if kn >= 0 {
|
||||
local name2 = JsonFragBox.read_string_after(s, kn)
|
||||
// scan backwards for Local name2 Int
|
||||
local pos = 0; local last = -1
|
||||
loop(true) {
|
||||
local kL = s.indexOf("\"type\":\"Local\"", pos)
|
||||
if kL < 0 || kL >= k_m { break }
|
||||
local nm = null
|
||||
{
|
||||
local kn2 = s.indexOf("\"name\":\"", kL)
|
||||
if kn2 >= 0 {
|
||||
local iii = kn2 + 8; local nnn = s.length(); local jjj = iii
|
||||
loop(jjj < nnn) { if s.substring(jjj,jjj+1) == '"' { break } jjj = jjj + 1 }
|
||||
nm = s.substring(iii, jjj)
|
||||
}
|
||||
}
|
||||
if nm != null && nm == name2 { last = kL }
|
||||
pos = kL + 1
|
||||
}
|
||||
if last >= 0 {
|
||||
local ki = s.indexOf("\"type\":\"Int\"", last)
|
||||
if ki >= 0 && ki < k_m {
|
||||
local kv = s.indexOf("\"value\":", ki)
|
||||
if kv >= 0 { local v = JsonFragBox.read_int_after(s, kv + 8); if v != null { b0.push(MirSchemaBox.inst_const(next_id, v)) args_ids.push(next_id) next_id = next_id + 1 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if k_v1 >= 0 && (k_i1 < 0 || k_v1 < k_i1) {
|
||||
// first arg is Var: resolve Local value (Int/Bool/Float/String)
|
||||
local kn = s.indexOf("\"name\":\"", k_v1)
|
||||
if kn >= 0 {
|
||||
local namev = JsonFragBox.read_string_after(s, kn)
|
||||
// find last Local namev Int before method
|
||||
local pos2 = 0; local last2 = -1
|
||||
loop(true) {
|
||||
local kL2 = s.indexOf("\"type\":\"Local\"", pos2)
|
||||
if kL2 < 0 || kL2 >= k_m { break }
|
||||
local nm2 = null
|
||||
{
|
||||
local knm = s.indexOf("\"name\":\"", kL2)
|
||||
if knm >= 0 {
|
||||
local iii2 = knm + 8; local nnn2 = s.length(); local jjj2 = iii2
|
||||
loop(jjj2 < nnn2) { if s.substring(jjj2,jjj2+1) == '"' { break } jjj2 = jjj2 + 1 }
|
||||
nm2 = s.substring(iii2, jjj2)
|
||||
}
|
||||
}
|
||||
if nm2 != null && nm2 == namev { last2 = kL2 }
|
||||
pos2 = kL2 + 1
|
||||
}
|
||||
if last2 >= 0 {
|
||||
// Int
|
||||
local ki3 = s.indexOf("\"type\":\"Int\"", last2)
|
||||
if ki3 >= 0 && ki3 < k_m { local kv3 = s.indexOf("\"value\":", ki3); if kv3 >= 0 { local v = JsonFragBox.read_int_after(s, kv3 + 8); if v != null { b0.push(MirSchemaBox.inst_const(next_id, v)) args_ids.push(next_id) next_id = next_id + 1 } } }
|
||||
// Bool
|
||||
local kb3 = s.indexOf("\"type\":\"Bool\"", last2)
|
||||
if kb3 >= 0 && kb3 < k_m { local kvb = s.indexOf("\"value\":", kb3); if kvb >= 0 { local vb = JsonFragBox.read_bool_after(s, kvb + 8); if vb != null { b0.push(MirSchemaBox.inst_const(next_id, vb)) args_ids.push(next_id) next_id = next_id + 1 } } }
|
||||
// Float
|
||||
local kf3 = s.indexOf("\"type\":\"Float\"", last2)
|
||||
if kf3 >= 0 && kf3 < k_m { local kvf = s.indexOf("\"value\":", kf3); if kvf >= 0 { local fv = JsonFragBox.read_float_after(s, kvf + 8); if fv != null { b0.push(MirSchemaBox.inst_const_f64(next_id, fv)) args_ids.push(next_id) next_id = next_id + 1 } } }
|
||||
// String
|
||||
local ks3 = s.indexOf("\"type\":\"String\"", last2)
|
||||
if ks3 >= 0 && ks3 < k_m { local kvs = s.indexOf("\"value\":\"", ks3); if kvs >= 0 { local sv = JsonFragBox.read_string_after(s, kvs + 8); if sv != null { b0.push(MirSchemaBox.inst_const_str(next_id, sv)) args_ids.push(next_id) next_id = next_id + 1 } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// mir_call method -> dst 4, ret 4
|
||||
b0.push(MirSchemaBox.inst_mir_call_method(method, 1, args_ids, 4))
|
||||
b0.push(MirSchemaBox.inst_ret(4))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
21
lang/src/mir/builder/internal/lower_return_string_box.hako
Normal file
21
lang/src/mir/builder/internal/lower_return_string_box.hako
Normal file
@ -0,0 +1,21 @@
|
||||
// lower_return_string_box.hako — Return(String) → const(string) + ret
|
||||
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box LowerReturnStringBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||
if k_ret < 0 { return null }
|
||||
local k_str = s.indexOf("\"type\":\"String\"", k_ret)
|
||||
if k_str < 0 { return null }
|
||||
local k_val = s.indexOf("\"value\":", k_str)
|
||||
if k_val < 0 { return null }
|
||||
local sval = JsonFragBox.read_string_after(s, k_val+8)
|
||||
if sval == null { return null }
|
||||
local b0 = new ArrayBox(); b0.push(MirSchemaBox.inst_const_str(1, sval)); b0.push(MirSchemaBox.inst_ret(1))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
// lower_return_var_local_box.hako — Pattern: [ Local name=Int, Return Var(name) ] → const+ret
|
||||
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.shared.mir.schema as MirSchemaBox
|
||||
|
||||
static box LowerReturnVarLocalBox {
|
||||
try_lower(program_json) {
|
||||
local s = "" + program_json
|
||||
// Find Local with name:"x" and expr Int value
|
||||
local k_loc = s.indexOf("\"type\":\"Local\"")
|
||||
if k_loc < 0 { return null }
|
||||
// Extract name
|
||||
local k_name = s.indexOf("\"name\":", k_loc); if k_name < 0 { return null }
|
||||
local name = JsonFragBox.read_string_after(s, k_name + 5)
|
||||
if name == null || name == "" { return null }
|
||||
// Ensure expr Int after this Local
|
||||
local k_int = s.indexOf("\"type\":\"Int\"", k_loc)
|
||||
if k_int < 0 { return null }
|
||||
local kv = s.indexOf("\"value\":", k_int); if kv < 0 { return null }
|
||||
local val = JsonFragBox.read_int_after(s, kv + 8)
|
||||
if val == null { return null }
|
||||
// Following Return Var(name)
|
||||
local k_ret = s.indexOf("\"type\":\"Return\"", k_loc)
|
||||
if k_ret < 0 { return null }
|
||||
// Verify Var(name)
|
||||
local k_var = s.indexOf("\"type\":\"Var\"", k_ret)
|
||||
if k_var < 0 { return null }
|
||||
local k_vn = s.indexOf("\"name\":", k_var); if k_vn < 0 { return null }
|
||||
local vname = JsonFragBox.read_string_after(s, k_vn + 5)
|
||||
if vname == null || vname != name { return null }
|
||||
|
||||
// Build MIR(JSON): const+ret
|
||||
local b0 = new ArrayBox()
|
||||
b0.push(MirSchemaBox.inst_const(1, val))
|
||||
b0.push(MirSchemaBox.inst_ret(1))
|
||||
local blocks = new ArrayBox(); blocks.push(MirSchemaBox.block(0, b0))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
23
lang/src/mir/builder/internal/pattern_util_box.hako
Normal file
23
lang/src/mir/builder/internal/pattern_util_box.hako
Normal file
@ -0,0 +1,23 @@
|
||||
// pattern_util_box.hako — Shared utilities for MirBuilder lowers
|
||||
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
|
||||
static box PatternUtilBox {
|
||||
map_cmp(sym) { if sym=="<" {return "Lt"} if sym==">" {return "Gt"} if sym=="<=" {return "Le"} if sym==">=" {return "Ge"} if sym=="==" {return "Eq"} if sym=="!=" {return "Ne"} return null }
|
||||
find_local_int_before(s, name, before_pos) {
|
||||
local pos=0; local last=-1
|
||||
loop(true){ local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos); if k<0||k>=before_pos{break}; local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k); if kn>=0{ local ii=kn+8; local nn=s.length(); local jj=ii; loop(jj<nn){ if s.substring(jj,jj+1)=="\"" {break} jj=jj+1 } if s.substring(ii,jj)==name { last=k } } pos=k+1 }
|
||||
if last<0 { return null }
|
||||
local ki=s.indexOf("\"type\":\"Int\"",last); if ki<0||ki>=before_pos { return null }
|
||||
local kv=s.indexOf("\"value\":",ki); if kv<0 { return null }
|
||||
return JsonFragBox.read_int_after(s, kv+8)
|
||||
}
|
||||
find_local_bool_before(s, name, before_pos) {
|
||||
local pos=0; local last=-1
|
||||
loop(true){ local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos); if k<0||k>=before_pos{break}; local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k); if kn>=0{ local ii=kn+8; local nn=s.length(); local jj=ii; loop(jj<nn){ if s.substring(jj,jj+1)=="\"" {break} jj=jj+1 } if s.substring(ii,jj)==name { last=k } } pos=k+1 }
|
||||
if last<0 { return null }
|
||||
local kb=s.indexOf("\"type\":\"Bool\"",last); if kb<0||kb>=before_pos { return null }
|
||||
local kv=s.indexOf("\"value\":",kb); if kv<0 { return null }
|
||||
return JsonFragBox.read_bool_after(s, kv+8)
|
||||
}
|
||||
}
|
||||
67
lang/src/mir/builder/internal/prog_scan_box.hako
Normal file
67
lang/src/mir/builder/internal/prog_scan_box.hako
Normal file
@ -0,0 +1,67 @@
|
||||
// prog_scan_box.hako — Tiny string scanner helpers for Program(JSON v0)
|
||||
|
||||
static box ProgScanBox {
|
||||
// Find substring starting at or after pos; returns index or -1
|
||||
find(s, pat, pos) {
|
||||
local s1 = "" + s
|
||||
local p = pos
|
||||
if p < 0 { p = 0 }
|
||||
if p >= s1.length() { return -1 }
|
||||
local sub = s1.substring(p)
|
||||
local k = sub.indexOf("" + pat)
|
||||
if k < 0 { return -1 }
|
||||
return p + k
|
||||
}
|
||||
|
||||
// Skip ASCII spaces from pos; returns first non-space index
|
||||
skip_spaces(s, pos) {
|
||||
local i = pos
|
||||
local n = ("" + s).length()
|
||||
loop(i < n) { if ("" + s).substring(i,i+1) != " " { break } i = i + 1 }
|
||||
return i
|
||||
}
|
||||
|
||||
// Read an integer (optional '-') starting at pos; returns string or null
|
||||
read_int_at(s, pos) {
|
||||
local i = pos
|
||||
local n = ("" + s).length()
|
||||
if i < n && ("" + s).substring(i,i+1) == "-" { i = i + 1 }
|
||||
local j = i
|
||||
local had = 0
|
||||
loop(j < n) {
|
||||
local ch = ("" + s).substring(j,j+1)
|
||||
if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break }
|
||||
}
|
||||
if had == 1 { return ("" + s).substring(pos, j) }
|
||||
return null
|
||||
}
|
||||
|
||||
// Read quoted string at or after key: "key": "..." (returns content or null)
|
||||
read_quoted_after_key(s, start, key) {
|
||||
local s1 = "" + s
|
||||
local k = me.find(s1, "\"" + key + "\":", start)
|
||||
if k < 0 { return null }
|
||||
local i = k + 3 + key.length() // position after colon
|
||||
local n = s1.length()
|
||||
// skip spaces until first quote
|
||||
loop(i < n) {
|
||||
local ch = s1.substring(i,i+1)
|
||||
if ch == "\"" { i = i + 1 break }
|
||||
if ch != " " { break }
|
||||
i = i + 1
|
||||
}
|
||||
local j = i
|
||||
loop(j < n) { if s1.substring(j,j+1) == "\"" { break } j = j + 1 }
|
||||
if j <= n { return s1.substring(i, j) }
|
||||
return null
|
||||
}
|
||||
|
||||
// Read integer value after a "value": key near start
|
||||
read_value_int_after(s, start) {
|
||||
local s1 = "" + s
|
||||
local k = me.find(s1, "\"value\":", start)
|
||||
if k < 0 { return null }
|
||||
local i = me.skip_spaces(s1, k + 8)
|
||||
return me.read_int_at(s1, i)
|
||||
}
|
||||
}
|
||||
26
lang/src/mir/builder/pattern_registry.hako
Normal file
26
lang/src/mir/builder/pattern_registry.hako
Normal file
@ -0,0 +1,26 @@
|
||||
// pattern_registry.hako — Minimal priority registry for MirBuilder lowers (scaffold)
|
||||
|
||||
static box PatternRegistryBox {
|
||||
// Return prioritized candidate names (subset); MirBuilder maps names to concrete try_lower calls.
|
||||
candidates() {
|
||||
local a = new ArrayBox()
|
||||
// Higher first
|
||||
a.push("if.compare.intint")
|
||||
a.push("if.compare.fold.binints")
|
||||
a.push("if.compare.fold.varint")
|
||||
a.push("if.compare.varint")
|
||||
a.push("if.compare.varvar")
|
||||
a.push("return.method.arraymap")
|
||||
a.push("return.var.local")
|
||||
a.push("return.string")
|
||||
a.push("return.float")
|
||||
a.push("return.bool")
|
||||
a.push("return.logical")
|
||||
a.push("return.binop.varint")
|
||||
a.push("return.binop.varvar")
|
||||
a.push("return.binop.intint")
|
||||
a.push("return.int")
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,86 @@ using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
using selfhost.shared.common.string_helpers as StringHelpers
|
||||
|
||||
static box JsonFragBox {
|
||||
// 基本ヘルパ
|
||||
index_of_from(hay, needle, pos) { return JsonCursorBox.index_of_from(hay, needle, pos) }
|
||||
// 基本ヘルパ - VM fallback implementations for cross-box static calls
|
||||
index_of_from(hay, needle, pos) {
|
||||
// VM fallback: implement using substring + indexOf
|
||||
if hay == null || needle == null { return -1 }
|
||||
local s = "" + hay
|
||||
local n = s.length()
|
||||
local p2 = pos
|
||||
if p2 < 0 { p2 = 0 }
|
||||
if p2 >= n { return -1 }
|
||||
// Extract substring from pos onwards
|
||||
local substr = s.substring(p2, n)
|
||||
// Find needle in substring
|
||||
local idx = substr.indexOf(needle)
|
||||
if idx < 0 { return -1 }
|
||||
return p2 + idx
|
||||
}
|
||||
read_digits(text, pos) { return StringHelpers.read_digits(text, pos) }
|
||||
_str_to_int(s) { return StringHelpers.to_i64(s) }
|
||||
_to_bool10(ch) { if ch == "t" { return 1 } if ch == "f" { return 0 } return null }
|
||||
|
||||
// Read helpers (pos-based)
|
||||
read_int_from(text, pos) {
|
||||
if text == null { return null }
|
||||
local s = "" + text
|
||||
local i = pos
|
||||
local n = s.length()
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i
|
||||
if j < n && (s.substring(j,j+1) == "-" || s.substring(j,j+1) == "+") { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) {
|
||||
local ch = s.substring(j,j+1)
|
||||
if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break }
|
||||
}
|
||||
if had == 0 { return null }
|
||||
return s.substring(i, j)
|
||||
}
|
||||
read_bool_from(text, pos) {
|
||||
if text == null { return null }
|
||||
local s = "" + text
|
||||
local i = pos
|
||||
local n = s.length()
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
if i < n { return me._to_bool10(s.substring(i,i+1)) }
|
||||
return null
|
||||
}
|
||||
read_string_from(text, pos) {
|
||||
if text == null { return null }
|
||||
local s = "" + text
|
||||
local i = pos
|
||||
local n = s.length()
|
||||
// Find opening quote
|
||||
loop(i < n) { if s.substring(i,i+1) == "\"" { i = i + 1 break } if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i
|
||||
loop(j < n) { if s.substring(j,j+1) == "\"" { break } j = j + 1 }
|
||||
if j <= i { return null }
|
||||
return s.substring(i, j)
|
||||
}
|
||||
read_float_from(text, pos) {
|
||||
if text == null { return null }
|
||||
local s = "" + text
|
||||
local i = pos
|
||||
local n = s.length()
|
||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
||||
local j = i
|
||||
if j < n && (s.substring(j,j+1) == "+" || s.substring(j,j+1) == "-") { j = j + 1 }
|
||||
local had = 0
|
||||
loop(j < n) {
|
||||
local ch = s.substring(j,j+1)
|
||||
if (ch >= "0" && ch <= "9") || ch == "." { had = 1 j = j + 1 } else { break }
|
||||
}
|
||||
if had == 0 { return null }
|
||||
return s.substring(i, j)
|
||||
}
|
||||
|
||||
// Read helpers (key-based, start at keyPos)
|
||||
read_int_after(text, key_pos) { return me.read_int_from(text, key_pos) }
|
||||
read_bool_after(text, key_pos) { return me.read_bool_from(text, key_pos) }
|
||||
read_string_after(text, key_pos) { return me.read_string_from(text, key_pos) }
|
||||
read_float_after(text, key_pos) { return me.read_float_from(text, key_pos) }
|
||||
|
||||
// key に続く数値(最初の一致)を返す。見つからなければ null。
|
||||
get_int(seg, key) {
|
||||
@ -22,13 +98,61 @@ static box JsonFragBox {
|
||||
return null
|
||||
}
|
||||
|
||||
// Scan for closing quote (VM fallback for scan_string_end)
|
||||
_scan_string_end(text, quote_pos) {
|
||||
// quote_pos is the position of opening quote
|
||||
// Return position of closing quote, or -1 if not found
|
||||
if text == null { return -1 }
|
||||
local s = "" + text
|
||||
local n = s.length()
|
||||
local i = quote_pos + 1
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\"" { return i }
|
||||
if ch == "\\" {
|
||||
i = i + 1 // Skip escaped character
|
||||
if i >= n { return -1 }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Seek matching closing bracket (VM fallback for seek_array_end)
|
||||
_seek_array_end(text, lbracket_pos) {
|
||||
// lbracket_pos is the position of '['
|
||||
// Return position of matching ']', or -1 if not found
|
||||
if text == null { return -1 }
|
||||
local s = "" + text
|
||||
local n = s.length()
|
||||
local depth = 0
|
||||
local i = lbracket_pos
|
||||
local in_str = 0
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if in_str == 1 {
|
||||
if ch == "\"" { in_str = 0 }
|
||||
if ch == "\\" { i = i + 1 } // Skip escaped char
|
||||
} else {
|
||||
if ch == "\"" { in_str = 1 }
|
||||
if ch == "[" { depth = depth + 1 }
|
||||
if ch == "]" {
|
||||
depth = depth - 1
|
||||
if depth == 0 { return i }
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// key に続く "..." の文字列(最初の一致)を返す。見つからなければ空文字。
|
||||
get_str(seg, key) {
|
||||
local pat = "\"" + key + "\":\""
|
||||
local p = me.index_of_from(seg, pat, 0)
|
||||
if p >= 0 {
|
||||
local vstart = p + pat.length() // start of value (right after opening quote)
|
||||
local vend = JsonCursorBox.scan_string_end(seg, vstart - 1)
|
||||
local vend = me._scan_string_end(seg, vstart - 1)
|
||||
if vend > vstart { return seg.substring(vstart, vend) }
|
||||
}
|
||||
return ""
|
||||
@ -62,7 +186,7 @@ static box JsonFragBox {
|
||||
// '[' position
|
||||
local arr_bracket = pk + key.length() - 1
|
||||
// Use escape-aware scanner to find matching ']'
|
||||
local endp = JsonCursorBox.seek_array_end(mjson, arr_bracket)
|
||||
local endp = me._seek_array_end(mjson, arr_bracket)
|
||||
if endp < 0 { return "" }
|
||||
return mjson.substring(arr_bracket + 1, endp)
|
||||
}
|
||||
|
||||
@ -101,4 +101,126 @@ static box LoopFormBox {
|
||||
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
|
||||
// loop_count — Minimal counting loop that returns the final counter value i
|
||||
// Shape:
|
||||
// preheader: r1=0 (i0), r2=limit, r3=1 (step), jump header
|
||||
// header: r10 = PHI(i), r11 = (i < limit), branch then:body else:exit
|
||||
// body: r12 = i + step, jump latch
|
||||
// latch: jump header (PHI incoming from body)
|
||||
// exit: ret r10
|
||||
loop_count(limit) {
|
||||
// Preheader
|
||||
local pre = new ArrayBox()
|
||||
pre.push(MirSchemaBox.inst_const(1, 0))
|
||||
pre.push(MirSchemaBox.inst_const(2, limit))
|
||||
pre.push(MirSchemaBox.inst_const(3, 1))
|
||||
pre.push(MirSchemaBox.inst_jump(1))
|
||||
|
||||
// Header
|
||||
local header = new ArrayBox()
|
||||
local inc = new ArrayBox(); inc.push(MirSchemaBox.phi_incoming(0, 1)); inc.push(MirSchemaBox.phi_incoming(3, 12))
|
||||
header.push(MirSchemaBox.inst_phi(10, inc))
|
||||
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 11))
|
||||
header.push(MirSchemaBox.inst_branch(11, 2, 4))
|
||||
|
||||
// Body
|
||||
local body = new ArrayBox()
|
||||
body.push(MirSchemaBox.inst_binop("Add", 10, 3, 12))
|
||||
body.push(MirSchemaBox.inst_jump(3))
|
||||
|
||||
// Latch
|
||||
local latch = new ArrayBox()
|
||||
latch.push(MirSchemaBox.inst_jump(1))
|
||||
|
||||
// Exit
|
||||
local exit = new ArrayBox()
|
||||
exit.push(MirSchemaBox.inst_ret(10))
|
||||
|
||||
local blocks = new ArrayBox()
|
||||
blocks.push(MirSchemaBox.block(0, pre))
|
||||
blocks.push(MirSchemaBox.block(1, header))
|
||||
blocks.push(MirSchemaBox.block(2, body))
|
||||
blocks.push(MirSchemaBox.block(3, latch))
|
||||
blocks.push(MirSchemaBox.block(4, exit))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
|
||||
// loop_count_param — counting loop with init/step parameters
|
||||
// Returns final i value, starting from init, incremented by step while i < limit
|
||||
loop_count_param(init, limit, step) {
|
||||
// Preheader
|
||||
local pre = new ArrayBox()
|
||||
pre.push(MirSchemaBox.inst_const(1, init))
|
||||
pre.push(MirSchemaBox.inst_const(2, limit))
|
||||
pre.push(MirSchemaBox.inst_const(3, step))
|
||||
pre.push(MirSchemaBox.inst_jump(1))
|
||||
|
||||
// Header
|
||||
local header = new ArrayBox()
|
||||
local inc = new ArrayBox(); inc.push(MirSchemaBox.phi_incoming(0, 1)); inc.push(MirSchemaBox.phi_incoming(3, 12))
|
||||
header.push(MirSchemaBox.inst_phi(10, inc))
|
||||
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 11))
|
||||
header.push(MirSchemaBox.inst_branch(11, 2, 4))
|
||||
|
||||
// Body
|
||||
local body = new ArrayBox()
|
||||
body.push(MirSchemaBox.inst_binop("Add", 10, 3, 12))
|
||||
body.push(MirSchemaBox.inst_jump(3))
|
||||
|
||||
// Latch
|
||||
local latch = new ArrayBox(); latch.push(MirSchemaBox.inst_jump(1))
|
||||
|
||||
// Exit
|
||||
local exit = new ArrayBox(); exit.push(MirSchemaBox.inst_ret(10))
|
||||
|
||||
local blocks = new ArrayBox()
|
||||
blocks.push(MirSchemaBox.block(0, pre))
|
||||
blocks.push(MirSchemaBox.block(1, header))
|
||||
blocks.push(MirSchemaBox.block(2, body))
|
||||
blocks.push(MirSchemaBox.block(3, latch))
|
||||
blocks.push(MirSchemaBox.block(4, exit))
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
|
||||
// Unified entry — build(mode, limit, skip_value, break_value)
|
||||
// mode:
|
||||
// - "count" : counting loop that returns final i (uses loop_count)
|
||||
// - "sum_bc" : sum with break/continue sentinels (uses loop_counter)
|
||||
build(mode, limit, skip_value, break_value) {
|
||||
local m = "" + mode
|
||||
if m == "count" {
|
||||
return me.loop_count(limit)
|
||||
}
|
||||
if m == "count_param" {
|
||||
// Here, skip_value carries init and break_value carries step (temporary param slots)
|
||||
local init = skip_value
|
||||
local step = break_value
|
||||
if init == null { init = 0 }
|
||||
if step == null { step = 1 }
|
||||
return me.loop_count_param(init, limit, step)
|
||||
}
|
||||
if m == "sum_bc" {
|
||||
if skip_value == null { skip_value = 2 }
|
||||
if break_value == null { break_value = limit }
|
||||
return me.loop_counter(limit, skip_value, break_value)
|
||||
}
|
||||
print("[loopform/unsupported-mode] " + m)
|
||||
return null
|
||||
}
|
||||
|
||||
// Map-based builder: build2({ mode, init, limit, step, skip, break })
|
||||
build2(opts) {
|
||||
if opts == null { return null }
|
||||
local mode = "" + opts.get("mode")
|
||||
local init = opts.get("init")
|
||||
local limit = opts.get("limit")
|
||||
local step = opts.get("step")
|
||||
local skip_v = opts.get("skip")
|
||||
local break_v = opts.get("break")
|
||||
if mode == "count" { if init == null { init = 0 } if step == null { step = 1 } return me.loop_count_param(init, limit, step) }
|
||||
if mode == "sum_bc" { return me.loop_counter(limit, skip_v, break_v) }
|
||||
print("[loopform/unsupported-mode] " + mode)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +84,22 @@ static box MirSchemaBox {
|
||||
m.set("value", this.i(val))
|
||||
return m
|
||||
}
|
||||
inst_const_f64(dst, fval) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "const")
|
||||
m.set("dst", this.i(dst))
|
||||
local v = new MapBox(); v.set("type", "f64"); v.set("value", fval)
|
||||
m.set("value", v)
|
||||
return m
|
||||
}
|
||||
inst_const_str(dst, sval) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "const")
|
||||
m.set("dst", this.i(dst))
|
||||
local v = new MapBox(); v.set("type", "string"); v.set("value", sval)
|
||||
m.set("value", v)
|
||||
return m
|
||||
}
|
||||
inst_ret(val) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "ret")
|
||||
|
||||
@ -5,6 +5,30 @@ Current
|
||||
- `lang/src/vm/boxes/` — Shared helpers (op_handlers, scanners, compare, etc.)
|
||||
- Mini‑VM minimal executor lives as boxes (e.g., `boxes/mir_vm_min.hako`)
|
||||
|
||||
Mini VM vs Hakorune VM (Roles)
|
||||
- Mini VM (Hako): reference semantic executor for MIR(JSON v0). Scope is the
|
||||
minimal instruction set (const/compare/branch/jump/ret/phi). Its job in the
|
||||
system is verification: given MIR(JSON v0), compute the return value and
|
||||
map it to an exit code (Int → value, Bool → 1/0). It must not depend on
|
||||
env/get or include; inputs are passed as inline JSON strings.
|
||||
- Hakorune VM (Rust): production runtime with Boxes/Plugins/Externs and wider
|
||||
semantics. Used for day‑to‑day execution and integration. Mini VM validates
|
||||
meanings; Hakorune VM executes applications.
|
||||
|
||||
Verify Pipeline (hakovm primary)
|
||||
1) Emit MIR(JSON v0) as a single JSON string (noise trimmed in runner).
|
||||
2) Runner embeds JSON into a tiny Hako driver:
|
||||
`using selfhost.vm.entry as MiniVmEntryBox; return MiniVmEntryBox.run_min(j)`
|
||||
3) The driver prints the numeric return; the runner converts it into a process
|
||||
exit code for canaries. No env.get or file I/O is required.
|
||||
|
||||
Resolver Policy (Modules)
|
||||
- Prefer `using alias.name` with workspaces declared in `hako_module.toml` and
|
||||
aliases in `nyash.toml`.
|
||||
- Implement transitive resolution (bounded depth, cycle detection, caching).
|
||||
- Dev profile may allow quoted file paths ("lang/…") for bring‑up only; prod
|
||||
profile uses aliases exclusively.
|
||||
|
||||
Target (post‑20.12b, gradual)
|
||||
- `engines/hakorune/` — mainline nyvm engine
|
||||
- `engines/mini/` — Mini‑VM engine (educational/minimal)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Responsibility: safe decimal Add/Sub/Mul helpers and simple i64 adapters.
|
||||
// Non-responsibility: VM execution, JSON parsing, compare semantics.
|
||||
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using selfhost.shared.common.string_helpers as StringHelpers
|
||||
|
||||
static box ArithmeticBox {
|
||||
// Internal helpers operate on decimal strings to avoid overflow.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// cfg_navigator.hako — CfgNavigatorBox(ブロックの先頭/末尾シーク)
|
||||
|
||||
using selfhost.shared.common.string_ops as StringOps
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
|
||||
static box CfgNavigatorBox {
|
||||
// Provide index_of_from to avoid tail-based fallback/ambiguous resolution
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
// - v1: operation:"==" maps to Eq (CompareOpsBox)
|
||||
// Returns: map({ dst:int|null, lhs:int|null, rhs:int|null, kind:String }) or null
|
||||
|
||||
using "lang/src/shared/json/utils/json_frag.hako" as JsonFragBox
|
||||
using "lang/src/vm/boxes/compare_ops.hako" as CompareOpsBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.vm.helpers.compare_ops as CompareOpsBox
|
||||
|
||||
static box CompareScanBox {
|
||||
parse(seg) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// instruction_scanner.hako — InstructionScannerBox
|
||||
// Minimal JSON v0 instruction object scanner with tolerant parsing.
|
||||
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using "lang/src/vm/boxes/cfg_navigator.hako" as CfgNavigatorBox
|
||||
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
using selfhost.vm.helpers.cfg_navigator as CfgNavigatorBox
|
||||
|
||||
static box InstructionScannerBox {
|
||||
_tprint(msg) { if msg.indexOf("[ERROR]") >= 0 { print(msg) } }
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
// mir_vm_min.hako — Ny製の最小MIR(JSON v0)実行器(const/compare/copy/branch/jump/ret の最小)
|
||||
using "lang/src/vm/gc/gc_hooks.hako" as GcHooks
|
||||
using "lang/src/vm/boxes/op_handlers.hako" as OpHandlersBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using selfhost.vm.helpers.gc_hooks as GcHooks
|
||||
using selfhost.vm.helpers.op_handlers as OpHandlersBox
|
||||
using selfhost.shared.common.string_helpers as StringHelpers
|
||||
using selfhost.shared.common.string_ops as StringOps
|
||||
using "lang/src/shared/json/utils/json_frag.hako" as JsonFragBox
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using "lang/src/vm/boxes/operator_box.hako" as OperatorBox
|
||||
using "lang/src/vm/boxes/compare_ops.hako" as CompareOpsBox
|
||||
using "lang/src/vm/boxes/compare_scan_box.hako" as CompareScanBox
|
||||
using "lang/src/vm/boxes/phi_apply_box.hako" as PhiApplyBox
|
||||
using "lang/src/vm/boxes/guard_box.hako" as GuardBox
|
||||
using "lang/src/vm/boxes/result_box.hako" as Result
|
||||
using "lang/src/vm/boxes/phi_decode_box.hako" as PhiDecodeBox
|
||||
using "lang/src/vm/boxes/ret_resolve_simple.hako" as RetResolveSimpleBox
|
||||
using "lang/src/vm/boxes/instruction_scanner.hako" as InstructionScannerBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
using selfhost.vm.helpers.operator as OperatorBox
|
||||
using selfhost.vm.helpers.compare_ops as CompareOpsBox
|
||||
using selfhost.vm.helpers.compare_scan as CompareScanBox
|
||||
using selfhost.vm.helpers.phi_apply as PhiApplyBox
|
||||
using selfhost.vm.helpers.guard as GuardBox
|
||||
using selfhost.vm.helpers.result as Result
|
||||
using selfhost.vm.helpers.phi_decode as PhiDecodeBox
|
||||
using selfhost.vm.helpers.ret_resolve as RetResolveSimpleBox
|
||||
using selfhost.vm.helpers.instruction_scanner as InstructionScannerBox
|
||||
|
||||
// Minimal map for Mini‑VM registers (avoid dependency on provider MapBox)
|
||||
box MiniMap {
|
||||
@ -70,18 +70,29 @@ static box MirVmMin {
|
||||
_d(msg, trace) { if trace == 1 { print(msg) } }
|
||||
_parse_callee_name(seg) {
|
||||
// naive scan: '"callee":{"name":"<sym>"'
|
||||
local key = '"callee":{"name":"'
|
||||
local key = "\"callee\":{\"name\":\""
|
||||
local p = seg.indexOf(key)
|
||||
if p < 0 { return "" }
|
||||
p = p + key.length()
|
||||
local rest = seg.substring(p, seg.length())
|
||||
local q = rest.indexOf('"')
|
||||
local q = rest.indexOf("\"")
|
||||
if q < 0 { return "" }
|
||||
return rest.substring(0, q)
|
||||
}
|
||||
_parse_method_name(seg) {
|
||||
// naive scan: '"callee":{"type":"Method","method":"<sym>"'
|
||||
local key = "\"callee\":{\"type\":\"Method\",\"method\":\""
|
||||
local p = seg.indexOf(key)
|
||||
if p < 0 { return "" }
|
||||
p = p + key.length()
|
||||
local rest = seg.substring(p, seg.length())
|
||||
local q = rest.indexOf("\"")
|
||||
if q < 0 { return "" }
|
||||
return rest.substring(0, q)
|
||||
}
|
||||
_parse_first_arg(seg) {
|
||||
// naive scan: '"args":[ <int>'
|
||||
local key = '"args":'
|
||||
local key = "\"args\":"
|
||||
local p = seg.indexOf(key)
|
||||
if p < 0 { return null }
|
||||
p = p + key.length()
|
||||
@ -108,6 +119,39 @@ static box MirVmMin {
|
||||
local name = me._parse_callee_name(seg)
|
||||
local arg0id = me._parse_first_arg(seg)
|
||||
if name == "" {
|
||||
// Try Method callee
|
||||
local mname = me._parse_method_name(seg)
|
||||
if mname != "" {
|
||||
// Optional: minimal stateful bridge for size/len/length/push
|
||||
// Enabled by HAKO_VM_MIRCALL_SIZESTATE=1 (default OFF). When OFF, emit stub tag and dst=0.
|
||||
local size_state = env.get("HAKO_VM_MIRCALL_SIZESTATE")
|
||||
if size_state == null { size_state = "0" }
|
||||
if ("" + size_state) != "1" {
|
||||
local stub = env.get("HAKO_VM_MIRCALL_STUB"); if stub == null { stub = "1" }
|
||||
if ("" + stub) == "1" { print("[vm/method/stub:" + mname + "]") }
|
||||
local dst0 = JsonFragBox.get_int(seg, "dst"); if dst0 != null { regs.set("" + dst0, "0") }
|
||||
return
|
||||
}
|
||||
// Stateful branch
|
||||
// Keep a simple per-run length counter in regs["__vm_len"]. Default 0.
|
||||
local cur_len_raw = regs.getField("__vm_len"); if cur_len_raw == null { cur_len_raw = "0" }
|
||||
local cur_len = JsonFragBox._str_to_int(cur_len_raw)
|
||||
if mname == "push" {
|
||||
cur_len = cur_len + 1
|
||||
regs.set("__vm_len", "" + cur_len)
|
||||
// push returns void/0 in this minimal path
|
||||
local d1 = JsonFragBox.get_int(seg, "dst"); if d1 != null { regs.set("" + d1, "0") }
|
||||
return
|
||||
}
|
||||
if mname == "len" || mname == "length" || mname == "size" {
|
||||
local d2 = JsonFragBox.get_int(seg, "dst"); if d2 != null { regs.set("" + d2, "" + cur_len) }
|
||||
return
|
||||
}
|
||||
// Others: no-op but keep stub tag for observability
|
||||
print("[vm/method/stub:" + mname + "]")
|
||||
local d3 = JsonFragBox.get_int(seg, "dst"); if d3 != null { regs.set("" + d3, "0") }
|
||||
return
|
||||
}
|
||||
me._tprint("[ERROR] mir_call: missing callee")
|
||||
return
|
||||
}
|
||||
@ -398,7 +442,7 @@ else if op == "ret" {
|
||||
return rv
|
||||
}
|
||||
}
|
||||
# (typed const fallback moved above)
|
||||
// (typed const fallback moved above)
|
||||
if false {
|
||||
// Grab first two "value":{"type":"i64","value":X}
|
||||
local first = ""
|
||||
@ -447,13 +491,11 @@ else if op == "ret" {
|
||||
if rid4 != null { return cv }
|
||||
}
|
||||
}
|
||||
{
|
||||
local r = RetResolveSimpleBox.resolve(inst_seg, regs, last_cmp_dst, last_cmp_val)
|
||||
if r != null {
|
||||
if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
|
||||
return r
|
||||
}
|
||||
}
|
||||
// Final fallback: return first const (dst=1) if present
|
||||
local first = me._load_reg(regs, 1)
|
||||
return first
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// op_handlers.hako — OpHandlersBox
|
||||
// Minimal handlers for const/compare/ret etc. with loose JSON key parsing.
|
||||
using "lang/src/vm/boxes/arithmetic.hako" as ArithmeticBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using "lang/src/vm/boxes/compare_ops.hako" as CompareOpsBox
|
||||
using "lang/src/shared/json/utils/json_frag.hako" as JsonFragBox
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using selfhost.vm.helpers.arithmetic as ArithmeticBox
|
||||
using selfhost.shared.common.string_helpers as StringHelpers
|
||||
using selfhost.vm.helpers.compare_ops as CompareOpsBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
// Prefer logical module names to avoid file-path using in strict profiles
|
||||
static box OpHandlersBox {
|
||||
_tprint(msg) {
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
// Output: [dst:int, vin:int] when resolvable; otherwise [null,null]
|
||||
// Non-goals: MIR execution, register I/O (caller applies values), full JSON parser.
|
||||
|
||||
using "lang/src/shared/json/utils/json_frag.hako" as JsonFragBox
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using "lang/src/vm/boxes/result_box.hako" as Result
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
using selfhost.vm.helpers.result as Result
|
||||
|
||||
static box PhiDecodeBox {
|
||||
// tiny helpers to avoid raw quote/backslash sequences in source (prelude‑safe)
|
||||
|
||||
@ -2,7 +2,37 @@
|
||||
// 責務: 処理結果の統一表現(成功値 or エラーメッセージ)
|
||||
// 使い方: Result.Ok(val) / Result.Err(msg) → ResultBox
|
||||
|
||||
@enum Result {
|
||||
Ok(value)
|
||||
Err(error)
|
||||
box ResultBox {
|
||||
_tag: StringBox
|
||||
_value: InstanceBox
|
||||
|
||||
birth(tag, val) {
|
||||
me._tag = tag
|
||||
me._value = val
|
||||
}
|
||||
|
||||
is_Ok() {
|
||||
return me._tag == "Ok"
|
||||
}
|
||||
|
||||
is_Err() {
|
||||
return me._tag == "Err"
|
||||
}
|
||||
|
||||
as_Ok() {
|
||||
return me._value
|
||||
}
|
||||
|
||||
as_Err() {
|
||||
return me._value
|
||||
}
|
||||
}
|
||||
|
||||
static box Result {
|
||||
Ok(value) {
|
||||
return new ResultBox("Ok", value)
|
||||
}
|
||||
Err(error) {
|
||||
return new ResultBox("Err", error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
// Inputs: inst_seg (string of objects), regs (MapBox), last_cmp_dst/val (ints)
|
||||
// Output: i64 value or null when no ret is present
|
||||
|
||||
using "lang/src/shared/json/utils/json_frag.hako" as JsonFragBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using selfhost.shared.json.utils.json_frag as JsonFragBox
|
||||
using selfhost.shared.common.string_helpers as StringHelpers
|
||||
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
||||
|
||||
static box RetResolveSimpleBox {
|
||||
_to_i64(s) { return StringHelpers.to_i64(s) }
|
||||
|
||||
@ -81,12 +81,16 @@ static box BoxCallHandlerBox {
|
||||
result_val = receiver.length()
|
||||
} else if method_sig == "isEmpty/0" {
|
||||
result_val = receiver.isEmpty()
|
||||
} else if method_sig == "substring/1" {
|
||||
result_val = receiver.substring(args_array.get(0))
|
||||
} else if method_sig == "substring/2" {
|
||||
result_val = receiver.substring(args_array.get(0), args_array.get(1))
|
||||
} else if method_sig == "charAt/1" {
|
||||
result_val = receiver.charAt(args_array.get(0))
|
||||
} else if method_sig == "indexOf/1" {
|
||||
result_val = receiver.indexOf(args_array.get(0))
|
||||
} else if method_sig == "indexOf/2" {
|
||||
result_val = receiver.indexOf(args_array.get(0), args_array.get(1))
|
||||
}
|
||||
|
||||
// ArrayBox methods
|
||||
|
||||
@ -34,6 +34,15 @@ static box ExternCallHandlerBox {
|
||||
if me._policy_allowlist_on() == 1 {
|
||||
if me._allow(name) == 0 { return Result.Err("extern not allowed: " + name) }
|
||||
}
|
||||
// Fast-path: handle common small externs locally (avoid hostbridge dependency)
|
||||
if name == "env.get" {
|
||||
local key = null
|
||||
if args_array != null && args_array.length() >= 1 { key = "" + args_array.get(0) }
|
||||
local v = null
|
||||
if key != null { v = env.get(key) }
|
||||
if dst_reg != null { ValueManagerBox.set(regs, dst_reg, v) }
|
||||
return Result.Ok(0)
|
||||
}
|
||||
// Delegation: forward externs to Rust via hostbridge trampoline.
|
||||
if args_array == null { args_array = new ArrayBox() }
|
||||
local ret
|
||||
|
||||
43
nyash.toml
43
nyash.toml
@ -148,6 +148,22 @@ path = "lang/src/shared/common/string_helpers.hako"
|
||||
"selfhost.shared.mir.builder" = "lang/src/shared/mir/block_builder_box.hako"
|
||||
"selfhost.shared.mir.io" = "lang/src/shared/mir/mir_io_box.hako"
|
||||
"selfhost.shared.mir.json_emit" = "lang/src/shared/mir/json_emit_box.hako"
|
||||
"selfhost.vm.entry" = "lang/src/vm/boxes/mini_vm_entry.hako"
|
||||
"selfhost.vm.mir_min" = "lang/src/vm/boxes/mir_vm_min.hako"
|
||||
"selfhost.vm.core" = "lang/src/vm/boxes/mini_vm_core.hako"
|
||||
"selfhost.vm.helpers.op_handlers" = "lang/src/vm/boxes/op_handlers.hako"
|
||||
"selfhost.vm.helpers.operator" = "lang/src/vm/boxes/operator_box.hako"
|
||||
"selfhost.vm.helpers.compare_ops" = "lang/src/vm/boxes/compare_ops.hako"
|
||||
"selfhost.vm.helpers.compare_scan" = "lang/src/vm/boxes/compare_scan_box.hako"
|
||||
"selfhost.vm.helpers.phi_apply" = "lang/src/vm/boxes/phi_apply_box.hako"
|
||||
"selfhost.vm.helpers.guard" = "lang/src/vm/boxes/guard_box.hako"
|
||||
"selfhost.vm.helpers.result" = "lang/src/vm/boxes/result_box.hako"
|
||||
"selfhost.vm.helpers.phi_decode" = "lang/src/vm/boxes/phi_decode_box.hako"
|
||||
"selfhost.vm.helpers.ret_resolve" = "lang/src/vm/boxes/ret_resolve_simple.hako"
|
||||
"selfhost.vm.helpers.instruction_scanner" = "lang/src/vm/boxes/instruction_scanner.hako"
|
||||
"selfhost.vm.helpers.gc_hooks" = "lang/src/vm/gc/gc_hooks.hako"
|
||||
"selfhost.vm.helpers.arithmetic" = "lang/src/vm/boxes/arithmetic.hako"
|
||||
"selfhost.vm.helpers.cfg_navigator" = "lang/src/vm/boxes/cfg_navigator.hako"
|
||||
"hakorune.vm.entry" = "lang/src/vm/boxes/mini_vm_entry.hako"
|
||||
"hakorune.vm.mir_min" = "lang/src/vm/boxes/mir_vm_min.hako"
|
||||
"hakorune.vm.core" = "lang/src/vm/boxes/mini_vm_core.hako"
|
||||
@ -155,6 +171,33 @@ path = "lang/src/shared/common/string_helpers.hako"
|
||||
# Phase 20.34 — Box‑First selfhost build line (aliases for Hako boxes)
|
||||
"hako.mir.builder" = "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
"hako.llvm.emit" = "lang/src/llvm_ir/emit/LLVMEmitBox.hako"
|
||||
"hako.mir.builder.internal.prog_scan" = "lang/src/mir/builder/internal/prog_scan_box.hako"
|
||||
"hako.mir.builder.internal.pattern_util" = "lang/src/mir/builder/internal/pattern_util_box.hako"
|
||||
"hako.mir.builder.internal.lower.logical" = "lang/src/mir/builder/internal/lower_return_logical_box.hako"
|
||||
|
||||
# MirBuilder internal lowers (alias for using)
|
||||
"hako.mir.builder.internal.lower_if_then_else_following_return" = "lang/src/mir/builder/internal/lower_if_then_else_following_return_box.hako"
|
||||
"hako.mir.builder.internal.lower_if_nested" = "lang/src/mir/builder/internal/lower_if_nested_box.hako"
|
||||
"hako.mir.builder.internal.lower_if_compare" = "lang/src/mir/builder/internal/lower_if_compare_box.hako"
|
||||
"hako.mir.builder.internal.lower_if_compare_fold_binints" = "lang/src/mir/builder/internal/lower_if_compare_fold_binints_box.hako"
|
||||
"hako.mir.builder.internal.lower_if_compare_fold_varint" = "lang/src/mir/builder/internal/lower_if_compare_fold_varint_box.hako"
|
||||
"hako.mir.builder.internal.lower_if_compare_varint" = "lang/src/mir/builder/internal/lower_if_compare_varint_box.hako"
|
||||
"hako.mir.builder.internal.lower_if_compare_varvar" = "lang/src/mir/builder/internal/lower_if_compare_varvar_box.hako"
|
||||
"hako.mir.builder.internal.lower_loop_sum_bc" = "lang/src/mir/builder/internal/lower_loop_sum_bc_box.hako"
|
||||
"hako.mir.builder.internal.lower_loop_count_param" = "lang/src/mir/builder/internal/lower_loop_count_param_box.hako"
|
||||
"hako.mir.builder.internal.lower_loop_simple" = "lang/src/mir/builder/internal/lower_loop_simple_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_var_local" = "lang/src/mir/builder/internal/lower_return_var_local_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_string" = "lang/src/mir/builder/internal/lower_return_string_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_float" = "lang/src/mir/builder/internal/lower_return_float_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_method_array_map" = "lang/src/mir/builder/internal/lower_return_method_array_map_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_bool" = "lang/src/mir/builder/internal/lower_return_bool_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_binop_varint" = "lang/src/mir/builder/internal/lower_return_binop_varint_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_binop_varvar" = "lang/src/mir/builder/internal/lower_return_binop_varvar_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_binop" = "lang/src/mir/builder/internal/lower_return_binop_box.hako"
|
||||
"hako.mir.builder.internal.lower_return_int" = "lang/src/mir/builder/internal/lower_return_int_box.hako"
|
||||
|
||||
# Missing alias for JsonFragBox (used widely in lowers)
|
||||
"selfhost.shared.json.utils.json_frag" = "lang/src/shared/json/utils/json_frag.hako"
|
||||
|
||||
# Temporary alias keys removed (Phase‑20.33 TTL reached). Use `selfhost.shared.*` above.
|
||||
|
||||
|
||||
@ -61,13 +61,49 @@ pub(super) fn try_handle_string_box(
|
||||
return Ok(true);
|
||||
}
|
||||
"indexOf" => {
|
||||
// indexOf(substr) -> first index or -1
|
||||
// Support both 1-arg indexOf(search) and 2-arg indexOf(search, fromIndex)
|
||||
let (needle, from_index) = match args.len() {
|
||||
1 => {
|
||||
// indexOf(search) - search from beginning
|
||||
let n = this.reg_load(args[0])?.to_string();
|
||||
(n, 0)
|
||||
}
|
||||
2 => {
|
||||
// indexOf(search, fromIndex) - search from specified position
|
||||
let n = this.reg_load(args[0])?.to_string();
|
||||
let from = this.reg_load(args[1])?.as_integer().unwrap_or(0);
|
||||
(n, from.max(0) as usize)
|
||||
}
|
||||
_ => {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"indexOf expects 1 or 2 args (search [, fromIndex])".into(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Search for needle starting from from_index
|
||||
let search_str = if from_index >= sb_norm.value.len() {
|
||||
""
|
||||
} else {
|
||||
&sb_norm.value[from_index..]
|
||||
};
|
||||
|
||||
let idx = search_str.find(&needle)
|
||||
.map(|i| (from_index + i) as i64)
|
||||
.unwrap_or(-1);
|
||||
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::Integer(idx)); }
|
||||
return Ok(true);
|
||||
}
|
||||
"contains" => {
|
||||
// contains(search) -> boolean (true if found, false otherwise)
|
||||
// Implemented as indexOf(search) >= 0
|
||||
if args.len() != 1 {
|
||||
return Err(VMError::InvalidInstruction("indexOf expects 1 arg".into()));
|
||||
return Err(VMError::InvalidInstruction("contains expects 1 arg".into()));
|
||||
}
|
||||
let needle = this.reg_load(args[0])?.to_string();
|
||||
let idx = sb_norm.value.find(&needle).map(|i| i as i64).unwrap_or(-1);
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::Integer(idx)); }
|
||||
let found = sb_norm.value.contains(&needle);
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::Bool(found)); }
|
||||
return Ok(true);
|
||||
}
|
||||
"lastIndexOf" => {
|
||||
@ -102,13 +138,26 @@ pub(super) fn try_handle_string_box(
|
||||
return Ok(true);
|
||||
}
|
||||
"substring" => {
|
||||
if args.len() != 2 {
|
||||
// Support both 1-arg (start to end) and 2-arg (start, end) forms
|
||||
let (s_idx, e_idx) = match args.len() {
|
||||
1 => {
|
||||
// substring(start) - from start to end of string
|
||||
let s = this.reg_load(args[0])?.as_integer().unwrap_or(0);
|
||||
let len = sb_norm.value.chars().count() as i64;
|
||||
(s, len)
|
||||
}
|
||||
2 => {
|
||||
// substring(start, end) - half-open interval [start, end)
|
||||
let s = this.reg_load(args[0])?.as_integer().unwrap_or(0);
|
||||
let e = this.reg_load(args[1])?.as_integer().unwrap_or(0);
|
||||
(s, e)
|
||||
}
|
||||
_ => {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"substring expects 2 args (start, end)".into(),
|
||||
"substring expects 1 or 2 args (start [, end])".into(),
|
||||
));
|
||||
}
|
||||
let s_idx = this.reg_load(args[0])?.as_integer().unwrap_or(0);
|
||||
let e_idx = this.reg_load(args[1])?.as_integer().unwrap_or(0);
|
||||
};
|
||||
let len = sb_norm.value.chars().count() as i64;
|
||||
let start = s_idx.max(0).min(len) as usize;
|
||||
let end = e_idx.max(start as i64).min(len) as usize;
|
||||
|
||||
@ -133,9 +133,13 @@ impl MirInterpreter {
|
||||
VMValue::String(ref s) => s.clone(),
|
||||
other => other.to_string(),
|
||||
};
|
||||
if std::env::var("HAKO_DEBUG_LEGACY_CALL").ok().as_deref() == Some("1") {
|
||||
eprintln!("[vm-debug] legacy-call raw='{}' argc={}", raw, args.len());
|
||||
}
|
||||
|
||||
// Minimal builtin bridge: support print-like globals in legacy form
|
||||
// Accept: "print", "nyash.console.log", "env.console.log", "nyash.builtin.print"
|
||||
// Also bridge hostbridge.extern_invoke to the extern handler (legacy form)
|
||||
match raw.as_str() {
|
||||
"print" | "nyash.console.log" | "env.console.log" | "nyash.builtin.print" => {
|
||||
if let Some(a0) = args.get(0) {
|
||||
@ -146,6 +150,12 @@ impl MirInterpreter {
|
||||
}
|
||||
return Ok(VMValue::Void);
|
||||
}
|
||||
name if name == "hostbridge.extern_invoke" || name.starts_with("hostbridge.extern_invoke/") => {
|
||||
return self.execute_extern_function("hostbridge.extern_invoke", args);
|
||||
}
|
||||
name if name == "env.get" || name.starts_with("env.get/") || name.contains("env.get") => {
|
||||
return self.execute_extern_function("env.get", args);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -337,6 +347,77 @@ impl MirInterpreter {
|
||||
args: &[ValueId],
|
||||
) -> Result<VMValue, VMError> {
|
||||
match func_name {
|
||||
name if name == "env.get" || name.starts_with("env.get/") => {
|
||||
// Route env.get global to extern handler
|
||||
return self.execute_extern_function("env.get", args);
|
||||
}
|
||||
name if name == "hostbridge.extern_invoke" || name.starts_with("hostbridge.extern_invoke/") => {
|
||||
// Treat as extern_invoke in legacy/global-resolved form
|
||||
if args.len() < 3 {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"hostbridge.extern_invoke expects 3 args".into(),
|
||||
));
|
||||
}
|
||||
let name = self.reg_load(args[0])?.to_string();
|
||||
let method = self.reg_load(args[1])?.to_string();
|
||||
let v = self.reg_load(args[2])?;
|
||||
let mut first_arg_str: Option<String> = None;
|
||||
match v {
|
||||
VMValue::BoxRef(b) => {
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
let idx: Box<dyn crate::box_trait::NyashBox> =
|
||||
Box::new(crate::box_trait::IntegerBox::new(0));
|
||||
let elem = ab.get(idx);
|
||||
first_arg_str = Some(elem.to_string_box().value);
|
||||
} else {
|
||||
first_arg_str = Some(b.to_string_box().value);
|
||||
}
|
||||
}
|
||||
_ => first_arg_str = Some(v.to_string()),
|
||||
}
|
||||
match (name.as_str(), method.as_str()) {
|
||||
("env.mirbuilder", "emit") => {
|
||||
if let Some(s) = first_arg_str {
|
||||
match crate::host_providers::mir_builder::program_json_to_mir_json(&s) {
|
||||
Ok(out) => Ok(VMValue::String(out)),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.mirbuilder.emit: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.mirbuilder.emit expects 1 arg".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
("env.codegen", "emit_object") => {
|
||||
if let Some(s) = first_arg_str {
|
||||
let opts = crate::host_providers::llvm_codegen::Opts {
|
||||
out: None,
|
||||
nyrt: std::env::var("NYASH_EMIT_EXE_NYRT").ok().map(std::path::PathBuf::from),
|
||||
opt_level: std::env::var("HAKO_LLVM_OPT_LEVEL").ok(),
|
||||
timeout_ms: None,
|
||||
};
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&s, opts) {
|
||||
Ok(p) => Ok(VMValue::String(p.to_string_lossy().into_owned())),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.codegen.emit_object: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.codegen.emit_object expects 1 arg".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
"hostbridge.extern_invoke unsupported for {}.{}",
|
||||
name, method
|
||||
))),
|
||||
}
|
||||
}
|
||||
"nyash.builtin.print" | "print" | "nyash.console.log" => {
|
||||
if let Some(arg_id) = args.get(0) {
|
||||
let val = self.reg_load(*arg_id)?;
|
||||
@ -591,6 +672,80 @@ impl MirInterpreter {
|
||||
};
|
||||
panic!("{}", msg);
|
||||
}
|
||||
"hostbridge.extern_invoke" => {
|
||||
// Legacy global-call form: hostbridge.extern_invoke(name, method, args?)
|
||||
if args.len() < 2 {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"extern_invoke expects at least 2 args".into(),
|
||||
));
|
||||
}
|
||||
let name = self.reg_load(args[0])?.to_string();
|
||||
let method = self.reg_load(args[1])?.to_string();
|
||||
|
||||
// Extract first arg as string when a third argument exists (ArrayBox or primitive)
|
||||
let mut first_arg_str: Option<String> = None;
|
||||
if let Some(a2) = args.get(2) {
|
||||
let v = self.reg_load(*a2)?;
|
||||
match v {
|
||||
VMValue::BoxRef(b) => {
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
let idx: Box<dyn crate::box_trait::NyashBox> =
|
||||
Box::new(crate::box_trait::IntegerBox::new(0));
|
||||
let elem = ab.get(idx);
|
||||
first_arg_str = Some(elem.to_string_box().value);
|
||||
} else {
|
||||
first_arg_str = Some(b.to_string_box().value);
|
||||
}
|
||||
}
|
||||
_ => first_arg_str = Some(v.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
match (name.as_str(), method.as_str()) {
|
||||
("env.mirbuilder", "emit") => {
|
||||
if let Some(s) = first_arg_str {
|
||||
match crate::host_providers::mir_builder::program_json_to_mir_json(&s) {
|
||||
Ok(out) => Ok(VMValue::String(out)),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.mirbuilder.emit: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.mirbuilder.emit expects 1 arg".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
("env.codegen", "emit_object") => {
|
||||
if let Some(s) = first_arg_str {
|
||||
let opts = crate::host_providers::llvm_codegen::Opts {
|
||||
out: None,
|
||||
nyrt: std::env::var("NYASH_EMIT_EXE_NYRT")
|
||||
.ok()
|
||||
.map(std::path::PathBuf::from),
|
||||
opt_level: std::env::var("HAKO_LLVM_OPT_LEVEL").ok(),
|
||||
timeout_ms: None,
|
||||
};
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&s, opts) {
|
||||
Ok(p) => Ok(VMValue::String(p.to_string_lossy().into_owned())),
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.codegen.emit_object: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.codegen.emit_object expects 1 arg".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
"hostbridge.extern_invoke unsupported for {}.{}",
|
||||
name, method
|
||||
))),
|
||||
}
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
"Unknown extern function: {}",
|
||||
extern_name
|
||||
|
||||
@ -9,6 +9,21 @@ impl MirInterpreter {
|
||||
args: &[ValueId],
|
||||
) -> Result<(), VMError> {
|
||||
match (iface, method) {
|
||||
("env", "get") => {
|
||||
if let Some(a0) = args.get(0) {
|
||||
let key = self.reg_load(*a0)?.to_string();
|
||||
let val = std::env::var(&key).ok();
|
||||
if let Some(d) = dst {
|
||||
if let Some(s) = val {
|
||||
self.regs.insert(d, VMValue::String(s));
|
||||
} else {
|
||||
// Represent missing env as null-equivalent (Void)
|
||||
self.regs.insert(d, VMValue::Void);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
("env.console", "log") => {
|
||||
if let Some(a0) = args.get(0) {
|
||||
let v = self.reg_load(*a0)?;
|
||||
@ -115,6 +130,144 @@ impl MirInterpreter {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
("env", "get") => {
|
||||
// env.get(key) - get environment variable
|
||||
if let Some(a0) = args.get(0) {
|
||||
let k = self.reg_load(*a0)?.to_string();
|
||||
let val = std::env::var(&k).ok();
|
||||
if let Some(d) = dst {
|
||||
if let Some(v) = val {
|
||||
self.regs.insert(d, VMValue::String(v));
|
||||
} else {
|
||||
self.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::VoidBox::new())));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
("env.mirbuilder", "emit") => {
|
||||
// program_json -> mir_json (delegate provider)
|
||||
if let Some(a0) = args.get(0) {
|
||||
let program_json = self.reg_load(*a0)?.to_string();
|
||||
match crate::host_providers::mir_builder::program_json_to_mir_json(&program_json) {
|
||||
Ok(s) => {
|
||||
if let Some(d) = dst { self.regs.insert(d, VMValue::String(s)); }
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!("env.mirbuilder.emit: {}", e))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction("env.mirbuilder.emit expects 1 arg".into()))
|
||||
}
|
||||
}
|
||||
("env.codegen", "emit_object") => {
|
||||
// mir_json -> object path (ny-llvmc or harness)
|
||||
if let Some(a0) = args.get(0) {
|
||||
let mir_json = self.reg_load(*a0)?.to_string();
|
||||
let opts = crate::host_providers::llvm_codegen::Opts { out: None, nyrt: std::env::var("NYASH_EMIT_EXE_NYRT").ok().map(std::path::PathBuf::from), opt_level: std::env::var("HAKO_LLVM_OPT_LEVEL").ok(), timeout_ms: None };
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&mir_json, opts) {
|
||||
Ok(p) => {
|
||||
if let Some(d) = dst { self.regs.insert(d, VMValue::String(p.to_string_lossy().into_owned())); }
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!("env.codegen.emit_object: {}", e))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction("env.codegen.emit_object expects 1 arg".into()))
|
||||
}
|
||||
}
|
||||
("hostbridge", "extern_invoke") => {
|
||||
// hostbridge.extern_invoke(name, method, args?)
|
||||
if args.len() < 2 {
|
||||
return Err(VMError::InvalidInstruction(
|
||||
"extern_invoke expects at least 2 args".into(),
|
||||
));
|
||||
}
|
||||
let name = self.reg_load(args[0])?.to_string();
|
||||
let method = self.reg_load(args[1])?.to_string();
|
||||
|
||||
// Extract first payload argument as string if provided.
|
||||
// MirBuilder uses: extern_invoke("env.mirbuilder","emit", [program_json])
|
||||
let mut first_arg_str: Option<String> = None;
|
||||
if let Some(a2) = args.get(2) {
|
||||
let v = self.reg_load(*a2)?;
|
||||
match v {
|
||||
VMValue::BoxRef(b) => {
|
||||
// If it's an ArrayBox, read element[0]
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
let idx: Box<dyn crate::box_trait::NyashBox> =
|
||||
Box::new(crate::box_trait::IntegerBox::new(0));
|
||||
let elem = ab.get(idx);
|
||||
first_arg_str = Some(elem.to_string_box().value);
|
||||
} else {
|
||||
// Fallback: stringify the box
|
||||
first_arg_str = Some(b.to_string_box().value);
|
||||
}
|
||||
}
|
||||
// For primitive VM values, use their string form
|
||||
_ => first_arg_str = Some(v.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch to known providers
|
||||
match (name.as_str(), method.as_str()) {
|
||||
("env.mirbuilder", "emit") => {
|
||||
if let Some(s) = first_arg_str {
|
||||
match crate::host_providers::mir_builder::program_json_to_mir_json(&s) {
|
||||
Ok(out) => {
|
||||
if let Some(d) = dst {
|
||||
self.regs.insert(d, VMValue::String(out));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.mirbuilder.emit: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.mirbuilder.emit expects 1 arg".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
("env.codegen", "emit_object") => {
|
||||
if let Some(s) = first_arg_str {
|
||||
let opts = crate::host_providers::llvm_codegen::Opts {
|
||||
out: None,
|
||||
nyrt: std::env::var("NYASH_EMIT_EXE_NYRT")
|
||||
.ok()
|
||||
.map(std::path::PathBuf::from),
|
||||
opt_level: std::env::var("HAKO_LLVM_OPT_LEVEL").ok(),
|
||||
timeout_ms: None,
|
||||
};
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&s, opts) {
|
||||
Ok(p) => {
|
||||
if let Some(d) = dst {
|
||||
self.regs.insert(
|
||||
d,
|
||||
VMValue::String(p.to_string_lossy().into_owned()),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(VMError::InvalidInstruction(format!(
|
||||
"env.codegen.emit_object: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(VMError::InvalidInstruction(
|
||||
"extern_invoke env.codegen.emit_object expects 1 arg".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
"hostbridge.extern_invoke unsupported for {}.{}",
|
||||
name, method
|
||||
))),
|
||||
}
|
||||
}
|
||||
_ => Err(VMError::InvalidInstruction(format!(
|
||||
"ExternCall {}.{} not supported",
|
||||
iface, method
|
||||
|
||||
@ -21,6 +21,11 @@ fn resolve_ny_llvmc() -> PathBuf {
|
||||
/// Compile MIR(JSON v0) to an object file (.o) using ny-llvmc. Returns the output path.
|
||||
/// Fail‑Fast: prints stable tags and returns Err with the same message.
|
||||
pub fn mir_json_to_object(mir_json: &str, opts: Opts) -> Result<PathBuf, String> {
|
||||
// Optional provider selection (default: ny-llvmc)
|
||||
match std::env::var("HAKO_LLVM_EMIT_PROVIDER").ok().as_deref() {
|
||||
Some("llvmlite") => return mir_json_to_object_llvmlite(mir_json, &opts),
|
||||
_ => {}
|
||||
}
|
||||
// Basic shape check for MIR(JSON v0)
|
||||
if !mir_json.contains("\"functions\"") || !mir_json.contains("\"blocks\"") {
|
||||
let tag = "[llvmemit/input/invalid] missing functions/blocks keys";
|
||||
@ -73,3 +78,70 @@ pub fn mir_json_to_object(mir_json: &str, opts: Opts) -> Result<PathBuf, String>
|
||||
Ok(out_path)
|
||||
}
|
||||
|
||||
fn resolve_python3() -> Option<PathBuf> {
|
||||
if let Ok(p) = which::which("python3") { return Some(p); }
|
||||
if let Ok(p) = which::which("python") { return Some(p); }
|
||||
None
|
||||
}
|
||||
|
||||
fn resolve_llvmlite_harness() -> Option<PathBuf> {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let p = PathBuf::from(root).join("tools/llvmlite_harness.py");
|
||||
if p.exists() { return Some(p); }
|
||||
}
|
||||
let p = PathBuf::from("tools/llvmlite_harness.py");
|
||||
if p.exists() { return Some(p); }
|
||||
// Also try repo-relative (target may run elsewhere)
|
||||
let p2 = PathBuf::from("../tools/llvmlite_harness.py");
|
||||
if p2.exists() { return Some(p2); }
|
||||
None
|
||||
}
|
||||
|
||||
/// Compile via llvmlite harness (opt-in provider). Returns output path or tagged error.
|
||||
fn mir_json_to_object_llvmlite(mir_json: &str, opts: &Opts) -> Result<PathBuf, String> {
|
||||
if !mir_json.contains("\"functions\"") || !mir_json.contains("\"blocks\"") {
|
||||
let tag = "[llvmemit/input/invalid] missing functions/blocks keys";
|
||||
eprintln!("{}", tag);
|
||||
return Err(tag.into());
|
||||
}
|
||||
let py = resolve_python3().ok_or_else(|| {
|
||||
let tag = String::from("[llvmemit/llvmlite/python-not-found]");
|
||||
eprintln!("{}", tag);
|
||||
tag
|
||||
})?;
|
||||
let harness = resolve_llvmlite_harness().ok_or_else(|| {
|
||||
let tag = String::from("[llvmemit/llvmlite/harness-not-found] tools/llvmlite_harness.py");
|
||||
eprintln!("{}", tag);
|
||||
tag
|
||||
})?;
|
||||
|
||||
// Write MIR JSON to temp
|
||||
let tmp_dir = std::env::temp_dir();
|
||||
let in_path = tmp_dir.join("hako_llvm_in.json");
|
||||
{
|
||||
let mut f = fs::File::create(&in_path).map_err(|e| format!("[llvmemit/tmp/write-failed] {}", e))?;
|
||||
f.write_all(mir_json.as_bytes()).map_err(|e| format!("[llvmemit/tmp/write-failed] {}", e))?;
|
||||
}
|
||||
let out_path = if let Some(p) = opts.out.clone() { p } else { tmp_dir.join("hako_llvm_out.o") };
|
||||
if let Some(parent) = out_path.parent() { let _ = fs::create_dir_all(parent); }
|
||||
|
||||
// Run: python3 tools/llvmlite_harness.py --in <json> --out <out>
|
||||
let status = Command::new(&py)
|
||||
.arg(&harness)
|
||||
.arg("--in").arg(&in_path)
|
||||
.arg("--out").arg(&out_path)
|
||||
.status()
|
||||
.map_err(|e| format!("[llvmemit/llvmlite/spawn/error] {}", e))?;
|
||||
if !status.success() {
|
||||
let code = status.code().unwrap_or(1);
|
||||
let tag = format!("[llvmemit/llvmlite/failed status={}]", code);
|
||||
eprintln!("{}", tag);
|
||||
return Err(tag);
|
||||
}
|
||||
if !out_path.exists() {
|
||||
let tag = format!("[llvmemit/output/missing] {}", out_path.display());
|
||||
eprintln!("{}", tag);
|
||||
return Err(tag);
|
||||
}
|
||||
Ok(out_path)
|
||||
}
|
||||
|
||||
@ -69,6 +69,9 @@ pub mod syntax; // syntax sugar config and helpers
|
||||
pub mod runner;
|
||||
pub mod using; // using resolver scaffolding (Phase 15)
|
||||
|
||||
// Host providers (extern bridge for Hako boxes)
|
||||
pub mod host_providers;
|
||||
|
||||
// Expose the macro engine module under a raw identifier; the source lives under `src/macro/`.
|
||||
#[path = "macro/mod.rs"]
|
||||
pub mod r#macro;
|
||||
|
||||
@ -124,6 +124,14 @@ pub fn get_env_method_spec(
|
||||
true,
|
||||
)),
|
||||
|
||||
// Direct env access
|
||||
("env", "get") => Some((
|
||||
"env".to_string(),
|
||||
"get".to_string(),
|
||||
EffectMask::READ,
|
||||
true,
|
||||
)),
|
||||
|
||||
// Unknown
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@ -21,9 +21,9 @@ pub(crate) mod child_env;
|
||||
mod cli_directives;
|
||||
mod demos;
|
||||
mod dispatch;
|
||||
mod json_v0_bridge;
|
||||
pub mod json_v0_bridge;
|
||||
mod json_v1_bridge;
|
||||
mod mir_json_emit;
|
||||
pub mod mir_json_emit;
|
||||
pub mod modes;
|
||||
mod pipe_io;
|
||||
mod core_executor;
|
||||
|
||||
@ -61,18 +61,31 @@ pub fn collect_using_and_strip(
|
||||
} else {
|
||||
(rest0.to_string(), None)
|
||||
};
|
||||
let is_path = target.starts_with('"')
|
||||
|| target.starts_with("./")
|
||||
// Strip quotes from target for alias/module lookup
|
||||
let target_unquoted = target.trim_matches('"').to_string();
|
||||
|
||||
// Check if this is a known alias or module FIRST before treating as file path
|
||||
let is_known_alias_or_module = using_ctx.aliases.contains_key(&target_unquoted)
|
||||
|| using_ctx.pending_modules.iter().any(|(k, _)| k == &target_unquoted)
|
||||
|| using_ctx.packages.contains_key(&target_unquoted);
|
||||
|
||||
let is_path = if is_known_alias_or_module {
|
||||
// Known alias/module - don't treat as file path even if quoted
|
||||
false
|
||||
} else {
|
||||
// Only treat as file path if not a known alias/module
|
||||
target.starts_with("./")
|
||||
|| target.starts_with('/')
|
||||
|| target.ends_with(".nyash")
|
||||
|| target.ends_with(".hako");
|
||||
|| target.ends_with(".hako")
|
||||
};
|
||||
if is_path {
|
||||
// SSOT: Disallow file-using at top-level; allow only for sources located
|
||||
// under a declared package root (internal package wiring), so that packages
|
||||
// can organize their modules via file paths.
|
||||
if (prod || !crate::config::env::allow_using_file()) && !inside_pkg {
|
||||
return Err(format!(
|
||||
"{}:{}: using: file paths are disallowed in this profile. Add it to nyash.toml [using] (packages/aliases) and reference by name: {}",
|
||||
"{}:{}: using: file paths are disallowed in this profile. Add it to nyash.toml [using]/[modules] and reference by name: {}\n suggestions: using \"alias.name\" as Name | dev/test: set NYASH_PREINCLUDE=1 to expand includes ahead of VM\n docs: see docs/reference/using.md",
|
||||
filename,
|
||||
line_no,
|
||||
target
|
||||
@ -159,8 +172,8 @@ pub fn collect_using_and_strip(
|
||||
// Resolve namespaces/packages
|
||||
if prod {
|
||||
// prod: only allow names present in aliases/packages (toml)
|
||||
let mut pkg_name: String = target.clone();
|
||||
if let Some(v) = using_ctx.aliases.get(&target) {
|
||||
let mut pkg_name: String = target_unquoted.clone();
|
||||
if let Some(v) = using_ctx.aliases.get(&target_unquoted) {
|
||||
pkg_name = v.clone();
|
||||
}
|
||||
if let Some(pkg) = using_ctx.packages.get(&pkg_name) {
|
||||
@ -222,16 +235,16 @@ pub fn collect_using_and_strip(
|
||||
}
|
||||
} else {
|
||||
return Err(format!(
|
||||
"{}:{}: using: '{}' not found in nyash.toml [using]. Define a package or alias and use its name (prod profile)",
|
||||
"{}:{}: using: '{}' not found in nyash.toml [using]/[modules]. Define a package or alias and use its name (prod profile)\n suggestions: add an alias in nyash.toml and use 'using \"alias.name\" as Name' | dev/test: NYASH_PREINCLUDE=1",
|
||||
filename,
|
||||
line_no,
|
||||
target
|
||||
target_unquoted
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// dev/ci: allow broader resolution via resolver
|
||||
match crate::runner::pipeline::resolve_using_target(
|
||||
&target,
|
||||
&target_unquoted,
|
||||
false,
|
||||
&using_ctx.pending_modules,
|
||||
&using_ctx.using_paths,
|
||||
|
||||
@ -53,11 +53,23 @@ impl NyashRunner {
|
||||
let mut interp = MirInterpreter::new();
|
||||
match interp.execute_module(&module_interp) {
|
||||
Ok(result) => {
|
||||
use nyash_rust::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox};
|
||||
|
||||
// Extract exit code from return value
|
||||
let exit_code = if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||
ib.value as i32
|
||||
} else if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
|
||||
if bb.value { 1 } else { 0 }
|
||||
} else {
|
||||
// For non-integer/bool returns, default to 0 (success)
|
||||
0
|
||||
};
|
||||
|
||||
// Pretty-print using MIR return type when available (only in verbose mode)
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!("✅ MIR interpreter execution completed!");
|
||||
// Pretty-print using MIR return type when available
|
||||
if let Some(func) = module_interp.functions.get("main") {
|
||||
use nyash_rust::mir::MirType;
|
||||
use nyash_rust::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox};
|
||||
use nyash_rust::boxes::FloatBox;
|
||||
let (ety, sval) = match &func.signature.return_type {
|
||||
MirType::Float => {
|
||||
@ -92,6 +104,10 @@ impl NyashRunner {
|
||||
println!("Result: {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
// Exit with the return value as exit code
|
||||
process::exit(exit_code);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR interpreter error: {}", e);
|
||||
process::exit(1);
|
||||
|
||||
@ -258,7 +258,22 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
match vm.execute_module(&module_vm) {
|
||||
Ok(_ret) => { /* interpreter already prints via println/console in program */ }
|
||||
Ok(ret) => {
|
||||
use crate::box_trait::{NyashBox, IntegerBox, BoolBox};
|
||||
|
||||
// Extract exit code from return value
|
||||
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
||||
ib.value as i32
|
||||
} else if let Some(bb) = ret.as_any().downcast_ref::<BoolBox>() {
|
||||
if bb.value { 1 } else { 0 }
|
||||
} else {
|
||||
// For non-integer/bool returns, default to 0 (success)
|
||||
0
|
||||
};
|
||||
|
||||
// Exit with the return value as exit code
|
||||
process::exit(exit_code);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ VM fallback error: {}", e);
|
||||
process::exit(1);
|
||||
|
||||
@ -147,6 +147,37 @@ static EXTERNS: Lazy<Vec<ExternSpec>> = Lazy::new(|| {
|
||||
max_arity: 255,
|
||||
slot: Some(50),
|
||||
},
|
||||
// basic env access
|
||||
ExternSpec {
|
||||
iface: "env",
|
||||
method: "get",
|
||||
min_arity: 1,
|
||||
max_arity: 1,
|
||||
slot: Some(60),
|
||||
},
|
||||
// host providers (delegate path)
|
||||
ExternSpec {
|
||||
iface: "env.mirbuilder",
|
||||
method: "emit",
|
||||
min_arity: 1,
|
||||
max_arity: 1,
|
||||
slot: Some(70),
|
||||
},
|
||||
ExternSpec {
|
||||
iface: "env.codegen",
|
||||
method: "emit_object",
|
||||
min_arity: 1,
|
||||
max_arity: 2,
|
||||
slot: Some(71),
|
||||
},
|
||||
// hostbridge trampoline (dev/testing)
|
||||
ExternSpec {
|
||||
iface: "hostbridge",
|
||||
method: "extern_invoke",
|
||||
min_arity: 2,
|
||||
max_arity: 3,
|
||||
slot: Some(80),
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@ -214,7 +214,11 @@ fn handle_codegen(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Op
|
||||
let nyrt = std::env::var("NYASH_EMIT_EXE_NYRT").ok().map(std::path::PathBuf::from);
|
||||
let opts = crate::host_providers::llvm_codegen::Opts { out, nyrt, opt_level, timeout_ms: None };
|
||||
match crate::host_providers::llvm_codegen::mir_json_to_object(&mir_json, opts) {
|
||||
Ok(p) => Ok(Some(Box::new(StringBox::new(&p.to_string_lossy())) as Box<dyn NyashBox>)),
|
||||
Ok(p) => {
|
||||
// Convert PathBuf → String via lossy conversion (owned)
|
||||
let s = p.to_string_lossy().into_owned();
|
||||
Ok(Some(Box::new(StringBox::new(s)) as Box<dyn NyashBox>))
|
||||
},
|
||||
Err(_e) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
25
tools/dev/check_lang_includes.sh
Normal file
25
tools/dev/check_lang_includes.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
TARGET_DIR="$ROOT_DIR/lang/src"
|
||||
|
||||
if [ ! -d "$TARGET_DIR" ]; then
|
||||
echo "[check] target dir not found: $TARGET_DIR" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "[check] scanning for include statements under lang/src ..." >&2
|
||||
if rg -n '^\s*include\s+"' "$TARGET_DIR" >/tmp/hako_include_hits_$$.txt; then
|
||||
echo "[FAIL] include statements found (VM backend does not support include):" >&2
|
||||
cat /tmp/hako_include_hits_$$.txt >&2
|
||||
echo "[hint] Prefer 'using "alias" as Name' with nyash.toml [modules]." >&2
|
||||
echo "[hint] For dev/tests, set NYASH_PREINCLUDE=1 to expand includes temporarily." >&2
|
||||
rm -f /tmp/hako_include_hits_$$.txt
|
||||
exit 1
|
||||
else
|
||||
echo "[OK] no include statements found under lang/src" >&2
|
||||
fi
|
||||
rm -f /tmp/hako_include_hits_$$.txt
|
||||
exit 0
|
||||
|
||||
102
tools/dev/hako_debug_run.sh
Normal file
102
tools/dev/hako_debug_run.sh
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# hako_debug_run.sh — Stable wrapper to run .hako with Stage‑3
|
||||
# Usage:
|
||||
# tools/dev/hako_debug_run.sh [--internal|--delegate] [--core] [--print-env] <file.hako>
|
||||
# tools/dev/hako_debug_run.sh [--internal|--delegate] -c '<code>'
|
||||
# Notes:
|
||||
# - Enables Stage‑3 + semicolon tolerance(smokes runner と同等)
|
||||
# - 実行モード:
|
||||
# --raw (既定): 直実行。inline Ny コンパイラ有効(timeoutは延長)。include が必要な時はこちら。
|
||||
# --safe : ランナー経由。inline Ny コンパイラ無効化+ノイズフィルタ。
|
||||
# --no-compiler : inline Ny コンパイラを明示的に無効化(--raw と併用可)。
|
||||
# - Uses tools/smokes/v2/lib/test_runner.sh under the hood(safe モード時)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env >/dev/null || exit 2
|
||||
|
||||
MODE_CODE=0
|
||||
CODE=""
|
||||
FILE=""
|
||||
USE_INTERNAL=0
|
||||
USE_DELEGATE=0
|
||||
USE_CORE=0
|
||||
PRINT_ENV=0
|
||||
USE_RAW=1
|
||||
NO_COMPILER=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-c|--code)
|
||||
MODE_CODE=1
|
||||
CODE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--internal)
|
||||
USE_INTERNAL=1; shift ;;
|
||||
--delegate)
|
||||
USE_DELEGATE=1; shift ;;
|
||||
--core)
|
||||
USE_CORE=1; shift ;;
|
||||
--raw)
|
||||
USE_RAW=1; shift ;;
|
||||
--safe)
|
||||
USE_RAW=0; shift ;;
|
||||
--no-compiler)
|
||||
NO_COMPILER=1; shift ;;
|
||||
--print-env)
|
||||
PRINT_ENV=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--internal|--delegate] [--core] [--print-env] <file.hako> | -c '<code>'"; exit 0 ;;
|
||||
*)
|
||||
FILE="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$MODE_CODE" -eq 0 && -z "$FILE" ]]; then
|
||||
echo "[ERR] No file or -c '<code>' specified" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Base env (Stage-3 + tolerance)
|
||||
export NYASH_PARSER_STAGE3=1
|
||||
export HAKO_PARSER_STAGE3=1
|
||||
export NYASH_PARSER_ALLOW_SEMICOLON=1
|
||||
export NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1
|
||||
|
||||
# Compiler policy (default: enabled, longer timeout). Use --no-compiler to disable.
|
||||
export NYASH_NY_COMPILER_TIMEOUT_MS="${NYASH_NY_COMPILER_TIMEOUT_MS:-8000}"
|
||||
if [[ "$NO_COMPILER" -eq 1 ]]; then
|
||||
export NYASH_DISABLE_NY_COMPILER=1
|
||||
export HAKO_DISABLE_NY_COMPILER=1
|
||||
fi
|
||||
|
||||
if [[ "$USE_INTERNAL" -eq 1 ]]; then export HAKO_MIR_BUILDER_INTERNAL=1; fi
|
||||
if [[ "$USE_DELEGATE" -eq 1 ]]; then export HAKO_MIR_BUILDER_DELEGATE=1; fi
|
||||
if [[ "$USE_CORE" -eq 1 ]]; then export NYASH_GATE_C_CORE=1; export HAKO_GATE_C_CORE=1; fi
|
||||
|
||||
if [[ "$PRINT_ENV" -eq 1 ]]; then
|
||||
echo "[ENV] NYASH_BIN=$NYASH_BIN"
|
||||
env | grep -E '^(NYASH_|HAKO_)' | sort
|
||||
fi
|
||||
|
||||
if [[ "$USE_RAW" -eq 1 ]]; then
|
||||
# Direct run (inline compiler allowed unless --no-compiler)
|
||||
if [[ "$MODE_CODE" -eq 1 ]]; then
|
||||
tmpf="/tmp/hako_debug_run_$$.hako"; printf "%s\n" "$CODE" > "$tmpf"
|
||||
"$NYASH_BIN" --backend vm "$tmpf"; rc=$?; rm -f "$tmpf"; exit $rc
|
||||
else
|
||||
"$NYASH_BIN" --backend vm "$FILE"
|
||||
fi
|
||||
else
|
||||
# Safe run via test runner (inline compiler disabled; noise filtered)
|
||||
if [[ "$MODE_CODE" -eq 1 ]]; then
|
||||
run_nyash_vm -c "$CODE"
|
||||
else
|
||||
run_nyash_vm "$FILE"
|
||||
fi
|
||||
fi
|
||||
102
tools/dev/hako_preinclude.sh
Normal file
102
tools/dev/hako_preinclude.sh
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# hako_preinclude.sh — Expand include "path" directives into inlined content.
|
||||
# Usage: tools/dev/hako_preinclude.sh <in.hako> <out.hako>
|
||||
#
|
||||
# Env (optional):
|
||||
# HAKO_PREINCLUDE_CACHE=1 # enable cache (default: 1)
|
||||
# HAKO_PREINCLUDE_CACHE_DIR=/tmp/... # cache dir (default: /tmp/hako_preinclude_cache)
|
||||
# HAKO_PREINCLUDE_MAX_DEPTH=10 # include nesting max (default: 12)
|
||||
# HAKO_PREINCLUDE_MAX_SIZE=1048576 # expanded size bytes (default: 1 MiB)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Usage: $0 <in.hako> <out.hako>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
IN="$1"
|
||||
OUT="$2"
|
||||
|
||||
ROOT="${NYASH_ROOT:-}"
|
||||
if [[ -z "$ROOT" ]]; then
|
||||
if ROOT_GIT=$(git -C "$(dirname "$IN")" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(pwd)"
|
||||
fi
|
||||
fi
|
||||
|
||||
declare -A SEEN
|
||||
declare -A IMPORT_SEEN
|
||||
|
||||
MAX_DEPTH="${HAKO_PREINCLUDE_MAX_DEPTH:-12}"
|
||||
MAX_SIZE="${HAKO_PREINCLUDE_MAX_SIZE:-1048576}"
|
||||
USE_CACHE="${HAKO_PREINCLUDE_CACHE:-1}"
|
||||
CACHE_DIR="${HAKO_PREINCLUDE_CACHE_DIR:-/tmp/hako_preinclude_cache}"
|
||||
mkdir -p "$CACHE_DIR" >/dev/null 2>&1 || true
|
||||
|
||||
sum_file() { sha256sum "$1" 2>/dev/null | awk '{print $1}' || echo ""; }
|
||||
cache_key() {
|
||||
local key
|
||||
key="$(sum_file "$1")"
|
||||
echo "$key"
|
||||
}
|
||||
|
||||
expand_file() {
|
||||
local file="$1"
|
||||
local abspath="$file"
|
||||
if [[ "$abspath" != /* ]]; then
|
||||
abspath="$ROOT/$abspath"
|
||||
fi
|
||||
if [[ -n "${SEEN[$abspath]:-}" ]]; then
|
||||
echo "// [preinclude] Skipping already included: $abspath"; return
|
||||
fi
|
||||
SEEN[$abspath]=1
|
||||
if [[ ! -f "$abspath" ]]; then
|
||||
echo "// [preinclude][ERROR] Not found: $abspath" >&2
|
||||
return 1
|
||||
fi
|
||||
local depth="${2:-0}"
|
||||
if (( depth > MAX_DEPTH )); then
|
||||
echo "// [preinclude][ERROR] max depth exceeded at: $abspath" >&2
|
||||
return 1
|
||||
fi
|
||||
local current_size=0
|
||||
while IFS='' read -r line || [[ -n "$line" ]]; do
|
||||
if [[ "$line" =~ ^[[:space:]]*include[[:space:]]+\"([^\"]+)\" ]]; then
|
||||
inc="${BASH_REMATCH[1]}"
|
||||
expand_file "$inc" $((depth+1))
|
||||
elif [[ "$line" =~ ^[[:space:]]*using[[:space:]].* ]]; then
|
||||
key="${line//[[:space:]]/ }" # normalize spaces a bit
|
||||
if [[ -n "${IMPORT_SEEN[$key]:-}" ]]; then
|
||||
echo "// [preinclude] Skip duplicate using: $line"
|
||||
else
|
||||
IMPORT_SEEN[$key]=1
|
||||
printf '%s\n' "$line"
|
||||
fi
|
||||
else
|
||||
printf '%s\n' "$line"
|
||||
fi
|
||||
# crude size guard
|
||||
current_size=$((current_size + ${#line} + 1))
|
||||
if (( current_size > MAX_SIZE )); then
|
||||
echo "// [preinclude][ERROR] expanded size exceeded at: $abspath" >&2
|
||||
return 1
|
||||
fi
|
||||
done <"$abspath"
|
||||
}
|
||||
if [[ "$USE_CACHE" = "1" ]]; then
|
||||
key=$(cache_key "$IN")
|
||||
if [[ -n "$key" && -f "$CACHE_DIR/$key.hako" ]]; then
|
||||
cp "$CACHE_DIR/$key.hako" "$OUT"
|
||||
echo "[preinclude/cache-hit] $IN" >&2
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
expand_file "$IN" >"$OUT"
|
||||
if [[ "$USE_CACHE" = "1" && -n "${key:-}" ]]; then
|
||||
cp "$OUT" "$CACHE_DIR/$key.hako" 2>/dev/null || true
|
||||
fi
|
||||
echo "[preinclude] Wrote: $OUT" >&2
|
||||
@ -181,13 +181,25 @@ run_nyash_vm() {
|
||||
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$tmpfile" || true
|
||||
fi
|
||||
# プラグイン初期化メッセージを除外
|
||||
# Optional preinclude for include-based code
|
||||
local runfile="$tmpfile"
|
||||
if [ "${NYASH_PREINCLUDE:-0}" = "1" ] || [ "${HAKO_PREINCLUDE:-0}" = "1" ]; then
|
||||
local prefile="/tmp/nyash_pre_$$.nyash"
|
||||
"$NYASH_ROOT/tools/dev/hako_preinclude.sh" "$tmpfile" "$prefile" >/dev/null || true
|
||||
runfile="$prefile"
|
||||
fi
|
||||
# Optional hint for include lines when preinclude is OFF
|
||||
if grep -q '^include\s\"' "$tmpfile" 2>/dev/null && [ "${NYASH_PREINCLUDE:-0}" != "1" ] && [ "${HAKO_PREINCLUDE:-0}" != "1" ]; then
|
||||
echo "[WARN] VM backend does not support include. Prefer using+alias, or set NYASH_PREINCLUDE=1 for dev." >&2
|
||||
fi
|
||||
NYASH_VM_USE_PY="$USE_PYVM" NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 \
|
||||
NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
|
||||
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||
"${ENV_PREFIX[@]}" \
|
||||
"$NYASH_BIN" --backend vm "$tmpfile" "${EXTRA_ARGS[@]}" "$@" 2>&1 | filter_noise
|
||||
"$NYASH_BIN" --backend vm "$runfile" "${EXTRA_ARGS[@]}" "$@" 2>&1 | filter_noise
|
||||
local exit_code=${PIPESTATUS[0]}
|
||||
rm -f "$tmpfile"
|
||||
# prefile may be unset when preinclude is OFF; use default expansion to avoid set -u errors
|
||||
rm -f "$tmpfile" "${prefile:-}" 2>/dev/null || true
|
||||
return $exit_code
|
||||
else
|
||||
# 軽量ASIFix(テスト用): ブロック終端の余剰セミコロンを寛容に除去
|
||||
@ -195,12 +207,93 @@ run_nyash_vm() {
|
||||
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$program" || true
|
||||
fi
|
||||
# プラグイン初期化メッセージを除外
|
||||
# Optional preinclude
|
||||
local runfile2="$program"
|
||||
if [ "${NYASH_PREINCLUDE:-0}" = "1" ] || [ "${HAKO_PREINCLUDE:-0}" = "1" ]; then
|
||||
local prefile2="/tmp/nyash_pre_$$.nyash"
|
||||
"$NYASH_ROOT/tools/dev/hako_preinclude.sh" "$program" "$prefile2" >/dev/null || true
|
||||
runfile2="$prefile2"
|
||||
fi
|
||||
# Optional hint for include lines when preinclude is OFF
|
||||
if [ -f "$program" ] && grep -q '^include\s\"' "$program" 2>/dev/null && [ "${NYASH_PREINCLUDE:-0}" != "1" ] && [ "${HAKO_PREINCLUDE:-0}" != "1" ]; then
|
||||
echo "[WARN] VM backend does not support include. Prefer using+alias, or set NYASH_PREINCLUDE=1 for dev." >&2
|
||||
fi
|
||||
NYASH_VM_USE_PY="$USE_PYVM" NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 \
|
||||
NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
|
||||
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||
"${ENV_PREFIX[@]}" \
|
||||
"$NYASH_BIN" --backend vm "$program" "${EXTRA_ARGS[@]}" "$@" 2>&1 | filter_noise
|
||||
return ${PIPESTATUS[0]}
|
||||
"$NYASH_BIN" --backend vm "$runfile2" "${EXTRA_ARGS[@]}" "$@" 2>&1 | filter_noise
|
||||
local exit_code=${PIPESTATUS[0]}
|
||||
# prefile2 may be unset when preinclude is OFF
|
||||
rm -f "${prefile2:-}" 2>/dev/null || true
|
||||
return $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Verify MIR JSON rc using selected primary (Core or Hakorune VM)
|
||||
verify_mir_rc() {
|
||||
local json_path="$1"
|
||||
local primary="${HAKO_VERIFY_PRIMARY:-core}"
|
||||
if [ "$primary" = "hakovm" ]; then
|
||||
# Build a tiny driver to call MiniVmEntryBox.run_min with JSON literal embedded
|
||||
if [ ! -f "$json_path" ]; then
|
||||
echo "[FAIL] verify_mir_rc: json not found: $json_path" >&2
|
||||
return 2
|
||||
fi
|
||||
# Escape JSON as a single string literal via jq -Rs (preserves newlines)
|
||||
local json_literal
|
||||
json_literal="$(jq -Rs . < "$json_path")"
|
||||
build_and_run_driver_alias() {
|
||||
local header="$1"
|
||||
local code=$(cat <<HCODE
|
||||
$header
|
||||
static box Main { method main(args) {
|
||||
local j = __MIR_JSON__;
|
||||
local rc = MiniVmEntryBox.run_min(j)
|
||||
print(MiniVmEntryBox.int_to_str(rc))
|
||||
return rc
|
||||
} }
|
||||
HCODE
|
||||
)
|
||||
code="${code/__MIR_JSON__/$json_literal}"
|
||||
NYASH_USING_AST=1 run_nyash_vm -c "$code" 2>/dev/null | tr -d '\r' | tail -n 1
|
||||
}
|
||||
build_and_run_driver_include() {
|
||||
local inc_path="$1"
|
||||
local code=$(cat <<HCODE
|
||||
include "$inc_path"
|
||||
static box Main { method main(args) {
|
||||
local j = __MIR_JSON__;
|
||||
local rc = MiniVmEntryBox.run_min(j)
|
||||
print(MiniVmEntryBox.int_to_str(rc))
|
||||
return rc
|
||||
} }
|
||||
HCODE
|
||||
)
|
||||
code="${code/__MIR_JSON__/$json_literal}"
|
||||
NYASH_PREINCLUDE=1 run_nyash_vm -c "$code" 2>/dev/null | tr -d '\r' | tail -n 1
|
||||
}
|
||||
# Try alias header first; fallback to dev-file header; final fallback: include+preinclude
|
||||
local out
|
||||
out="$(build_and_run_driver_alias 'using selfhost.vm.entry as MiniVmEntryBox')"
|
||||
if ! [[ "$out" =~ ^-?[0-9]+$ ]]; then
|
||||
out="$(build_and_run_driver_alias 'using "lang/src/vm/boxes/mini_vm_entry.hako" as MiniVmEntryBox')"
|
||||
fi
|
||||
if ! [[ "$out" =~ ^-?[0-9]+$ ]]; then
|
||||
out="$(build_and_run_driver_include 'lang/src/vm/boxes/mini_vm_entry.hako')"
|
||||
fi
|
||||
if [[ "$out" =~ ^-?[0-9]+$ ]]; then
|
||||
local n=$out
|
||||
# normalize into [0,255]
|
||||
if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi
|
||||
return $n
|
||||
fi
|
||||
# Fallback: core primary when MiniVM resolution is unavailable
|
||||
NYASH_GATE_C_CORE=1 HAKO_GATE_C_CORE=1 "$NYASH_BIN" --json-file "$json_path" >/dev/null 2>&1
|
||||
return $?
|
||||
else
|
||||
NYASH_GATE_C_CORE=1 HAKO_GATE_C_CORE=1 "$NYASH_BIN" --json-file "$json_path" >/dev/null 2>&1
|
||||
return $?
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# llvmemit llvmlite canary — opt-in provider; SKIP if python/llvmlite/harness not present
|
||||
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/llvmemit_llvmlite_canary_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/llvm_ir/emit/LLVMEmitBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local mir = "{\"functions\":{\"Main.main\":{\"params\":[],\"locals\":[],\"blocks\":[{\"label\":\"bb0\",\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"ret\",\"value\":1}] }] }},\"blocks\":1}";
|
||||
local argsA = new ArrayBox(); argsA.push(mir)
|
||||
local out = hostbridge.extern_invoke("env.codegen", "emit_object", argsA)
|
||||
if out == null { return 0 }
|
||||
print("" + out)
|
||||
return 1
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_LLVM_EMIT_PROVIDER=llvmlite "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
path="$(echo "$out" | tail -n1 | tr -d '\r')"
|
||||
if [ "$rc" -eq 1 ] && [ -n "$path" ] && [ -f "$path" ]; then
|
||||
echo "[PASS] llvmemit_llvmlite_canary_vm"; rm -f "$path" || true; exit 0
|
||||
fi
|
||||
if echo "$out" | grep -q "\[llvmemit/llvmlite/\(python-not-found\|harness-not-found\|failed\)"; then
|
||||
echo "[SKIP] llvmemit_llvmlite (provider missing)"; exit 0
|
||||
fi
|
||||
echo "[FAIL] llvmemit_llvmlite_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
# mirbuilder_internal_binop_canary_vm.sh — Program(JSON v0) → MIR(JSON) internal box binop canary
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_internal_binop_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Program(JSON v0) with Return(Binary(Int,Int)) — 1 + 2
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
// Must contain two consts for 1 and 2, a binop with operation "+", and a ret of dst 3
|
||||
if s.indexOf("\"op\":\"const\"") >= 0 && s.indexOf("\"operation\":\"+\"") >= 0 && s.indexOf("\"op\":\"binop\"") >= 0 && s.indexOf("\"op\":\"ret\"") >= 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||
"$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" 2>/dev/null || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
echo "[PASS] mirbuilder_internal_binop_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_binop_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
# mirbuilder_internal_canary_vm.sh — Program(JSON v0) → MIR(JSON) internal box canary
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Default ON in quick: runs internal Return(Int) path with local toggle only for this test.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_internal_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Minimal Program(JSON v0) with Return(Int)
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":7}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"functions\"") >= 0 && s.indexOf("\"blocks\"") >= 0 && s.indexOf("\"value\":7") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||
"$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" 2>/dev/null || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
echo "[PASS] mirbuilder_internal_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_canary_vm (rc=$rc)" >&2; exit 1
|
||||
@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
# MirBuilder internal → Gate‑C/Core exec canary — rc verification
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_emit_$$.hako"
|
||||
tmp_json="/tmp/mirbuilder_emit_$$.json"
|
||||
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
static box Main { method main(args) {
|
||||
// Program: if (1 < 2) return 10; else return 20;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":10}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":20}}]}]}";
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 1 }
|
||||
print("" + out)
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
# 1) Emit MIR(JSON) to a temp file
|
||||
set +e
|
||||
out="$(NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
if ! echo "$out" | sed -n '/^{/,$p' | jq -e . > "$tmp_json"; then
|
||||
echo "[FAIL] mirbuilder_internal_core_exec_canary_vm (no MIR JSON)" >&2
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2) Core‑Direct exec and rc check (expect rc=10)
|
||||
set +e
|
||||
HAKO_VERIFY_PRIMARY=hakovm verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
|
||||
if [ "$rc" -eq 10 ]; then
|
||||
echo "[PASS] mirbuilder_internal_core_exec_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_core_exec_canary_vm (rc=$rc, expect 10)" >&2; exit 1
|
||||
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
# mirbuilder_internal_if_canary_vm.sh — Program(JSON v0) If/Compare → MIR(JSON) internal box canary
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_internal_if_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Program(JSON v0): if (1 < 2) return 10; else return 20;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":10}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":20}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
// Verify compare+branch presence and two return blocks
|
||||
if s.indexOf("\"op\":\"compare\"") >= 0 && s.indexOf("\"op\":\"branch\"") >= 0 && s.indexOf("\"label\":\"bb1\"") >= 0 && s.indexOf("\"label\":\"bb2\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||
"$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" 2>/dev/null || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
echo "[PASS] mirbuilder_internal_if_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_if_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# If(Compare ==) internal canary — structure check
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp="/tmp/mirbuilder_if_eq_$$.hako"
|
||||
cat > "$tmp" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"==\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":1}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":10}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":20}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"operation\":\"==\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e; HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp" >/dev/null 2>&1; rc=$?; set -e
|
||||
rm -f "$tmp" || true
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_compare_eq_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_compare_eq_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# If(Compare >=) internal canary — structure check
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp="/tmp/mirbuilder_if_ge_$$.hako"
|
||||
cat > "$tmp" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\">=\",\"lhs\":{\"type\":\"Int\",\"value\":2},\"rhs\":{\"type\":\"Int\",\"value\":1}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":10}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":20}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"operation\":\">=\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e; HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp" >/dev/null 2>&1; rc=$?; set -e
|
||||
rm -f "$tmp" || true
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_compare_ge_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_compare_ge_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# If(Compare <=) internal canary — structure check
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp="/tmp/mirbuilder_if_le_$$.hako"
|
||||
cat > "$tmp" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<=\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":10}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":20}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"compare\"") >= 0 || s.indexOf("\"op\":\"compare\"") >= 0 { }
|
||||
if s.indexOf("\"operation\":\"<=\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e; HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp" >/dev/null 2>&1; rc=$?; set -e
|
||||
rm -f "$tmp" || true
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_compare_le_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_compare_le_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# If(Compare !=) internal canary — structure check
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp="/tmp/mirbuilder_if_ne_$$.hako"
|
||||
cat > "$tmp" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"!=\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":10}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":20}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"operation\":\"!=\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e; HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp" >/dev/null 2>&1; rc=$?; set -e
|
||||
rm -f "$tmp" || true
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_compare_ne_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_compare_ne_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# If(Compare Var/Int or Int/Var) with prior Local Int → compare+branch+ret
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_if_varint_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local a=5; if (a >= 3) return 1; else return 0;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"a\",\"expr\":{\"type\":\"Int\",\"value\":5}}," +
|
||||
"{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\">=\",\"lhs\":{\"type\":\"Var\",\"name\":\"a\"},\"rhs\":{\"type\":\"Int\",\"value\":3}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":1}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"compare\"") >= 0 && s.indexOf("\"op\":\"branch\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_compare_varint_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_compare_varint_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
# If(Compare Var vs Var) with prior Local Ints → compare+branch+ret
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_if_varvar_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local a=1, b=2; if (a < b) return 7; else return 9;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"a\",\"expr\":{\"type\":\"Int\",\"value\":1}}," +
|
||||
"{\"type\":\"Local\",\"name\":\"b\",\"expr\":{\"type\":\"Int\",\"value\":2}}," +
|
||||
"{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Var\",\"name\":\"a\"},\"rhs\":{\"type\":\"Var\",\"name\":\"b\"}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":7}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":9}}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"compare\"") >= 0 && s.indexOf("\"op\":\"branch\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_compare_varvar_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_compare_varvar_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Nested If: if (1<2) return 3; else if (2<1) return 4; else return 5;
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_if_nested_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":3}}],\"else\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Int\",\"value\":2},\"rhs\":{\"type\":\"Int\",\"value\":1}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":4}}],\"else\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":5}}]}]}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"compare\"") >= 0 && s.indexOf("\"label\":\"bb2\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_nested_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_nested_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# If(then Return Int) and following Return(Int) — else-omitted lowering canary
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_if_then_follow_ret_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Program: if (1 < 2) return 7; return 9;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Int\",\"value\":1},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":7}}]},{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":9}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"compare\"") >= 0 && s.indexOf("\"op\":\"branch\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_if_then_follow_return_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_if_then_follow_return_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# Loop internal → Core exec canary — rc should equal limit (3)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_emit_loop_$$.hako"
|
||||
tmp_json="/tmp/mirbuilder_emit_loop_$$.json"
|
||||
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
static box Main { method main(args) {
|
||||
// Canonical loop Program(JSON): i=0; s=0; loop (i<3) { s=s+1; i=i+1 }; return s;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Local\",\"name\":\"i\",\"expr\":{\"type\":\"Int\",\"value\":0}},{\"type\":\"Local\",\"name\":\"s\",\"expr\":{\"type\":\"Int\",\"value\":0}},{\"type\":\"Loop\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":3}},\"body\":[{\"type\":\"Local\",\"name\":\"s\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Var\",\"name\":\"s\"},\"rhs\":{\"type\":\"Int\",\"value\":1}}},{\"type\":\"Local\",\"name\":\"i\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":1}}}]},{\"type\":\"Return\",\"expr\":{\"type\":\"Var\",\"name\":\"s\"}}]}";
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 1 }
|
||||
print("" + out)
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
if ! echo "$out" | sed -n '/^{/,$p' | jq -e . > "$tmp_json"; then
|
||||
echo "[FAIL] mirbuilder_internal_loop_core_exec_canary_vm (no MIR JSON)" >&2
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set +e
|
||||
HAKO_VERIFY_PRIMARY=hakovm verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
|
||||
if [ "$rc" -eq 3 ]; then
|
||||
echo "[PASS] mirbuilder_internal_loop_core_exec_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_loop_core_exec_canary_vm (rc=$rc, expect 3)" >&2; exit 1
|
||||
@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Loop count param: i=2; loop(i<7){ i=i+2 }; return i; → rc=6
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_emit_loop_count_param_$$.hako"
|
||||
tmp_json="/tmp/mirbuilder_emit_loop_count_param_$$.json"
|
||||
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"i\",\"expr\":{\"type\":\"Int\",\"value\":2}}," +
|
||||
"{\"type\":\"Loop\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":7}},\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"i\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":2}}}" +
|
||||
"]}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Var\",\"name\":\"i\"}}" +
|
||||
"]}";
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 1 }
|
||||
print("" + out)
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
if ! echo "$out" | sed -n '/^{/,$p' | jq -e . > "$tmp_json"; then
|
||||
echo "[FAIL] mirbuilder_internal_loop_count_param_core_exec_canary_vm (no MIR JSON)" >&2
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set +e
|
||||
HAKO_VERIFY_PRIMARY=hakovm verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
|
||||
if [ "$rc" -eq 6 ]; then
|
||||
echo "[PASS] mirbuilder_internal_loop_count_param_core_exec_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_loop_count_param_core_exec_canary_vm (rc=$rc, expect 6)" >&2; exit 1
|
||||
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Loop(sum with break/continue) internal → Core exec canary — expect 0+1+3+4 = 8
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_emit_loop_bc_$$.hako"
|
||||
tmp_json="/tmp/mirbuilder_emit_loop_bc_$$.json"
|
||||
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
static box Main { method main(args) {
|
||||
// Program: i=0; s=0; loop(true){ s=s+1; if(i==4) break; if(i==2) continue; i=i+1 } return s
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"i\",\"expr\":{\"type\":\"Int\",\"value\":0}}," +
|
||||
"{\"type\":\"Local\",\"name\":\"s\",\"expr\":{\"type\":\"Int\",\"value\":0}}," +
|
||||
"{\"type\":\"Loop\",\"cond\":{\"type\":\"Compare\",\"op\":\"<\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":5}},\"body\":[" +
|
||||
"{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"==\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":4}},\"then\":[{\"type\":\"Break\"}]}," +
|
||||
"{\"type\":\"If\",\"cond\":{\"type\":\"Compare\",\"op\":\"==\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":2}},\"then\":[{\"type\":\"Continue\"}]}," +
|
||||
"{\"type\":\"Expr\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Var\",\"name\":\"s\"},\"rhs\":{\"type\":\"Var\",\"name\":\"i\"}}}," +
|
||||
"{\"type\":\"Expr\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Var\",\"name\":\"i\"},\"rhs\":{\"type\":\"Int\",\"value\":1}}}" +
|
||||
"]}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Var\",\"name\":\"s\"}}" +
|
||||
"]}";
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 1 }
|
||||
print("" + out)
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
if ! echo "$out" | sed -n '/^{/,$p' | jq -e . > "$tmp_json"; then
|
||||
echo "[FAIL] mirbuilder_internal_loop_sum_bc_core_exec_canary_vm (no MIR JSON)" >&2
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set +e
|
||||
HAKO_VERIFY_PRIMARY=hakovm verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
|
||||
if [ "$rc" -eq 8 ]; then
|
||||
echo "[PASS] mirbuilder_internal_loop_sum_bc_core_exec_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "[FAIL] mirbuilder_internal_loop_sum_bc_core_exec_canary_vm (rc=$rc, expect 8)" >&2; exit 1
|
||||
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# Return(Method set with String value) → const string + mir_call structure
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_method_set_str_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local m = new MapBox(); return m.set(1, "x");
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"m\",\"expr\":{\"type\":\"New\",\"class\":\"MapBox\",\"args\":[]}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Method\",\"recv\":{\"type\":\"Var\",\"name\":\"m\"},\"method\":\"set\",\"args\":[{\"type\":\"Int\",\"value\":1},{\"type\":\"String\",\"value\":\"x\"}]}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"type\":\"string\"") >= 0 && s.indexOf("\"op\":\"mir_call\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_method_set_string_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_method_set_string_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Return(Binary Var/Int) with Local Int → const+const+binop+ret
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_binop_varint_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local x=5; return x + 3;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Local\",\"name\":\"x\",\"expr\":{\"type\":\"Int\",\"value\":5}},{\"type\":\"Return\",\"expr\":{\"type\":\"Binary\",\"op\":\"+\",\"lhs\":{\"type\":\"Var\",\"name\":\"x\"},\"rhs\":{\"type\":\"Int\",\"value\":3}}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"binop\"") >= 0 && s.indexOf("\"operation\":\"+\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_binop_varint_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_binop_varint_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Return(Bool) → const 0/1 + ret canary
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_bool_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Bool\",\"value\":true}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"type\":\"i64\"") >= 0 && s.indexOf("\"value\":1") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_bool_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_bool_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Return(Float) → const f64 + ret (structure check only)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_float_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Float\",\"value\":3.14}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"type\":\"f64\"") >= 0 && s.indexOf("\"value\":3.14") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_float_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_float_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Return(Logical &&/|| with Bool literal lhs/rhs) → branch+ret structure
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_logical_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Return(true && false)
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Logical\",\"op\":\"&&\",\"lhs\":{\"type\":\"Bool\",\"value\":true},\"rhs\":{\"type\":\"Bool\",\"value\":false}}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"branch\"") >= 0 && s.indexOf("\"op\":\"ret\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_logical_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_logical_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# Return(Logical Var || Bool) with prior Local Bool → branch+ret structure
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_logical_var_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local b=true; return b || false;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"b\",\"expr\":{\"type\":\"Bool\",\"value\":true}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Logical\",\"op\":\"||\",\"lhs\":{\"type\":\"Var\",\"name\":\"b\"},\"rhs\":{\"type\":\"Bool\",\"value\":false}}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"branch\"") >= 0 && s.indexOf("\"op\":\"ret\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_logical_var_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_logical_var_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Return(Logical Var && Var) with prior Local Bool → branch+ret
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
gen_case() {
|
||||
local lhs="$1"; local rhs="$2"; local op="$3"
|
||||
local tmp_hako="/tmp/mirbuilder_return_logical_varvar_${op}_$$.hako"
|
||||
cat > "$tmp_hako" <<HAKO
|
||||
static box Main { method main(args) {
|
||||
// Local b1=${lhs}; Local b2=${rhs}; return b1 ${op} b2;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"b1\",\"expr\":{\"type\":\"Bool\",\"value\":${lhs}}}," +
|
||||
"{\"type\":\"Local\",\"name\":\"b2\",\"expr\":{\"type\":\"Bool\",\"value\":${rhs}}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Logical\",\"op\":\"${op}\",\"lhs\":{\"type\":\"Var\",\"name\":\"b1\"},\"rhs\":{\"type\":\"Var\",\"name\":\"b2\"}}}]}";
|
||||
// Use delegate extern to avoid builder toggles/env.get
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
print(s)
|
||||
if s.indexOf("\"op\":\"branch\"") >= 0 && s.indexOf("\"op\":\"ret\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
set +e
|
||||
# Preinclude to avoid VM include limitation
|
||||
pre="/tmp/pre_$$.hako"; "$ROOT/tools/dev/hako_preinclude.sh" "$tmp_hako" "$pre" >/dev/null
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 NYASH_PREINCLUDE=0 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||
run_nyash_vm "$pre" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$pre" || true
|
||||
# Success if output contains branch+ret markers (compact or spaced JSON)
|
||||
if echo "$out" | grep -qiE 'Invalid instruction|call unresolved|panic|Undefined variable'; then return 0; fi
|
||||
local has_branch=0; local has_ret=0
|
||||
if echo "$out" | grep -q '"op":"branch"' || echo "$out" | grep -q '"op": "branch"'; then has_branch=1; fi
|
||||
if echo "$out" | grep -q '"op":"ret"' || echo "$out" | grep -q '"op": "ret"'; then has_ret=1; fi
|
||||
if [ $has_branch -eq 1 ] && [ $has_ret -eq 1 ]; then return 1; fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test case 1: true && false → should emit branch+ret
|
||||
gen_case true false "&&"; rc1=$?
|
||||
if [ "$rc1" -ne 1 ]; then
|
||||
echo "[FAIL] mirbuilder_internal_return_logical_varvar_canary_vm (case 1: rc=$rc1)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test case 2: false || true → should emit branch+ret
|
||||
gen_case false true "||"; rc2=$?
|
||||
if [ "$rc2" -ne 1 ]; then
|
||||
echo "[FAIL] mirbuilder_internal_return_logical_varvar_canary_vm (case 2: rc=$rc2)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[PASS] mirbuilder_internal_return_logical_varvar_canary_vm"
|
||||
exit 0
|
||||
@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Core exec: Return(b1 && b2) / Return(b1 || b2) with Var/Var → rc check
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
run_case() {
|
||||
local lhs_tf="$1" # true|false
|
||||
local rhs_tf="$2" # true|false
|
||||
local op="$3" # && or ||
|
||||
local expect_rc="$4"
|
||||
local tmp_hako="/tmp/mirbuilder_logical_core_${op}_$$.hako"
|
||||
local tmp_json="/tmp/mirbuilder_logical_core_${op}_$$.json"
|
||||
cat > "$tmp_hako" <<HAKO
|
||||
static box Main { method main(args) {
|
||||
// Local b1=${lhs_tf}; Local b2=${rhs_tf}; return b1 ${op} b2;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"b1\",\"expr\":{\"type\":\"Bool\",\"value\":${lhs_tf}}}," +
|
||||
"{\"type\":\"Local\",\"name\":\"b2\",\"expr\":{\"type\":\"Bool\",\"value\":${rhs_tf}}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Logical\",\"op\":\"${op}\",\"lhs\":{\"type\":\"Var\",\"name\":\"b1\"},\"rhs\":{\"type\":\"Var\",\"name\":\"b2\"}}}]}";
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 1 }
|
||||
print("" + out)
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
set +e
|
||||
out="$(NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
if ! echo "$out" | sed -n '/^{/,$p' | jq -e . > "$tmp_json"; then
|
||||
echo "[FAIL] logical_varvar_core (emit)" >&2; rm -f "$tmp_hako" "$tmp_json" || true; return 1
|
||||
fi
|
||||
set +e
|
||||
HAKO_VERIFY_PRIMARY=hakovm verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||
rc2=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
if [ "$rc2" -ne "$expect_rc" ]; then
|
||||
echo "[FAIL] logical_varvar_core ${lhs_tf} ${op} ${rhs_tf}: rc=$rc2 expect=$expect_rc" >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
run_case true false "&&" 0 || exit 1
|
||||
run_case true false "||" 1 || exit 1
|
||||
run_case false true "&&" 0 || exit 1
|
||||
run_case false true "||" 1 || exit 1
|
||||
run_case false false "||" 0 || exit 1
|
||||
echo "[PASS] mirbuilder_internal_return_logical_varvar_core_exec_canary_vm"
|
||||
exit 0
|
||||
@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# mirbuilder_internal_return_logical_varvar_lower_canary_vm.sh
|
||||
# Purpose: Verify LowerReturnLogicalBox.try_lower directly (bypass MirBuilderBox integration)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_lower_logical_varvar_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/internal/lower_return_logical_box.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local b1=true; Local b2=false; return b1 && b2;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"b1\",\"expr\":{\"type\":\"Bool\",\"value\":true}}," +
|
||||
"{\"type\":\"Local\",\"name\":\"b2\",\"expr\":{\"type\":\"Bool\",\"value\":false}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Logical\",\"op\":\"&&\",\"lhs\":{\"type\":\"Var\",\"name\":\"b1\"},\"rhs\":{\"type\":\"Var\",\"name\":\"b2\"}}}]}";
|
||||
local out = LowerReturnLogicalBox.try_lower(j);
|
||||
if out == null { return 0 }
|
||||
// Structural check using Map API
|
||||
local fns = out.get("functions"); local ver = out.get("version")
|
||||
if fns == null || ver == null { return 0 }
|
||||
return 1
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
pre="/tmp/pre_$$.hako"; "$ROOT/tools/dev/hako_preinclude.sh" "$tmp_hako" "$pre" >/dev/null
|
||||
out="$(NYASH_PREINCLUDE=0 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 NYASH_NY_COMPILER_TIMEOUT_MS=20000 \
|
||||
run_nyash_vm "$pre" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$pre" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
echo "[PASS] mirbuilder_internal_return_logical_varvar_lower_canary_vm"
|
||||
exit 0
|
||||
fi
|
||||
echo "$out" >&2
|
||||
echo "[FAIL] mirbuilder_internal_return_logical_varvar_lower_canary_vm (rc=$rc)" >&2
|
||||
exit 1
|
||||
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# Return(Method recv Var, method=size/get/set/push) → mir_call structure check
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_method_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Local a = new ArrayBox(); return a.size(); (shape only, not executed here)
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"a\",\"expr\":{\"type\":\"New\",\"class\":\"ArrayBox\",\"args\":[]}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Method\",\"recv\":{\"type\":\"Var\",\"name\":\"a\"},\"method\":\"size\",\"args\":[]}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"mir_call\"") >= 0 && s.indexOf("\"method\":\"size\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_method_array_map_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_method_array_map_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Return(String) → const string + ret (structure check only)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_string_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"String\",\"value\":\"hello\"}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"type\":\"string\"") >= 0 && s.indexOf("\"op\":\"ret\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_string_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_string_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Return Var(Local Int) → const+ret canary
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_return_var_local_$$.hako"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
include "lang/src/mir/builder/MirBuilderBox.hako"
|
||||
static box Main { method main(args) {
|
||||
// Program: local x=7; return x;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Local\",\"name\":\"x\",\"expr\":{\"type\":\"Int\",\"value\":7}},{\"type\":\"Return\",\"expr\":{\"type\":\"Var\",\"name\":\"x\"}}]}";
|
||||
local out = MirBuilderBox.emit_from_program_json_v0(j, null);
|
||||
if out == null { return 0 }
|
||||
local s = "" + out
|
||||
if s.indexOf("\"op\":\"const\"") >= 0 && s.indexOf("\"op\":\"ret\"") >= 0 { return 1 }
|
||||
return 0
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_INTERNAL=1 "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] mirbuilder_internal_return_var_local_canary_vm"; exit 0; fi
|
||||
echo "[FAIL] mirbuilder_internal_return_var_local_canary_vm (rc=$rc)" >&2; exit 1
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
# Verify Logical Var/Var via Delegate + Core direct (structure lock)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp_hako="/tmp/mirbuilder_varvar_delegate_core_$$.hako"
|
||||
tmp_json="/tmp/mirbuilder_varvar_delegate_core_$$.json"
|
||||
cat > "$tmp_hako" <<'HAKO'
|
||||
static box Main { method main(args) {
|
||||
// Local b1=true; Local b2=false; return b1 && b2;
|
||||
local j = "{\"version\":0,\"kind\":\"Program\",\"body\":[" +
|
||||
"{\"type\":\"Local\",\"name\":\"b1\",\"expr\":{\"type\":\"Bool\",\"value\":true}}," +
|
||||
"{\"type\":\"Local\",\"name\":\"b2\",\"expr\":{\"type\":\"Bool\",\"value\":false}}," +
|
||||
"{\"type\":\"Return\",\"expr\":{\"type\":\"Logical\",\"op\":\"&&\",\"lhs\":{\"type\":\"Var\",\"name\":\"b1\"},\"rhs\":{\"type\":\"Var\",\"name\":\"b2\"}}}]}";
|
||||
// Call provider via extern directly (avoid MirBuilderBox toggles)
|
||||
local arr = new ArrayBox(); arr.push(j)
|
||||
local out = hostbridge.extern_invoke("env.mirbuilder", "emit", arr)
|
||||
if out == null { return 0 }
|
||||
print("" + out)
|
||||
return 1
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(HAKO_MIR_BUILDER_DELEGATE=1 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 run_nyash_vm "$tmp_hako" 2>&1)"; rc=$?
|
||||
set -e
|
||||
if [ "$rc" -ne 1 ]; then echo "$out" >&2; echo "[FAIL] varvar_delegate_core (emit)" >&2; rm -f "$tmp_hako" "$tmp_json"; exit 1; fi
|
||||
# Be tolerant of pretty-printed JSON (multi-line). Validate and capture all.
|
||||
echo "$out" | jq -e . > "$tmp_json" || { echo "$out" >&2; echo "[FAIL] varvar_delegate_core (no MIR)" >&2; rm -f "$tmp_hako" "$tmp_json"; exit 1; }
|
||||
|
||||
set +e
|
||||
NYASH_GATE_C_CORE=1 HAKO_GATE_C_CORE=1 "$NYASH_BIN" --json-file "$tmp_json" >/dev/null 2>&1
|
||||
rc2=$?
|
||||
set -e
|
||||
rm -f "$tmp_hako" "$tmp_json" || true
|
||||
if [ "$rc2" -eq 0 ]; then
|
||||
echo "[FAIL] varvar_delegate_core rc=0 (expected non-zero for && with true,false)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[PASS] mirbuilder_varvar_delegate_core_canary_vm"
|
||||
exit 0
|
||||
@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# string_vm_api_canary_vm.sh — Verify VM String API parity with Rust
|
||||
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../.." && pwd)"; fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2
|
||||
|
||||
tmp="/tmp/string_vm_api_canary_$$.hako"
|
||||
cat > "$tmp" <<'HAKO'
|
||||
static box Main { method main(args) {
|
||||
local s = "hello"
|
||||
// substring(start,end)
|
||||
local a = s.substring(1,4) // "ell"
|
||||
if (""+a) != "ell" { return 0 }
|
||||
|
||||
// indexOf(search)
|
||||
local p1 = s.indexOf("l")
|
||||
if (""+p1) != "2" { return 0 }
|
||||
local p2 = s.indexOf("z")
|
||||
if (""+p2) != "-1" { return 0 }
|
||||
|
||||
// charAt via substring(i,i+1)
|
||||
local c = s.substring(0,1)
|
||||
if (""+c) != "h" { return 0 }
|
||||
return 1
|
||||
} }
|
||||
HAKO
|
||||
|
||||
set +e
|
||||
out="$(NYASH_PREINCLUDE=0 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 run_nyash_vm "$tmp" 2>&1)"; rc=$?
|
||||
set -e
|
||||
rm -f "$tmp" || true
|
||||
|
||||
if [ "$rc" -eq 1 ]; then echo "[PASS] string_vm_api_canary_vm"; exit 0; fi
|
||||
echo "$out" >&2
|
||||
echo "[FAIL] string_vm_api_canary_vm (rc=$rc)" >&2; exit 1
|
||||
Reference in New Issue
Block a user