diff --git a/docs/private b/docs/private index 983e5143..0866b96a 160000 --- a/docs/private +++ b/docs/private @@ -1 +1 @@ -Subproject commit 983e5143d456e5a8e18a4522c43a9817ba178cd9 +Subproject commit 0866b96a8516e4a32374091049fbe6e0c552e8db diff --git a/src/mir/join_ir/frontend/ast_lowerer/expr.rs b/src/mir/join_ir/frontend/ast_lowerer/expr.rs index 52fbf57e..bbd6ff49 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/expr.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/expr.rs @@ -300,6 +300,17 @@ impl AstToJoinIrLowerer { (dst, insts) } + // Refactor-A: Null literal 対応(実ループ基盤整備) + "Null" => { + let dst = ctx.alloc_var(); + let inst = JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const { + dst, + value: ConstValue::Null, + }); + + (dst, vec![inst]) + } + // Phase 56: Call(関数呼び出し、pred(v) 等) // 変数参照の関数呼び出しは MethodCall で代替(receiver=func_var, method="__call__") "Call" => { diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_return_pattern.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_return_pattern.rs index a572015f..700688c1 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_return_pattern.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns/continue_return_pattern.rs @@ -41,6 +41,11 @@ use super::{AstToJoinIrLowerer, JoinModule, LoweringError}; use crate::mir::join_ir::{CompareOp, ConstValue, JoinFunction, JoinInst, MirLikeInst}; use crate::mir::ValueId; +/// Refactor-B: JSON 値の等価性チェック(serde_json::Value の PartialEq を使用) +fn json_values_equal(a: &serde_json::Value, b: &serde_json::Value) -> bool { + a == b +} + /// ContinueReturn パターンを JoinModule に変換 /// /// # Arguments @@ -70,7 +75,7 @@ pub fn lower( message: "Loop must have 'body' array".to_string(), })?; - // 5-1. Return If を探す(Fail-Fast: 複数あればエラー) + // 5-1. Return If を探す(Refactor-B: 複数許可、値の等価性チェック) let return_if_stmts: Vec<_> = loop_body .iter() .filter(|stmt| { @@ -87,16 +92,37 @@ pub fn lower( }); } + // Refactor-B: 複数の return-if がある場合、返す値が全て同一かチェック if return_if_stmts.len() > 1 { - return Err(LoweringError::InvalidLoopBody { - message: format!( - "ContinueReturn pattern validation failed: multiple Return statements found.\n\ - Expected: Exactly one 'if {{ return ... }}' statement in loop body.\n\ - Found: {} Return statements.\n\ - Hint: Combine multiple return conditions into a single if statement.", - return_if_stmts.len() - ), - }); + // 各 return-if の return 値を抽出 + let return_values: Vec<&serde_json::Value> = return_if_stmts + .iter() + .map(|stmt| { + let then = stmt["then"].as_array().expect("then must be array"); + let ret = then + .iter() + .find(|s| s["type"].as_str() == Some("Return")) + .expect("return must exist"); + &ret["expr"] + }) + .collect(); + + // 最初の値と他の値を比較 + let first_value = return_values[0]; + for (i, value) in return_values.iter().enumerate().skip(1) { + if !json_values_equal(first_value, value) { + return Err(LoweringError::InvalidLoopBody { + message: format!( + "ContinueReturn pattern validation failed: multiple return-if statements with different values.\n\ + Expected: All early returns must have identical values.\n\ + First return: {:?}\n\ + Return #{}: {:?}\n\ + Hint: Ensure all early returns have identical values, or combine conditions.", + first_value, i + 1, value + ), + }); + } + } } let return_if_stmt = return_if_stmts[0]; diff --git a/src/mir/join_ir/normalized/dev_fixtures.rs b/src/mir/join_ir/normalized/dev_fixtures.rs index 76aad00e..52fd1978 100644 --- a/src/mir/join_ir/normalized/dev_fixtures.rs +++ b/src/mir/join_ir/normalized/dev_fixtures.rs @@ -21,6 +21,9 @@ pub enum NormalizedDevFixture { PatternContinueReturnMin, /// Parse String Composite minimal (Phase 90 P0) ParseStringCompositeMin, + /// Refactor-B: ContinueReturn multi minimal (multiple return-if with same value) + /// Note: This also tests Null literal support from Refactor-A + ContinueReturnMultiMin, } impl NormalizedDevFixture { @@ -36,6 +39,7 @@ impl NormalizedDevFixture { } Self::PatternContinueReturnMin => "pattern_continue_return_minimal", Self::ParseStringCompositeMin => "parse_string_composite_minimal", + Self::ContinueReturnMultiMin => "continue_return_multi_minimal", } } @@ -57,6 +61,9 @@ impl NormalizedDevFixture { Self::ParseStringCompositeMin => { "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/parse_string_composite_min.program.json" } + Self::ContinueReturnMultiMin => { + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/continue_return_multi_min.program.json" + } } } @@ -68,7 +75,8 @@ impl NormalizedDevFixture { | Self::Pattern4JsonParserParseArrayContinueSkipWs | Self::Pattern4JsonParserParseObjectContinueSkipWs | Self::PatternContinueReturnMin - | Self::ParseStringCompositeMin => FunctionRoute::LoopFrontend, + | Self::ParseStringCompositeMin + | Self::ContinueReturnMultiMin => FunctionRoute::LoopFrontend, } } @@ -93,6 +101,9 @@ impl NormalizedDevFixture { Self::ParseStringCompositeMin => include_str!( "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/parse_string_composite_min.program.json" ), + Self::ContinueReturnMultiMin => include_str!( + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/continue_return_multi_min.program.json" + ), } } @@ -122,6 +133,7 @@ pub const ALL_DEV_FIXTURES: &[NormalizedDevFixture] = &[ NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs, NormalizedDevFixture::PatternContinueReturnMin, NormalizedDevFixture::ParseStringCompositeMin, + NormalizedDevFixture::ContinueReturnMultiMin, ]; #[cfg(test)] @@ -156,7 +168,7 @@ mod tests { } #[test] - fn test_pattern4_fixtures_route_to_loop_frontend() { + fn test_all_fixtures_route_to_loop_frontend() { for fixture in ALL_DEV_FIXTURES { assert_eq!( fixture.route(), diff --git a/tests/normalized_joinir_min/shapes.rs b/tests/normalized_joinir_min/shapes.rs index 1cb3fa16..37d33570 100644 --- a/tests/normalized_joinir_min/shapes.rs +++ b/tests/normalized_joinir_min/shapes.rs @@ -599,3 +599,20 @@ fn test_parse_string_composite_min_expected_output() { "Expected acc=5 for n=10 (i=0,1,2,5,6 increments acc, i=3 escape continue, i=7 close quote return)" ); } + +/// Refactor-A+B: ContinueReturn multi minimal - tests both Null literal and multiple return-if +#[test] +fn test_continue_return_multi_min_returns_null_at_first_match() { + use nyash_rust::mir::join_ir::normalized::dev_fixtures::NormalizedDevFixture; + + let _ctx = normalized_dev_test_ctx(); + let structured = NormalizedDevFixture::ContinueReturnMultiMin.load_and_lower(); + let entry = structured.entry.expect("entry required"); + + let input = [JoinValue::Int(10)]; + let result = run_joinir_vm_bridge(&structured, entry, &input, true); + // Tests: + // - Refactor-A: Null literal support (returns ConstValue::Null → JoinValue::Unit) + // - Refactor-B: Multiple return-if with same value (i==3, i==7 both return null) + assert_eq!(result, JoinValue::Unit, "Expected Unit (null) from first return-if at i=3"); +}