Files
hakorune/src/mir/join_ir_vm_bridge/tests.rs

302 lines
10 KiB
Rust
Raw Normal View History

use super::convert::convert_mir_like_inst;
use super::*;
use crate::mir::join_ir::frontend::AstToJoinIrLowerer;
use crate::mir::join_ir_ops::JoinValue;
use crate::mir::{BinaryOp, CompareOp as MirCompareOp, MirInstruction, ValueId};
#[test]
fn test_convert_const_inst() {
let join_const = crate::mir::join_ir::MirLikeInst::Const {
dst: ValueId(10),
value: crate::mir::join_ir::ConstValue::Integer(42),
};
let mir_inst = convert_mir_like_inst(&join_const).unwrap();
match mir_inst {
MirInstruction::Const { dst, value } => {
assert_eq!(dst, ValueId(10));
assert!(matches!(value, crate::mir::ConstValue::Integer(42)));
}
_ => panic!("Expected Const instruction"),
}
}
#[test]
fn test_convert_binop_inst() {
let join_binop = crate::mir::join_ir::MirLikeInst::BinOp {
dst: ValueId(20),
op: crate::mir::join_ir::BinOpKind::Add,
lhs: ValueId(10),
rhs: ValueId(11),
};
let mir_inst = convert_mir_like_inst(&join_binop).unwrap();
match mir_inst {
MirInstruction::BinOp { dst, op, lhs, rhs } => {
assert_eq!(dst, ValueId(20));
assert_eq!(op, BinaryOp::Add);
assert_eq!(lhs, ValueId(10));
assert_eq!(rhs, ValueId(11));
}
_ => panic!("Expected BinOp instruction"),
}
}
#[test]
fn test_convert_compare_inst() {
let join_cmp = crate::mir::join_ir::MirLikeInst::Compare {
dst: ValueId(30),
op: crate::mir::join_ir::CompareOp::Ge,
lhs: ValueId(10),
rhs: ValueId(11),
};
let mir_inst = convert_mir_like_inst(&join_cmp).unwrap();
match mir_inst {
MirInstruction::Compare { dst, op, lhs, rhs } => {
assert_eq!(dst, ValueId(30));
assert_eq!(op, MirCompareOp::Ge);
assert_eq!(lhs, ValueId(10));
assert_eq!(rhs, ValueId(11));
}
_ => panic!("Expected Compare instruction"),
}
}
// ========================================
// Phase 45: read_quoted_from Bridge Tests
// ========================================
/// Phase 45: read_quoted_from JoinIR → MIR 変換テスト
///
/// HAKO_JOINIR_READ_QUOTED=1 が設定されている場合のみ実行。
#[test]
fn test_read_quoted_from_joinir_to_mir_conversion() {
// Dev flag がない場合はスキップ
if std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() != Some("1") {
eprintln!(
"[Phase 45] Skipping test_read_quoted_from_joinir_to_mir_conversion: \
Set HAKO_JOINIR_READ_QUOTED=1 to enable"
);
return;
}
// 1. JoinModule を生成lower_read_quoted_pattern を使用)
let program_json = serde_json::json!({
"defs": [{
"name": "read_quoted_from",
"params": ["s", "pos"],
"body": { "body": [] }
}]
});
let mut lowerer = AstToJoinIrLowerer::new();
let join_module = lowerer.lower_read_quoted_pattern(&program_json);
// 2. JoinIR → MIR 変換
let mir_module = convert_joinir_to_mir(&join_module);
assert!(
mir_module.is_ok(),
"JoinIR → MIR conversion should succeed: {:?}",
mir_module.err()
);
let mir_module = mir_module.unwrap();
// 3. MIR 構造の検証
// 4 つの関数がある: entry, k_guard_fail, loop_step, k_exit
assert_eq!(mir_module.functions.len(), 4, "MIR should have 4 functions");
// 関数名を確認
let func_names: Vec<&str> = mir_module.functions.keys().map(|s| s.as_str()).collect();
eprintln!("[Phase 45] MIR function names: {:?}", func_names);
// join_func_0 (entry), join_func_1 (loop_step), join_func_2 (k_exit), join_func_3 (k_guard_fail)
assert!(
func_names.contains(&"join_func_0"),
"Should have entry function join_func_0"
);
assert!(
func_names.contains(&"join_func_1"),
"Should have loop_step function join_func_1"
);
assert!(
func_names.contains(&"join_func_2"),
"Should have k_exit function join_func_2"
);
assert!(
func_names.contains(&"join_func_3"),
"Should have k_guard_fail function join_func_3"
);
eprintln!("[Phase 45] test_read_quoted_from_joinir_to_mir_conversion PASSED");
}
/// Phase 45: String 定数の MIR 変換テスト
#[test]
fn test_convert_string_const_inst() {
let join_const = crate::mir::join_ir::MirLikeInst::Const {
dst: ValueId(50),
value: crate::mir::join_ir::ConstValue::String("\"".to_string()),
};
let mir_inst = convert_mir_like_inst(&join_const).unwrap();
match mir_inst {
MirInstruction::Const { dst, value } => {
assert_eq!(dst, ValueId(50));
match value {
crate::mir::ConstValue::String(s) => assert_eq!(s, "\""),
_ => panic!("Expected String value"),
}
}
_ => panic!("Expected Const instruction"),
}
}
/// Phase 45: A/B テスト - Route B (JoinIR) E2E 実行テスト
///
/// HAKO_JOINIR_READ_QUOTED=1 が設定されている場合のみ実行。
///
/// # Test Cases (from Phase 45 fixture)
///
/// - T1: `"abc"` at pos 0 → `abc`
/// - T2: `""` at pos 0 → `` (empty)
/// - T3: `abc` at pos 0 → `` (guard fail, no quote)
/// - T4: `xx"def"` at pos 2 → `def`
///
/// # Known Limitation
///
/// T5 (escape handling) is skipped due to known PHI issue
/// with variable reassignment inside if-blocks.
#[test]
fn test_read_quoted_from_route_b_e2e() {
// Dev flag がない場合はスキップ
if std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() != Some("1") {
eprintln!(
"[Phase 45] Skipping test_read_quoted_from_route_b_e2e: \
Set HAKO_JOINIR_READ_QUOTED=1 to enable"
);
return;
}
// 1. JoinModule を生成
let program_json = serde_json::json!({
"defs": [{
"name": "read_quoted_from",
"params": ["s", "pos"],
"body": { "body": [] }
}]
});
let mut lowerer = AstToJoinIrLowerer::new();
let join_module = lowerer.lower_read_quoted_pattern(&program_json);
let entry_func = join_module.entry.expect("Entry function should exist");
// 2. A/B テスト実行
// Note: Route B (JoinIR) は run_joinir_via_vm で実行
// Route A (既存) は別途フィクスチャで検証済み
// T1: "abc" at pos 0 → "abc"
let t1_result = run_joinir_via_vm(
&join_module,
entry_func,
&[JoinValue::Str("\"abc\"".to_string()), JoinValue::Int(0)],
);
match &t1_result {
Ok(JoinValue::Str(s)) => {
assert_eq!(s, "abc", "T1: Expected 'abc', got '{}'", s);
eprintln!("[Phase 45] T1 PASS: \"abc\" at pos 0 → '{}'", s);
}
Ok(v) => panic!("T1: Expected Str, got {:?}", v),
Err(e) => eprintln!("[Phase 45] T1 SKIP (execution not supported): {:?}", e),
}
// T2: "" at pos 0 → "" (empty)
let t2_result = run_joinir_via_vm(
&join_module,
entry_func,
&[JoinValue::Str("\"\"".to_string()), JoinValue::Int(0)],
);
match &t2_result {
Ok(JoinValue::Str(s)) => {
assert_eq!(s, "", "T2: Expected '', got '{}'", s);
eprintln!("[Phase 45] T2 PASS: \"\" at pos 0 → '{}'", s);
}
Ok(v) => panic!("T2: Expected Str, got {:?}", v),
Err(e) => eprintln!("[Phase 45] T2 SKIP (execution not supported): {:?}", e),
}
// T3: abc at pos 0 → "" (guard fail)
let t3_result = run_joinir_via_vm(
&join_module,
entry_func,
&[JoinValue::Str("abc".to_string()), JoinValue::Int(0)],
);
match &t3_result {
Ok(JoinValue::Str(s)) => {
assert_eq!(s, "", "T3: Expected '', got '{}'", s);
eprintln!("[Phase 45] T3 PASS: abc at pos 0 → '{}'", s);
}
Ok(v) => panic!("T3: Expected Str, got {:?}", v),
Err(e) => eprintln!("[Phase 45] T3 SKIP (execution not supported): {:?}", e),
}
// T4: xx"def" at pos 2 → "def"
let t4_result = run_joinir_via_vm(
&join_module,
entry_func,
&[JoinValue::Str("xx\"def\"".to_string()), JoinValue::Int(2)],
);
match &t4_result {
Ok(JoinValue::Str(s)) => {
assert_eq!(s, "def", "T4: Expected 'def', got '{}'", s);
eprintln!("[Phase 45] T4 PASS: xx\"def\" at pos 2 → '{}'", s);
}
Ok(v) => panic!("T4: Expected Str, got {:?}", v),
Err(e) => eprintln!("[Phase 45] T4 SKIP (execution not supported): {:?}", e),
}
// T5: Escape handling - "a\"b" at pos 0 → "a"b" (escaped quote)
// Phase 46: IfMerge で if-body 後の i と ch をマージ
let enable_escape_ifmerge = std::env::var("HAKO_JOINIR_READ_QUOTED_IFMERGE")
.ok()
.as_deref()
== Some("1");
if enable_escape_ifmerge {
// 入力: "a\"b" → 「"」で始まり、a, \", b, 「"」で終わる
// 期待出力: a"bエスケープされた引用符を含む
let t5_input = "\"a\\\"b\""; // Rust エスケープ: "a\"b" → JSON "a\"b"
let t5_result = run_joinir_via_vm(
&join_module,
entry_func,
&[JoinValue::Str(t5_input.to_string()), JoinValue::Int(0)],
);
match &t5_result {
Ok(JoinValue::Str(s)) => {
let expected = "a\"b"; // エスケープ後: a"b
assert_eq!(s, expected, "T5: Expected '{}', got '{}'", expected, s);
eprintln!(
"[Phase 46] T5 PASS: \"a\\\"b\" at pos 0 → '{}' (escape handling works!)",
s
);
}
Ok(v) => panic!("T5: Expected Str, got {:?}", v),
Err(e) => eprintln!("[Phase 46] T5 SKIP (execution not supported): {:?}", e),
}
} else {
eprintln!(
"[Phase 45] T5 SKIP: Set HAKO_JOINIR_READ_QUOTED_IFMERGE=1 to enable \
escape handling (Phase 46)"
);
}
eprintln!("[Phase 45] test_read_quoted_from_route_b_e2e completed");
}