302 lines
10 KiB
Rust
302 lines
10 KiB
Rust
|
|
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");
|
|||
|
|
}
|