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>
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");
|
||
}
|