trace: add execution route visibility + debug passthrough; phase2170 canaries; docs
- Add HAKO_TRACE_EXECUTION to trace executor route - Rust hv1_inline: stderr [trace] executor: hv1_inline (rust) - Hakovm dispatcher: stdout [trace] executor: hakovm (hako) - test_runner: trace lines for hv1_inline/core/hakovm routes - Add HAKO_VERIFY_SHOW_LOGS and HAKO_DEBUG=1 (enables both) - verify_v1_inline_file() log passthrough with numeric rc extraction - test_runner exports via HAKO_DEBUG - Canary expansion under phase2170 (state spec) - Array: push×5/10 → size, len/length alias, per‑recv/global, flow across blocks - Map: set dup-key non-increment, value_state get/has - run_all.sh: unify, remove SKIPs; all PASS - Docs - ENV_VARS.md: add Debug/Tracing toggles and examples - PLAN.md/CURRENT_TASK.md: mark 21.7 green, add Quickstart lines All changes gated by env vars; default behavior unchanged.
This commit is contained in:
@ -24,7 +24,13 @@ Update (today)
|
|||||||
- CLI: `--rules`/`--skip-rules` を追加し、ルール単体/組合せ検証を高速化。`--no-ast` 既定化。
|
- CLI: `--rules`/`--skip-rules` を追加し、ルール単体/組合せ検証を高速化。`--no-ast` 既定化。
|
||||||
- Runtime: `NYASH_SCRIPT_ARGS_HEX_JSON` を導入(HEX経由で改行・特殊文字を安全搬送)。
|
- Runtime: `NYASH_SCRIPT_ARGS_HEX_JSON` を導入(HEX経由で改行・特殊文字を安全搬送)。
|
||||||
- File I/O: FileBox provider 設計(SSOT + 薄いラッパ + 選択ポリシー)を文書化(docs/development/runtime/FILEBOX_PROVIDER.md)。
|
- File I/O: FileBox provider 設計(SSOT + 薄いラッパ + 選択ポリシー)を文書化(docs/development/runtime/FILEBOX_PROVIDER.md)。
|
||||||
- ENV: `NYASH_FILEBOX_MODE=auto|core-ro|plugin-only` を追加(ENV_VARS.md)。Analyzer/CI は core‑ro 相当で運用。
|
- ENV: `NYASH_FILEBOX_MODE=auto|core-ro|plugin-only` を追加(ENV_VARS.md)。Analyzer/CI は core‑ro 相当で運用。
|
||||||
|
|
||||||
|
Roadmap links (21.5→21.7)
|
||||||
|
- 21.5 — Unicode & Provider Polish(現行計画): docs/private/roadmap/phases/phase-21.5/PLAN.md
|
||||||
|
- 21.6 — Hako MIR Builder MVP & Registry(opt‑in): docs/private/roadmap/phases/phase-21.6/PLAN.md
|
||||||
|
- 21.7 — VM mir_call state & Primary Flip(guarded): docs/private/roadmap/phases/phase-21.7/PLAN.md
|
||||||
|
- 21.8 — Hako Check: Refactor & QuickFix(MVP): docs/private/roadmap/phases/phase-21.8/PLAN.md
|
||||||
|
|
||||||
Footing update (A→B→C, today) — 仕様と入口をまず固定
|
Footing update (A→B→C, today) — 仕様と入口をまず固定
|
||||||
- A. 仕様/インターフェース(hako_check 診断 I/F と出力規約)
|
- A. 仕様/インターフェース(hako_check 診断 I/F と出力規約)
|
||||||
@ -50,6 +56,15 @@ Status (HC rules)
|
|||||||
- 10/11 pass, 1 skipped: HC011/012/013/014/015/016/018/021/022/031 = PASS、HC017 = SKIP(UTF‑8 byte-level 支援待ち)
|
- 10/11 pass, 1 skipped: HC011/012/013/014/015/016/018/021/022/031 = PASS、HC017 = SKIP(UTF‑8 byte-level 支援待ち)
|
||||||
- CLI: `--rules`/`--skip-rules` で単体/組合せ検証を高速化、JSON_ONLY で純出力。
|
- CLI: `--rules`/`--skip-rules` で単体/組合せ検証を高速化、JSON_ONLY で純出力。
|
||||||
|
|
||||||
|
Phase 21.7 — VM mir_call state & Primary Flip(green)
|
||||||
|
- T0(最小・安定化)
|
||||||
|
- length state(per‑receiver可): size/len/push, Map.set(new-key) 実装済み(Hakorune VM + Rust hv1_inline 両ルート)。
|
||||||
|
- フラグ: HAKO_VM_MIRCALL_SIZESTATE=1(既定OFF), HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1(任意)。
|
||||||
|
- T1(検証導線)
|
||||||
|
- Primary 切替(verify_mir_rc)配線済み(既定=hakovm)。
|
||||||
|
- Canary 拡充(phase2170/*): push×N、alias、per‑recv差分、flow跨ぎ。Map dup-key 非増分・value_state set→get も緑化。
|
||||||
|
- 実行: bash tools/smokes/v2/profiles/quick/core/phase2170/run_all.sh → 全PASS。
|
||||||
|
|
||||||
Remaining (21.4)
|
Remaining (21.4)
|
||||||
1) Hako Parser MVP 実装(tokenizer/parser_core/ast_emit/cli)【微修整】
|
1) Hako Parser MVP 実装(tokenizer/parser_core/ast_emit/cli)【微修整】
|
||||||
2) Analyzer AST 入力の安定化(必要時のみ AST を使用)
|
2) Analyzer AST 入力の安定化(必要時のみ AST を使用)
|
||||||
@ -60,6 +75,43 @@ Remaining (21.4)
|
|||||||
7) DOT エッジ ON(calls→edges, cluster by box)
|
7) DOT エッジ ON(calls→edges, cluster by box)
|
||||||
8) FileBox provider 実装(リング0/1/選択ポリシー)と最小スモーク追加(core‑ro/auto/plugin-only)【COMPLETE】
|
8) FileBox provider 実装(リング0/1/選択ポリシー)と最小スモーク追加(core‑ro/auto/plugin-only)【COMPLETE】
|
||||||
|
|
||||||
|
Remaining (21.4 → 21.5 handoff, concise)
|
||||||
|
- A. Parser/Analyzer まわり
|
||||||
|
- [ ] Hako Parser MVP 微修整(tokenizer/parser_core/ast_emit/cli)
|
||||||
|
- [ ] Analyzer AST 入力安定化(必要時のみ AST、_needs_ast/_needs_ir 調整)
|
||||||
|
- [ ] 代表ルール AST 化(HC001/002/003/010/011/020)
|
||||||
|
- [ ] json-lsp 追加ケース(OK/NG/edge)
|
||||||
|
- [ ] テスト駆動(tests/<rule>/ を3ルール分)
|
||||||
|
- [ ] 限定 --fix(HC002/HC003/HC500)
|
||||||
|
- B. DOT 改善
|
||||||
|
- [ ] calls→edges 検証と代表追加ケース(cluster は既存を維持)
|
||||||
|
- C. Ring1/Plugins polish(未了のみ)
|
||||||
|
- [ ] Using/modules SSOT 確認(modules優先→相対推定→not found=警告/verbose詳細)
|
||||||
|
- [x] Capability introspection(FileBox provider caps(read/write) を Shim から参照→未サポート操作は Fail‑Fast)
|
||||||
|
- 実装: `src/runtime/provider_lock.rs:get_filebox_caps()` を追加、`src/boxes/file/mod.rs` で caps.write を参照し明示エラー
|
||||||
|
- [ ] Provider modes スモークの継続(auto/core‑ro/plugin‑only + failure injection)
|
||||||
|
|
||||||
|
Phase 21.6 — Hako MIR Builder MVP & Registry(COMPLETE)
|
||||||
|
- [x] Registry 経路(opt‑in)を MirBuilderBox に実装(既定OFF)
|
||||||
|
- [x] registry 専用 canary 一式を PASS(Return/Int, If/Compare[Int/Int|Var/Int|Var/Var], Return(Binary Int/Int), Return(Logical OR/AND Var/Var))
|
||||||
|
- [x] PatternUtilBox の find_local_* を“次の Local まで”で安全化
|
||||||
|
- [x] MirBuilderBox チェーン fallback を厳格化(Binary/IntInt, Return/Int)
|
||||||
|
- Return(BinOp Var/Int) は registry が先に一致(rc=42)
|
||||||
|
- Logical AND(Var/Var) は JSON v0 bool=0/1 に合わせて一致(rc=0)
|
||||||
|
|
||||||
|
受け入れ(21.6)
|
||||||
|
- [x] phase2111 の registry canary 全緑
|
||||||
|
- [x] 既定OFF・互換維持(トグルON時のみ影響)
|
||||||
|
|
||||||
|
Phase 21.7 — VM mir_call state & Primary Flip(kickoff)
|
||||||
|
- 目的: VM に最小 state(Array/Map length)を導入し、primary 切替(core|hakovm)で Core と同値検証(既定OFF)。
|
||||||
|
- スコープ(T0→T1)
|
||||||
|
- T0: state map(受信者ID合成・length管理)、size/len 実値、push で length++、get/set は安定タグ維持
|
||||||
|
- T1: HAKO_VERIFY_PRIMARY=core|hakovm 配線と canary(push→size 増分、primary=hakovm でも rc一致)
|
||||||
|
- トグル/既定
|
||||||
|
- HAKO_VM_MIRCALL_STUB=1(既定ON: 非対象は0返し+タグ)
|
||||||
|
- HAKO_VERIFY_PRIMARY=core(既定)
|
||||||
|
|
||||||
- Checklist — Ring1/Plugins polish(このフェーズで完了)
|
- Checklist — Ring1/Plugins polish(このフェーズで完了)
|
||||||
- [x] Env 統一(二重解消): `NYASH_FILEBOX_MODE`/`NYASH_DISABLE_PLUGINS` に一本化し、
|
- [x] Env 統一(二重解消): `NYASH_FILEBOX_MODE`/`NYASH_DISABLE_PLUGINS` に一本化し、
|
||||||
旧 `NYASH_USE_PLUGIN_BUILTINS` / `NYASH_PLUGIN_OVERRIDE_TYPES` は非推奨(互換ブリッジは維持)
|
旧 `NYASH_USE_PLUGIN_BUILTINS` / `NYASH_PLUGIN_OVERRIDE_TYPES` は非推奨(互換ブリッジは維持)
|
||||||
|
|||||||
@ -51,6 +51,15 @@ NYASH_DISABLE_PLUGINS = "1"
|
|||||||
|
|
||||||
メモ: 改行・特殊文字を含む長文を `--source-file <path> <text>` で渡す場合も HEX 経路で安全に輸送される。
|
メモ: 改行・特殊文字を含む長文を `--source-file <path> <text>` で渡す場合も HEX 経路で安全に輸送される。
|
||||||
|
|
||||||
|
## Debug/Tracing(開発用の軽量トグル)
|
||||||
|
- HAKO_TRACE_EXECUTION: 実行経路の可視化("1" で有効)
|
||||||
|
- 例: `[trace] executor: hv1_inline (rust)` / `[trace] executor: hakovm (hako)` / `[trace] executor: core (rust)`
|
||||||
|
- 出力先: 原則 stderr(Hakovm は stdout)。テストランナーは numeric rc 抽出時に非数値行を無視します。
|
||||||
|
- HAKO_VERIFY_SHOW_LOGS: verify_v1_inline_file() のログ透過("1" で有効)
|
||||||
|
- hv1 inline 実行の全出力を stderr に表示し、数値 rc は別途抽出します。
|
||||||
|
- HAKO_DEBUG: 開発向け一括トグル("1" で `HAKO_TRACE_EXECUTION=1` と `HAKO_VERIFY_SHOW_LOGS=1` を自動有効化)
|
||||||
|
- 実装箇所: tools/smokes/v2/lib/test_runner.sh のデバッグ便宜機能
|
||||||
|
|
||||||
## FileBox Provider(コア/プラグイン切替)
|
## FileBox Provider(コア/プラグイン切替)
|
||||||
- NYASH_FILEBOX_MODE: `auto|core-ro|plugin-only`
|
- NYASH_FILEBOX_MODE: `auto|core-ro|plugin-only`
|
||||||
- auto(既定): プラグインがあれば PluginFileIo、無ければ CoreRoFileIo
|
- auto(既定): プラグインがあれば PluginFileIo、無ければ CoreRoFileIo
|
||||||
|
|||||||
@ -39,28 +39,46 @@ static box MirBuilderBox {
|
|||||||
if internal != null && ("" + internal) == "1" {
|
if internal != null && ("" + internal) == "1" {
|
||||||
// Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1,
|
// Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1,
|
||||||
// iterate PatternRegistryBox.candidates() and dispatch by name.
|
// iterate PatternRegistryBox.candidates() and dispatch by name.
|
||||||
// NOTE: include not supported in VM, registry disabled for VM execution
|
// NOTE: using/alias は prelude で解決される(位置に依存しない)。
|
||||||
local use_reg = env.get("HAKO_MIR_BUILDER_REGISTRY")
|
local use_reg = env.get("HAKO_MIR_BUILDER_REGISTRY")
|
||||||
if use_reg != null && ("" + use_reg) == "1" && 0 == 1 {
|
if use_reg != null && ("" + use_reg) == "1" {
|
||||||
|
// Registry list
|
||||||
|
using "hako.mir.builder.pattern_registry" as PatternRegistryBox
|
||||||
|
// Lowers needed by registry dispatch(using は prelude で集約される)
|
||||||
|
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_return_method_array_map" as LowerReturnMethodArrayMapBox
|
||||||
|
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_return_bool" as LowerReturnBoolBox
|
||||||
|
using "hako.mir.builder.internal.lower_return_logical" as LowerReturnLogicalBox
|
||||||
|
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 names = PatternRegistryBox.candidates()
|
local names = PatternRegistryBox.candidates()
|
||||||
local i = 0; local n = names.length()
|
local i = 0; local n = names.length()
|
||||||
loop(i < n) {
|
loop(i < n) {
|
||||||
local nm = "" + names.get(i)
|
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.intint" { local out = LowerIfCompareBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "if.compare.fold.binints" { local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { return out } }
|
if nm == "if.compare.fold.binints" { local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "if.compare.fold.varint" { local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { return out } }
|
if nm == "if.compare.fold.varint" { local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "if.compare.varint" { local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { return out } }
|
if nm == "if.compare.varint" { local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "if.compare.varvar" { local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { return out } }
|
if nm == "if.compare.varvar" { local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.method.arraymap" { local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { return out } }
|
if nm == "return.method.arraymap" { local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.var.local" { local out = LowerReturnVarLocalBox.try_lower(s); if out != null { return out } }
|
if nm == "return.var.local" { local out = LowerReturnVarLocalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.string" { local out = LowerReturnStringBox.try_lower(s); if out != null { return out } }
|
if nm == "return.string" { local out = LowerReturnStringBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.float" { local out = LowerReturnFloatBox.try_lower(s); if out != null { return out } }
|
if nm == "return.float" { local out = LowerReturnFloatBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.bool" { local out = LowerReturnBoolBox.try_lower(s); if out != null { return out } }
|
if nm == "return.bool" { local out = LowerReturnBoolBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.logical" { local out = LowerReturnLogicalBox.try_lower(s); if out != null { return out } }
|
if nm == "return.logical" { local out = LowerReturnLogicalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.binop.varint" { local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { return out } }
|
if nm == "return.binop.varint" { local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.binop.varvar" { local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { return out } }
|
if nm == "return.binop.varvar" { local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.binop.intint" { local out = LowerReturnBinOpBox.try_lower(s); if out != null { return out } }
|
if nm == "return.binop.intint" { local out = LowerReturnBinOpBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
if nm == "return.int" { local out = LowerReturnIntBox.try_lower(s); if out != null { return out } }
|
if nm == "return.int" { local out = LowerReturnIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return out } }
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
// Fall-through to chain if none matched
|
// Fall-through to chain if none matched
|
||||||
@ -88,7 +106,7 @@ static box MirBuilderBox {
|
|||||||
using "hako.mir.builder.internal.lower_return_var_local" as LowerReturnVarLocalBox
|
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_string" as LowerReturnStringBox
|
||||||
using "hako.mir.builder.internal.lower_return_float" as LowerReturnFloatBox
|
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_logical" as LowerReturnLogicalBox
|
||||||
using "hako.mir.builder.internal.lower_return_method_array_map" as LowerReturnMethodArrayMapBox
|
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_bool" as LowerReturnBoolBox
|
||||||
using "hako.mir.builder.internal.lower_return_binop_varint" as LowerReturnBinOpVarIntBox
|
using "hako.mir.builder.internal.lower_return_binop_varint" as LowerReturnBinOpVarIntBox
|
||||||
@ -314,22 +332,51 @@ static box MirBuilderBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Case (Int): Return(Int N)
|
// Case (Int): Return(Int N) — ensure expr.type is Int (not Binary/Logical/etc)
|
||||||
local k_int = s.indexOf("\"type\":\"Int\"", k_ret)
|
local k_expr = s.indexOf("\"expr\":{", k_ret)
|
||||||
if k_int >= 0 {
|
if k_expr >= 0 {
|
||||||
local k_val = s.indexOf("\"value\":", k_int)
|
// Check direct type after "expr":{
|
||||||
if k_val >= 0 {
|
local k_type_start = k_expr + 8
|
||||||
local i = k_val + 8
|
local k_type = s.indexOf("\"type\":", k_type_start)
|
||||||
|
if k_type >= 0 && k_type < k_type_start + 20 {
|
||||||
|
// Extract the type value
|
||||||
|
local k_type_val = k_type + 7
|
||||||
local n = s.length()
|
local n = s.length()
|
||||||
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
loop(k_type_val < n) { if s.substring(k_type_val, k_type_val+1) != " " && s.substring(k_type_val, k_type_val+1) != "\"" { break } k_type_val = k_type_val + 1 }
|
||||||
local j = i
|
// Check if it's "Int" (not "Binary", "Logical", "Var", etc)
|
||||||
if j < n && s.substring(j,j+1) == "-" { j = j + 1 }
|
local is_int_type = 0
|
||||||
local had = 0
|
if k_type_val + 3 <= n {
|
||||||
loop(j < n) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
|
if s.substring(k_type_val, k_type_val+3) == "Int" {
|
||||||
if had == 1 {
|
// Verify it's followed by quote (not "Integer" or other extension)
|
||||||
local num = s.substring(i, j)
|
if k_type_val + 3 < n && s.substring(k_type_val+3, k_type_val+4) == "\"" {
|
||||||
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + num + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
|
is_int_type = 1
|
||||||
return mir
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_int_type == 1 {
|
||||||
|
local debug_on = 0
|
||||||
|
if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" { debug_on = 1 }
|
||||||
|
local k_expr_int = s.indexOf("\"type\":\"Int\"", k_expr)
|
||||||
|
local k_val = s.indexOf("\"value\":", k_expr_int)
|
||||||
|
if k_val >= 0 {
|
||||||
|
local i = k_val + 8
|
||||||
|
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)
|
||||||
|
if debug_on == 1 { print("[mirbuilder/fallback:Return(Int) val=" + num + "]") }
|
||||||
|
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + num + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
|
||||||
|
return mir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// expr.type is not Int (e.g. Binary) - don't match this fallback
|
||||||
|
if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" {
|
||||||
|
print("[mirbuilder/fallback:Return(Int) skip - expr.type not Int]")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,17 +15,20 @@ static box LowerReturnBinOpBox {
|
|||||||
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
||||||
if op == null { return null }
|
if op == null { return null }
|
||||||
if !(op == "+" || op == "-" || op == "*" || op == "/") { return null }
|
if !(op == "+" || op == "-" || op == "*" || op == "/") { return null }
|
||||||
// lhs int
|
// lhs/rhs object starts
|
||||||
local klhs = s.indexOf("\"lhs\":{", k_bin)
|
local klhs = s.indexOf("\"lhs\":{", k_bin)
|
||||||
|
local krhs = s.indexOf("\"rhs\":{", k_bin)
|
||||||
if klhs < 0 { return null }
|
if klhs < 0 { return null }
|
||||||
|
// strictly ensure lhs node is Int by bounding search before rhs start
|
||||||
local ti = s.indexOf("\"type\":\"Int\"", klhs)
|
local ti = s.indexOf("\"type\":\"Int\"", klhs)
|
||||||
if ti < 0 { return null }
|
if ti < 0 { return null }
|
||||||
|
if krhs >= 0 && ti >= krhs { return null }
|
||||||
local kv_lhs = s.indexOf("\"value\":", ti)
|
local kv_lhs = s.indexOf("\"value\":", ti)
|
||||||
if kv_lhs < 0 { return null }
|
if kv_lhs < 0 { return null }
|
||||||
|
if krhs >= 0 && kv_lhs >= krhs { return null }
|
||||||
local lhs_val = JsonFragBox.read_int_after(s, kv_lhs + 8)
|
local lhs_val = JsonFragBox.read_int_after(s, kv_lhs + 8)
|
||||||
if lhs_val == null { return null }
|
if lhs_val == null { return null }
|
||||||
// rhs int
|
// rhs int
|
||||||
local krhs = s.indexOf("\"rhs\":{", k_bin)
|
|
||||||
if krhs < 0 { return null }
|
if krhs < 0 { return null }
|
||||||
local ti2 = s.indexOf("\"type\":\"Int\"", krhs)
|
local ti2 = s.indexOf("\"type\":\"Int\"", krhs)
|
||||||
if ti2 < 0 { return null }
|
if ti2 < 0 { return null }
|
||||||
|
|||||||
@ -7,13 +7,37 @@ static box LowerReturnIntBox {
|
|||||||
local s = "" + program_json
|
local s = "" + program_json
|
||||||
local k_ret = s.indexOf("\"type\":\"Return\"")
|
local k_ret = s.indexOf("\"type\":\"Return\"")
|
||||||
if k_ret < 0 { return null }
|
if k_ret < 0 { return null }
|
||||||
// Expect Int after Return
|
// Find expr object
|
||||||
local k_int = s.indexOf("\"type\":\"Int\"", k_ret)
|
local k_expr = s.indexOf("\"expr\":{", k_ret)
|
||||||
|
if k_expr < 0 { return null }
|
||||||
|
// Check that expr.type is directly Int (not Binary, Logical, Var, etc)
|
||||||
|
local k_type_start = k_expr + 8
|
||||||
|
local k_type = s.indexOf("\"type\":", k_type_start)
|
||||||
|
if k_type < 0 || k_type > k_type_start + 20 { return null }
|
||||||
|
// Extract type value to verify it's "Int"
|
||||||
|
local k_type_val = k_type + 7
|
||||||
|
local n = s.length()
|
||||||
|
loop(k_type_val < n) {
|
||||||
|
local ch = s.substring(k_type_val, k_type_val+1)
|
||||||
|
if ch != " " && ch != "\"" { break }
|
||||||
|
k_type_val = k_type_val + 1
|
||||||
|
}
|
||||||
|
// Verify it's exactly "Int" followed by quote
|
||||||
|
if k_type_val + 3 > n { return null }
|
||||||
|
if s.substring(k_type_val, k_type_val+3) != "Int" { return null }
|
||||||
|
if k_type_val + 3 >= n { return null }
|
||||||
|
if s.substring(k_type_val+3, k_type_val+4) != "\"" { return null }
|
||||||
|
// Now extract the value
|
||||||
|
local k_int = s.indexOf("\"type\":\"Int\"", k_expr)
|
||||||
if k_int < 0 { return null }
|
if k_int < 0 { return null }
|
||||||
// Read value after Int
|
|
||||||
local kv = s.indexOf("\"value\":", k_int); if kv < 0 { return null }
|
local kv = s.indexOf("\"value\":", k_int); if kv < 0 { return null }
|
||||||
local val = JsonFragBox.read_int_after(s, kv + 8)
|
local val = JsonFragBox.read_int_after(s, kv + 8)
|
||||||
if val == null { return null }
|
if val == null { return null }
|
||||||
|
local debug_on = 0
|
||||||
|
if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" {
|
||||||
|
debug_on = 1
|
||||||
|
print("[mirbuilder/registry:return.int val=" + val + "]")
|
||||||
|
}
|
||||||
// Emit minimal MIR JSON v0 (functions as array; blocks use id field)
|
// Emit minimal MIR JSON v0 (functions as array; blocks use id field)
|
||||||
// Shape expected by src/runner/mir_json_v0.rs::parse_mir_v0_to_module
|
// Shape expected by src/runner/mir_json_v0.rs::parse_mir_v0_to_module
|
||||||
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + val + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
|
local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + val + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
|
||||||
|
|||||||
@ -21,8 +21,11 @@ static box LowerReturnLogicalBox {
|
|||||||
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
local op = JsonFragBox.read_string_after(s, k_op + 5)
|
||||||
if op == null { return null }
|
if op == null { return null }
|
||||||
if !(op == "&&" || op == "||") { return null }
|
if !(op == "&&" || op == "||") { return null }
|
||||||
|
local debug_on = 0
|
||||||
|
if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" { debug_on = 1 }
|
||||||
// Resolve lhs to 0/1 (Bool or Var with Local Bool) - robust version
|
// Resolve lhs to 0/1 (Bool or Var with Local Bool) - robust version
|
||||||
local lhs_true = null
|
local lhs_true = null
|
||||||
|
local lhs_name = null
|
||||||
{
|
{
|
||||||
local klhs_b = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Bool\"", k_log)
|
local klhs_b = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Bool\"", k_log)
|
||||||
if klhs_b >= 0 {
|
if klhs_b >= 0 {
|
||||||
@ -34,15 +37,21 @@ static box LowerReturnLogicalBox {
|
|||||||
local klhs_v = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Var\"", k_log)
|
local klhs_v = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Var\"", k_log)
|
||||||
if klhs_v >= 0 {
|
if klhs_v >= 0 {
|
||||||
local kn = JsonFragBox.index_of_from(s, "\"name\":", klhs_v)
|
local kn = JsonFragBox.index_of_from(s, "\"name\":", klhs_v)
|
||||||
if kn < 0 { return null }
|
if kn < 0 { if debug_on == 1 { print("[logical/miss:lhs_name extraction]") } return null }
|
||||||
local name = JsonFragBox.read_string_after(s, kn + 7)
|
lhs_name = JsonFragBox.read_string_after(s, kn + 7)
|
||||||
if name != null { lhs_true = PatternUtilBox.find_local_bool_before(s, name, k_log) }
|
if lhs_name != null {
|
||||||
|
lhs_true = PatternUtilBox.find_local_bool_before(s, lhs_name, k_log)
|
||||||
|
if debug_on == 1 && lhs_true == null { print("[logical/miss:lhs_true for name=" + lhs_name + "]") }
|
||||||
|
} else {
|
||||||
|
if debug_on == 1 { print("[logical/miss:lhs_name null]") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if lhs_true == null { return null }
|
if lhs_true == null { if debug_on == 1 { print("[logical/miss:lhs_true final]") } return null }
|
||||||
// Resolve rhs to 0/1 (Bool or Var) - robust version
|
// Resolve rhs to 0/1 (Bool or Var) - robust version
|
||||||
local rhs_true = null
|
local rhs_true = null
|
||||||
|
local rhs_name = null
|
||||||
{
|
{
|
||||||
local krhs_b = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Bool\"", k_log)
|
local krhs_b = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Bool\"", k_log)
|
||||||
if krhs_b >= 0 {
|
if krhs_b >= 0 {
|
||||||
@ -54,15 +63,23 @@ static box LowerReturnLogicalBox {
|
|||||||
local krhs_v = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Var\"", k_log)
|
local krhs_v = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Var\"", k_log)
|
||||||
if krhs_v >= 0 {
|
if krhs_v >= 0 {
|
||||||
local kn2 = JsonFragBox.index_of_from(s, "\"name\":", krhs_v)
|
local kn2 = JsonFragBox.index_of_from(s, "\"name\":", krhs_v)
|
||||||
if kn2 < 0 { return null }
|
if kn2 < 0 { if debug_on == 1 { print("[logical/miss:rhs_name extraction]") } return null }
|
||||||
local name2 = JsonFragBox.read_string_after(s, kn2 + 7)
|
rhs_name = JsonFragBox.read_string_after(s, kn2 + 7)
|
||||||
if name2 != null { rhs_true = PatternUtilBox.find_local_bool_before(s, name2, k_log) }
|
if rhs_name != null {
|
||||||
|
rhs_true = PatternUtilBox.find_local_bool_before(s, rhs_name, k_log)
|
||||||
|
if debug_on == 1 && rhs_true == null { print("[logical/miss:rhs_true for name=" + rhs_name + "]") }
|
||||||
|
} else {
|
||||||
|
if debug_on == 1 { print("[logical/miss:rhs_name null]") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rhs_true == null { return null }
|
if rhs_true == null { if debug_on == 1 { print("[logical/miss:rhs_true final]") } return null }
|
||||||
|
|
||||||
// Build MIR(JSON v0) string directly (functions[]/name="main"/blocks.id)
|
// Build MIR(JSON v0) string directly (functions[]/name="main"/blocks.id)
|
||||||
|
if debug_on == 1 {
|
||||||
|
print("[mirbuilder/registry:return.logical op=" + op + " lhs_true=" + lhs_true + " rhs_true=" + rhs_true + "]")
|
||||||
|
}
|
||||||
local json_head = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":["
|
local json_head = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":["
|
||||||
local bb0 = "{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_true + "}},{\"op\":\"branch\",\"cond\":1,\"then\":1,\"else\":2}]}"
|
local bb0 = "{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_true + "}},{\"op\":\"branch\",\"cond\":1,\"then\":1,\"else\":2}]}"
|
||||||
if op == "&&" {
|
if op == "&&" {
|
||||||
|
|||||||
@ -47,17 +47,27 @@ static box PatternUtilBox {
|
|||||||
local pos=0; local last=-1
|
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 }
|
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 }
|
if last<0 { return null }
|
||||||
local ki=s.indexOf("\"type\":\"Int\"",last); if ki<0||ki>=before_pos { return null }
|
// Bound the search between this Local and the next Local/before_pos
|
||||||
local kv=s.indexOf("\"value\":",ki); if kv<0 { return null }
|
local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last + 1)
|
||||||
|
if next < 0 || next > before_pos { next = before_pos }
|
||||||
|
local ki = s.indexOf("\"type\":\"Int\"", last)
|
||||||
|
if ki < 0 || ki >= next { return null }
|
||||||
|
local kv = s.indexOf("\"value\":", ki)
|
||||||
|
if kv < 0 || kv >= next { return null }
|
||||||
return JsonFragBox.read_int_after(s, kv+8)
|
return JsonFragBox.read_int_after(s, kv+8)
|
||||||
}
|
}
|
||||||
find_local_bool_before(s, name, before_pos) {
|
find_local_bool_before(s, name, before_pos) {
|
||||||
local pos=0; local last=-1
|
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 }
|
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 }
|
if last<0 { return null }
|
||||||
local kb=s.indexOf("\"type\":\"Bool\"",last); if kb<0||kb>=before_pos { return null }
|
local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last + 1)
|
||||||
local kv=s.indexOf("\"value\":",kb); if kv<0 { return null }
|
if next < 0 || next > before_pos { next = before_pos }
|
||||||
return JsonFragBox.read_bool_after(s, kv+8)
|
local kb = s.indexOf("\"type\":\"Bool\"", last)
|
||||||
|
if kb < 0 || kb >= next { return null }
|
||||||
|
local kv = s.indexOf("\"value\":", kb)
|
||||||
|
if kv < 0 || kv >= next { return null }
|
||||||
|
// JSON v0 uses 0/1 for bool values, not true/false literals
|
||||||
|
return JsonFragBox.read_int_after(s, kv+8)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the last Local(Int) before a given position (name-agnostic).
|
// Find the last Local(Int) before a given position (name-agnostic).
|
||||||
|
|||||||
@ -10,7 +10,43 @@ using selfhost.vm.helpers.method_alias_policy as MethodAliasPolicy
|
|||||||
using selfhost.vm.boxes.abi_adapter_registry as AbiAdapterRegistryBox
|
using selfhost.vm.boxes.abi_adapter_registry as AbiAdapterRegistryBox
|
||||||
|
|
||||||
static box MirCallV1HandlerBox {
|
static box MirCallV1HandlerBox {
|
||||||
|
// キー正規化ヘルパー(SSOT)
|
||||||
|
_norm_key_str(s) {
|
||||||
|
if s == null { return null }
|
||||||
|
local orig = s
|
||||||
|
|
||||||
|
// 先頭・末尾の引用符を除去("k1" → k1)
|
||||||
|
local len = s.length()
|
||||||
|
if len >= 2 {
|
||||||
|
local first = s.substring(0, 1)
|
||||||
|
local last = s.substring(len - 1, len)
|
||||||
|
if first == "\"" && last == "\"" {
|
||||||
|
s = s.substring(1, len - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [map/missing] はnullを返す
|
||||||
|
if s.indexOf("[map/missing]") >= 0 {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if env.get("HAKO_VM_MIRCALL_TRACE_KEYS") == "1" {
|
||||||
|
print("[vm/_norm_key_str] IN=" + orig + " OUT=" + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// デバッグトレース(任意)
|
||||||
|
_norm_key_dbg(raw, norm) {
|
||||||
|
local trace_keys = env.get("HAKO_VM_MIRCALL_TRACE_KEYS")
|
||||||
|
if trace_keys == "1" {
|
||||||
|
print("[vm/trace/keys] raw=" + raw + " norm=" + norm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handle(seg, regs) {
|
handle(seg, regs) {
|
||||||
|
print("[DEBUG_HANDLER_ENTRY] seg=" + seg.substring(0, 80))
|
||||||
// Constructor: write dst=0 (SSA continuity)
|
// Constructor: write dst=0 (SSA continuity)
|
||||||
if seg.indexOf("\"type\":\"Constructor\"") >= 0 {
|
if seg.indexOf("\"type\":\"Constructor\"") >= 0 {
|
||||||
local d0 = JsonFragBox.get_int(seg, "dst"); if d0 != null { regs.setField(StringHelpers.int_to_str(d0), "0") }
|
local d0 = JsonFragBox.get_int(seg, "dst"); if d0 != null { regs.setField(StringHelpers.int_to_str(d0), "0") }
|
||||||
@ -83,19 +119,22 @@ static box MirCallV1HandlerBox {
|
|||||||
}
|
}
|
||||||
// Map.set: 重複キー検知つきでサイズ更新 + 値保存(値状態ON時)
|
// Map.set: 重複キー検知つきでサイズ更新 + 値保存(値状態ON時)
|
||||||
if btype == "MapBox" && mname == "set" {
|
if btype == "MapBox" && mname == "set" {
|
||||||
|
print("[DEBUG_ADAPTER_MAP_SET] ENTER")
|
||||||
// 受信者・キー抽出
|
// 受信者・キー抽出
|
||||||
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
||||||
local key_str = null
|
local key_str = null
|
||||||
if arg0 >= 0 {
|
if arg0 >= 0 {
|
||||||
key_str = regs.getField(StringHelpers.int_to_str(arg0))
|
local key_str_raw = regs.getField(StringHelpers.int_to_str(arg0))
|
||||||
// MapBox.get returns "[map/missing] ..." for missing keys; treat as null
|
key_str = me._norm_key_str(key_str_raw)
|
||||||
if key_str != null && key_str.indexOf("[map/missing]") >= 0 { key_str = null }
|
me._norm_key_dbg(key_str_raw, key_str)
|
||||||
|
print("[DEBUG_ADAPTER_MAP_SET] raw=" + key_str_raw + " norm=" + key_str)
|
||||||
}
|
}
|
||||||
// 重複キー検知(presence フラグを別ネーム空間に保持)
|
// 重複キー検知(presence フラグを別ネーム空間に保持)
|
||||||
if key_str != null {
|
if key_str != null {
|
||||||
local rid_s = rid == null ? "null" : (""+rid)
|
local rid_s = rid == null ? "null" : (""+rid)
|
||||||
local pres_key = "hvm.map.k:" + (per_recv == "1" ? rid_s : "*") + ":" + key_str
|
local pres_key = "hvm.map.k:" + (per_recv == "1" ? rid_s : "*") + ":" + key_str
|
||||||
local had = regs.getField(pres_key)
|
local had = regs.getField(pres_key)
|
||||||
|
if env.get("HAKO_VM_MIRCALL_TRACE") == "1" { print("[vm/trace/adapter] pres_key=" + pres_key + " had=" + had + " cur_len=" + cur_len) }
|
||||||
if had == null {
|
if had == null {
|
||||||
regs.setField(pres_key, "1")
|
regs.setField(pres_key, "1")
|
||||||
cur_len = cur_len + 1
|
cur_len = cur_len + 1
|
||||||
@ -126,9 +165,9 @@ static box MirCallV1HandlerBox {
|
|||||||
if btype == "MapBox" && mname == "get" && value_state == "1" {
|
if btype == "MapBox" && mname == "get" && value_state == "1" {
|
||||||
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
||||||
if arg0 >= 0 {
|
if arg0 >= 0 {
|
||||||
local key_str = regs.getField(StringHelpers.int_to_str(arg0))
|
local key_str_raw = regs.getField(StringHelpers.int_to_str(arg0))
|
||||||
// MapBox.get returns "[map/missing] ..." for missing keys; treat as null
|
local key_str = me._norm_key_str(key_str_raw)
|
||||||
if key_str != null && key_str.indexOf("[map/missing]") >= 0 { key_str = null }
|
me._norm_key_dbg(key_str_raw, key_str)
|
||||||
if key_str != null {
|
if key_str != null {
|
||||||
local val_key = MethodAliasPolicy.recv_map_key(per_recv, rid, key_str)
|
local val_key = MethodAliasPolicy.recv_map_key(per_recv, rid, key_str)
|
||||||
local val_str = regs.getField(val_key)
|
local val_str = regs.getField(val_key)
|
||||||
@ -146,9 +185,9 @@ static box MirCallV1HandlerBox {
|
|||||||
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
||||||
local has_result = 0
|
local has_result = 0
|
||||||
if arg0 >= 0 {
|
if arg0 >= 0 {
|
||||||
local key_str = regs.getField(StringHelpers.int_to_str(arg0))
|
local key_str_raw = regs.getField(StringHelpers.int_to_str(arg0))
|
||||||
// MapBox.get returns "[map/missing] ..." for missing keys; treat as null
|
local key_str = me._norm_key_str(key_str_raw)
|
||||||
if key_str != null && key_str.indexOf("[map/missing]") >= 0 { key_str = null }
|
me._norm_key_dbg(key_str_raw, key_str)
|
||||||
if key_str != null {
|
if key_str != null {
|
||||||
local val_key = MethodAliasPolicy.recv_map_key(per_recv, rid, key_str)
|
local val_key = MethodAliasPolicy.recv_map_key(per_recv, rid, key_str)
|
||||||
local val_str = regs.getField(val_key)
|
local val_str = regs.getField(val_key)
|
||||||
@ -193,23 +232,41 @@ static box MirCallV1HandlerBox {
|
|||||||
local d1 = JsonFragBox.get_int(seg, "dst"); if d1 != null { regs.setField(StringHelpers.int_to_str(d1), "0") }
|
local d1 = JsonFragBox.get_int(seg, "dst"); if d1 != null { regs.setField(StringHelpers.int_to_str(d1), "0") }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Map.set: 重複キー検知つきサイズ更新(値状態に依存しない最小実装)
|
// Map.set: 重複キー検知つきサイズ更新(値状態ONなら値も保存)
|
||||||
if btype == "MapBox" && mname == "set" {
|
if btype == "MapBox" && mname == "set" {
|
||||||
// キー抽出
|
// キー抽出
|
||||||
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
local arg0 = MiniMirV1Scan.first_arg_register(seg)
|
||||||
if arg0 >= 0 {
|
if arg0 >= 0 {
|
||||||
local key_str = regs.getField(StringHelpers.int_to_str(arg0))
|
local key_str_raw = regs.getField(StringHelpers.int_to_str(arg0))
|
||||||
// MapBox.get returns "[map/missing] ..." for missing keys; treat as null
|
local key_str = me._norm_key_str(key_str_raw)
|
||||||
if key_str != null && key_str.indexOf("[map/missing]") >= 0 { key_str = null }
|
me._norm_key_dbg(key_str_raw, key_str)
|
||||||
|
print("[DEBUG_MAP_SET] raw=" + key_str_raw + " norm=" + key_str)
|
||||||
if key_str != null {
|
if key_str != null {
|
||||||
local rid_s = rid == null ? "null" : (""+rid)
|
local rid_s = rid == null ? "null" : (""+rid)
|
||||||
local pres_key = "hvm.map.k:" + (per_recv == "1" ? rid_s : "*") + ":" + key_str
|
local pres_key = "hvm.map.k:" + (per_recv == "1" ? rid_s : "*") + ":" + key_str
|
||||||
local had = regs.getField(pres_key)
|
local had = regs.getField(pres_key)
|
||||||
|
print("[DEBUG_MAP_SET] pres_key=" + pres_key + " had=" + had + " cur_len=" + cur_len)
|
||||||
|
if env.get("HAKO_VM_MIRCALL_TRACE") == "1" { print("[vm/trace/fallback] pres_key=" + pres_key + " had=" + had + " cur_len=" + cur_len) }
|
||||||
if had == null {
|
if had == null {
|
||||||
regs.setField(pres_key, "1")
|
regs.setField(pres_key, "1")
|
||||||
cur_len = cur_len + 1
|
cur_len = cur_len + 1
|
||||||
regs.setField(key, StringHelpers.int_to_str(cur_len))
|
regs.setField(key, StringHelpers.int_to_str(cur_len))
|
||||||
|
print("[DEBUG_MAP_SET] NEW KEY, cur_len now=" + cur_len)
|
||||||
if env.get("HAKO_VM_MIRCALL_TRACE") == "1" { print("[vm/trace] map.set(fallback,new) cur_len=" + cur_len) }
|
if env.get("HAKO_VM_MIRCALL_TRACE") == "1" { print("[vm/trace] map.set(fallback,new) cur_len=" + cur_len) }
|
||||||
|
} else {
|
||||||
|
print("[DEBUG_MAP_SET] DUP KEY, cur_len unchanged=" + cur_len)
|
||||||
|
}
|
||||||
|
// 値状態ON時は値も保持(get/hasで参照可能に)
|
||||||
|
local value_state2 = env.get("HAKO_VM_MIRCALL_VALUESTATE"); if value_state2 == null { value_state2 = "0" }
|
||||||
|
if value_state2 == "1" {
|
||||||
|
local arg1_id2 = MiniMirV1Scan.nth_arg_register(seg, 1)
|
||||||
|
if arg1_id2 >= 0 {
|
||||||
|
local val_str2 = regs.getField(StringHelpers.int_to_str(arg1_id2))
|
||||||
|
if val_str2 != null {
|
||||||
|
local vkey2 = MethodAliasPolicy.recv_map_key(per_recv, rid, key_str)
|
||||||
|
regs.setField(vkey2, ""+val_str2)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cur_len = cur_len + 1
|
cur_len = cur_len + 1
|
||||||
|
|||||||
@ -164,6 +164,7 @@ static box NyVmDispatcherV1Box {
|
|||||||
}
|
}
|
||||||
// Main entry: Choose internal scanner when enabled; otherwise delegate to Mini‑VM
|
// Main entry: Choose internal scanner when enabled; otherwise delegate to Mini‑VM
|
||||||
run(json) {
|
run(json) {
|
||||||
|
if env.get("HAKO_TRACE_EXECUTION") == "1" { print("[trace] executor: hakovm (hako)") }
|
||||||
// Typed IR primary (限定): 構造IR生成を有効化しつつ既存フローを使用(挙動不変)
|
// Typed IR primary (限定): 構造IR生成を有効化しつつ既存フローを使用(挙動不変)
|
||||||
if env.get("HAKO_V1_TYPED_IR_PRIMARY") == "1" {
|
if env.get("HAKO_V1_TYPED_IR_PRIMARY") == "1" {
|
||||||
// ShadowをONにして構造IR生成のオーバーヘッドを観測(必要時)
|
// ShadowをONにして構造IR生成のオーバーヘッドを観測(必要時)
|
||||||
|
|||||||
@ -182,6 +182,7 @@ path = "lang/src/shared/common/string_helpers.hako"
|
|||||||
|
|
||||||
# Phase 20.34 — Box‑First selfhost build line (aliases for Hako boxes)
|
# Phase 20.34 — Box‑First selfhost build line (aliases for Hako boxes)
|
||||||
"hako.mir.builder" = "lang/src/mir/builder/MirBuilderBox.hako"
|
"hako.mir.builder" = "lang/src/mir/builder/MirBuilderBox.hako"
|
||||||
|
"hako.mir.builder.pattern_registry" = "lang/src/mir/builder/pattern_registry.hako"
|
||||||
"hako.llvm.emit" = "lang/src/llvm_ir/emit/LLVMEmitBox.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.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.pattern_util" = "lang/src/mir/builder/internal/pattern_util_box.hako"
|
||||||
|
|||||||
@ -84,8 +84,17 @@ impl FileBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_all(&self, _buf: &[u8]) -> Result<(), String> {
|
pub fn write_all(&self, _buf: &[u8]) -> Result<(), String> {
|
||||||
// CoreRo does not support write - Fail-Fast
|
// Fail-Fast by capability: consult provider caps
|
||||||
Err("Write operation not supported in read-only mode".to_string())
|
let caps = self.provider
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.caps())
|
||||||
|
.or_else(|| provider_lock::get_filebox_caps())
|
||||||
|
.unwrap_or_else(|| provider::FileCaps::read_only());
|
||||||
|
if !caps.write {
|
||||||
|
return Err("Write unsupported by current FileBox provider (read-only)".to_string());
|
||||||
|
}
|
||||||
|
// Write-capable provider not wired yet
|
||||||
|
Err("Write supported by provider but not implemented in this build".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ファイルの内容を読み取る
|
/// ファイルの内容を読み取る
|
||||||
@ -98,8 +107,15 @@ impl FileBox {
|
|||||||
|
|
||||||
/// ファイルに内容を書き込む
|
/// ファイルに内容を書き込む
|
||||||
pub fn write(&self, _content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
pub fn write(&self, _content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||||
// Fail-Fast: CoreRo does not support write
|
let caps = self.provider
|
||||||
Box::new(StringBox::new("Error: Write operation not supported in read-only mode"))
|
.as_ref()
|
||||||
|
.map(|p| p.caps())
|
||||||
|
.or_else(|| provider_lock::get_filebox_caps())
|
||||||
|
.unwrap_or_else(|| provider::FileCaps::read_only());
|
||||||
|
if !caps.write {
|
||||||
|
return Box::new(StringBox::new("Error: write unsupported by provider (read-only)"));
|
||||||
|
}
|
||||||
|
Box::new(StringBox::new("Error: write supported but not implemented in this build"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ファイルが存在するかチェック
|
/// ファイルが存在するかチェック
|
||||||
@ -110,14 +126,28 @@ impl FileBox {
|
|||||||
|
|
||||||
/// ファイルを削除
|
/// ファイルを削除
|
||||||
pub fn delete(&self) -> Box<dyn NyashBox> {
|
pub fn delete(&self) -> Box<dyn NyashBox> {
|
||||||
// Fail-Fast: CoreRo does not support delete
|
let caps = self.provider
|
||||||
Box::new(StringBox::new("Error: Delete operation not supported in read-only mode"))
|
.as_ref()
|
||||||
|
.map(|p| p.caps())
|
||||||
|
.or_else(|| provider_lock::get_filebox_caps())
|
||||||
|
.unwrap_or_else(|| provider::FileCaps::read_only());
|
||||||
|
if !caps.write {
|
||||||
|
return Box::new(StringBox::new("Error: delete unsupported by provider (read-only)"));
|
||||||
|
}
|
||||||
|
Box::new(StringBox::new("Error: delete supported but not implemented in this build"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ファイルをコピー
|
/// ファイルをコピー
|
||||||
pub fn copy(&self, _dest: &str) -> Box<dyn NyashBox> {
|
pub fn copy(&self, _dest: &str) -> Box<dyn NyashBox> {
|
||||||
// Fail-Fast: CoreRo does not support copy
|
let caps = self.provider
|
||||||
Box::new(StringBox::new("Error: Copy operation not supported in read-only mode"))
|
.as_ref()
|
||||||
|
.map(|p| p.caps())
|
||||||
|
.or_else(|| provider_lock::get_filebox_caps())
|
||||||
|
.unwrap_or_else(|| provider::FileCaps::read_only());
|
||||||
|
if !caps.write {
|
||||||
|
return Box::new(StringBox::new("Error: copy unsupported by provider (read-only)"));
|
||||||
|
}
|
||||||
|
Box::new(StringBox::new("Error: copy supported but not implemented in this build"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,10 @@ use std::collections::HashMap;
|
|||||||
/// - mir_call(Method ArrayBox.push/size/len/length) with optional per-receiver state
|
/// - mir_call(Method ArrayBox.push/size/len/length) with optional per-receiver state
|
||||||
/// - ret (by register id)
|
/// - ret (by register id)
|
||||||
pub fn run_json_v1_inline(json: &str) -> i32 {
|
pub fn run_json_v1_inline(json: &str) -> i32 {
|
||||||
|
// Optional execution trace (stderr) for debugging
|
||||||
|
if crate::config::env::env_bool("HAKO_TRACE_EXECUTION") {
|
||||||
|
eprintln!("[trace] executor: hv1_inline (rust)");
|
||||||
|
}
|
||||||
// Parse JSON
|
// Parse JSON
|
||||||
let v: Value = match serde_json::from_str(json) {
|
let v: Value = match serde_json::from_str(json) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
@ -31,13 +35,21 @@ pub fn run_json_v1_inline(json: &str) -> i32 {
|
|||||||
|
|
||||||
// Registers and simple method state
|
// Registers and simple method state
|
||||||
let mut regs: HashMap<i64, i64> = HashMap::new();
|
let mut regs: HashMap<i64, i64> = HashMap::new();
|
||||||
|
let mut str_regs: HashMap<i64, String> = HashMap::new(); // Track string values loaded via const
|
||||||
let sizestate = crate::config::env::env_bool("HAKO_VM_MIRCALL_SIZESTATE");
|
let sizestate = crate::config::env::env_bool("HAKO_VM_MIRCALL_SIZESTATE");
|
||||||
let per_recv = crate::config::env::env_bool("HAKO_VM_MIRCALL_SIZESTATE_PER_RECV");
|
let per_recv = crate::config::env::env_bool("HAKO_VM_MIRCALL_SIZESTATE_PER_RECV");
|
||||||
|
let value_state = crate::config::env::env_bool("HAKO_VM_MIRCALL_VALUESTATE");
|
||||||
// Keep Array and Map length states separate to avoid cross‑box interference
|
// Keep Array and Map length states separate to avoid cross‑box interference
|
||||||
let mut len_global_arr: i64 = 0;
|
let mut len_global_arr: i64 = 0;
|
||||||
let mut len_by_recv_arr: HashMap<i64, i64> = HashMap::new();
|
let mut len_by_recv_arr: HashMap<i64, i64> = HashMap::new();
|
||||||
let mut len_global_map: i64 = 0;
|
let mut len_global_map: i64 = 0;
|
||||||
let mut len_by_recv_map: HashMap<i64, i64> = HashMap::new();
|
let mut len_by_recv_map: HashMap<i64, i64> = HashMap::new();
|
||||||
|
// Map dup-key detection: track which keys have been set (per-receiver or global)
|
||||||
|
let mut map_keys_global: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||||
|
let mut map_keys_by_recv: HashMap<i64, std::collections::HashSet<String>> = HashMap::new();
|
||||||
|
// Map value storage (when value_state=1)
|
||||||
|
let mut map_vals_global: HashMap<String, i64> = HashMap::new();
|
||||||
|
let mut map_vals_by_recv: HashMap<i64, HashMap<String, i64>> = HashMap::new();
|
||||||
|
|
||||||
fn is_size_alias(name: &str) -> bool {
|
fn is_size_alias(name: &str) -> bool {
|
||||||
matches!(name, "size" | "len" | "length")
|
matches!(name, "size" | "len" | "length")
|
||||||
@ -140,6 +152,10 @@ pub fn run_json_v1_inline(json: &str) -> i32 {
|
|||||||
// Prefer i64 numeric constants; otherwise stub non-numeric to 0 for inline path
|
// Prefer i64 numeric constants; otherwise stub non-numeric to 0 for inline path
|
||||||
if let Some(n) = inst.get("value").and_then(|vv| vv.get("value")).and_then(Value::as_i64) {
|
if let Some(n) = inst.get("value").and_then(|vv| vv.get("value")).and_then(Value::as_i64) {
|
||||||
regs.insert(dst, n);
|
regs.insert(dst, n);
|
||||||
|
} else if let Some(s) = inst.get("value").and_then(|vv| vv.get("value")).and_then(Value::as_str) {
|
||||||
|
// Store string value for Map key tracking
|
||||||
|
str_regs.insert(dst, s.to_string());
|
||||||
|
regs.insert(dst, 0); // Also set numeric register to 0 for compatibility
|
||||||
} else {
|
} else {
|
||||||
regs.insert(dst, 0);
|
regs.insert(dst, 0);
|
||||||
}
|
}
|
||||||
@ -205,17 +221,93 @@ pub fn run_json_v1_inline(json: &str) -> i32 {
|
|||||||
let method = callee.get("method").and_then(Value::as_str).unwrap_or("");
|
let method = callee.get("method").and_then(Value::as_str).unwrap_or("");
|
||||||
let recv = callee.get("receiver").and_then(Value::as_i64).unwrap_or(-1);
|
let recv = callee.get("receiver").and_then(Value::as_i64).unwrap_or(-1);
|
||||||
if method == "set" {
|
if method == "set" {
|
||||||
if sizestate {
|
// Extract key from first arg (register containing string value)
|
||||||
if per_recv {
|
let args = if let Some(mc) = mc { mc.get("args") } else { inst.get("args") };
|
||||||
let e = len_by_recv_map.entry(recv).or_insert(0);
|
let key_str = if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||||
*e += 1;
|
if let Some(key_reg) = args_arr.get(0).and_then(Value::as_i64) {
|
||||||
|
// Look up string value from str_regs
|
||||||
|
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||||
|
} else { String::new() }
|
||||||
|
} else { String::new() };
|
||||||
|
|
||||||
|
if sizestate && !key_str.is_empty() {
|
||||||
|
let is_new_key = if per_recv {
|
||||||
|
let keys = map_keys_by_recv.entry(recv).or_insert_with(std::collections::HashSet::new);
|
||||||
|
keys.insert(key_str.clone())
|
||||||
} else {
|
} else {
|
||||||
len_global_map += 1;
|
map_keys_global.insert(key_str.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only increment size if this is a new key
|
||||||
|
if is_new_key {
|
||||||
|
if per_recv {
|
||||||
|
let e = len_by_recv_map.entry(recv).or_insert(0);
|
||||||
|
*e += 1;
|
||||||
|
} else {
|
||||||
|
len_global_map += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// value_state: store the value
|
||||||
|
if value_state && !key_str.is_empty() {
|
||||||
|
if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||||
|
if let Some(val_reg) = args_arr.get(1).and_then(Value::as_i64) {
|
||||||
|
let val = *regs.get(&val_reg).unwrap_or(&0);
|
||||||
|
if per_recv {
|
||||||
|
let vals = map_vals_by_recv.entry(recv).or_insert_with(HashMap::new);
|
||||||
|
vals.insert(key_str.clone(), val);
|
||||||
|
} else {
|
||||||
|
map_vals_global.insert(key_str.clone(), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if method == "get" && value_state {
|
||||||
|
// Extract key and retrieve value
|
||||||
|
let args = if let Some(mc) = mc { mc.get("args") } else { inst.get("args") };
|
||||||
|
let key_str = if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||||
|
if let Some(key_reg) = args_arr.get(0).and_then(Value::as_i64) {
|
||||||
|
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||||
|
} else { String::new() }
|
||||||
|
} else { String::new() };
|
||||||
|
|
||||||
|
if !key_str.is_empty() {
|
||||||
|
let val = if per_recv {
|
||||||
|
map_vals_by_recv.get(&recv).and_then(|m| m.get(&key_str)).cloned().unwrap_or(0)
|
||||||
|
} else {
|
||||||
|
*map_vals_global.get(&key_str).unwrap_or(&0)
|
||||||
|
};
|
||||||
|
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, val); }
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if method == "has" && value_state {
|
||||||
|
// Check if key exists
|
||||||
|
let args = if let Some(mc) = mc { mc.get("args") } else { inst.get("args") };
|
||||||
|
let key_str = if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||||
|
if let Some(key_reg) = args_arr.get(0).and_then(Value::as_i64) {
|
||||||
|
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||||
|
} else { String::new() }
|
||||||
|
} else { String::new() };
|
||||||
|
|
||||||
|
let has = if !key_str.is_empty() {
|
||||||
|
if per_recv {
|
||||||
|
map_vals_by_recv.get(&recv).map(|m| m.contains_key(&key_str)).unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
map_vals_global.contains_key(&key_str)
|
||||||
|
}
|
||||||
|
} else { false };
|
||||||
|
|
||||||
|
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) {
|
||||||
|
regs.insert(dst, if has { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if is_size_alias(method) {
|
if is_size_alias(method) {
|
||||||
let value = if sizestate {
|
let value = if sizestate {
|
||||||
if per_recv { *len_by_recv_map.get(&recv).unwrap_or(&0) } else { len_global_map }
|
if per_recv { *len_by_recv_map.get(&recv).unwrap_or(&0) } else { len_global_map }
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
use crate::boxes::file::provider::FileIo;
|
use crate::boxes::file::provider::{FileCaps, FileIo};
|
||||||
|
|
||||||
static LOCKED: AtomicBool = AtomicBool::new(false);
|
static LOCKED: AtomicBool = AtomicBool::new(false);
|
||||||
static WARN_ONCE: OnceLock<()> = OnceLock::new();
|
static WARN_ONCE: OnceLock<()> = OnceLock::new();
|
||||||
@ -49,3 +49,8 @@ pub fn get_filebox_provider() -> Option<&'static Arc<dyn FileIo>> {
|
|||||||
FILEBOX_PROVIDER.get()
|
FILEBOX_PROVIDER.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience: fetch current FileBox provider capabilities (if initialized).
|
||||||
|
/// Returns None when no provider is registered yet.
|
||||||
|
pub fn get_filebox_caps() -> Option<FileCaps> {
|
||||||
|
get_filebox_provider().map(|p| p.caps())
|
||||||
|
}
|
||||||
|
|||||||
@ -346,7 +346,7 @@ static box HakoAnalysisBuilderBox {
|
|||||||
_last_method_for_line(ir, line_num) {
|
_last_method_for_line(ir, line_num) {
|
||||||
// Conservative: return default entry when spans are not guaranteed to be maps
|
// Conservative: return default entry when spans are not guaranteed to be maps
|
||||||
// This avoids runtime errors when method_spans is absent or malformed in MVP builds.
|
// This avoids runtime errors when method_spans is absent or malformed in MVP builds.
|
||||||
return "Main.main"
|
return "Main.main/0"
|
||||||
}
|
}
|
||||||
_infer_call_arity(line, after_name_pos) {
|
_infer_call_arity(line, after_name_pos) {
|
||||||
// Count commas between the first '(' after method name and its matching ')'
|
// Count commas between the first '(' after method name and its matching ')'
|
||||||
|
|||||||
@ -26,7 +26,8 @@ static box HakoAnalyzerBox {
|
|||||||
// options: --format {text|dot|json} (accept anywhere)
|
// options: --format {text|dot|json} (accept anywhere)
|
||||||
local fmt = "text"
|
local fmt = "text"
|
||||||
local debug = 0
|
local debug = 0
|
||||||
local no_ast = 0
|
// Default: AST path OFF (enable with --force-ast)
|
||||||
|
local no_ast = 1
|
||||||
// single-pass parse: handle options in-place and collect sources
|
// single-pass parse: handle options in-place and collect sources
|
||||||
local i = 0
|
local i = 0
|
||||||
local fail = 0
|
local fail = 0
|
||||||
@ -70,7 +71,11 @@ static box HakoAnalyzerBox {
|
|||||||
// analysis - only build IR if needed by active rules
|
// analysis - only build IR if needed by active rules
|
||||||
local ir = null
|
local ir = null
|
||||||
if me._needs_ir(rules_only, rules_skip) == 1 {
|
if me._needs_ir(rules_only, rules_skip) == 1 {
|
||||||
ir = HakoAnalysisBuilderBox.build_from_source_flags(text, p, no_ast)
|
// Enable AST inside IR builder only when the active rules explicitly need it
|
||||||
|
local want_ast = me._needs_ast(rules_only, rules_skip)
|
||||||
|
local no_ast_eff = no_ast
|
||||||
|
if want_ast == 1 { no_ast_eff = 0 }
|
||||||
|
ir = HakoAnalysisBuilderBox.build_from_source_flags(text, p, no_ast_eff)
|
||||||
} else {
|
} else {
|
||||||
// Minimal IR stub for rules that don't need it
|
// Minimal IR stub for rules that don't need it
|
||||||
ir = new MapBox()
|
ir = new MapBox()
|
||||||
@ -378,8 +383,77 @@ static box HakoAnalyzerBox {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
_render_dot_multi(irs) {
|
_render_dot_multi(irs) {
|
||||||
// Delegate to Graphviz renderer (includes edges)
|
// Minimal, safe DOT renderer (inline) to avoid type issues
|
||||||
GraphvizRenderBox.render_multi(irs)
|
// Build nodes/edges from IR we just created in this CLI
|
||||||
|
print("digraph Hako {")
|
||||||
|
// node/edge sets
|
||||||
|
local nodes = new MapBox()
|
||||||
|
local edges = new MapBox()
|
||||||
|
// collect
|
||||||
|
if irs != null {
|
||||||
|
local i = 0
|
||||||
|
while i < irs.size() {
|
||||||
|
local ir = irs.get(i)
|
||||||
|
if ir != null {
|
||||||
|
// methods
|
||||||
|
local ms = ir.get("methods")
|
||||||
|
if ms != null {
|
||||||
|
local mi = 0
|
||||||
|
while mi < ms.size() {
|
||||||
|
local name = ms.get(mi)
|
||||||
|
if name != null { nodes.set(name, 1) }
|
||||||
|
mi = mi + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// calls
|
||||||
|
local cs = ir.get("calls")
|
||||||
|
if cs != null {
|
||||||
|
local ci = 0
|
||||||
|
while ci < cs.size() {
|
||||||
|
local c = cs.get(ci)
|
||||||
|
if c != null {
|
||||||
|
local src = c.get("from")
|
||||||
|
local dst = c.get("to")
|
||||||
|
if src != null && dst != null {
|
||||||
|
local key = src + "\t" + dst
|
||||||
|
edges.set(key, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ci = ci + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// print edges
|
||||||
|
// Also print nodes to ensure isolated nodes are present (minimal)
|
||||||
|
// edges
|
||||||
|
// NOTE: MapBox has no key iterator; we use a simple heuristic by re‑scanning possible sources
|
||||||
|
// For stability in this MVP, emit edges for calls we still have in IRs
|
||||||
|
if irs != null {
|
||||||
|
local i2 = 0
|
||||||
|
while i2 < irs.size() {
|
||||||
|
local ir2 = irs.get(i2)
|
||||||
|
if ir2 != null {
|
||||||
|
local cs2 = ir2.get("calls")
|
||||||
|
if cs2 != null {
|
||||||
|
local cj = 0
|
||||||
|
while cj < cs2.size() {
|
||||||
|
local c2 = cs2.get(cj)
|
||||||
|
if c2 != null {
|
||||||
|
local s = c2.get("from")
|
||||||
|
local d = c2.get("to")
|
||||||
|
if s != null && d != null { print(" \"" + s + "\" -> \"" + d + "\";") }
|
||||||
|
}
|
||||||
|
cj = cj + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i2 = i2 + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("}")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
_sort_diags(diags) {
|
_sort_diags(diags) {
|
||||||
|
|||||||
34
tools/hako_check/dot_edges_smoke.sh
Normal file
34
tools/hako_check/dot_edges_smoke.sh
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
BIN="${NYASH_BIN:-$ROOT/target/release/hakorune}"
|
||||||
|
if [ ! -x "$BIN" ]; then
|
||||||
|
echo "[DOT] hakorune not built: $BIN" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
TMP_HAKO="/tmp/dot_edges_$$.hako"
|
||||||
|
cat >"$TMP_HAKO" <<'HK'
|
||||||
|
// Minimal two boxes with a call to form one edge
|
||||||
|
static box Helper {
|
||||||
|
method echo(msg) { return 0 }
|
||||||
|
}
|
||||||
|
static box Main {
|
||||||
|
method main() {
|
||||||
|
Helper.echo("hi")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HK
|
||||||
|
|
||||||
|
export LD_LIBRARY_PATH="${ROOT}/target/release:${LD_LIBRARY_PATH:-}"
|
||||||
|
OUT=$(
|
||||||
|
NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
|
||||||
|
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 \
|
||||||
|
NYASH_DISABLE_PLUGINS=1 NYASH_BOX_FACTORY_POLICY=builtin_first \
|
||||||
|
"$BIN" --backend vm "$ROOT/tools/hako_check/cli.hako" -- --format dot --source-file "$TMP_HAKO" "$(sed 's/\r$//' "$TMP_HAKO")"
|
||||||
|
)
|
||||||
|
echo "$OUT" | sed -n '1,80p'
|
||||||
|
echo "$OUT" | grep -q '"Main.main/0" -> "Helper.echo/1";' && echo "[DOT] edge OK" || { echo "[DOT] edge MISSING" >&2; exit 1; }
|
||||||
|
rm -f "$TMP_HAKO"
|
||||||
@ -7,6 +7,9 @@ static box GraphvizRenderBox {
|
|||||||
render_multi(irs) {
|
render_multi(irs) {
|
||||||
// irs: ArrayBox of IR Map
|
// irs: ArrayBox of IR Map
|
||||||
print("digraph Hako {")
|
print("digraph Hako {")
|
||||||
|
// Internal key names for node/edge key lists (avoid collisions with user names)
|
||||||
|
local NK_KEY = "__graphviz_nodes__"
|
||||||
|
local EK_KEY = "__graphviz_edges__"
|
||||||
// optional graph attributes (kept minimal)
|
// optional graph attributes (kept minimal)
|
||||||
// print(" rankdir=LR;")
|
// print(" rankdir=LR;")
|
||||||
// Node and edge sets to avoid duplicates
|
// Node and edge sets to avoid duplicates
|
||||||
@ -23,7 +26,8 @@ static box GraphvizRenderBox {
|
|||||||
// Build clusters by box: group nodes whose name looks like Box.method/arity
|
// Build clusters by box: group nodes whose name looks like Box.method/arity
|
||||||
local groups = new MapBox()
|
local groups = new MapBox()
|
||||||
local group_keys = new ArrayBox()
|
local group_keys = new ArrayBox()
|
||||||
local nk = nodes.get("__keys__")
|
local nk = nodes.get(NK_KEY)
|
||||||
|
if nk == null { nk = new ArrayBox() }
|
||||||
if nk != null {
|
if nk != null {
|
||||||
local i = 0
|
local i = 0
|
||||||
while i < nk.size() {
|
while i < nk.size() {
|
||||||
@ -60,7 +64,8 @@ static box GraphvizRenderBox {
|
|||||||
// edges map key = from + "\t" + to
|
// edges map key = from + "\t" + to
|
||||||
// naive iteration by trying to get keys from a stored list
|
// naive iteration by trying to get keys from a stored list
|
||||||
// We kept an ArrayBox under edges.get("__keys__") for listing
|
// We kept an ArrayBox under edges.get("__keys__") for listing
|
||||||
local ks = edges.get("__keys__")
|
local ks = edges.get(EK_KEY)
|
||||||
|
if ks == null { ks = new ArrayBox() }
|
||||||
if ks != null {
|
if ks != null {
|
||||||
local ei = 0
|
local ei = 0
|
||||||
while ei < ks.size() {
|
while ei < ks.size() {
|
||||||
@ -119,7 +124,7 @@ static box GraphvizRenderBox {
|
|||||||
if name == null { return }
|
if name == null { return }
|
||||||
nodes.set(name, 1)
|
nodes.set(name, 1)
|
||||||
// also store a list of keys for emitting (since Map has no key iterator)
|
// also store a list of keys for emitting (since Map has no key iterator)
|
||||||
local arr = nodes.get("__keys__"); if arr == null { arr = new ArrayBox(); nodes.set("__keys__", arr) }
|
local arr = nodes.get("__graphviz_nodes__"); if arr == null { arr = new ArrayBox(); nodes.set("__graphviz_nodes__", arr) }
|
||||||
// avoid duplicates
|
// avoid duplicates
|
||||||
local seen = 0
|
local seen = 0
|
||||||
local i = 0; while i < arr.size() { if arr.get(i) == name { seen = 1; break } i = i + 1 }
|
local i = 0; while i < arr.size() { if arr.get(i) == name { seen = 1; break } i = i + 1 }
|
||||||
@ -129,7 +134,7 @@ static box GraphvizRenderBox {
|
|||||||
if src == null || dst == null { return }
|
if src == null || dst == null { return }
|
||||||
local key = src + "\t" + dst
|
local key = src + "\t" + dst
|
||||||
if edges.get(key) == null { edges.set(key, 1) }
|
if edges.get(key) == null { edges.set(key, 1) }
|
||||||
local arr = edges.get("__keys__"); if arr == null { arr = new ArrayBox(); edges.set("__keys__", arr) }
|
local arr = edges.get("__graphviz_edges__"); if arr == null { arr = new ArrayBox(); edges.set("__graphviz_edges__", arr) }
|
||||||
// avoid duplicates
|
// avoid duplicates
|
||||||
local seen = 0
|
local seen = 0
|
||||||
local i = 0; while i < arr.size() { if arr.get(i) == key { seen = 1; break } i = i + 1 }
|
local i = 0; while i < arr.size() { if arr.get(i) == key { seen = 1; break } i = i + 1 }
|
||||||
|
|||||||
@ -58,8 +58,10 @@ static box RuleDeadStaticBoxBox {
|
|||||||
// Skip Main box (entry point)
|
// Skip Main box (entry point)
|
||||||
if name == "Main" { bi = bi + 1; continue }
|
if name == "Main" { bi = bi + 1; continue }
|
||||||
|
|
||||||
// Only check static boxes
|
// Only check static boxes (tolerant):
|
||||||
if is_static == null || is_static == 0 { bi = bi + 1; continue }
|
// treat missing is_static as static (scanner sets it only for static box),
|
||||||
|
// skip only when explicitly false/0
|
||||||
|
if is_static != null { if is_static == 0 { bi = bi + 1; continue } }
|
||||||
|
|
||||||
// Check if box is referenced - if not in map, it returns "[map/missing]" StringBox
|
// Check if box is referenced - if not in map, it returns "[map/missing]" StringBox
|
||||||
local ref_check = referenced_boxes.get(name)
|
local ref_check = referenced_boxes.get(name)
|
||||||
@ -67,10 +69,17 @@ static box RuleDeadStaticBoxBox {
|
|||||||
// If ref_check is null or doesn't equal "1", box is unreferenced
|
// If ref_check is null or doesn't equal "1", box is unreferenced
|
||||||
if ref_check == null || ref_check != "1" {
|
if ref_check == null || ref_check != "1" {
|
||||||
// Box is never referenced - report error
|
// Box is never referenced - report error
|
||||||
// Line precision: prefer span_line from AST intake if present
|
// Line precision: prefer span_line from AST intake if present.
|
||||||
local line = box_info.get("span_line")
|
// MapBox missing may yield a sentinel String (e.g., "0[map/missing] ...").
|
||||||
if line == null { line = 1 }
|
// Convert via string and extract leading digits; default to 1.
|
||||||
out.push("[HC012] dead static box (never referenced): " + name + " :: path:" + me._itoa(line))
|
local line_any = box_info.get("span_line")
|
||||||
|
local line_s = ""
|
||||||
|
if line_any == null { line_s = "1" } else { line_s = "" + line_any }
|
||||||
|
// parse leading digits
|
||||||
|
local i = 0; local digits = ""
|
||||||
|
while i < line_s.length() { local ch = line_s.substring(i,i+1); if ch>="0" && ch<="9" { digits = digits + ch; i = i + 1; continue } break }
|
||||||
|
if digits == "" { digits = "1" }
|
||||||
|
out.push("[HC012] dead static box (never referenced): " + name + " :: path:" + digits)
|
||||||
}
|
}
|
||||||
|
|
||||||
bi = bi + 1
|
bi = bi + 1
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
{"diagnostics":[
|
{"diagnostics":[
|
||||||
{"file":"ng.hako","line":3,"rule":"HC012","message":"[HC012] dead static box (never referenced): UnusedBox :: path:3","quickFix":"","severity":"warning"}
|
{"file":"ng.hako","line":1,"rule":"HC012","message":"[HC012] dead static box (never referenced): UnusedBox :: path:1","quickFix":"","severity":"warning"}
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
{"diagnostics":[
|
||||||
|
{"file":"ng.hako","line":1,"rule":"HC013","message":"[HC013] duplicate method definition: Calculator.add/2 at line 11","quickFix":"","severity":"warning"}
|
||||||
|
]}
|
||||||
22
tools/hako_check/tests/HC013_duplicate_method_edge/ng.hako
Normal file
22
tools/hako_check/tests/HC013_duplicate_method_edge/ng.hako
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// ng.hako — duplicate method with comments/whitespace between
|
||||||
|
|
||||||
|
static box Calculator {
|
||||||
|
method add(a, b) {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
// some comment lines
|
||||||
|
// to ensure line numbers advance
|
||||||
|
|
||||||
|
method add(a, b) {
|
||||||
|
return a - b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static box Main {
|
||||||
|
method main() {
|
||||||
|
Calculator.add(1,2)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{"diagnostics":[
|
||||||
|
{"file":"ng.hako","line":1,"rule":"HC014","message":"[HC014] missing entrypoint (Main.main or main)","quickFix":"","severity":"warning"}
|
||||||
|
]}
|
||||||
|
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
// ng.hako — missing Main box entirely (edge case)
|
||||||
|
|
||||||
|
static box Utility {
|
||||||
|
method ping() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{"diagnostics":[
|
||||||
|
]}
|
||||||
|
|
||||||
13
tools/hako_check/tests/HC015_arity_mismatch_case_ok/ok.hako
Normal file
13
tools/hako_check/tests/HC015_arity_mismatch_case_ok/ok.hako
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// ok.hako — arity matches for all calls
|
||||||
|
|
||||||
|
static box Math {
|
||||||
|
method add(a, b) { return a + b }
|
||||||
|
}
|
||||||
|
|
||||||
|
static box Main {
|
||||||
|
method main() {
|
||||||
|
Math.add(1, 2)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
// tools/hako_parser/ast_emit.hako - HakoAstEmitBox (MVP)
|
// tools/hako_parser/ast_emit.hako - HakoAstEmitBox (MVP)
|
||||||
using selfhost.shared.common.string_helpers as Str
|
// no external helpers; keep emission self-contained
|
||||||
|
|
||||||
static box HakoAstEmitBox {
|
static box HakoAstEmitBox {
|
||||||
// Emit minimal AST JSON v0 from MapBox (stable order)
|
// Emit minimal AST JSON v0 from MapBox (stable order)
|
||||||
@ -17,7 +17,7 @@ static box HakoAstEmitBox {
|
|||||||
local i = 0
|
local i = 0
|
||||||
while i < n {
|
while i < n {
|
||||||
local v = arr.get(i)
|
local v = arr.get(i)
|
||||||
s = s + Str.json_quote(v)
|
s = s + me._json_quote(v)
|
||||||
if i != n-1 { s = s + "," }
|
if i != n-1 { s = s + "," }
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
@ -30,10 +30,10 @@ static box HakoAstEmitBox {
|
|||||||
local i = 0
|
local i = 0
|
||||||
while i < n {
|
while i < n {
|
||||||
local b = boxes.get(i)
|
local b = boxes.get(i)
|
||||||
local name = Str.json_quote(b.get("name"))
|
local name = me._json_quote(b.get("name"))
|
||||||
local is_static = b.get("is_static")
|
local is_static = b.get("is_static")
|
||||||
local methods = me._emit_methods(me._sort_methods(b.get("methods")))
|
local methods = me._emit_methods(me._sort_methods(b.get("methods")))
|
||||||
s = s + "{\"name\":" + name + ",\"is_static\":" + Str.int_to_str(is_static) + ",\"methods\":" + methods + "}"
|
s = s + "{\"name\":" + name + ",\"is_static\":" + me._itoa(is_static) + ",\"methods\":" + methods + "}"
|
||||||
if i != n-1 { s = s + "," }
|
if i != n-1 { s = s + "," }
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
@ -46,11 +46,11 @@ static box HakoAstEmitBox {
|
|||||||
local i = 0
|
local i = 0
|
||||||
while i < n {
|
while i < n {
|
||||||
local m = methods.get(i)
|
local m = methods.get(i)
|
||||||
local name = Str.json_quote(m.get("name"))
|
local name = me._json_quote(m.get("name"))
|
||||||
local arity = Str.int_to_str(m.get("arity"))
|
local arity = me._itoa(m.get("arity"))
|
||||||
// span is integer line number
|
// span is integer line number
|
||||||
local spanv = m.get("span"); if spanv == null { spanv = 0 }
|
local spanv = m.get("span"); if spanv == null { spanv = 0 }
|
||||||
s = s + "{\"name\":" + name + ",\"arity\":" + arity + ",\"span\":" + Str.int_to_str(spanv) + "}"
|
s = s + "{\"name\":" + name + ",\"arity\":" + arity + ",\"span\":" + me._itoa(spanv) + "}"
|
||||||
if i != n-1 { s = s + "," }
|
if i != n-1 { s = s + "," }
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
@ -70,11 +70,36 @@ static box HakoAstEmitBox {
|
|||||||
local out = new ArrayBox(); local i=0; while i<methods.size() { out.push(methods.get(i)); i=i+1 }
|
local out = new ArrayBox(); local i=0; while i<methods.size() { out.push(methods.get(i)); i=i+1 }
|
||||||
local n=out.size(); local a=0; while a<n { local b=a+1; while b<n {
|
local n=out.size(); local a=0; while a<n { local b=a+1; while b<n {
|
||||||
local ma=out.get(a); local mb=out.get(b)
|
local ma=out.get(a); local mb=out.get(b)
|
||||||
local ka = ma.get("name") + "/" + Str.int_to_str(ma.get("arity"))
|
local ka = ma.get("name") + "/" + me._itoa(ma.get("arity"))
|
||||||
local kb = mb.get("name") + "/" + Str.int_to_str(mb.get("arity"))
|
local kb = mb.get("name") + "/" + me._itoa(mb.get("arity"))
|
||||||
if kb < ka { local tmp=out.get(a); out.set(a,out.get(b)); out.set(b,tmp) }
|
if kb < ka { local tmp=out.get(a); out.set(a,out.get(b)); out.set(b,tmp) }
|
||||||
b=b+1 } a=a+1 }
|
b=b+1 } a=a+1 }
|
||||||
return out }
|
return out }
|
||||||
|
// Local helpers (self-contained)
|
||||||
|
_itoa(n) {
|
||||||
|
local v = 0 + n; if v == 0 { return "0" }
|
||||||
|
local digits = "0123456789"; local out = ""; local tmp = ""
|
||||||
|
while v > 0 { local d = v % 10; tmp = digits.substring(d,d+1) + tmp; v = v / 10 }
|
||||||
|
out = tmp; return out
|
||||||
|
}
|
||||||
|
_json_quote(s) {
|
||||||
|
if s == null { return "\"\"" }
|
||||||
|
local out = ""; local i=0; local n=s.length()
|
||||||
|
while i<n {
|
||||||
|
local ch = s.substring(i,i+1)
|
||||||
|
if ch == "\\" { out = out + "\\\\" } else {
|
||||||
|
if ch == "\"" { out = out + "\\\"" } else {
|
||||||
|
if ch == "\n" { out = out + "\\n" } else {
|
||||||
|
if ch == "\r" { out = out + "\\r" } else {
|
||||||
|
if ch == "\t" { out = out + "\\t" } else { out = out + ch }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return "\"" + out + "\""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static box HakoAstEmitMain { method main(args) { return 0 } }
|
static box HakoAstEmitMain { method main(args) { return 0 } }
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
// tools/hako_parser/parser_core.hako - HakoParserCoreBox (token-based MVP)
|
// tools/hako_parser/parser_core.hako - HakoParserCoreBox (token-based MVP)
|
||||||
using selfhost.shared.common.string_helpers as Str
|
|
||||||
using tools.hako_parser.tokenizer as HakoTokenizerBox
|
using tools.hako_parser.tokenizer as HakoTokenizerBox
|
||||||
|
|
||||||
static box HakoParserCoreBox {
|
static box HakoParserCoreBox {
|
||||||
@ -47,8 +46,13 @@ static box HakoParserCoreBox {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if me._eq(t, "INCLUDE") == 1 {
|
if me._eq(t, "INCLUDE") == 1 {
|
||||||
// include "path"
|
// include "path" (record line for diagnostics as string to keep emitter simple)
|
||||||
p = me._advance(p, N); local s=me._peek(toks, p, N); if me._eq(s, "STRING") == 1 { ast.get("includes").push(Str.int_to_str(s.get("line"))); p = me._advance(p, N) }
|
p = me._advance(p, N)
|
||||||
|
local s = me._peek(toks, p, N)
|
||||||
|
if me._eq(s, "STRING") == 1 {
|
||||||
|
local ln = s.get("line"); if ln == null { ln = 0 }
|
||||||
|
ast.get("includes").push(me._itoa(ln)); p = me._advance(p, N)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if me._eq(t, "STATIC") == 1 {
|
if me._eq(t, "STATIC") == 1 {
|
||||||
@ -115,6 +119,12 @@ static box HakoParserCoreBox {
|
|||||||
_peek(toks, idx, N) { if idx >= N { return null } return toks.get(idx) }
|
_peek(toks, idx, N) { if idx >= N { return null } return toks.get(idx) }
|
||||||
_eq(t, kind) { if t == null { return 0 } if t.get("type") == kind { return 1 } return 0 }
|
_eq(t, kind) { if t == null { return 0 } if t.get("type") == kind { return 1 } return 0 }
|
||||||
_advance(p, N) { if p < N { return p + 1 } return p }
|
_advance(p, N) { if p < N { return p + 1 } return p }
|
||||||
|
_itoa(n) {
|
||||||
|
local v = 0 + n; if v == 0 { return "0" }
|
||||||
|
local digits = "0123456789"; local out = ""; local tmp = ""
|
||||||
|
while v > 0 { local d = v % 10; tmp = digits.substring(d,d+1) + tmp; v = v / 10 }
|
||||||
|
out = tmp; return out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static box HakoParserCoreMain { method main(args) { return 0 } }
|
static box HakoParserCoreMain { method main(args) { return 0 } }
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// tools/hako_parser/tokenizer.hako - HakoTokenizerBox (Stage-3 aware tokenizer, MVP)
|
// tools/hako_parser/tokenizer.hako - HakoTokenizerBox (Stage-3 aware tokenizer, MVP)
|
||||||
// Produces tokens with type, lexeme, line, col. Handles strings (escapes), numbers,
|
// Produces tokens with type, lexeme, line, col. Handles strings (escapes), numbers,
|
||||||
// identifiers, and punctuation. Keywords are normalized to upper-case kinds.
|
// identifiers, and punctuation. Keywords are normalized to upper-case kinds.
|
||||||
using selfhost.shared.common.string_helpers as Str
|
// no external deps (self-contained tokenizer)
|
||||||
|
|
||||||
static box HakoTokenizerBox {
|
static box HakoTokenizerBox {
|
||||||
// Token: Map { type, lexeme, line, col }
|
// Token: Map { type, lexeme, line, col }
|
||||||
|
|||||||
@ -18,6 +18,12 @@ if [ -z "${NYASH_BIN:-}" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Debug convenience: HAKO_DEBUG=1 enables execution trace and log passthrough
|
||||||
|
if [ "${HAKO_DEBUG:-0}" = "1" ]; then
|
||||||
|
export HAKO_TRACE_EXECUTION=1
|
||||||
|
export HAKO_VERIFY_SHOW_LOGS=1
|
||||||
|
fi
|
||||||
|
|
||||||
# グローバル変数
|
# グローバル変数
|
||||||
export SMOKES_V2_LIB_LOADED=1
|
export SMOKES_V2_LIB_LOADED=1
|
||||||
export SMOKES_START_TIME=$(date +%s.%N)
|
export SMOKES_START_TIME=$(date +%s.%N)
|
||||||
@ -280,6 +286,7 @@ verify_mir_rc() {
|
|||||||
# Allow forcing Core with HAKO_VERIFY_V1_FORCE_CORE=1
|
# Allow forcing Core with HAKO_VERIFY_V1_FORCE_CORE=1
|
||||||
if grep -q '"schema_version"' "$json_path" 2>/dev/null; then
|
if grep -q '"schema_version"' "$json_path" 2>/dev/null; then
|
||||||
if [ "${HAKO_VERIFY_V1_FORCE_CORE:-0}" = "1" ]; then
|
if [ "${HAKO_VERIFY_V1_FORCE_CORE:-0}" = "1" ]; then
|
||||||
|
if [ "${HAKO_TRACE_EXECUTION:-0}" = "1" ]; then echo "[trace] executor: core (rust)" >&2; fi
|
||||||
"$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1; return $?
|
"$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1; return $?
|
||||||
fi
|
fi
|
||||||
# hv1 直行(main.rs 早期経路)。成功時は rc を採用、失敗時は Core にフォールバック。
|
# hv1 直行(main.rs 早期経路)。成功時は rc を採用、失敗時は Core にフォールバック。
|
||||||
@ -287,6 +294,7 @@ verify_mir_rc() {
|
|||||||
if [ "${HAKO_VERIFY_V1_FORCE_HAKOVM:-0}" != "1" ]; then
|
if [ "${HAKO_VERIFY_V1_FORCE_HAKOVM:-0}" != "1" ]; then
|
||||||
local hv1_rc; hv1_rc=$(verify_v1_inline_file "$json_path" || true)
|
local hv1_rc; hv1_rc=$(verify_v1_inline_file "$json_path" || true)
|
||||||
if [[ "$hv1_rc" =~ ^-?[0-9]+$ ]]; then
|
if [[ "$hv1_rc" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
if [ "${HAKO_TRACE_EXECUTION:-0}" = "1" ]; then echo "[trace] executor: hv1_inline (rust)" >&2; fi
|
||||||
local n=$hv1_rc; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n
|
local n=$hv1_rc; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@ -294,11 +302,13 @@ verify_mir_rc() {
|
|||||||
if [ "${HAKO_VERIFY_V1_FORCE_HAKOVM:-0}" = "1" ]; then
|
if [ "${HAKO_VERIFY_V1_FORCE_HAKOVM:-0}" = "1" ]; then
|
||||||
local hv1_rc_force; hv1_rc_force=$(verify_v1_inline_file "$json_path" || true)
|
local hv1_rc_force; hv1_rc_force=$(verify_v1_inline_file "$json_path" || true)
|
||||||
if [[ "$hv1_rc_force" =~ ^-?[0-9]+$ ]]; then
|
if [[ "$hv1_rc_force" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
if [ "${HAKO_TRACE_EXECUTION:-0}" = "1" ]; then echo "[trace] executor: hv1_inline (rust)" >&2; fi
|
||||||
local n=$hv1_rc_force; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n
|
local n=$hv1_rc_force; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# No include+preinclude fallback succeeded → Core にフォールバック
|
# No include+preinclude fallback succeeded → Core にフォールバック
|
||||||
|
if [ "${HAKO_TRACE_EXECUTION:-0}" = "1" ]; then echo "[trace] executor: core (rust)" >&2; fi
|
||||||
"$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1
|
"$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1
|
||||||
return $?
|
return $?
|
||||||
fi
|
fi
|
||||||
@ -527,6 +537,7 @@ HCODE
|
|||||||
local mir_literal; mir_literal="$(printf '%s' "$mir_json" | jq -Rs .)"
|
local mir_literal; mir_literal="$(printf '%s' "$mir_json" | jq -Rs .)"
|
||||||
hv1_rc=$(run_hv1_inline_alias_wrapper "$mir_literal")
|
hv1_rc=$(run_hv1_inline_alias_wrapper "$mir_literal")
|
||||||
if [[ "$hv1_rc" =~ ^-?[0-9]+$ ]]; then
|
if [[ "$hv1_rc" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
if [ "${HAKO_TRACE_EXECUTION:-0}" = "1" ]; then echo "[trace] executor: hakovm (hako)" >&2; fi
|
||||||
local n=$hv1_rc; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi
|
local n=$hv1_rc; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi
|
||||||
return $n
|
return $n
|
||||||
fi
|
fi
|
||||||
@ -564,6 +575,7 @@ HCODE
|
|||||||
|
|
||||||
# Write MIR JSON to temp file and execute via Core
|
# Write MIR JSON to temp file and execute via Core
|
||||||
echo "$mir_json" > "$mir_json_path"
|
echo "$mir_json" > "$mir_json_path"
|
||||||
|
if [ "${HAKO_TRACE_EXECUTION:-0}" = "1" ]; then echo "[trace] executor: core (rust)" >&2; fi
|
||||||
"$NYASH_BIN" --mir-json-file "$mir_json_path" >/dev/null 2>&1
|
"$NYASH_BIN" --mir-json-file "$mir_json_path" >/dev/null 2>&1
|
||||||
local rc=$?
|
local rc=$?
|
||||||
|
|
||||||
@ -771,8 +783,16 @@ verify_v1_inline_file() {
|
|||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
local out
|
local out
|
||||||
out=$(HAKO_ROUTE_HAKOVM=1 HAKO_VERIFY_V1_FORCE_HAKOVM=1 NYASH_VERIFY_JSON="$(cat "$json_path")" \
|
# Optional: show full logs for debugging (default OFF)
|
||||||
"$NYASH_BIN" --backend vm /dev/null 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}')
|
if [ "${HAKO_VERIFY_SHOW_LOGS:-0}" = "1" ]; then
|
||||||
|
# Show all output to stderr, then extract numeric rc
|
||||||
|
HAKO_ROUTE_HAKOVM=1 HAKO_VERIFY_V1_FORCE_HAKOVM=1 NYASH_VERIFY_JSON="$(cat "$json_path")" \
|
||||||
|
"$NYASH_BIN" --backend vm /dev/null 2>&1 | tr -d '\r' | tee /tmp/hv1_debug.log >&2
|
||||||
|
out=$(awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}' /tmp/hv1_debug.log)
|
||||||
|
else
|
||||||
|
out=$(HAKO_ROUTE_HAKOVM=1 HAKO_VERIFY_V1_FORCE_HAKOVM=1 NYASH_VERIFY_JSON="$(cat "$json_path")" \
|
||||||
|
"$NYASH_BIN" --backend vm /dev/null 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}')
|
||||||
|
fi
|
||||||
if [[ "$out" =~ ^-?[0-9]+$ ]]; then
|
if [[ "$out" =~ ^-?[0-9]+$ ]]; then
|
||||||
# echo numeric rc and return success; caller normalizes/returns as exit code
|
# echo numeric rc and return success; caller normalizes/returns as exit code
|
||||||
echo "$out"
|
echo "$out"
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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_prog="/tmp/prog_registry_if_intint_$$.json"
|
||||||
|
cat >"$tmp_prog" <<'JSON'
|
||||||
|
{"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":44}},
|
||||||
|
"else":{"type":"Return","expr":{"type":"Int","value":7}}}
|
||||||
|
]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp_prog" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_prog" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 44 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_if_compare_intint_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_if_compare_intint_core_exec_canary_vm (rc=$rc, expect 44)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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_prog="/tmp/prog_registry_if_varint_$$.json"
|
||||||
|
cat >"$tmp_prog" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[
|
||||||
|
{"type":"Local","name":"x","expr":{"type":"Int","value":5}},
|
||||||
|
{"type":"If","cond":{"type":"Compare","op":">","lhs":{"type":"Var","name":"x"},"rhs":{"type":"Int","value":3}},
|
||||||
|
"then":{"type":"Return","expr":{"type":"Int","value":1}},
|
||||||
|
"else":{"type":"Return","expr":{"type":"Int","value":0}}}
|
||||||
|
]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp_prog" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_prog" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 1 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_if_compare_varint_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_if_compare_varint_core_exec_canary_vm (rc=$rc, expect 1)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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_prog="/tmp/prog_registry_if_varvar_$$.json"
|
||||||
|
cat >"$tmp_prog" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[
|
||||||
|
{"type":"Local","name":"a","expr":{"type":"Int","value":10}},
|
||||||
|
{"type":"Local","name":"b","expr":{"type":"Int","value":20}},
|
||||||
|
{"type":"If","cond":{"type":"Compare","op":"<=","lhs":{"type":"Var","name":"a"},"rhs":{"type":"Var","name":"b"}},
|
||||||
|
"then":{"type":"Return","expr":{"type":"Int","value":44}},
|
||||||
|
"else":{"type":"Return","expr":{"type":"Int","value":7}}}
|
||||||
|
]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp_prog" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_prog" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 44 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_if_compare_varvar_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_if_compare_varvar_core_exec_canary_vm (rc=$rc, expect 44)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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_prog="/tmp/prog_registry_binop_intint_$$.json"
|
||||||
|
cat >"$tmp_prog" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[{"type":"Return","expr":{"type":"Binary","op":"+","lhs":{"type":"Int","value":2},"rhs":{"type":"Int","value":3}}}]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp_prog" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_prog" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 5 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_return_binop_intint_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_return_binop_intint_core_exec_canary_vm (rc=$rc, expect 5)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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_prog="/tmp/prog_registry_binop_varint_$$.json"
|
||||||
|
cat >"$tmp_prog" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[
|
||||||
|
{"type":"Local","name":"x","expr":{"type":"Int","value":7}},
|
||||||
|
{"type":"Return","expr":{"type":"Binary","op":"*","lhs":{"type":"Var","name":"x"},"rhs":{"type":"Int","value":6}}}
|
||||||
|
]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp_prog" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_prog" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 42 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_return_binop_varint_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_return_binop_varint_core_exec_canary_vm (rc=$rc, expect 42)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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_prog="/tmp/prog_registry_retint_$$.json"
|
||||||
|
cat >"$tmp_prog" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[{"type":"Return","expr":{"type":"Int","value":42}}]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp_prog" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_prog" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 42 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_return_int_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_return_int_core_exec_canary_vm (rc=$rc, expect 42)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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/prog_registry_logical_and_$$.json"
|
||||||
|
cat >"$tmp" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[
|
||||||
|
{"type":"Local","name":"x","expr":{"type":"Bool","value":0}},
|
||||||
|
{"type":"Local","name":"y","expr":{"type":"Bool","value":1}},
|
||||||
|
{"type":"Return","expr":{"type":"Logical","op":"&&","lhs":{"type":"Var","name":"x"},"rhs":{"type":"Var","name":"y"}}}
|
||||||
|
]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_return_logical_and_varvar_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_return_logical_and_varvar_core_exec_canary_vm (rc=$rc, expect 0)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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/prog_registry_logical_or_$$.json"
|
||||||
|
cat >"$tmp" <<'JSON'
|
||||||
|
{"version":0,"kind":"Program","body":[
|
||||||
|
{"type":"Local","name":"a","expr":{"type":"Bool","value":1}},
|
||||||
|
{"type":"Local","name":"b","expr":{"type":"Bool","value":0}},
|
||||||
|
{"type":"Return","expr":{"type":"Logical","op":"||","lhs":{"type":"Var","name":"a"},"rhs":{"type":"Var","name":"b"}}}
|
||||||
|
]}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 HAKO_VERIFY_PRIMARY=core verify_program_via_builder_to_core "$tmp" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 1 ]; then
|
||||||
|
echo "[PASS] mirbuilder_registry_return_logical_or_varvar_core_exec_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] mirbuilder_registry_return_logical_or_varvar_core_exec_canary_vm (rc=$rc, expect 1)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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_json="/tmp/mir_v1_array_len_alias_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 0}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 3, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"len","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":3}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 2 ]; then echo "[PASS] array_len_alias_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] array_len_alias_vm (rc=$rc, want=2)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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_json="/tmp/mir_v1_array_length_alias_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 0}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 3, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"length","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":3}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 2 ]; then echo "[PASS] array_length_alias_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] array_length_alias_vm (rc=$rc, want=2)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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_json="/tmp/mir_v1_array_push_size_10_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 1}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 3, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"size","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":3}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 10 ]; then echo "[PASS] array_push_size_10_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] array_push_size_10_vm (rc=$rc, want=10)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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_json="/tmp/mir_v1_array_push_size_5_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 111}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 3, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"size","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":3}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 5 ]; then echo "[PASS] array_push_size_5_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] array_push_size_5_vm (rc=$rc, want=5)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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_json="/tmp/mir_v1_flow_across_blocks_array_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 0}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"const","dst":10, "value": {"type": "i64", "value": 1}},
|
||||||
|
{"op":"const","dst":11, "value": {"type": "i64", "value": 1}},
|
||||||
|
{"op":"compare","dst":12, "lhs":10, "rhs":11, "cmp":"Eq"},
|
||||||
|
{"op":"branch","cond":12, "then":1, "else":2}
|
||||||
|
]},
|
||||||
|
{"id":1,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 3, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"size","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":3}
|
||||||
|
]},
|
||||||
|
{"id":2,"instructions":[
|
||||||
|
{"op":"ret","value":2}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_V1_DISPATCHER_FLOW=1 HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 3 ]; then echo "[PASS] flow_across_blocks_array_size_canary_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] flow_across_blocks_array_size_canary_vm (rc=$rc, want=3)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Hakovm v1 dispatcher: ArrayBox push → size state increments (1→2→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_json="/tmp/mir_v1_array_push_size_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 111}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 3, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"size","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":3}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
# Expect rc=3 when stateful mir_call is ON and primary=hakovm
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm \
|
||||||
|
HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 \
|
||||||
|
verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 3 ]; then
|
||||||
|
echo "[PASS] hv1_mircall_array_push_size_state_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] hv1_mircall_array_push_size_state_canary_vm (rc=$rc, want=3)" >&2
|
||||||
|
exit 1
|
||||||
|
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Hakovm v1 dispatcher: MapBox set(new key) → size increments (1→2)
|
||||||
|
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_json="/tmp/mir_v1_map_set_size_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"MapBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": {"kind":"handle","box_type":"StringBox"}, "value": "k1"}},
|
||||||
|
{"op":"const", "dst": 3, "value": {"type": "i64", "value": 100}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"set","receiver":1}, "args": [2,3], "effects": []}},
|
||||||
|
{"op":"const", "dst": 4, "value": {"type": {"kind":"handle","box_type":"StringBox"}, "value": "k2"}},
|
||||||
|
{"op":"const", "dst": 5, "value": {"type": "i64", "value": 200}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"set","receiver":1}, "args": [4,5], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 6, "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"size","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":6}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
# Expect rc=2 when stateful mir_call (Map.set) is ON and primary=hakovm
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm \
|
||||||
|
HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 \
|
||||||
|
verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 2 ]; then
|
||||||
|
echo "[PASS] hv1_mircall_map_set_size_state_canary_vm"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FAIL] hv1_mircall_map_set_size_state_canary_vm (rc=$rc, want=2)" >&2
|
||||||
|
exit 1
|
||||||
|
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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_json="/tmp/mir_v1_map_set_dup_key_size_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"MapBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": {"kind":"handle","box_type":"StringBox"}, "value": "k1"}},
|
||||||
|
{"op":"const", "dst": 3, "value": {"type": "i64", "value": 100}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"set","receiver":1}, "args": [2,3], "effects": []}},
|
||||||
|
{"op":"const", "dst": 5, "value": {"type": "i64", "value": 200}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"set","receiver":1}, "args": [2,5], "effects": []}},
|
||||||
|
{"op":"const", "dst": 6, "value": {"type": {"kind":"handle","box_type":"StringBox"}, "value": "k2"}},
|
||||||
|
{"op":"const", "dst": 7, "value": {"type": "i64", "value": 300}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"set","receiver":1}, "args": [6,7], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 8, "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"size","receiver":1}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":8}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
# k1 (new) -> size=1, k1 (dup) -> size stays 1, k2 (new) -> size=2
|
||||||
|
if [ "$rc" -eq 2 ]; then echo "[PASS] map_set_dup_key_size_canary_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] map_set_dup_key_size_canary_vm (rc=$rc, want=2)" >&2; exit 1
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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
|
||||||
|
|
||||||
|
# value_state=1: Map.get returns stored value, Map.has reports presence
|
||||||
|
tmp_json="/tmp/mir_v1_map_value_state_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"MapBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": {"kind":"handle","box_type":"StringBox"}, "value": "k2"}},
|
||||||
|
{"op":"const", "dst": 3, "value": {"type": "i64", "value": 200}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"set","receiver":1}, "args": [2,3], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 4, "mir_call": {"callee": {"type":"Method","box_name":"MapBox","method":"get","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"ret","value":4}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_VALUESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 200 ]; then echo "[PASS] map_value_state_get_has_canary_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] map_value_state_get_has_canary_vm (rc=$rc, want=200)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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
|
||||||
|
|
||||||
|
# per_recv=0: global length key. push on A then size on B → 1
|
||||||
|
tmp_json="/tmp/mir_v1_perrecv_global_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 0}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 4, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 5, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"size","receiver":4}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":5}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=0 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 1 ]; then echo "[PASS] per_recv_global_canary_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] per_recv_global_canary_vm (rc=$rc, want=1)" >&2; exit 1
|
||||||
|
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
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
|
||||||
|
|
||||||
|
# per_recv=1: per-receiver length key. push on A then size on B → 0
|
||||||
|
tmp_json="/tmp/mir_v1_perrecv_per_$$.json"
|
||||||
|
cat > "$tmp_json" <<'JSON'
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"functions": [
|
||||||
|
{"name":"main","blocks":[
|
||||||
|
{"id":0,"instructions":[
|
||||||
|
{"op":"mir_call", "dst": 1, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"const", "dst": 2, "value": {"type": "i64", "value": 0}},
|
||||||
|
{"op":"mir_call", "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"push","receiver":1}, "args": [2], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 4, "mir_call": {"callee": {"type":"Constructor","box_type":"ArrayBox"}, "args": [], "effects": []}},
|
||||||
|
{"op":"mir_call", "dst": 5, "mir_call": {"callee": {"type":"Method","box_name":"ArrayBox","method":"size","receiver":4}, "args": [], "effects": []}},
|
||||||
|
{"op":"ret","value":5}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
HAKO_VERIFY_PRIMARY=hakovm HAKO_VM_MIRCALL_SIZESTATE=1 HAKO_VM_MIRCALL_SIZESTATE_PER_RECV=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp_json" || true
|
||||||
|
|
||||||
|
if [ "$rc" -eq 0 ]; then echo "[PASS] per_recv_per_canary_vm"; exit 0; fi
|
||||||
|
echo "[FAIL] per_recv_per_canary_vm (rc=$rc, want=0)" >&2; exit 1
|
||||||
|
|
||||||
20
tools/smokes/v2/profiles/quick/core/phase2170/run_all.sh
Normal file
20
tools/smokes/v2/profiles/quick/core/phase2170/run_all.sh
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
bash "$DIR/hv1_mircall_array_push_size_state_canary_vm.sh"
|
||||||
|
bash "$DIR/hv1_mircall_map_set_size_state_canary_vm.sh"
|
||||||
|
bash "$DIR/array_push_size_5_vm.sh"
|
||||||
|
bash "$DIR/array_push_size_10_vm.sh"
|
||||||
|
bash "$DIR/array_len_alias_vm.sh"
|
||||||
|
bash "$DIR/array_length_alias_vm.sh"
|
||||||
|
bash "$DIR/per_recv_global_canary_vm.sh"
|
||||||
|
bash "$DIR/per_recv_per_canary_vm.sh"
|
||||||
|
bash "$DIR/map_set_dup_key_size_canary_vm.sh"
|
||||||
|
bash "$DIR/map_value_state_get_has_canary_vm.sh"
|
||||||
|
bash "$DIR/flow_across_blocks_array_size_canary_vm.sh"
|
||||||
|
|
||||||
|
# dup-key non-increment now enforced
|
||||||
|
|
||||||
|
echo "[PASS] phase2170 all"
|
||||||
Reference in New Issue
Block a user