docs(20.39): add Next Steps and mark hv1 unify + string scanner fix DONE; loop count_param: accept swapped < / <= (cmp Gt/Ge); add canaries for swapped_lt/lte

This commit is contained in:
nyash-codex
2025-11-04 20:51:15 +09:00
parent 44a5158a14
commit 51bf7ff410
5 changed files with 113 additions and 26 deletions

View File

@ -21,6 +21,12 @@ Remaining (20.39 — typed IR & finalize)
- Remove include fallback from scripts直行のみ
- Sweep concatenations and update helpers.
Next Steps (ordered)
1) ループ比較の正規化拡張(>/>=/==/!= → Lt/Gt/Ge 正規化;左右スワップ++1調整、canary補強
2) SSOT 拡張compare/branch/jump/phi を cf_common に段階移行)
3) Unicode `\\uXXXX` 復号のトグル実装既定OFFcanary追加
4) hv1 一本化main入口と文字列スキャナ根治'対応+エスケープ拡張)を DONE に反映
Hotfix Plan — Using/Prelude Unification (SelfHost)
- Problem: .hako を NyashParser に通す経路でパース落ちInvalid expression
- Decision: プレリュードは“テキスト統合merge_prelude_text”に一本化。AST マージは撤退。
@ -50,29 +56,14 @@ Acceptance
- v1 φ/branch/jump の代表カナリーが IR 反復で strict PASS。tolerate ケースは期待と一致。
- Verify 既定が v1→HakoruneCore fallbackで quick 緑維持。
- Hako 構文を Nyash VM で実行しようとした場合、入口で FailFast診断メッセージ
- hv1 直行は main 入口で早期退出し、プラグイン初期化ログが出ないcanaryで保証
Changes (this step)
- Docs: phase20.39 に計画と進捗verify直行 DONEconcat-safety VM/Core DONEbuild2 適用済み lowersを反映
- Add typed IR box filehakorune-vm.ir.typesと module export構造のみ・参照用
- Concat-safety: StrCast 追加、VM/Core の “"" + <Box>” を置換canaries 緑維持)。
- V1PhiAdapterBox: robust incoming scan (multipair, spaces/newlines) using arrayend scanner.
- V1SchemaBox: new block_segment_wo_phi(json, bid) to build IR segment without φ.
- V1SchemaBox: phi_table_for_block(json,bid) 追加dst と incoming[[pred,val],…] を返す)
- NyVmDispatcherV1Box(FLOW): IRbased loopblock_segment_wo_phi entry φ は V1PhiTableBox.apply_table_at_entry で一括適用scan は後退互換のfallbackに
- Entry φ application: still centralized in V1PhiTableBox; added optional trace hooks (HAKO_V1_FLOW_TRACE=1).
- Canary updated to enable experiment flag: v1_hakovm_phi_simple_flow_canary_vm.sh sets HAKO_V1_ALLOW_PHI_EXPERIMENT=1.
- φ canaries 追加: multiincoming / multiphi / whitespace混在 → いずれも PASS
- v1 extern canaries20.38: env.get / warn / error / emit / codegen を PASS 化タグrc=0
- hv1 inline prelude: prelude_v1.hako を pathusing に切替inline -c での alias 揺れ回避)。
- phase2038 hv1 inline canary ドライバは ALLOW_USING_FILE を付与し、preinclude で安定化を準備(現状は alias 未解決で SKIP
- extern stub canariesemit/codegen: include 依存を撤去し、rc=0 のみ確認に簡素化(タグ観測は hv1 inline カナリーへ委譲)。
- test_runner の provider タグ用シムを撤去hv1 inline が安定したため)。
- vm.rs に hv1 ルーティング口を追加opt-in、FailFast 緩和下で NYASH_VERIFY_JSON の inline を hv1 wrapper にルート)。
- verify は envNYASH_VERIFY_JSON受け渡し末尾数値抽出で rc を安定化。
- Dispatcher(FLOW) は構造IRの反復へ完全切替scan断片を撤去
- V1Schema.get_function_ir: blocks/phi_table 構築を実装op/text一部フィールド抽出で dispatcher の負荷低減)。
- Program(JSON v0) → MIR(JSON v0) bridge: Continue 降下の根治増分注入→cond へ)。
- test_runner: verify_program_via_builder_to_coreenv 渡しRust CLI fallbackを導入。
- hv1 direct を main 入口で早期退出に一本化(プラグイン初期化前で rc のみ出力)
- Tokenizer 根治: Stage3 でシングルクォート受理、エスケープ(\\/ \\b \\f \\' \\r拡張
- SSOT: Builder BinOp を `emit_binop_to_dst` で発行(挙動不変)。
- Loop 正規化: CountParam に `L<i` / `L<=i`降順を追加受理cmp=Gt/Ge。canary 2本追加swapped_lt/lte
- Docs: phase20.39 Next Steps を追記、hv1一本化/文字列スキャナ根治を DONE として明記。
// Loop compares normalization (Step1)
- Loop lowerssimple/count_param/sum_bc: Compare 受理を拡張し Lt 形へ正規化。

View File

@ -79,8 +79,10 @@ static box LowerLoopCountParamBox {
cmp = (op == ">") ? "Gt" : "Ge"
} else { return null }
} else {
// swapped (Int on lhs, Var i on rhs): L > i / L >= i (ascending)
if op != ">" && op != ">=" { return null }
// swapped (Int on lhs, Var i on rhs):
// L > i / L >= i → ascendingi < L / i < L+1→ cmp="Lt"limit +1 for >=
// L < i / L <= i → descendingi > L / i >= L→ cmp="Gt" / "Ge"
if op != ">" && op != ">=" && op != "<" && op != "<=" { return null }
local k_lhs = s.indexOf("\"lhs\":{", k_cmp); if k_lhs < 0 { return null }
local k_lim_t2 = s.indexOf("\"type\":\"Int\"", k_lhs)
if k_lim_t2 >= 0 {
@ -91,8 +93,17 @@ static box LowerLoopCountParamBox {
limit = PatternUtilBox.find_local_int_before(s, lname3, k_cmp)
}
if limit == null { return null }
if op == ">=" { limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1) }
cmp = "Lt"
if op == ">" {
cmp = "Lt"
} else if op == ">=" {
limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1)
cmp = "Lt"
} else if op == "<" {
cmp = "Gt"
} else {
// op == "<="
cmp = "Ge"
}
}
// Body increment: Local i = Binary('+', Var i, Int step)
local k_body_i = s.indexOf("\"name\":\"" + varname + "\"", k_loop)

