diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md
index 38651a9b..4ce858fa 100644
--- a/CURRENT_TASK.md
+++ b/CURRENT_TASK.md
@@ -835,3 +835,9 @@ Notes — Display API Unification (spec‑neutral)
- tools/dev_env.sh に Unified 既定ON(明示OFFのみ無効)とレガシー関数化抑止を追加。
- `NYASH_MIR_UNIFIED_CALL=1`(既定ON明示)
- `NYASH_DEV_DISABLE_LEGACY_METHOD_REWRITE=1`(重複回避; 段階移行)
+- Update — 2025-11-02 (P1 part‑2) — v1 Closure 受理/Stage‑Bは引き続き opt‑in
+- Stage‑B canaries(print/binop/if/loop/array/map/string)は現状 opt‑in のまま(`SMOKES_ENABLE_STAGEB=1`)。
+ - Fallback TTL は既定OFF(必要時のみ `HAKO_STAGEB_ALLOW_FALLBACK=1`)。
+- Gate‑C v1 bridge: `mir_call` の `Closure` callee を受理(`NewClosure` を生成)。
+ - params/captures/me_capture を JSON から読み取り、ボディは空で生成。
+ - 実行は未対応(VM は Fail‑Fast)。生成経路の負例は別途スモーク化予定。
diff --git a/apps/examples/json_query_min/main.nyash b/apps/examples/json_query_min/main.nyash
index 0fc25bd6..935bc970 100644
--- a/apps/examples/json_query_min/main.nyash
+++ b/apps/examples/json_query_min/main.nyash
@@ -89,3 +89,4 @@ static box Main {
return null
}
}
+}
diff --git a/lang/src/compiler/entry/compiler_stageb.hako b/lang/src/compiler/entry/compiler_stageb.hako
index 22c01a17..39e66ad7 100644
--- a/lang/src/compiler/entry/compiler_stageb.hako
+++ b/lang/src/compiler/entry/compiler_stageb.hako
@@ -1,8 +1,11 @@
// Stage-B compiler entry — ParserBox → FlowEntry emit-only
+using sh_core as StringHelpers // Required: ParserStringUtilsBox depends on this (using chain unresolved)
using lang.compiler.parser.box as ParserBox
-static box StageBMain {
+// Note: Runner resolves entry as Main.main by default.
+// Provide static box Main with method main(args) as the entry point.
+static box Main {
// Minimal Stage‑B driver: parse args/env, extract main body if wrapped in `box Main { static method main(){...} }`,
// then run ParserBox → FlowEntry emit. Keep implementation single‑method to avoid parser drift issues.
main(args) {
@@ -25,25 +28,54 @@ static box StageBMain {
local p = new ParserBox()
p.stage3_enable(1)
- // 3) Extract using/externs from the original text
- p.extract_usings(src)
- local usings_json = p.get_usings_json()
- p.extract_externs(src)
- local externs_json = p.get_externs_json()
+ // 3) (Temporarily disabled) Extract using/externs — keep minimal path for emit stability
+ // p.extract_usings(src)
+ // local usings_json = p.get_usings_json()
+ // p.extract_externs(src)
+ // local externs_json = p.get_externs_json()
- // 4) If wrapped in `box Main { static method main() { ... } }`, extract the method body text
+ // 4) If wrapped in `box Main { method main() { ... } }` or `static box Main { method main() { ... } }`,
+ // extract the method body text
local body_src = null
{
- // naive search for "static method main" → '(' → ')' → '{' ... balanced '}'
+ // naive search for "method main" → '(' → ')' → '{' ... balanced '}'
local s = src
- local k0 = s.indexOf("static method main")
+ // naive substring search for "method main"
+ local k0 = -1
+ {
+ local pat = "method main"
+ local m = pat.length()
+ local i = 0
+ local n = s.length()
+ loop(i + m <= n) {
+ if s.substring(i, i + m) == pat { k0 = i break }
+ i = i + 1
+ }
+ }
if k0 >= 0 {
- local k1 = s.indexOf("(", k0)
+ // find '(' after k0
+ local k1 = -1
+ {
+ local j = k0
+ local n = s.length()
+ loop(j < n) { if s.substring(j, j + 1) == "(" { k1 = j break } j = j + 1 }
+ }
if k1 >= 0 {
- local k2 = s.indexOf(")", k1)
+ // find ')' after k1
+ local k2 = -1
+ {
+ local j = k1
+ local n = s.length()
+ loop(j < n) { if s.substring(j, j + 1) == ")" { k2 = j break } j = j + 1 }
+ }
if k2 >= 0 {
// Find opening '{' following ')'
- local k3 = s.indexOf("{", k2)
+ local k3 = -1
+ {
+ local j = k2
+ local n = s.length()
+ loop(j < n) { if s.substring(j, j + 1) == "{" { k3 = j break } j = j + 1 }
+ }
if k3 >= 0 {
// Balanced scan for matching '}'
local depth = 0
diff --git a/lang/src/vm/README.md b/lang/src/vm/README.md
index 743969bb..12f647b8 100644
--- a/lang/src/vm/README.md
+++ b/lang/src/vm/README.md
@@ -58,7 +58,8 @@ Quick profile opt‑in switches (smokes)
- `SMOKES_ENABLE_STAGEB_OOB=1` — Stage‑B OOB observation (array/map)
- `SMOKES_ENABLE_OOB_STRICT=1` — Gate‑C(Core) strict OOB fail‑fast canary (`gate_c_oob_strict_fail_vm.sh`)
- `SMOKES_ENABLE_LLVM_SELF_PARAM=1` — LLVM instruction boxes self‑param builder tests (const/binop/compare/branch/jump/ret)
- - `SMOKES_ENABLE_STAGEB=1` — Stage‑B positive canaries (emit→Gate‑C). Default OFF while v1 downconvert matures.
+- `SMOKES_ENABLE_STAGEB=1` — Stage‑B positive canaries(emit→Gate‑C)。既定OFF(安定化後に昇格予定)。
+ 必要に応じて各テスト内で `HAKO_STAGEB_ALLOW_FALLBACK=1` を付与(TTL; 既定OFF)。
Default quick canaries (regression)
- apps/json_lint_vm.sh — JSON Lint expected outputs (OK×10 / ERROR×6)
@@ -70,11 +71,11 @@ Dispatch policy: length()
- これにより、配列に対して誤って文字列長を返す回帰を防止する(2025‑11 修正)。
Deprecations
-- `NYASH_GATE_C_DIRECT` は移行中の互換トグル(TTL)だよ。将来は Gate‑C(Core)
+ - `NYASH_GATE_C_DIRECT` は移行中の互換トグル(TTL)だよ。将来は Gate‑C(Core)
直行(`HAKO_GATE_C_CORE=1`)に統一予定。新しい導線では Core の実行仕様(数値=rc,
安定化した診断タグ)が適用されるよ。
- 互換トグルを使うと起動時に警告が出るよ(`HAKO_GATE_C_DIRECT_SILENCE=1` で抑止可)。
- - Stage‑B fallback TTL: global既定はOFF。テスト内でのみ `HAKO_STAGEB_ALLOW_FALLBACK=1` を設定して使用する(TTL)。
+ - Stage‑B fallback TTL: 既定OFF(撤退方針)。必要な場合はテスト内限定で `HAKO_STAGEB_ALLOW_FALLBACK=1` を付与する。
Diagnostics (stable tags)
- 本フェーズでは、Gate‑C(Core) の境界で安定タグを整形して出力する:
@@ -109,7 +110,8 @@ Minimal mir_call semantics (Core)
- Methods (Map): `size()/len()/iterator()/set()/get()`(エントリ数メタを返す/更新する/メタから取得する)
- ModuleFunction: `ArrayBox.len/0` / `MapBox.len/0`(メタのサイズを返す)— 他はタグ付き Fail‑Fast
- Global/Extern: `env.console.{log|warn|error}`(数値引数のみ印字)
-- Others are Fail‑Fast(安定文言を出力)
+- Others are Fail‑Fast(安定文言を出力)。Closure 生成は v1 bridge で受理(NewClosure 発行)するが、
+ 実行時の呼出は未実装(VM 側は Fail‑Fast)。
See also: docs/development/architecture/collection_semantics.md(Array/Map のSSOT集約)
diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs
index 04ce81c9..1c586e1b 100644
--- a/src/mir/loop_builder.rs
+++ b/src/mir/loop_builder.rs
@@ -311,10 +311,16 @@ impl<'a> LoopBuilder<'a> {
let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst);
let current_vars = self.get_current_variable_map();
- // Debug: print current_vars before prepare
- eprintln!("[DEBUG] prepare_loop_variables call #{}", count);
- eprintln!("[DEBUG] current_vars = {:?}", current_vars);
- eprintln!("[DEBUG] preheader_id = {:?}, header_id = {:?}", preheader_id, header_id);
+ // Debug: print current_vars before prepare (guarded by env)
+ let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
+ if dbg {
+ eprintln!("[DEBUG] prepare_loop_variables call #{}", count);
+ eprintln!("[DEBUG] current_vars = {:?}", current_vars);
+ eprintln!(
+ "[DEBUG] preheader_id = {:?}, header_id = {:?}",
+ preheader_id, header_id
+ );
+ }
crate::mir::phi_core::loop_phi::save_block_snapshot(
&mut self.block_var_maps,
preheader_id,
@@ -417,34 +423,66 @@ impl<'a> LoopBuilder<'a> {
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
- eprintln!("[DEBUG] LoopBuilder::emit_phi_at_block_start: block={}, dst=%{}, inputs={:?}", block_id, dst.0, inputs);
+ let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
+ if dbg {
+ eprintln!(
+ "[DEBUG] LoopBuilder::emit_phi_at_block_start: block={}, dst=%{}, inputs={:?}",
+ block_id, dst.0, inputs
+ );
+ }
// Phi nodeをブロックの先頭に挿入
if let Some(ref mut function) = self.parent_builder.current_function {
if let Some(block) = function.get_block_mut(block_id) {
- eprintln!("[DEBUG] Block {} current instructions count: {}", block_id, block.instructions.len());
+ if dbg {
+ eprintln!(
+ "[DEBUG] Block {} current instructions count: {}",
+ block_id,
+ block.instructions.len()
+ );
+ }
// Phi命令は必ずブロックの先頭に配置
let phi_inst = MirInstruction::Phi { dst, inputs: inputs.clone() };
block.instructions.insert(0, phi_inst);
- eprintln!("[DEBUG] ✅ PHI instruction inserted at position 0");
- eprintln!("[DEBUG] Block {} after insert instructions count: {}", block_id, block.instructions.len());
+ if dbg {
+ eprintln!("[DEBUG] ✅ PHI instruction inserted at position 0");
+ eprintln!(
+ "[DEBUG] Block {} after insert instructions count: {}",
+ block_id,
+ block.instructions.len()
+ );
+ }
// Verify PHI is still there
if let Some(first_inst) = block.instructions.get(0) {
match first_inst {
MirInstruction::Phi { dst: phi_dst, .. } => {
- eprintln!("[DEBUG] Verified: First instruction is PHI dst=%{}", phi_dst.0);
+ if dbg {
+ eprintln!(
+ "[DEBUG] Verified: First instruction is PHI dst=%{}",
+ phi_dst.0
+ );
+ }
}
other => {
- eprintln!("[DEBUG] ⚠️ WARNING: First instruction is NOT PHI! It's {:?}", other);
+ if dbg {
+ eprintln!(
+ "[DEBUG] ⚠️ WARNING: First instruction is NOT PHI! It's {:?}",
+ other
+ );
+ }
}
}
}
Ok(())
} else {
- eprintln!("[DEBUG] ❌ Block {} not found!", block_id);
+ if dbg {
+ eprintln!("[DEBUG] ❌ Block {} not found!", block_id);
+ }
Err(format!("Block {} not found", block_id))
}
} else {
- eprintln!("[DEBUG] ❌ No current function!");
+ if dbg {
+ eprintln!("[DEBUG] ❌ No current function!");
+ }
Err("No current function".to_string())
}
}
@@ -490,7 +528,12 @@ impl<'a> LoopBuilder<'a> {
}
fn update_variable(&mut self, name: String, value: ValueId) {
- eprintln!("[DEBUG] LoopBuilder::update_variable: name={}, value=%{}", name, value.0);
+ if std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
+ eprintln!(
+ "[DEBUG] LoopBuilder::update_variable: name={}, value=%{}",
+ name, value.0
+ );
+ }
self.parent_builder.variable_map.insert(name, value);
}
@@ -749,18 +792,30 @@ impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
dst: ValueId,
src: ValueId,
) -> Result<(), String> {
- eprintln!("[DEBUG] emit_copy_at_preheader: preheader={}, dst=%{}, src=%{}", preheader_id, dst.0, src.0);
+ let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
+ if dbg {
+ eprintln!(
+ "[DEBUG] emit_copy_at_preheader: preheader={}, dst=%{}, src=%{}",
+ preheader_id, dst.0, src.0
+ );
+ }
if let Some(ref mut function) = self.parent_builder.current_function {
if let Some(block) = function.get_block_mut(preheader_id) {
- eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id);
+ if dbg {
+ eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id);
+ }
block.add_instruction(MirInstruction::Copy { dst, src });
Ok(())
} else {
- eprintln!("[DEBUG] ❌ Preheader block {} not found!", preheader_id);
+ if dbg {
+ eprintln!("[DEBUG] ❌ Preheader block {} not found!", preheader_id);
+ }
Err(format!("Preheader block {} not found", preheader_id))
}
} else {
- eprintln!("[DEBUG] ❌ No current function!");
+ if dbg {
+ eprintln!("[DEBUG] ❌ No current function!");
+ }
Err("No current function".to_string())
}
}
diff --git a/src/runner/json_v1_bridge.rs b/src/runner/json_v1_bridge.rs
index 6849748e..663b829b 100644
--- a/src/runner/json_v1_bridge.rs
+++ b/src/runner/json_v1_bridge.rs
@@ -254,7 +254,7 @@ pub fn try_parse_v1_to_module(json: &str) -> Result