Files
hakorune/src/mir/join_ir_vm_bridge/tests.rs
nyash-codex 447bbec998 refactor(joinir): Split ast_lowerer and join_ir_vm_bridge into modules
ast_lowerer.rs → ast_lowerer/ (10 files):
- mod.rs: public surface + entry dispatch
- context.rs: ExtractCtx helpers
- expr.rs: expression-to-JoinIR extraction
- if_return.rs: simple if→Select lowering
- loop_patterns.rs: loop variants (simple/break/continue)
- read_quoted.rs: read_quoted_from lowering (Phase 45-46)
- nested_if.rs: NestedIfMerge lowering
- analysis.rs: loop if-var analysis + metadata helpers
- tests.rs: frontend lowering tests
- README.md: module documentation

join_ir_vm_bridge.rs → join_ir_vm_bridge/ (5 files):
- mod.rs: public surface + shared helpers
- convert.rs: JoinIR→MIR lowering
- runner.rs: VM execution entry (run_joinir_via_vm)
- meta.rs: experimental metadata-aware hooks
- tests.rs: bridge-specific unit tests
- README.md: module documentation

Benefits:
- Clear separation of concerns per pattern
- Easier navigation and maintenance
- Each file has single responsibility
- README documents module boundaries

Co-authored-by: ChatGPT <noreply@openai.com>

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 17:42:19 +09:00

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