View File

@ -58,12 +58,19 @@ static box LowerLoopSimpleBox {
return null
}
} else {
// swapped: we expect op to be '>' or '>='
// swapped: L ? i
if op == ">" {
// L > i ≡ i < L
} else if op == ">=" {
// L >= i ≡ i <= L ≡ i < (L+1)
limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1)
} else if op == "<" {
// L < i ≡ i > L → for simple ascending loop (init=0, step=1) this pattern is not canonical;
// we treat as descending form not supported by simple box
return null
} else if op == "<=" {
// L <= i ≡ i >= L — same reason as above
return null
} else {
return null
}

View File

@ -0,0 +1,39 @@
#!/bin/bash
# Loop internal — swapped compare (Int < Var) → normalize to i > limit (descending); expect rc == 0 (no iterations)
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/program_loop_swapped_lt_$$.json"
cat > "$tmp_json" <<'JSON'
{
"version": 0,
"kind": "Program",
"body": [
{ "type":"Local", "name":"i", "expr": {"type":"Int","value":0} },
{ "type":"Local", "name":"s", "expr": {"type":"Int","value":0} },
{ "type":"Loop",
"cond": {"type":"Compare","op":"<","lhs":{"type":"Int","value":3},"rhs":{"type":"Var","name":"i"}},
"body": [
{ "type":"Local", "name":"s", "expr": {"type":"Binary","op":"+","lhs":{"type":"Var","name":"s"},"rhs":{"type":"Int","value":1}} },
{ "type":"Local", "name":"i", "expr": {"type":"Binary","op":"-","lhs":{"type":"Var","name":"i"},"rhs":{"type":"Int","value":1}} }
]
},
{ "type":"Return", "expr": {"type":"Var","name":"s"} }
]
}
JSON
set +e
HAKO_VERIFY_PRIMARY=core verify_mir_rc "$tmp_json" >/dev/null 2>&1
rc=$?
set -e
rm -f "$tmp_json" || true
if [ "$rc" -eq 0 ]; then
echo "[PASS] mirbuilder_loop_swapped_lt_core_exec_canary_vm"
exit 0
fi
echo "[FAIL] mirbuilder_loop_swapped_lt_core_exec_canary_vm (rc=$rc, expect 0)" >&2; exit 1

View File

@ -0,0 +1,39 @@
#!/bin/bash
# Loop internal — swapped compare (Int <= Var) → normalize to i >= limit (descending); with i starts at 0, no iterations → rc==0
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/program_loop_swapped_lte_$$.json"
cat > "$tmp_json" <<'JSON'
{
"version": 0,
"kind": "Program",
"body": [
{ "type":"Local", "name":"i", "expr": {"type":"Int","value":0} },
{ "type":"Local", "name":"s", "expr": {"type":"Int","value":0} },
{ "type":"Loop",
"cond": {"type":"Compare","op":"<=","lhs":{"type":"Int","value":3},"rhs":{"type":"Var","name":"i"}},
"body": [
{ "type":"Local", "name":"s", "expr": {"type":"Binary","op":"+","lhs":{"type":"Var","name":"s"},"rhs":{"type":"Int","value":1}} },
{ "type":"Local", "name":"i", "expr": {"type":"Binary","op":"-","lhs":{"type":"Var","name":"i"},"rhs":{"type":"Int","value":1}} }
]
},
{ "type":"Return", "expr": {"type":"Var","name":"s"} }
]
}
JSON
set +e
HAKO_VERIFY_PRIMARY=core verify_mir_rc "$tmp_json" >/dev/null 2>&1
rc=$?
set -e
rm -f "$tmp_json" || true
if [ "$rc" -eq 0 ]; then
echo "[PASS] mirbuilder_loop_swapped_lte_core_exec_canary_vm"
exit 0
fi
echo "[FAIL] mirbuilder_loop_swapped_lte_core_exec_canary_vm (rc=$rc, expect 0)" >&2; exit 1