#!/usr/bin/env bash # hakorune_emit_mir.sh — Emit MIR(JSON) using Hakorune Stage‑B + MirBuilder (Hako‑first) # # Usage: tools/hakorune_emit_mir.sh # Notes: # - Runs the Stage‑B compiler (Hako) to emit Program(JSON v0), then feeds it to MirBuilderBox.emit_from_program_json_v0. # - Defaults to the Hakorune VM path; no inline Ny compiler; Stage‑3 enabled. # - Keeps defaults conservative: no global flips; this is a helper for dev/CI scripts. set -euo pipefail if [ "$#" -ne 2 ]; then echo "Usage: $0 " >&2 exit 2 fi IN="$1" OUT="$2" 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 RAW_KEEP="${NYASH_EMIT_MIR_KEEP_RAW:-0}" RAW_DIR="${NYASH_EMIT_MIR_RAW_DIR:-$ROOT/logs/emit_mir}" if [ "$RAW_KEEP" = "1" ]; then mkdir -p "$RAW_DIR" 2>/dev/null || RAW_KEEP=0 fi timestamp_now() { date +%Y%m%d_%H%M%S; } # Resolve nyash/hakorune binary via test_runner helper (ensures consistent env) if [ ! -f "$IN" ]; then echo "[FAIL] input not found: $IN" >&2 exit 1 fi # Resolve nyash/hakorune binary (simple detection; test_runner will override later if sourced) if [ -z "${NYASH_BIN:-}" ]; then if [ -x "$ROOT/target/release/hakorune" ]; then export NYASH_BIN="$ROOT/target/release/hakorune" else export NYASH_BIN="$ROOT/target/release/nyash" fi fi # Allow legacy 'nyash' binary when invoked via this helper (Stage0 bootstrap). # This keeps Stage‑B / MirBuilder scripts working while the main CLI migrates to 'hakorune'. export HAKO_ALLOW_NYASH="${HAKO_ALLOW_NYASH:-1}" export NYASH_ALLOW_NYASH="${NYASH_ALLOW_NYASH:-1}" # Store CODE in temp file to avoid subshell expansion issues CODE_TMP=$(mktemp --suffix=.hako) trap 'rm -f "$CODE_TMP" || true' EXIT cp "$IN" "$CODE_TMP" # Stage1 using resolver: prepare modules list from nyash.toml if [ -z "${HAKO_STAGEB_MODULES_LIST:-}" ]; then HAKO_STAGEB_MODULES_LIST=$( python3 - "$ROOT" <<'PY' 2>/dev/null import sys from pathlib import Path root = Path(sys.argv[1]) toml_path = root / "nyash.toml" try: lines = toml_path.read_text(encoding="utf-8").splitlines() except Exception: print("", end="") raise SystemExit(0) collect = False entries = [] for raw in lines: line = raw.strip() if not line or line.startswith("#"): continue if line.startswith("["): collect = (line == "[modules]") continue if not collect: continue if "=" not in line: continue key, value = line.split("=", 1) key = key.strip().strip('"') value = value.split("#", 1)[0].strip() if value.startswith('"') and value.endswith('"'): value = value[1:-1] if not key or not value: continue entries.append(f"{key}={value}") print("|||".join(entries), end="") PY ) || HAKO_STAGEB_MODULES_LIST="" export HAKO_STAGEB_MODULES_LIST fi export HAKO_STAGEB_APPLY_USINGS="${HAKO_STAGEB_APPLY_USINGS:-1}" # Phase 21.8: Extract using imports and build JSON map for MirBuilder # This enables MirBuilder to recognize static box references like MatI64, IntArrayCore, MirBuilderBox. IMPORTS_JSON="{}" if [ "${NYASH_ENABLE_USING:-1}" = "1" ] || [ "${HAKO_ENABLE_USING:-1}" = "1" ]; then # Delegate parsing to Python for robustness: # - Handle `using ns.path.Type as Alias` → Alias # - Handle alias-less `using ns.path.Type` → Type # - Ignore path-style using (`"path.hako"`, `./`, `/`, `*.hako`) IMPORTS_JSON=$(python3 - "$CODE_TMP" <<'PY' 2>/dev/null || echo "{}" import sys, json path = sys.argv[1] try: text = open(path, "r", encoding="utf-8").read().splitlines() except Exception: print("{}", end="") raise SystemExit(0) imports = {} for raw in text: line = raw.lstrip() if not line.startswith("using "): continue # Strip trailing line comment if "//" in line: line = line.split("//", 1)[0] line = line.strip() if not line.startswith("using "): continue body = line[len("using "):].strip().rstrip(";").strip() if not body: continue alias = None # Split by " as " lower = body idx = lower.find(" as ") if idx >= 0: target = body[:idx].strip() alias = body[idx + 4 :].strip() else: target = body if not target: continue # Skip path-style targets if target.startswith('"') or target.startswith("./") or target.startswith("/") or target.endswith(".hako") or target.endswith(".nyash"): continue if alias is None: # Derive alias from last segment of namespace alias = target.split(".")[-1].strip() if not alias: continue # Very small ident check (letters/digits/_) if not all((c.isalnum() or c == "_") for c in alias): continue imports[alias] = alias print(json.dumps(imports), end="") PY ) if [ -z "$IMPORTS_JSON" ]; then IMPORTS_JSON="{}" fi fi export HAKO_MIRBUILDER_IMPORTS="$IMPORTS_JSON" # Run direct emit with optional raw capture (label helps identify caller) run_direct_emit() { local label="$1" out_path="$2" in_path="$3" local rc=0 if [ "$RAW_KEEP" = "1" ]; then local tmp_out tmp_out=$(mktemp --suffix .emit_direct_raw) "$NYASH_BIN" --emit-mir-json "$out_path" "$in_path" >"$tmp_out" 2>&1 || rc=$? local ts=$(timestamp_now) local log_path="$RAW_DIR/direct_emit_${label}_${ts}_$$.log" { local raw_len=$(wc -c < "$tmp_out" | tr -d ' ') echo "[emit/raw] label=$label rc=$rc raw_len=$raw_len" cat "$tmp_out" } > "$log_path" 2>/dev/null || true rm -f "$tmp_out" 2>/dev/null || true else "$NYASH_BIN" --emit-mir-json "$out_path" "$in_path" >/dev/null 2>&1 || rc=$? fi if [ $rc -ne 0 ]; then # Quick stderr summary to avoid opening the raw file local raw_len=0 if [ -f "${tmp_out:-}" ]; then raw_len=$(wc -c < "${tmp_out:-}" | tr -d ' ') echo "[emit/direct] rc=$rc raw_len=$raw_len (head 100 lines)" >&2 head -n 100 "${tmp_out:-}" >&2 || true else echo "[emit/direct] rc=$rc (no tmp_out captured)" >&2 fi fi return $rc } # Check if FORCE jsonfrag mode is requested (bypasses Stage-B entirely) if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then # Extract limit from code using grep/awk limit=$(cat "$CODE_TMP" | grep -o '[0-9]\+' | head -1 || echo "10") # Generate minimal while-form MIR(JSON) directly (executable semantics) # PHI incoming format: [[value_register, predecessor_block_id], ...] echo "[emit/jsonfrag] FORCE min-loop MIR (dev-only)" >&2 cat > "$OUT" <= 0: if stdin[pos] == '{': depth += 1 if depth == 1: # Found the start brace break elif stdin[pos] == '}': depth -= 1 pos -= 1 if pos < 0: sys.exit(1) # Now walk forward from pos, tracking braces to find the matching closing brace obj_start = pos depth = 0 in_string = False escape = False i = obj_start while i < len(stdin): ch = stdin[i] if escape: escape = False elif in_string: if ch == '\\': escape = True elif ch == '"': in_string = False else: if ch == '"': in_string = True elif ch == '{': depth += 1 elif ch == '}': depth -= 1 if depth == 0: # Found the matching closing brace print(stdin[obj_start:i+1]) sys.exit(0) i += 1 # If we get here, no matching brace found sys.exit(1) PYEOF } extract_program_json() { local input="$1" local result # Try 1: Python balancer (existing) result=$(echo "$input" | extract_program_json_py 2>/dev/null || true) if [ -n "$result" ] && echo "$result" | grep -q '"kind".*"Program"'; then echo "$result" return 0 fi # Try 2: Simple awk fallback result=$(echo "$input" | awk '/^\{/,/^\}$/') if [ -n "$result" ] && echo "$result" | grep -q '"kind".*"Program"'; then echo "$result" return 0 fi # Try 3: Ruby fallback (if available) if command -v ruby >/dev/null 2>&1; then result=$(echo "$input" | ruby -e 'puts STDIN.read[/\{.*"kind".*"Program".*\}/m]' 2>/dev/null || true) if [ -n "$result" ]; then echo "$result" return 0 fi fi # All fallbacks failed return 1 } # Helper: surface common Stage‑B / parser / using errors in a concise, friendly way. diagnose_stageb_failure() { local raw="$1" # Stage‑3 local keyword misconfig if printf '%s\n' "$raw" | grep -q "Undefined variable: local"; then echo "[stageb/diagnose] Undefined variable: local" >&2 echo "[stageb/hint] 'local' は Stage‑3 キーワードだよ。" >&2 echo "[stageb/hint] NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 を有効にするか、tools/hakorune_emit_mir.sh 経由で実行してね。" >&2 fi # Generic parse error if printf '%s\n' "$raw" | grep -q "Parse error"; then echo "[stageb/diagnose] Parse error detected in Stage‑B output." >&2 echo "[stageb/hint] HAKO_SELFHOST_TRACE=1 を立ててもう一度実行すると、最初の 200 文字が表示されるよ。" >&2 fi # VM execution errors from Stage‑B itself (MIR builder / verifier side) if printf '%s\n' "$raw" | grep -q "Invalid value: use of undefined value"; then echo "[stageb/diagnose] VM reported 'use of undefined value' during Stage‑B execution." >&2 echo "[stageb/hint] Stage‑B が生成した MIR に未定義レジスタ (%0 など) が含まれている可能性があるよ。" >&2 echo "[stageb/hint] 詳細を確認するには NYASH_VM_VERIFY_MIR=1 を立てて compiler_stageb.hako を直接実行してね。" >&2 echo "[stageb/hint] 代表的な原因と掘り先は docs/private/roadmap/phases/phase-20.33/DEBUG.md を参照してね (Stage1UsingResolverBox / FuncScannerBox 周り)。" >&2 fi # Using resolver issues (Stage‑B side) local using_lines using_lines=$(printf '%s\n' "$raw" | grep -E "\[using\] not found: " | sort -u || true) if [ -n "$using_lines" ]; then echo "[stageb/diagnose] Missing using modules detected (Stage‑B):" >&2 echo "$using_lines" >&2 echo "[stageb/hint] nyash.toml の [modules] に該当 Box を追加するか、HAKO_STAGEB_MODULES_LIST / HAKO_STAGEB_APPLY_USINGS を確認してね。" >&2 fi } set +e if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then code_len=$(wc -c < "$CODE_TMP" | tr -d ' ') echo "[emit:trace] Stage-B: Starting parse of input (${code_len} chars)..." >&2 fi stageb_raw_log="" # Decide Stage-B entry (direct compiler_stageb or compiler.hako --stage-b) stageb_target="$ROOT/lang/src/compiler/entry/compiler.hako" stageb_extra=(--stage-b --stage3) # Legacy: allow direct compiler_stageb.hako when explicitly requested if [ "${NYASH_EMIT_USE_COMPILER:-1}" = "0" ]; then stageb_target="$ROOT/lang/src/compiler/entry/compiler_stageb.hako" stageb_extra=() fi # Optional: log which entry we picked if [ "${NYASH_EMIT_MIR_TRACE:-0}" = "1" ]; then echo "[emit/trace] stageb target=$stageb_target extra=${stageb_extra[*]:-}" >&2 fi # Run Stage-B with temp file (avoid subshell CODE variable expansion) PROG_JSON_RAW=$(cd "$ROOT" && \ NYASH_JSON_ONLY=${NYASH_JSON_ONLY:-1} NYASH_DISABLE_NY_COMPILER=${NYASH_DISABLE_NY_COMPILER:-1} HAKO_DISABLE_NY_COMPILER=${HAKO_DISABLE_NY_COMPILER:-1} \ HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \ NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_ENABLE_USING=${NYASH_ENABLE_USING:-1} HAKO_ENABLE_USING=${HAKO_ENABLE_USING:-1} \ "$NYASH_BIN" --backend vm "$stageb_target" -- "${stageb_extra[@]}" --source "$(cat "$CODE_TMP")" 2>&1) rc=$? stageb_rc=$rc # Optional raw trace to stderr for debugging extraction/filter問題 if [ "${NYASH_EMIT_MIR_TRACE:-0}" = "1" ]; then echo "[emit/trace] Stage-B raw len=${#PROG_JSON_RAW} rc=${stageb_rc}" >&2 printf '%s\n' "$PROG_JSON_RAW" >&2 fi if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[emit:trace] Stage-B: Raw output length=${#PROG_JSON_RAW} chars, rc=$rc" >&2 fi # Extract Program JSON from raw output PROG_JSON_OUT=$(extract_program_json "$PROG_JSON_RAW" 2>/dev/null || true) extract_rc=$? stageb_hit=$(printf '%s' "$PROG_JSON_OUT" | grep -c '"kind":"Program"' || true) stageb_raw_len=${#PROG_JSON_RAW} echo "[emit/stageb] rc_stageb=${stageb_rc} extract_rc=${extract_rc} raw_len=${stageb_raw_len} program_hits=${stageb_hit}" >&2 if [ "$RAW_KEEP" = "1" ]; then ts=$(timestamp_now) stageb_raw_log="$RAW_DIR/stageb_emit_${ts}_$$.log" { echo "[emit/raw] cmd=${stageb_target##*/} rc_stageb=${stageb_rc} extract_rc=${extract_rc} raw_len=${stageb_raw_len} program_hits=${stageb_hit}" echo "[emit/raw] src=$IN" echo "[emit/raw] --- stdout+stderr ---" printf '%s' "$PROG_JSON_RAW" } > "$stageb_raw_log" 2>/dev/null || true if [ $extract_rc -eq 0 ] && [ -n "$PROG_JSON_OUT" ]; then printf '%s' "$PROG_JSON_OUT" > "$RAW_DIR/stageb_emit_${ts}_$$.program.json" 2>/dev/null || true fi fi if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then if [ $extract_rc -eq 0 ] && [ -n "$PROG_JSON_OUT" ]; then echo "[emit:trace] Stage-B: SUCCESS - Generated Program(JSON) (${#PROG_JSON_OUT} chars)" >&2 # Show first 200 chars for validation prog_head=$(printf '%s' "$PROG_JSON_OUT" | head -c 200) echo "[emit:trace] Stage-B: prog_json_head: $prog_head..." >&2 else echo "[emit:trace] Stage-B: FAILED - extract_rc=$extract_rc, output_len=${#PROG_JSON_OUT}" >&2 if [ -n "$PROG_JSON_RAW" ]; then echo "[emit:trace] Stage-B: Raw output first 200 chars:" >&2 printf '%s' "$PROG_JSON_RAW" | head -c 200 >&2 echo "" >&2 fi fi fi # Update rc to reflect extraction result stageb_ok=1 if [ $extract_rc -ne 0 ] || [ -z "$PROG_JSON_OUT" ]; then rc=1 stageb_ok=0 fi set -e # If Stage-B fails, skip to direct MIR emit paths (provider/legacy) if [ $rc -ne 0 ] || [ -z "$PROG_JSON_OUT" ] || [ "$stageb_hit" -eq 0 ]; then # Diagnose common Stage‑B errors before falling back diagnose_stageb_failure "$PROG_JSON_RAW" # Stage-B not available - fall back to legacy CLI path directly # Skip the intermediate Program(JSON) step and emit MIR directly echo "[emit/stageb] failed_or_empty stageb_ok=${stageb_ok} rc_stageb=${stageb_rc} extract_rc=${extract_rc} hits=${stageb_hit}" >&2 if [ "${NYASH_EMIT_MIR_TRACE:-0}" = "1" ]; then echo "[emit/stageb] raw_len=${stageb_raw_len}" >&2 fi direct_ok=0 if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \ HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \ HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \ NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \ NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \ run_direct_emit "fallback" "$OUT" "$IN"; then direct_ok=1 echo "[OK] MIR JSON written (direct-emit): $OUT" exit 0 fi echo "[emit/direct] failed rc_stageb=${stageb_rc} extract_rc=${extract_rc} direct_ok=${direct_ok} stageb_hits=${stageb_hit}" >&2 echo "[FAIL] Stage-B and direct MIR emit both failed" >&2 exit 1 fi # Quick validation for Program(JSON v0) if ! printf '%s' "$PROG_JSON_OUT" | grep -q '"kind"\s*:\s*"Program"'; then # Invalid Program JSON - fall back to direct emit(事前に簡単な診断を出す) diagnose_stageb_failure "$PROG_JSON_RAW" direct_ok=0 if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \ HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \ HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \ NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \ NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \ run_direct_emit "invalid_program" "$OUT" "$IN"; then direct_ok=1 echo "[OK] MIR JSON written (direct-emit-fallback): $OUT" exit 0 fi echo "[emit/direct] invalid_program rc_stageb=${stageb_rc} extract_rc=${extract_rc} direct_ok=${direct_ok} stageb_hits=${stageb_hit}" >&2 echo "[FAIL] Stage‑B output invalid and direct emit failed" >&2 exit 1 fi # 2) Convert Program(JSON v0) → MIR(JSON) # Prefer selfhost builder first when explicitly requested; otherwise use delegate (Gate‑C) for stability. try_selfhost_builder() { local prog_json="$1" out_path="$2" # FORCE=1 direct assembly shortcut (dev toggle, bypasses using resolution) if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then # Extract limit from Program(JSON) using grep/awk local limit=$(printf '%s' "$prog_json" | grep -o '"type":"Int","value":[0-9]*' | head -1 | grep -o '[0-9]*$' || echo "10") # Generate minimal while-form MIR(JSON) directly (executable semantics) # PHI incoming format: [[value_register, predecessor_block_id], ...] cat > "$out_path" <<'MIRJSON' { "functions": [{ "name": "main", "params": [], "locals": [], "blocks": [ { "id": 0, "instructions": [ {"op": "const", "dst": 1, "value": {"type": "i64", "value": 0}}, {"op": "const", "dst": 2, "value": {"type": "i64", "value": LIMIT_PLACEHOLDER}}, {"op": "jump", "target": 1} ] }, { "id": 1, "instructions": [ {"op": "phi", "dst": 6, "incoming": [[2, 0], [6, 2]]}, {"op": "phi", "dst": 3, "incoming": [[1, 0], [5, 2]]}, {"op": "compare", "operation": "<", "lhs": 3, "rhs": 6, "dst": 4}, {"op": "branch", "cond": 4, "then": 2, "else": 3} ] }, { "id": 2, "instructions": [ {"op": "const", "dst": 10, "value": {"type": "i64", "value": 1}}, {"op": "binop", "operation": "+", "lhs": 3, "rhs": 10, "dst": 5}, {"op": "jump", "target": 1} ] }, { "id": 3, "instructions": [ {"op": "ret", "value": 3} ] } ] }] } MIRJSON # Provider-first delegate: call env.mirbuilder.emit(prog_json) and capture v1 JSON try_provider_emit() { local prog_json="$1" out_path="$2" local tmp_hako; tmp_hako=$(mktemp --suffix .hako) cat >"$tmp_hako" <<'HCODE' using "hako.mir.builder.internal.jsonfrag_normalizer" as NormBox static box Main { method main(args) { local p = env.get("HAKO_BUILDER_PROGRAM_JSON") if p == null { print("[provider/emit:nojson]"); return 1 } local a = new ArrayBox(); a.push(p) local out = hostbridge.extern_invoke("env.mirbuilder", "emit", a) // Optional normalization (dev): apply JsonFrag normalizer/purifier to provider output { local nv = env.get("HAKO_MIR_NORMALIZE_PROVIDER") if nv != null && ("" + nv) == "1" { local out_s = "" + out out = NormBox.normalize_all(out_s) } } print("[provider/emit:ok]") print("[MIR_OUT_BEGIN]") print("" + out) print("[MIR_OUT_END]") return 0 } } HCODE local tmp_stdout; tmp_stdout=$(mktemp) trap 'rm -f "$tmp_hako" "$tmp_stdout" || true' RETURN set +e (cd "$ROOT" && \ NYASH_DISABLE_PLUGINS=1 NYASH_FILEBOX_MODE="core-ro" \ NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 NYASH_PARSER_ALLOW_SEMICOLON=1 \ HAKO_BUILDER_PROGRAM_JSON="$prog_json" \ "$NYASH_BIN" --backend vm "$tmp_hako" 2>&1 | tee "$tmp_stdout" >/dev/null) local rc=$? set -e if [ $rc -ne 0 ] || ! grep -q "\[provider/emit:ok\]" "$tmp_stdout"; then return 1 fi local mir mir=$(awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' "$tmp_stdout") if [ -z "$mir" ]; then return 1; fi # Write raw MIR JSON first printf '%s' "$mir" > "$out_path" # Optional AOT prep stage (run_json; no FileBox) # Trigger when HAKO_APPLY_AOT_PREP=1 or when fast/hoist/collections_hot are requested. if [ "${HAKO_APPLY_AOT_PREP:-0}" = "1" ] || [ "${NYASH_AOT_COLLECTIONS_HOT:-0}" = "1" ] || [ "${NYASH_LLVM_FAST:-0}" = "1" ] || [ "${NYASH_MIR_LOOP_HOIST:-0}" = "1" ]; then if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:trace] Applying AotPrep.run_json to MIR JSON..." >&2 fi _prep_hako=$(mktemp --suffix .hako) cat > "$_prep_hako" <<'HAKO' using selfhost.llvm.ir.aot_prep as AotPrepBox static box Main { method main(args) { local src = env.get("HAKO_PREP_INPUT") if src == null || src == "" { print("[prep:skip:empty]"); return 0 } local out = AotPrepBox.run_json(src) print("[PREP_OUT_BEGIN]") print(out) print("[PREP_OUT_END]") return 0 } } HAKO # Read MIR JSON and pass via env; capture between markers _prep_stdout=$(mktemp) _prep_stderr=$(mktemp) set +e HAKO_PREP_INPUT="$(cat "$out_path")" \ NYASH_FILEBOX_MODE=core-ro \ # AotPrep(run_json) は自己ホストVMで JSON を1回走査するだけなので、既定ではステップ上限なし(0)で実行する。 # 必要に応じて HAKO_VM_MAX_STEPS / NYASH_VM_MAX_STEPS を明示上書き可能。 HAKO_VM_MAX_STEPS="${HAKO_VM_MAX_STEPS:-0}" NYASH_VM_MAX_STEPS="${NYASH_VM_MAX_STEPS:-0}" \ NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 HAKO_USING_RESOLVER_FIRST=1 \ NYASH_AOT_COLLECTIONS_HOT=${NYASH_AOT_COLLECTIONS_HOT:-0} NYASH_LLVM_FAST=${NYASH_LLVM_FAST:-0} NYASH_MIR_LOOP_HOIST=${NYASH_MIR_LOOP_HOIST:-0} NYASH_AOT_MAP_KEY_MODE=${NYASH_AOT_MAP_KEY_MODE:-auto} \ NYASH_AOT_NUMERIC_CORE=${NYASH_AOT_NUMERIC_CORE:-0} NYASH_AOT_NUMERIC_CORE_TRACE=${NYASH_AOT_NUMERIC_CORE_TRACE:-0} \ NYASH_JSON_ONLY=${NYASH_JSON_ONLY:-1} \ "$NYASH_BIN" --backend vm "$_prep_hako" >"$_prep_stdout" 2>"$_prep_stderr" _rc=$? set -e if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:trace] AotPrep runner rc=$_rc" >&2 fi if [ $_rc -eq 0 ] && grep -q "\[PREP_OUT_BEGIN\]" "$_prep_stdout" && grep -q "\[PREP_OUT_END\]" "$_prep_stdout"; then awk '/\[PREP_OUT_BEGIN\]/{flag=1;next}/\[PREP_OUT_END\]/{flag=0}flag' "$_prep_stdout" > "$out_path" [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ] && echo "[provider/emit:trace] AotPrep applied (run_json)" >&2 # Optional: surface CollectionsHot trace lines for diagnostics when requested if [ "${NYASH_AOT_CH_TRACE:-0}" = "1" ]; then if command -v rg >/dev/null 2>&1; then rg -n '^\[aot/collections_hot\]' "$_prep_stdout" >&2 || true else grep '^\[aot/collections_hot\]' "$_prep_stdout" >&2 || true fi fi # Optional: surface NumericCore trace lines for diagnostics when requested if [ "${NYASH_AOT_NUMERIC_CORE_TRACE:-0}" = "1" ]; then if command -v rg >/dev/null 2>&1; then rg -n '^\[aot/numeric_core\]' "$_prep_stdout" >&2 || true else grep '^\[aot/numeric_core\]' "$_prep_stdout" >&2 || true fi fi else if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:trace] AotPrep skipped or failed (run_json) rc=$_rc" >&2 if [ -s "$_prep_stderr" ]; then echo "[provider/emit:trace] AotPrep stderr (tail):" >&2 tail -n 60 "$_prep_stderr" >&2 || true fi fi fi rm -f "$_prep_hako" "$_prep_stdout" "$_prep_stderr" 2>/dev/null || true fi echo "[OK] MIR JSON written (delegate:provider): $out_path" return 0 } # Replace LIMIT_PLACEHOLDER with actual limit sed -i "s/LIMIT_PLACEHOLDER/$limit/g" "$out_path" if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[selfhost-direct:ok] Direct MIR assembly (FORCE=1), limit=$limit" >&2 fi return 0 fi # Builder box selection (default: hako.mir.builder) local builder_box="${HAKO_MIR_BUILDER_BOX:-hako.mir.builder}" local tmp_hako; tmp_hako=$(mktemp --suffix .hako) if [ "$builder_box" = "hako.mir.builder.min" ]; then cat >"$tmp_hako" <<'HCODE' using "hako.mir.builder.internal.runner_min" as BuilderRunnerMinBox static box Main { method main(args) { local prog_json = env.get("HAKO_BUILDER_PROGRAM_JSON") if prog_json == null { print("[builder/selfhost-first:fail:nojson]"); return 1 } local mir_out = BuilderRunnerMinBox.run(prog_json) if mir_out == null { print("[builder/selfhost-first:fail:emit]"); return 1 } print("[builder/selfhost-first:ok]") print("[MIR_OUT_BEGIN]") print("" + mir_out) print("[MIR_OUT_END]") return 0 } } HCODE else cat >"$tmp_hako" <<'HCODE' using "__BUILDER_BOX__" as MirBuilderBox static box Main { method main(args) { local prog_json = env.get("HAKO_BUILDER_PROGRAM_JSON") if prog_json == null { print("[builder/selfhost-first:fail:nojson]"); return 1 } local mir_out = MirBuilderBox.emit_from_program_json_v0(prog_json, null) if mir_out == null { print("[builder/selfhost-first:fail:emit]"); return 1 } print("[builder/selfhost-first:ok]") print("[MIR_OUT_BEGIN]") print("" + mir_out) print("[MIR_OUT_END]") return 0 } } HCODE sed -i "s|__BUILDER_BOX__|$builder_box|g" "$tmp_hako" fi local tmp_stdout; tmp_stdout=$(mktemp) trap 'rm -f "$tmp_hako" "$tmp_stdout" || true' RETURN # Trace mode: analyze Program(JSON) before passing to builder if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then local prog_len=${#prog_json} local loop_count=$(printf '%s' "$prog_json" | grep -o '"type":"Loop"' 2>/dev/null | wc -l | tr -d ' \n') local cmp_count=$(printf '%s' "$prog_json" | grep -o '"type":"Compare"' 2>/dev/null | wc -l | tr -d ' \n') local array_count=$(printf '%s' "$prog_json" | grep -o '"type":"MethodCall"' 2>/dev/null | wc -l | tr -d ' \n') loop_count=${loop_count:-0} cmp_count=${cmp_count:-0} array_count=${array_count:-0} local cwd="$(pwd)" local toml_status="absent" if [ -f "$ROOT/nyash.toml" ]; then toml_status="present" fi echo "[builder/selfhost-first:trace] builder_box=$builder_box prog_json_len=$prog_len tokens=Loop:$loop_count,Compare:$cmp_count,MethodCall:$array_count cwd=$cwd nyash.toml=$toml_status" >&2 # Show first 200 chars of Program(JSON) for structural validation local prog_head=$(printf '%s' "$prog_json" | head -c 200) echo "[builder/selfhost-first:trace] prog_json_head: $prog_head..." >&2 fi set +e # Run from repo root to ensure nyash.toml is available for using resolution # Capture both stdout and stderr (2>&1) instead of discarding stderr local tmp_stderr; tmp_stderr=$(mktemp) trap 'rm -f "$tmp_hako" "$tmp_stdout" "$tmp_stderr" || true' RETURN if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[builder/selfhost-first:exec] Starting builder execution..." >&2 fi (cd "$ROOT" && \ HAKO_SELFHOST_NO_DELEGATE=1 \ HAKO_MIR_BUILDER_INTERNAL=1 HAKO_MIR_BUILDER_REGISTRY=1 \ HAKO_MIR_BUILDER_TRACE="${HAKO_SELFHOST_TRACE:-}" \ HAKO_MIR_BUILDER_LOOP_JSONFRAG="${HAKO_MIR_BUILDER_LOOP_JSONFRAG:-}" \ HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG="${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-}" \ HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE="${HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE:-}" \ HAKO_MIR_BUILDER_JSONFRAG_PURIFY="${HAKO_MIR_BUILDER_JSONFRAG_PURIFY:-}" \ HAKO_MIR_BUILDER_METHODIZE="${HAKO_MIR_BUILDER_METHODIZE:-}" \ HAKO_MIR_BUILDER_NORMALIZE_TAG="${HAKO_MIR_BUILDER_NORMALIZE_TAG:-}" \ HAKO_MIR_BUILDER_DEBUG="${HAKO_MIR_BUILDER_DEBUG:-}" \ NYASH_DISABLE_PLUGINS="${NYASH_DISABLE_PLUGINS:-0}" NYASH_FILEBOX_MODE="core-ro" HAKO_PROVIDER_POLICY="safe-core-first" \ NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 HAKO_USING_RESOLVER_FIRST=1 \ NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_USE_NY_COMPILER=0 HAKO_USE_NY_COMPILER=0 NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \ NYASH_MACRO_DISABLE=1 HAKO_MACRO_DISABLE=1 \ # Builder 実行時は Step budget を既定で「無制限(0)」にしておく(大きな Program(JSON) でも上限で落とさない)。 # 無限ループ疑いの診断時は HAKO_VM_MAX_STEPS/NYASH_VM_MAX_STEPS を明示上書きして使う想定。 HAKO_VM_MAX_STEPS="${HAKO_VM_MAX_STEPS:-0}" NYASH_VM_MAX_STEPS="${NYASH_VM_MAX_STEPS:-0}" \ HAKO_BUILDER_PROGRAM_JSON="$prog_json" \ NYASH_VM_DUMP_MERGED_HAKO="${NYASH_VM_DUMP_MERGED_HAKO:-${HAKO_SELFHOST_DUMP_MERGED_HAKO:-0}}" \ NYASH_VM_DUMP_MERGED_HAKO_PATH="${NYASH_VM_DUMP_MERGED_HAKO_PATH:-${HAKO_SELFHOST_DUMP_MERGED_HAKO_PATH:-}}" \ "$NYASH_BIN" --backend vm "$tmp_hako" 2>"$tmp_stderr" | tee "$tmp_stdout" >/dev/null) local rc=$? set -e if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[builder/selfhost-first:exec] Builder execution completed with rc=$rc" >&2 fi # Enhanced failure diagnostics with comprehensive logging if [ $rc -ne 0 ]; then if [ "${HAKO_SELFHOST_NO_DELEGATE:-0}" = "1" ] || [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[builder/selfhost-first:fail:child:rc=$rc]" >&2 echo "[builder/selfhost-first:fail:detail] First 20 lines of output:" >&2 head -n 20 "$tmp_stdout" >&2 || true echo "[builder/selfhost-first:fail:detail] Last 80 lines of output:" >&2 tail -n 80 "$tmp_stdout" >&2 || true if [ -s "$tmp_stderr" ]; then echo "[builder/selfhost-first:fail:stderr] First 20 lines:" >&2 head -n 20 "$tmp_stderr" >&2 || true echo "[builder/selfhost-first:fail:stderr] Last 40 lines:" >&2 tail -n 40 "$tmp_stderr" >&2 || true # Pretty diagnostics for missing using modules USING_MISSING=$(cat "$tmp_stdout" "$tmp_stderr" 2>/dev/null | grep -Eo "\[using\] not found: '[^']+'" | sort -u || true) if [ -n "$USING_MISSING" ]; then echo "[builder/selfhost-first:diagnose] Missing using modules detected:" >&2 echo "$USING_MISSING" >&2 echo "[builder/selfhost-first:diagnose] Hint: enable resolver-first (HAKO_USING_RESOLVER_FIRST=1) and ensure nyash.toml maps these modules." >&2 echo "[builder/selfhost-first:diagnose] Example entries (nyash.toml [modules]):" >&2 echo " \"selfhost.shared.json.core.json_canonical\" = \"lang/src/shared/json/json_canonical_box.hako\"" >&2 echo " \"selfhost.shared.common.common_imports\" = \"lang/src/shared/common/common_imports.hako\"" >&2 fi fi fi # Don't return immediately - check for fallback below fi if [ $rc -eq 0 ] && ! grep -q "\[builder/selfhost-first:ok\]" "$tmp_stdout"; then if [ "${HAKO_SELFHOST_NO_DELEGATE:-0}" = "1" ] || [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[builder/selfhost-first:fail:no-ok-marker]" >&2 echo "[builder/selfhost-first:fail:detail] First 20 lines of output:" >&2 head -n 20 "$tmp_stdout" >&2 || true echo "[builder/selfhost-first:fail:detail] Last 80 lines of output:" >&2 tail -n 80 "$tmp_stdout" >&2 || true fi rc=1 fi # Try min builder fallback if enabled and initial builder failed if [ "${HAKO_SELFHOST_TRY_MIN:-0}" = "1" ] && [ $rc -ne 0 ] && [ "$builder_box" != "hako.mir.builder.min" ]; then if [ "${HAKO_SELFHOST_NO_DELEGATE:-0}" = "1" ] || [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[builder/selfhost-first:trying-min-fallback]" >&2 fi # Retry with min builder HAKO_MIR_BUILDER_BOX="hako.mir.builder.min" try_selfhost_builder "$prog_json" "$out_path" local fallback_rc=$? if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[builder/selfhost-first:min-fallback:rc=$fallback_rc]" >&2 fi return $fallback_rc fi # Return original failure if no fallback or if fallback not triggered if [ $rc -ne 0 ]; then return 1 fi local mir mir=$(awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' "$tmp_stdout") if [ -z "$mir" ]; then return 1; fi # Surface key builder tags (multi-carrier detectionなど) when trace is enabled, # so that v2 smokes can reliably grep them without生 stdout 全量を流す必要がない。 if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then if grep -q "\[mirbuilder/internal/loop:multi_carrier:" "$tmp_stdout" 2>/dev/null; then echo "[builder/selfhost-first:trace:multi_carrier] detected" >&2 grep "\[mirbuilder/internal/loop:multi_carrier:" "$tmp_stdout" >&2 || true fi if grep -q "\[funcs/basic:loop.multi_carrier\]" "$tmp_stdout" 2>/dev/null; then echo "[builder/selfhost-first:trace:funcs:loop.multi_carrier]" >&2 grep "\[funcs/basic:loop.multi_carrier\]" "$tmp_stdout" >&2 || true fi fi printf '%s' "$mir" > "$out_path" echo "[OK] MIR JSON written (selfhost-first): $out_path" return 0 } # Provider-first delegate: call env.mirbuilder.emit(prog_json) and capture v1 JSON try_provider_emit() { local prog_json="$1" out_path="$2" local tmp_hako; tmp_hako=$(mktemp --suffix .hako) cat >"$tmp_hako" <<'HCODE' static box Main { method main(args) { local p = env.get("HAKO_BUILDER_PROGRAM_JSON") if p == null { print("[provider/emit:nojson]"); return 1 } local a = new ArrayBox(); a.push(p) local out = hostbridge.extern_invoke("env.mirbuilder", "emit", a) print("[provider/emit:ok]") print("[MIR_OUT_BEGIN]") print("" + out) print("[MIR_OUT_END]") return 0 } } HCODE local tmp_stdout; tmp_stdout=$(mktemp) local tmp_stderr; tmp_stderr=$(mktemp) trap 'rm -f "$tmp_hako" "$tmp_stdout" "$tmp_stderr" || true' RETURN if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:trace] Starting provider emit..." >&2 fi set +e (cd "$ROOT" && \ NYASH_DISABLE_PLUGINS="${NYASH_DISABLE_PLUGINS:-0}" NYASH_FILEBOX_MODE="core-ro" \ NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 NYASH_PARSER_ALLOW_SEMICOLON=1 \ HAKO_BUILDER_PROGRAM_JSON="$prog_json" \ NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 HAKO_USING_RESOLVER_FIRST=1 \ "$NYASH_BIN" --backend vm "$tmp_hako" 2>"$tmp_stderr" | tee "$tmp_stdout" >/dev/null) local rc=$? set -e if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:trace] Provider execution completed with rc=$rc" >&2 fi if [ $rc -ne 0 ] || ! grep -q "\[provider/emit:ok\]" "$tmp_stdout"; then if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:fail:rc=$rc]" >&2 if [ -s "$tmp_stderr" ]; then echo "[provider/emit:fail:stderr] First 20 lines:" >&2 head -n 20 "$tmp_stderr" >&2 || true echo "[provider/emit:fail:stderr] Last 40 lines:" >&2 tail -n 40 "$tmp_stderr" >&2 || true fi echo "[provider/emit:fail:stdout] Last 40 lines:" >&2 tail -n 40 "$tmp_stdout" >&2 || true USING_MISSING=$(cat "$tmp_stdout" "$tmp_stderr" 2>/dev/null | grep -Eo "\[using\] not found: '[^']+'" | sort -u || true) if [ -n "$USING_MISSING" ]; then echo "[provider/emit:diagnose] Missing using modules detected:" >&2 echo "$USING_MISSING" >&2 echo "[provider/emit:diagnose] Hint: enable resolver-first (HAKO_USING_RESOLVER_FIRST=1) and ensure nyash.toml maps these modules." >&2 fi fi return 1 fi local mir mir=$(awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' "$tmp_stdout") if [ -z "$mir" ]; then if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[provider/emit:fail:no-mir-output]" >&2 fi return 1 fi # Write raw MIR JSON first printf '%s' "$mir" > "$out_path" # Apply AotPrep via run_json when enabled if [ "${HAKO_APPLY_AOT_PREP:-0}" = "1" ] || [ "${NYASH_AOT_COLLECTIONS_HOT:-0}" = "1" ] || [ "${NYASH_LLVM_FAST:-0}" = "1" ] || [ "${NYASH_MIR_LOOP_HOIST:-0}" = "1" ]; then [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ] && echo "[provider/emit:trace] Applying AotPrep(run_json)..." >&2 local aot_runner; aot_runner=$(mktemp --suffix=.hako) cat > "$aot_runner" << 'EOF' using selfhost.llvm.ir.aot_prep as AotPrepBox static box Main { method main(args) { local src = env.get("HAKO_PREP_INPUT") if src == null || src == "" { print("[prep:skip:empty]"); return 0 } local out = AotPrepBox.run_json(src) print("[PREP_OUT_BEGIN]") print(out) print("[PREP_OUT_END]") return 0 } } EOF local aot_rc=0 local prep_stdout; prep_stdout=$(mktemp) local prep_stderr; prep_stderr=$(mktemp) set +e HAKO_PREP_INPUT="$(cat "$out_path")" \ NYASH_FILEBOX_MODE=core-ro \ NYASH_FEATURES=stage3 NYASH_FEATURES=stage3 NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 HAKO_USING_RESOLVER_FIRST=1 \ NYASH_AOT_COLLECTIONS_HOT=${NYASH_AOT_COLLECTIONS_HOT:-0} NYASH_LLVM_FAST=${NYASH_LLVM_FAST:-0} NYASH_MIR_LOOP_HOIST=${NYASH_MIR_LOOP_HOIST:-0} NYASH_AOT_MAP_KEY_MODE=${NYASH_AOT_MAP_KEY_MODE:-auto} \ NYASH_AOT_NUMERIC_CORE=${NYASH_AOT_NUMERIC_CORE:-0} NYASH_AOT_NUMERIC_CORE_TRACE=${NYASH_AOT_NUMERIC_CORE_TRACE:-0} \ HAKO_VM_MAX_STEPS="${HAKO_VM_MAX_STEPS:-0}" NYASH_VM_MAX_STEPS="${NYASH_VM_MAX_STEPS:-0}" \ "$NYASH_BIN" --backend vm "$aot_runner" >"$prep_stdout" 2>"$prep_stderr" aot_rc=$? set -e if [ $aot_rc -eq 0 ] && grep -q "\[PREP_OUT_BEGIN\]" "$prep_stdout" && grep -q "\[PREP_OUT_END\]" "$prep_stdout"; then awk '/\[PREP_OUT_BEGIN\]/{flag=1;next}/\[PREP_OUT_END\]/{flag=0}flag' "$prep_stdout" > "$out_path" [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ] && echo "[prep:ok] AotPrep applied (run_json)" >&2 # Surface numeric_core trace lines when requested(provider経路でも [aot/numeric_core] を見えるようにする) if [ "${NYASH_AOT_NUMERIC_CORE_TRACE:-0}" = "1" ]; then if command -v rg >/devnull 2>&1; then rg -n '^\[aot/numeric_core\]' "$prep_stdout" >&2 || true else grep '^\[aot/numeric_core\]' "$prep_stdout" >&2 || true fi fi # Optional strict post-check: after AotPrep(run_json) the MIR JSON is expected # to have MatI64.mul_naive lowered to Call when NYASH_AOT_NUMERIC_CORE=1. if [ "${NYASH_AOT_NUMERIC_CORE_STRICT:-0}" = "1" ]; then if rg -q '"op":"boxcall".*"method":"mul_naive"' "$out_path"; then echo "[prep/numeric_core/strict] NYASH_AOT_NUMERIC_CORE_STRICT=1 but boxcall(\"mul_naive\") remains after AotPrep.run_json; inspect numeric_core.hako patterns." >&2 return 1 fi fi else if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[prep:warn] AotPrep failed (rc=$aot_rc), using original MIR" >&2 if [ -s "$prep_stderr" ]; then echo "[prep:stderr:tail]" >&2 tail -n 60 "$prep_stderr" >&2 || true fi fi fi rm -f "$aot_runner" "$prep_stdout" "$prep_stderr" 2>/dev/null || true fi echo "[OK] MIR JSON written (delegate:provider): $out_path" return 0 } # When forcing JSONFrag loop, default-enable normalize+purify (dev-only, no default changes) if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then export HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE="${HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE:-1}" export HAKO_MIR_BUILDER_JSONFRAG_PURIFY="${HAKO_MIR_BUILDER_JSONFRAG_PURIFY:-1}" fi if [ "${HAKO_SELFHOST_BUILDER_FIRST:-0}" = "1" ]; then # Prefer Stage1 CLI (hakorune) when available to avoid Stage-3 parser issues stage1_cli_bin="${HAKO_STAGE1_CLI:-$ROOT/lang/bin/hakorune}" if [ -x "$stage1_cli_bin" ]; then prog_tmp=$(mktemp --suffix .json) printf '%s' "$PROG_JSON_OUT" > "$prog_tmp" if "$stage1_cli_bin" emit mir-json --from-program-json "$prog_tmp" -o "$OUT" --quiet >/dev/null 2>&1 && [ -s "$OUT" ]; then rm -f "$prog_tmp" || true echo "[OK] MIR JSON written (selfhost-first:stage1-cli): $OUT" exit 0 fi rm -f "$prog_tmp" || true if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then echo "[selfhost-first:stage1-cli] failed (binary: $stage1_cli_bin), falling back to legacy builder" >&2 fi fi if try_selfhost_builder "$PROG_JSON_OUT" "$OUT"; then exit 0 fi # Phase 25.1b: selfhost-first モードでは MirBuilder のフォールバックは禁止。 # selfhost builder が失敗した時点で非0終了とし、provider 経路には委譲しない。 echo "[FAIL] selfhost-first failed (MirBuilder) and delegate is disabled in HAKO_SELFHOST_BUILDER_FIRST=1 mode" >&2 exit 1 fi # Dev: force JsonFrag minimal loop even on provider-first path if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then # Extract limit from Program(JSON) or source file limit=$(printf '%s' "$PROG_JSON_OUT" | grep -o '"type":"Int","value":[0-9]*' | head -1 | grep -o '[0-9]*$' || cat "$CODE_TMP" | grep -o '[0-9]\+' | head -1 || echo "10") echo "[emit/jsonfrag] provider-force min-loop MIR (dev-only)" >&2 cat > "$OUT" < "$tmp_prog" # Provider-first delegate (v1固定): env.mirbuilder.emit を使用 if try_provider_emit "$PROG_JSON_OUT" "$OUT"; then exit 0 fi # 最終フォールバック: 旧CLI変換(環境でv1を促す) if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \ HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \ HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \ NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \ NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \ "$NYASH_BIN" --program-json-to-mir "$OUT" --json-file "$tmp_prog" >/dev/null 2>&1; then echo "[OK] MIR JSON written (delegate-legacy): $OUT" exit 0 fi echo "[FAIL] Program→MIR delegate failed (provider+legacy)" >&2 exit 1