Files
hakorune/tests/normalized_joinir_min/shapes.rs
nyash-codex b71a18495d feat(joinir): Phase 88 - Pattern4 continue + variable step increment
Continue Pattern 拡張:
- then側の i=i+const 差分加算 + acc更新を許可
- continue_pattern.rs:193 で可変ステップ検出

Dev Router 許可:
- ast_lowerer/mod.rs:92 で normalized_dev feature時に新パターンを有効化

Fixtures & Tests:
- jsonparser_unescape_string_step2_min fixture追加(submodule)
- normalized_joinir_min.rs に shape テスト追加
- shapes.rs に expected shape 定義

Documentation:
- joinir-architecture-overview.md に Phase 88 到達点を追記

Impact:
- Pattern4 continue + 可変インクリメント(i+=1 or i+=2)対応
- _unescape_string 制御構造の土台確立
- normalized_dev tests PASS

Next: _unescape_string 残り複合ループ対応

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 00:29:56 +09:00

481 lines
17 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use super::*;
#[test]
fn normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_pattern3_if_sum_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
// phase212_if_sum_min.hako 相当: sum=2 になることを期待
let input: [JoinValue; 0] = [];
let base = run_joinir_runner(&structured, entry, &input, false);
let dev = run_joinir_runner(&structured, entry, &input, true);
assert_eq!(base, dev, "runner mismatch for P3 minimal if-sum");
assert_eq!(
dev,
JoinValue::Int(2),
"unexpected result for P3 minimal if-sum (expected sum=2)",
);
}
#[test]
fn normalized_pattern3_if_sum_multi_vm_bridge_direct_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_pattern3_if_sum_multi_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let input = [JoinValue::Int(0)];
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for P3 if-sum multi");
assert_eq!(
dev,
JoinValue::Int(2),
"unexpected result for P3 if-sum multi (expected sum=2)"
);
}
#[test]
fn normalized_pattern3_json_if_sum_min_vm_bridge_direct_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_pattern3_json_if_sum_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let input = [JoinValue::Int(0)];
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for P3 json if-sum");
assert_eq!(
dev,
JoinValue::Int(10),
"unexpected result for P3 json if-sum (expected sum=10)"
);
}
#[cfg(feature = "normalized_dev")]
#[test]
fn test_phase46_canonical_set_includes_p2_mid() {
use nyash_rust::mir::join_ir::normalized::shape_guard::{
is_canonical_shape, NormalizedDevShape,
};
// Phase 46: Verify P2-Mid patterns are canonical
assert!(is_canonical_shape(&NormalizedDevShape::JsonparserAtoiReal));
assert!(is_canonical_shape(
&NormalizedDevShape::JsonparserParseNumberReal
));
assert!(is_canonical_shape(
&NormalizedDevShape::Pattern3IfSumMinimal
));
assert!(is_canonical_shape(&NormalizedDevShape::Pattern3IfSumMulti));
assert!(is_canonical_shape(&NormalizedDevShape::Pattern3IfSumJson));
// Verify P2-Core patterns still canonical
assert!(is_canonical_shape(&NormalizedDevShape::Pattern2Mini));
assert!(is_canonical_shape(
&NormalizedDevShape::JsonparserSkipWsMini
));
assert!(is_canonical_shape(
&NormalizedDevShape::JsonparserSkipWsReal
));
assert!(is_canonical_shape(&NormalizedDevShape::JsonparserAtoiMini));
}
/// Phase 47-A: Test P3 minimal normalization
#[test]
fn test_phase47a_pattern3_if_sum_minimal_normalization() {
use nyash_rust::mir::join_ir::normalized::normalize_pattern3_if_sum_minimal;
let module = build_pattern3_if_sum_min_structured_for_normalized_dev();
// Test that normalization succeeds (includes shape detection internally)
let result = normalize_pattern3_if_sum_minimal(&module);
assert!(
result.is_ok(),
"P3 normalization should succeed (shape detection + normalization): {:?}",
result.err()
);
let normalized = result.unwrap();
assert_eq!(
normalized.functions.len(),
module.functions.len(),
"Normalized function count should match Structured"
);
// Verify normalized module has proper phase
assert_eq!(
normalized.phase,
nyash_rust::mir::join_ir::JoinIrPhase::Normalized,
"Normalized module should have Normalized phase"
);
}
/// Phase 47-A: Test P3 VM execution (basic smoke test)
#[test]
fn test_phase47a_pattern3_if_sum_minimal_runner() {
let module = build_pattern3_if_sum_min_structured_for_normalized_dev();
// Basic test: module should be runnable through JoinIR runner
// This test verifies the P3 fixture is valid and generates proper JoinIR
assert_eq!(module.functions.len(), 3, "P3 should have 3 functions");
let entry = module.entry.expect("P3 should have entry function");
assert_eq!(entry.0, 0, "Entry should be function 0");
}
/// Phase 48-A: Test P4 minimal normalization
#[test]
fn test_phase48a_pattern4_continue_minimal_normalization() {
use nyash_rust::mir::join_ir::normalized::normalize_pattern4_continue_minimal;
let module = build_pattern4_continue_min_structured_for_normalized_dev();
// Test that normalization succeeds (includes shape detection internally)
let result = normalize_pattern4_continue_minimal(&module);
assert!(
result.is_ok(),
"P4 normalization should succeed (shape detection + normalization): {:?}",
result.err()
);
let normalized = result.unwrap();
assert_eq!(
normalized.functions.len(),
module.functions.len(),
"Normalized function count should match Structured"
);
// Verify normalized module has proper phase
assert_eq!(
normalized.phase,
nyash_rust::mir::join_ir::JoinIrPhase::Normalized,
"Normalized module should have Normalized phase"
);
}
/// Phase 48-A: Test P4 VM execution (basic smoke test)
#[test]
fn test_phase48a_pattern4_continue_minimal_runner() {
let module = build_pattern4_continue_min_structured_for_normalized_dev();
// Basic test: module should be runnable through JoinIR runner
// This test verifies the P4 fixture is valid and generates proper JoinIR
assert_eq!(module.functions.len(), 3, "P4 should have 3 functions");
let entry = module.entry.expect("P4 should have entry function");
assert_eq!(entry.0, 0, "Entry should be function 0");
}
/// Phase 48-A: Test P4 minimal Runner dev switch matches Structured
#[test]
fn test_normalized_pattern4_continue_minimal_runner_dev_switch_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_pattern4_continue_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
// pattern4_continue_min fixture: acc=4 (skipped i==2, so counted 0,1,3,4)
let input = [JoinValue::Int(5)]; // n = 5
let base = run_joinir_runner(&structured, entry, &input, false);
let dev = run_joinir_runner(&structured, entry, &input, true);
assert_eq!(base, dev, "runner mismatch for P4 minimal continue");
assert_eq!(
dev,
JoinValue::Int(4),
"unexpected result for P4 minimal continue (expected acc=4, skipped i==2)",
);
}
/// Phase 48-A: Test P4 minimal VM Bridge direct matches Structured
#[test]
fn test_normalized_pattern4_continue_minimal_vm_bridge_direct_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_pattern4_continue_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
// pattern4_continue_min fixture: acc=4 (skipped i==2)
let input = [JoinValue::Int(5)]; // n = 5
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for P4 minimal continue");
assert_eq!(
dev,
JoinValue::Int(4),
"unexpected result for P4 minimal continue (expected acc=4)",
);
}
/// Phase 48-C: P4 minimal should use canonical normalized route even without env
#[test]
fn test_normalized_pattern4_continue_minimal_canonical_matches_structured() {
let structured = build_pattern4_continue_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let input = [JoinValue::Int(5)];
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &input);
let canonical = run_joinir_vm_bridge(&structured, entry, &input, false);
assert_eq!(
structured_res, canonical,
"canonical P4 minimal result mismatch"
);
assert_eq!(canonical, JoinValue::Int(4));
}
/// Phase 48-B: JsonParser _parse_array continue skip_ws (dev-only) VM Bridge comparison
#[test]
fn test_normalized_pattern4_jsonparser_parse_array_continue_skip_ws_vm_bridge_direct_matches_structured(
) {
let _ctx = normalized_dev_test_ctx();
let structured = build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
// Fixture mirrors pattern4_continue_min: skip i == 2
let cases = [3, 5, 7];
for n in cases {
let args = [JoinValue::Int(n)];
let base = run_joinir_vm_bridge(&structured, entry, &args, false);
let dev = run_joinir_vm_bridge(&structured, entry, &args, true);
assert_eq!(
base, dev,
"vm bridge mismatch for array continue case n={}",
n
);
}
}
/// Phase 48-C: JsonParser _parse_array continue skip_ws canonical route should match Structured
#[test]
fn test_normalized_pattern4_jsonparser_parse_array_continue_skip_ws_canonical_matches_structured() {
let structured = build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let cases = [3, 5, 7];
for n in cases {
let args = [JoinValue::Int(n)];
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &args);
let canonical = run_joinir_vm_bridge(&structured, entry, &args, false);
assert_eq!(
structured_res, canonical,
"canonical array continue mismatch n={}",
n
);
}
}
/// Phase 48-B: JsonParser _parse_object continue skip_ws (dev-only) VM Bridge comparison
#[test]
fn test_normalized_pattern4_jsonparser_parse_object_continue_skip_ws_vm_bridge_direct_matches_structured(
) {
let _ctx = normalized_dev_test_ctx();
let structured = build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
// Fixture mirrors pattern4_continue_min: skip i == 2
let cases = [4, 6, 8];
for n in cases {
let args = [JoinValue::Int(n)];
let base = run_joinir_vm_bridge(&structured, entry, &args, false);
let dev = run_joinir_vm_bridge(&structured, entry, &args, true);
assert_eq!(
base, dev,
"vm bridge mismatch for object continue case n={}",
n
);
}
}
/// Phase 88: JsonParser _unescape_string コアi+=2 + continueを canonical route で固定する。
#[test]
fn test_phase88_jsonparser_unescape_string_step2_min_canonical_matches_structured() {
let structured = build_jsonparser_unescape_string_step2_min_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
// n=10 → i=0,2,4,6,8 で acc++ → 5
let args = [JoinValue::Int(10)];
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &args);
let canonical = run_joinir_vm_bridge(&structured, entry, &args, false);
assert_eq!(
structured_res, canonical,
"canonical unescape(step2) mismatch"
);
assert_eq!(canonical, JoinValue::Int(5));
}
/// Phase 48-C: JsonParser _parse_object continue skip_ws canonical route should match Structured
#[test]
fn test_normalized_pattern4_jsonparser_parse_object_continue_skip_ws_canonical_matches_structured()
{
let structured = build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let cases = [4, 6, 8];
for n in cases {
let args = [JoinValue::Int(n)];
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &args);
let canonical = run_joinir_vm_bridge(&structured, entry, &args, false);
assert_eq!(
structured_res, canonical,
"canonical object continue mismatch n={}",
n
);
}
}
/// Phase 54: False positive observation test - P2 structural axis discrimination
///
/// This test validates that structural detection can discriminate between
/// canonical P2 and selfhost P2 shapes using structural features alone.
#[test]
fn test_phase54_structural_axis_discrimination_p2() {
use nyash_rust::mir::join_ir::normalized::shape_guard::{
detect_shapes, is_canonical_shape, NormalizedDevShape,
};
// Canonical P2 shapes
let canonical_p2_shapes = vec![
build_pattern2_minimal_structured(),
build_jsonparser_skip_ws_structured_for_normalized_dev(),
];
// Selfhost P2 shapes (Phase 53)
let selfhost_p2_shapes = vec![
build_selfhost_args_parse_p2_structured_for_normalized_dev(),
build_selfhost_token_scan_p2_structured_for_normalized_dev(),
];
// Canonical P2 should be detected as canonical, NOT selfhost
for canonical in &canonical_p2_shapes {
let shapes = detect_shapes(canonical);
let has_canonical = shapes.iter().any(|s| is_canonical_shape(s));
let has_selfhost_p2 = shapes.iter().any(|s| {
matches!(
s,
NormalizedDevShape::SelfhostArgsParseP2
| NormalizedDevShape::SelfhostTokenScanP2
| NormalizedDevShape::SelfhostTokenScanP2Accum
)
});
assert!(
has_canonical,
"canonical P2 should be detected as canonical: {:?}",
shapes
);
assert!(
!has_selfhost_p2,
"canonical P2 should NOT be detected as selfhost: {:?}",
shapes
);
}
// Selfhost P2 should be detected as selfhost, NOT canonical
for selfhost in &selfhost_p2_shapes {
let shapes = detect_shapes(selfhost);
let has_canonical = shapes.iter().any(|s| is_canonical_shape(s));
let has_selfhost_p2 = shapes.iter().any(|s| {
matches!(
s,
NormalizedDevShape::SelfhostArgsParseP2
| NormalizedDevShape::SelfhostTokenScanP2
| NormalizedDevShape::SelfhostTokenScanP2Accum
)
});
assert!(
!has_canonical,
"selfhost P2 should NOT be detected as canonical: {:?}",
shapes
);
assert!(
has_selfhost_p2,
"selfhost P2 should be detected as selfhost (with name guard): {:?}",
shapes
);
}
}
/// Phase 54: False positive observation test - P3 structural axis discrimination
///
/// This test validates that structural detection can discriminate between
/// canonical P3 and selfhost P3 shapes using structural features alone.
#[test]
fn test_phase54_structural_axis_discrimination_p3() {
use nyash_rust::mir::join_ir::normalized::shape_guard::{
detect_shapes, is_canonical_shape, NormalizedDevShape,
};
// Canonical P3 shapes
let canonical_p3_shapes = vec![
build_pattern3_if_sum_min_structured_for_normalized_dev(),
build_pattern3_if_sum_multi_min_structured_for_normalized_dev(),
];
// Selfhost P3 shapes (Phase 53)
let selfhost_p3_shapes = vec![
build_selfhost_stmt_count_p3_structured_for_normalized_dev(),
build_selfhost_if_sum_p3_structured_for_normalized_dev(),
];
// Canonical P3 should be detected as canonical, NOT selfhost
for canonical in &canonical_p3_shapes {
let shapes = detect_shapes(canonical);
let has_canonical = shapes.iter().any(|s| is_canonical_shape(s));
let has_selfhost_p3 = shapes.iter().any(|s| {
matches!(
s,
NormalizedDevShape::SelfhostStmtCountP3
| NormalizedDevShape::SelfhostIfSumP3
| NormalizedDevShape::SelfhostIfSumP3Ext
)
});
assert!(
has_canonical,
"canonical P3 should be detected as canonical: {:?}",
shapes
);
assert!(
!has_selfhost_p3,
"canonical P3 should NOT be detected as selfhost: {:?}",
shapes
);
}
// Selfhost P3 should be detected as selfhost, NOT canonical
for selfhost in &selfhost_p3_shapes {
let shapes = detect_shapes(selfhost);
let has_canonical = shapes.iter().any(|s| is_canonical_shape(s));
let has_selfhost_p3 = shapes.iter().any(|s| {
matches!(
s,
NormalizedDevShape::SelfhostStmtCountP3
| NormalizedDevShape::SelfhostIfSumP3
| NormalizedDevShape::SelfhostIfSumP3Ext
)
});
assert!(
!has_canonical,
"selfhost P3 should NOT be detected as canonical: {:?}",
shapes
);
assert!(
has_selfhost_p3,
"selfhost P3 should be detected as selfhost (with name guard): {:?}",
shapes
);
}
}