Add experimental JoinIR runner and tests
This commit is contained in:
@ -106,6 +106,7 @@ NYASH_PARSER_STAGE3=1 NYASH_ENABLE_USING=1 \
|
||||
| `NYASH_STAGE1_SCAN_GE=1` | OFF | Compare Ge命令スキャン | Phase 25.x |
|
||||
| `NYASH_TO_I64_DEBUG=1` | OFF | to_i64変換デバッグ | Phase 25.x |
|
||||
| `NYASH_FUNCSCANNER_DEBUG=1` | OFF | FuncScanner詳細ログ | Phase 25.3 |
|
||||
| `NYASH_JOINIR_EXPERIMENT=1` | OFF | JoinIR実験モード(MIR→JoinIR変換テストを有効化) | Phase 26-H/27 |
|
||||
|
||||
### 使用例
|
||||
|
||||
@ -120,6 +121,9 @@ NYASH_VM_DUMP_MIR=1 ./target/release/hakorune program.hako
|
||||
|
||||
# Stage-1 CLI + MIRダンプ
|
||||
NYASH_STAGE1_MIR_DUMP=1 cargo test mir_stage1_cli_emit_program_min
|
||||
|
||||
# JoinIR実験(テスト限定)
|
||||
NYASH_JOINIR_EXPERIMENT=1 cargo test --release mir_joinir_funcscanner_trim_auto_lowering -- --ignored
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@ -82,12 +82,16 @@ pub enum JoinInst {
|
||||
func: JoinFuncId,
|
||||
args: Vec<VarId>,
|
||||
k_next: Option<JoinContId>,
|
||||
/// 呼び出し結果を書き込む変数(None の場合は末尾呼び出しとして扱う)
|
||||
dst: Option<VarId>,
|
||||
},
|
||||
|
||||
/// 継続呼び出し(join / exit 継続など)
|
||||
Jump {
|
||||
cont: JoinContId,
|
||||
args: Vec<VarId>,
|
||||
/// None のときは無条件ジャンプ、Some(var) のときは var が truthy のときだけ実行
|
||||
cond: Option<VarId>,
|
||||
},
|
||||
|
||||
/// ルート関数 or 上位への戻り
|
||||
@ -259,6 +263,7 @@ pub fn lower_min_loop_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMo
|
||||
func: loop_step_id,
|
||||
args: vec![i_init],
|
||||
k_next: None, // main は直接 loop_step を呼ぶ
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(main_func);
|
||||
@ -315,6 +320,7 @@ pub fn lower_min_loop_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMo
|
||||
func: loop_step_id,
|
||||
args: vec![i_plus_1],
|
||||
k_next: None,
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(loop_step_func);
|
||||
@ -404,11 +410,13 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMod
|
||||
func: loop_step_id,
|
||||
args: vec![s_param, i_init, n],
|
||||
k_next: None,
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.entry = Some(skip_id);
|
||||
join_module.add_function(skip_func);
|
||||
|
||||
// loop_step 関数: if i >= n { k_exit(i) } else { ... nested if ... }
|
||||
// loop_step 関数: if i >= n { return i } else if ch == " " { loop_step(i + 1) } else { return i }
|
||||
let s_loop = ValueId(4000);
|
||||
let i_loop = ValueId(4001);
|
||||
let n_loop = ValueId(4002);
|
||||
@ -423,9 +431,9 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMod
|
||||
let cmp2_result = ValueId(4005);
|
||||
let i_plus_1 = ValueId(4006);
|
||||
let const_1 = ValueId(4007);
|
||||
let i_start = ValueId(4008);
|
||||
let i_end = ValueId(4009);
|
||||
let const_space = ValueId(4010);
|
||||
let bool_false = ValueId(4011);
|
||||
let cmp2_is_false = ValueId(4012);
|
||||
|
||||
// cmp1_result = (i >= n)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
@ -435,41 +443,33 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMod
|
||||
rhs: n_loop,
|
||||
}));
|
||||
|
||||
// if cmp1_result { k_exit(i) } - break path 1
|
||||
// Phase 27.1 簡略化: 分岐なしで両方のパスを列挙
|
||||
loop_step_func.body.push(JoinInst::Ret {
|
||||
value: Some(i_loop),
|
||||
// if i >= n { return i }
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(0),
|
||||
args: vec![i_loop],
|
||||
cond: Some(cmp1_result),
|
||||
});
|
||||
|
||||
// ch = s.substring(i, i + 1)
|
||||
// i_start = i
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_start,
|
||||
op: BinOpKind::Add,
|
||||
lhs: i_loop,
|
||||
rhs: ValueId(4011), // const 0
|
||||
}));
|
||||
|
||||
// const 1
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_1,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
// i_end = i + 1
|
||||
// i_plus_1 = i + 1 (再利用: substring end / continue path)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_end,
|
||||
dst: i_plus_1,
|
||||
op: BinOpKind::Add,
|
||||
lhs: i_loop,
|
||||
rhs: const_1,
|
||||
}));
|
||||
|
||||
// ch = s.substring(i_start, i_end)
|
||||
// ch = s.substring(i, i + 1)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||
dst: Some(ch),
|
||||
box_name: "StringBox".to_string(),
|
||||
method: "substring".to_string(),
|
||||
args: vec![s_loop, i_start, i_end],
|
||||
args: vec![s_loop, i_loop, i_plus_1],
|
||||
}));
|
||||
|
||||
// const " " (space)
|
||||
@ -486,23 +486,33 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option<JoinMod
|
||||
rhs: const_space,
|
||||
}));
|
||||
|
||||
// if ch == " " { loop_step(s, i + 1, n, k_exit) }
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_plus_1,
|
||||
op: BinOpKind::Add,
|
||||
lhs: i_loop,
|
||||
rhs: const_1,
|
||||
// bool false (for negation)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: bool_false,
|
||||
value: ConstValue::Bool(false),
|
||||
}));
|
||||
|
||||
// cmp2_is_false = (cmp2_result == false)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp2_is_false,
|
||||
op: CompareOp::Eq,
|
||||
lhs: cmp2_result,
|
||||
rhs: bool_false,
|
||||
}));
|
||||
|
||||
// if ch != " " { return i }
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(1),
|
||||
args: vec![i_loop],
|
||||
cond: Some(cmp2_is_false),
|
||||
});
|
||||
|
||||
// continue path: loop_step(s, i + 1, n)
|
||||
loop_step_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![s_loop, i_plus_1, n_loop],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// else { k_exit(i) } - break path 2
|
||||
loop_step_func.body.push(JoinInst::Ret {
|
||||
value: Some(i_loop),
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(loop_step_func);
|
||||
@ -566,31 +576,30 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio
|
||||
|
||||
let mut join_module = JoinModule::new();
|
||||
|
||||
// trim_main 関数: 前処理 + loop_step 呼び出し
|
||||
// trim_main 関数: 前処理 + 先頭/末尾の空白を除去
|
||||
let trim_main_id = JoinFuncId::new(0);
|
||||
let s_param = ValueId(5000);
|
||||
let mut trim_main_func = JoinFunction::new(trim_main_id, "trim_main".to_string(), vec![s_param]);
|
||||
|
||||
// 変数定義(固定 ValueId 割り当て)
|
||||
let str_val = ValueId(5001);
|
||||
let n_val = ValueId(5002);
|
||||
let b_val = ValueId(5003);
|
||||
let e_init = ValueId(5004);
|
||||
let const_empty = ValueId(5005);
|
||||
let const_zero = ValueId(5006);
|
||||
|
||||
// str = "" + s_param (文字列化)
|
||||
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_empty,
|
||||
value: ConstValue::String("".to_string()),
|
||||
}));
|
||||
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: str_val,
|
||||
lhs: ValueId(5005), // empty string const
|
||||
lhs: const_empty,
|
||||
rhs: s_param,
|
||||
op: BinOpKind::Add,
|
||||
}));
|
||||
|
||||
// 空文字列定数
|
||||
trim_main_func.body.insert(trim_main_func.body.len() - 1, JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: ValueId(5005),
|
||||
value: ConstValue::String("".to_string()),
|
||||
}));
|
||||
|
||||
// n = str.length()
|
||||
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||
dst: Some(n_val),
|
||||
@ -599,29 +608,42 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio
|
||||
args: vec![str_val],
|
||||
}));
|
||||
|
||||
// b = skip_whitespace(str, 0) - 簡略化のため const 0 で代用
|
||||
// const 0
|
||||
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: b_val,
|
||||
dst: const_zero,
|
||||
value: ConstValue::Integer(0),
|
||||
}));
|
||||
|
||||
// e_init = n
|
||||
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
// b = skip_leading_whitespace(str, 0, n)
|
||||
let skip_leading_id = JoinFuncId::new(2);
|
||||
trim_main_func.body.push(JoinInst::Call {
|
||||
func: skip_leading_id,
|
||||
args: vec![str_val, const_zero, n_val],
|
||||
k_next: None,
|
||||
dst: Some(b_val),
|
||||
});
|
||||
|
||||
// e_init = n (コピー)
|
||||
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: e_init,
|
||||
value: ConstValue::Integer(0), // placeholder - 実際は n のコピー
|
||||
op: BinOpKind::Add,
|
||||
lhs: n_val,
|
||||
rhs: const_zero,
|
||||
}));
|
||||
|
||||
// loop_step(str, b, e_init, k_exit)
|
||||
// loop_step(str, b, e_init) -> 戻り値をそのまま返す
|
||||
let loop_step_id = JoinFuncId::new(1);
|
||||
trim_main_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![str_val, b_val, e_init],
|
||||
k_next: None,
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.entry = Some(trim_main_id);
|
||||
join_module.add_function(trim_main_func);
|
||||
|
||||
// loop_step 関数: ループボディ
|
||||
// loop_step 関数: 末尾の空白を削り、最終的に substring(b, e) を返す
|
||||
let str_loop = ValueId(6000);
|
||||
let b_loop = ValueId(6001);
|
||||
let e_loop = ValueId(6002);
|
||||
@ -640,21 +662,55 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio
|
||||
op: CompareOp::Gt,
|
||||
}));
|
||||
|
||||
// ch = str.substring(e - 1, e)
|
||||
let e_minus_1 = ValueId(6004);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: e_minus_1,
|
||||
lhs: e_loop,
|
||||
rhs: ValueId(6005), // const 1
|
||||
op: BinOpKind::Sub,
|
||||
// bool false (共通)
|
||||
let bool_false = ValueId(6019);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: bool_false,
|
||||
value: ConstValue::Bool(false),
|
||||
}));
|
||||
|
||||
// trimmed_base = str.substring(b, e)
|
||||
let trimmed_base = ValueId(6004);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||
dst: Some(trimmed_base),
|
||||
box_name: "StringBox".to_string(),
|
||||
method: "substring".to_string(),
|
||||
args: vec![str_loop, b_loop, e_loop],
|
||||
}));
|
||||
|
||||
// cond_is_false = (cond == false)
|
||||
let cond_is_false = ValueId(6020);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cond_is_false,
|
||||
lhs: cond,
|
||||
rhs: bool_false,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
// if !(e > b) { return substring(b, e) }
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(0),
|
||||
args: vec![trimmed_base],
|
||||
cond: Some(cond_is_false),
|
||||
});
|
||||
|
||||
// const 1
|
||||
let const_1 = ValueId(6005);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: ValueId(6005),
|
||||
dst: const_1,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
let ch = ValueId(6006);
|
||||
// e_minus_1 = e - 1
|
||||
let e_minus_1 = ValueId(6006);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: e_minus_1,
|
||||
lhs: e_loop,
|
||||
rhs: const_1,
|
||||
op: BinOpKind::Sub,
|
||||
}));
|
||||
|
||||
let ch = ValueId(6007);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||
dst: Some(ch),
|
||||
box_name: "StringBox".to_string(),
|
||||
@ -662,61 +718,65 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio
|
||||
args: vec![str_loop, e_minus_1, e_loop],
|
||||
}));
|
||||
|
||||
// is_space = (ch == " " || ch == "\t" || ch == "\n" || ch == "\r")
|
||||
// 4つの比較を OR でつなぐ
|
||||
let cmp_space = ValueId(6007);
|
||||
let cmp_tab = ValueId(6008);
|
||||
let cmp_newline = ValueId(6009);
|
||||
let cmp_cr = ValueId(6010);
|
||||
// is_space = (ch == " " || ch == "\\t" || ch == "\\n" || ch == "\\r")
|
||||
let cmp_space = ValueId(6008);
|
||||
let cmp_tab = ValueId(6009);
|
||||
let cmp_newline = ValueId(6010);
|
||||
let cmp_cr = ValueId(6011);
|
||||
|
||||
let const_space = ValueId(6012);
|
||||
let const_tab = ValueId(6013);
|
||||
let const_newline = ValueId(6014);
|
||||
let const_cr = ValueId(6015);
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: ValueId(6011),
|
||||
dst: const_space,
|
||||
value: ConstValue::String(" ".to_string()),
|
||||
}));
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_space,
|
||||
lhs: ch,
|
||||
rhs: ValueId(6011),
|
||||
rhs: const_space,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: ValueId(6012),
|
||||
value: ConstValue::String("\t".to_string()),
|
||||
dst: const_tab,
|
||||
value: ConstValue::String("\\t".to_string()),
|
||||
}));
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_tab,
|
||||
lhs: ch,
|
||||
rhs: ValueId(6012),
|
||||
rhs: const_tab,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: ValueId(6013),
|
||||
value: ConstValue::String("\n".to_string()),
|
||||
dst: const_newline,
|
||||
value: ConstValue::String("\\n".to_string()),
|
||||
}));
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_newline,
|
||||
lhs: ch,
|
||||
rhs: ValueId(6013),
|
||||
rhs: const_newline,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: ValueId(6014),
|
||||
value: ConstValue::String("\r".to_string()),
|
||||
dst: const_cr,
|
||||
value: ConstValue::String("\\r".to_string()),
|
||||
}));
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_cr,
|
||||
lhs: ch,
|
||||
rhs: ValueId(6014),
|
||||
rhs: const_cr,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
// OR chain: (cmp_space || cmp_tab) || cmp_newline || cmp_cr
|
||||
let or1 = ValueId(6015);
|
||||
let or2 = ValueId(6016);
|
||||
let is_space = ValueId(6017);
|
||||
let or1 = ValueId(6016);
|
||||
let or2 = ValueId(6017);
|
||||
let is_space = ValueId(6018);
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: or1,
|
||||
@ -739,31 +799,197 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio
|
||||
op: BinOpKind::Or,
|
||||
}));
|
||||
|
||||
// if is_space { e_next = e - 1; loop_step(...) } else { k_exit(e) }
|
||||
let e_next = ValueId(6018);
|
||||
// is_space_false = (is_space == false)
|
||||
let is_space_false = ValueId(6021);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: is_space_false,
|
||||
lhs: is_space,
|
||||
rhs: bool_false,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
// if !is_space { return substring(b, e) }
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(1),
|
||||
args: vec![trimmed_base],
|
||||
cond: Some(is_space_false),
|
||||
});
|
||||
|
||||
// continue path: e_next = e - 1; loop_step(str, b, e_next)
|
||||
let e_next = ValueId(6022);
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: e_next,
|
||||
lhs: e_loop,
|
||||
rhs: ValueId(6005), // const 1 (already defined)
|
||||
rhs: const_1,
|
||||
op: BinOpKind::Sub,
|
||||
}));
|
||||
|
||||
// Branch on is_space
|
||||
// then: loop_step(str, b, e_next, k_exit)
|
||||
// else: k_exit(e)
|
||||
// 簡略化: 直接 Ret で終了(実際の分岐は MIR から推測が必要)
|
||||
loop_step_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id, // 再帰呼び出し
|
||||
args: vec![str_loop, b_loop, e_next],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// Break path: k_exit(e)
|
||||
loop_step_func.body.push(JoinInst::Ret {
|
||||
value: Some(e_loop),
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(loop_step_func);
|
||||
|
||||
// skip_leading 関数: 先頭の空白をスキップして位置を返す
|
||||
let mut skip_func = JoinFunction::new(
|
||||
skip_leading_id,
|
||||
"skip_leading".to_string(),
|
||||
vec![ValueId(7000), ValueId(7001), ValueId(7002)], // (s, i, n)
|
||||
);
|
||||
let s_skip = ValueId(7000);
|
||||
let i_skip = ValueId(7001);
|
||||
let n_skip = ValueId(7002);
|
||||
let cmp_len = ValueId(7003);
|
||||
let const_1_skip = ValueId(7004);
|
||||
let i_plus_1_skip = ValueId(7005);
|
||||
let ch_skip = ValueId(7006);
|
||||
let cmp_space_skip = ValueId(7007);
|
||||
let cmp_tab_skip = ValueId(7008);
|
||||
let cmp_newline_skip = ValueId(7009);
|
||||
let cmp_cr_skip = ValueId(7010);
|
||||
let const_space_skip = ValueId(7011);
|
||||
let const_tab_skip = ValueId(7012);
|
||||
let const_newline_skip = ValueId(7013);
|
||||
let const_cr_skip = ValueId(7014);
|
||||
let or1_skip = ValueId(7015);
|
||||
let or2_skip = ValueId(7016);
|
||||
let is_space_skip = ValueId(7017);
|
||||
let bool_false_skip = ValueId(7018);
|
||||
let is_space_false_skip = ValueId(7019);
|
||||
|
||||
// cmp_len = (i >= n)
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_len,
|
||||
lhs: i_skip,
|
||||
rhs: n_skip,
|
||||
op: CompareOp::Ge,
|
||||
}));
|
||||
|
||||
// if i >= n { return i }
|
||||
skip_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(2),
|
||||
args: vec![i_skip],
|
||||
cond: Some(cmp_len),
|
||||
});
|
||||
|
||||
// const 1
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_1_skip,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
// i_plus_1 = i + 1
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_plus_1_skip,
|
||||
lhs: i_skip,
|
||||
rhs: const_1_skip,
|
||||
op: BinOpKind::Add,
|
||||
}));
|
||||
|
||||
// ch = s.substring(i, i + 1)
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||
dst: Some(ch_skip),
|
||||
box_name: "StringBox".to_string(),
|
||||
method: "substring".to_string(),
|
||||
args: vec![s_skip, i_skip, i_plus_1_skip],
|
||||
}));
|
||||
|
||||
// whitespace constants + comparisons
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_space_skip,
|
||||
value: ConstValue::String(" ".to_string()),
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_space_skip,
|
||||
lhs: ch_skip,
|
||||
rhs: const_space_skip,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_tab_skip,
|
||||
value: ConstValue::String("\\t".to_string()),
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_tab_skip,
|
||||
lhs: ch_skip,
|
||||
rhs: const_tab_skip,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_newline_skip,
|
||||
value: ConstValue::String("\\n".to_string()),
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_newline_skip,
|
||||
lhs: ch_skip,
|
||||
rhs: const_newline_skip,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_cr_skip,
|
||||
value: ConstValue::String("\\r".to_string()),
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_cr_skip,
|
||||
lhs: ch_skip,
|
||||
rhs: const_cr_skip,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
// is_space_skip = OR chain
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: or1_skip,
|
||||
lhs: cmp_space_skip,
|
||||
rhs: cmp_tab_skip,
|
||||
op: BinOpKind::Or,
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: or2_skip,
|
||||
lhs: or1_skip,
|
||||
rhs: cmp_newline_skip,
|
||||
op: BinOpKind::Or,
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: is_space_skip,
|
||||
lhs: or2_skip,
|
||||
rhs: cmp_cr_skip,
|
||||
op: BinOpKind::Or,
|
||||
}));
|
||||
|
||||
// bool false + negation
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: bool_false_skip,
|
||||
value: ConstValue::Bool(false),
|
||||
}));
|
||||
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: is_space_false_skip,
|
||||
lhs: is_space_skip,
|
||||
rhs: bool_false_skip,
|
||||
op: CompareOp::Eq,
|
||||
}));
|
||||
|
||||
// if not space -> return i
|
||||
skip_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(3),
|
||||
args: vec![i_skip],
|
||||
cond: Some(is_space_false_skip),
|
||||
});
|
||||
|
||||
// continue path: skip_leading(s, i + 1, n)
|
||||
skip_func.body.push(JoinInst::Call {
|
||||
func: skip_leading_id,
|
||||
args: vec![s_skip, i_plus_1_skip, n_skip],
|
||||
k_next: None,
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(skip_func);
|
||||
eprintln!("[joinir/trim] Generated {} JoinIR functions", join_module.functions.len());
|
||||
|
||||
Some(join_module)
|
||||
|
||||
327
src/mir/join_ir_runner.rs
Normal file
327
src/mir/join_ir_runner.rs
Normal file
@ -0,0 +1,327 @@
|
||||
//! JoinIR 実験用のミニ実行器(Phase 27.2)
|
||||
//!
|
||||
//! 目的: hand-written / minimal JoinIR を VM と A/B 比較するための軽量ランナー。
|
||||
//! - 対応値: i64 / bool / String / Unit
|
||||
//! - 対応命令: Const / BinOp / Compare / BoxCall(StringBox: length, substring) /
|
||||
//! Call / Jump / Ret
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::mir::join_ir::{
|
||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum JoinValue {
|
||||
Int(i64),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JoinRuntimeError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl JoinRuntimeError {
|
||||
fn new(msg: impl Into<String>) -> Self {
|
||||
Self {
|
||||
message: msg.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for JoinRuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for JoinRuntimeError {}
|
||||
|
||||
pub fn run_joinir_function(
|
||||
module: &JoinModule,
|
||||
entry: JoinFuncId,
|
||||
args: &[JoinValue],
|
||||
) -> Result<JoinValue, JoinRuntimeError> {
|
||||
execute_function(module, entry, args.to_vec())
|
||||
}
|
||||
|
||||
fn execute_function(
|
||||
module: &JoinModule,
|
||||
mut current_func: JoinFuncId,
|
||||
mut current_args: Vec<JoinValue>,
|
||||
) -> Result<JoinValue, JoinRuntimeError> {
|
||||
'exec: loop {
|
||||
let func = module
|
||||
.functions
|
||||
.get(¤t_func)
|
||||
.ok_or_else(|| JoinRuntimeError::new(format!("Function {:?} not found", current_func)))?;
|
||||
|
||||
if func.params.len() != current_args.len() {
|
||||
return Err(JoinRuntimeError::new(format!(
|
||||
"Arity mismatch for {:?}: expected {}, got {}",
|
||||
func.id,
|
||||
func.params.len(),
|
||||
current_args.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut locals: HashMap<VarId, JoinValue> = HashMap::new();
|
||||
for (param, arg) in func.params.iter().zip(current_args.iter()) {
|
||||
locals.insert(*param, arg.clone());
|
||||
}
|
||||
|
||||
let mut ip = 0usize;
|
||||
while ip < func.body.len() {
|
||||
match &func.body[ip] {
|
||||
JoinInst::Compute(inst) => {
|
||||
eval_compute(inst, &mut locals)?;
|
||||
ip += 1;
|
||||
}
|
||||
JoinInst::Call {
|
||||
func: target,
|
||||
args,
|
||||
k_next,
|
||||
dst,
|
||||
} => {
|
||||
if k_next.is_some() {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"Join continuation (k_next) is not supported in the experimental runner",
|
||||
));
|
||||
}
|
||||
let resolved_args = materialize_args(args, &locals)?;
|
||||
if let Some(dst_var) = dst {
|
||||
let value = execute_function(module, *target, resolved_args)?;
|
||||
locals.insert(*dst_var, value);
|
||||
ip += 1;
|
||||
} else {
|
||||
current_func = *target;
|
||||
current_args = resolved_args;
|
||||
continue 'exec;
|
||||
}
|
||||
}
|
||||
JoinInst::Jump { cont: _, args, cond } => {
|
||||
let should_jump = match cond {
|
||||
Some(var) => as_bool(&read_var(&locals, *var)?)?,
|
||||
None => true,
|
||||
};
|
||||
if should_jump {
|
||||
let ret = if let Some(first) = args.first() {
|
||||
read_var(&locals, *first)?
|
||||
} else {
|
||||
JoinValue::Unit
|
||||
};
|
||||
return Ok(ret);
|
||||
}
|
||||
ip += 1;
|
||||
}
|
||||
JoinInst::Ret { value } => {
|
||||
let ret = match value {
|
||||
Some(var) => read_var(&locals, *var)?,
|
||||
None => JoinValue::Unit,
|
||||
};
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallthrough without explicit return
|
||||
return Ok(JoinValue::Unit);
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_compute(inst: &MirLikeInst, locals: &mut HashMap<VarId, JoinValue>) -> Result<(), JoinRuntimeError> {
|
||||
match inst {
|
||||
MirLikeInst::Const { dst, value } => {
|
||||
let v = match value {
|
||||
ConstValue::Integer(i) => JoinValue::Int(*i),
|
||||
ConstValue::Bool(b) => JoinValue::Bool(*b),
|
||||
ConstValue::String(s) => JoinValue::Str(s.clone()),
|
||||
ConstValue::Null => JoinValue::Unit,
|
||||
};
|
||||
locals.insert(*dst, v);
|
||||
}
|
||||
MirLikeInst::BinOp { dst, op, lhs, rhs } => {
|
||||
let l = read_var(locals, *lhs)?;
|
||||
let r = read_var(locals, *rhs)?;
|
||||
let v = match op {
|
||||
BinOpKind::Add => match (l, r) {
|
||||
(JoinValue::Int(a), JoinValue::Int(b)) => JoinValue::Int(a + b),
|
||||
(JoinValue::Str(a), JoinValue::Str(b)) => JoinValue::Str(format!("{a}{b}")),
|
||||
_ => {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"Add supported only for (int,int) or (str,str)",
|
||||
))
|
||||
}
|
||||
},
|
||||
BinOpKind::Sub => match (l, r) {
|
||||
(JoinValue::Int(a), JoinValue::Int(b)) => JoinValue::Int(a - b),
|
||||
_ => return Err(JoinRuntimeError::new("Sub supported only for integers")),
|
||||
},
|
||||
BinOpKind::Mul => match (l, r) {
|
||||
(JoinValue::Int(a), JoinValue::Int(b)) => JoinValue::Int(a * b),
|
||||
_ => return Err(JoinRuntimeError::new("Mul supported only for integers")),
|
||||
},
|
||||
BinOpKind::Div => match (l, r) {
|
||||
(JoinValue::Int(_), JoinValue::Int(0)) => {
|
||||
return Err(JoinRuntimeError::new("Division by zero"))
|
||||
}
|
||||
(JoinValue::Int(a), JoinValue::Int(b)) => JoinValue::Int(a / b),
|
||||
_ => return Err(JoinRuntimeError::new("Div supported only for integers")),
|
||||
},
|
||||
BinOpKind::Or => match (l, r) {
|
||||
(JoinValue::Bool(a), JoinValue::Bool(b)) => JoinValue::Bool(a || b),
|
||||
_ => return Err(JoinRuntimeError::new("Or supported only for bools")),
|
||||
},
|
||||
BinOpKind::And => match (l, r) {
|
||||
(JoinValue::Bool(a), JoinValue::Bool(b)) => JoinValue::Bool(a && b),
|
||||
_ => return Err(JoinRuntimeError::new("And supported only for bools")),
|
||||
},
|
||||
};
|
||||
locals.insert(*dst, v);
|
||||
}
|
||||
MirLikeInst::Compare { dst, op, lhs, rhs } => {
|
||||
let l = read_var(locals, *lhs)?;
|
||||
let r = read_var(locals, *rhs)?;
|
||||
let v = match (l, r) {
|
||||
(JoinValue::Int(a), JoinValue::Int(b)) => match op {
|
||||
CompareOp::Lt => a < b,
|
||||
CompareOp::Le => a <= b,
|
||||
CompareOp::Gt => a > b,
|
||||
CompareOp::Ge => a >= b,
|
||||
CompareOp::Eq => a == b,
|
||||
CompareOp::Ne => a != b,
|
||||
},
|
||||
(JoinValue::Bool(a), JoinValue::Bool(b)) => match op {
|
||||
CompareOp::Eq => a == b,
|
||||
CompareOp::Ne => a != b,
|
||||
_ => {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"Bool comparison only supports Eq/Ne in the JoinIR runner",
|
||||
))
|
||||
}
|
||||
},
|
||||
(JoinValue::Str(a), JoinValue::Str(b)) => match op {
|
||||
CompareOp::Eq => a == b,
|
||||
CompareOp::Ne => a != b,
|
||||
_ => {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"String comparison only supports Eq/Ne in the JoinIR runner",
|
||||
))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"Type mismatch in Compare (expected homogeneous operands)",
|
||||
))
|
||||
}
|
||||
};
|
||||
locals.insert(*dst, JoinValue::Bool(v));
|
||||
}
|
||||
MirLikeInst::BoxCall {
|
||||
dst,
|
||||
box_name,
|
||||
method,
|
||||
args,
|
||||
} => {
|
||||
if box_name != "StringBox" {
|
||||
return Err(JoinRuntimeError::new(format!(
|
||||
"Unsupported box call target: {}",
|
||||
box_name
|
||||
)));
|
||||
}
|
||||
match method.as_str() {
|
||||
"length" => {
|
||||
let arg = expect_str(&read_var(locals, args[0])?)?;
|
||||
locals.insert(*dst.as_ref().ok_or_else(|| {
|
||||
JoinRuntimeError::new("length call requires destination")
|
||||
})?, JoinValue::Int(arg.len() as i64));
|
||||
}
|
||||
"substring" => {
|
||||
if args.len() != 3 {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"substring expects 3 arguments (s, start, end)",
|
||||
));
|
||||
}
|
||||
let s = expect_str(&read_var(locals, args[0])?)?;
|
||||
let start = expect_int(&read_var(locals, args[1])?)?;
|
||||
let end = expect_int(&read_var(locals, args[2])?)?;
|
||||
let slice = safe_substring(&s, start, end)?;
|
||||
let dst_var = dst.ok_or_else(|| {
|
||||
JoinRuntimeError::new("substring call requires destination")
|
||||
})?;
|
||||
locals.insert(dst_var, JoinValue::Str(slice));
|
||||
}
|
||||
_ => {
|
||||
return Err(JoinRuntimeError::new(format!(
|
||||
"Unsupported StringBox method: {}",
|
||||
method
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn safe_substring(s: &str, start: i64, end: i64) -> Result<String, JoinRuntimeError> {
|
||||
if start < 0 || end < 0 {
|
||||
return Err(JoinRuntimeError::new("substring indices must be non-negative"));
|
||||
}
|
||||
let (start_usize, end_usize) = (start as usize, end as usize);
|
||||
if start_usize > end_usize {
|
||||
return Err(JoinRuntimeError::new("substring start > end"));
|
||||
}
|
||||
if start_usize > s.len() || end_usize > s.len() {
|
||||
return Err(JoinRuntimeError::new("substring indices out of bounds"));
|
||||
}
|
||||
Ok(s[start_usize..end_usize].to_string())
|
||||
}
|
||||
|
||||
fn read_var(locals: &HashMap<VarId, JoinValue>, var: VarId) -> Result<JoinValue, JoinRuntimeError> {
|
||||
locals
|
||||
.get(&var)
|
||||
.cloned()
|
||||
.ok_or_else(|| JoinRuntimeError::new(format!("Variable {:?} not bound", var)))
|
||||
}
|
||||
|
||||
fn materialize_args(
|
||||
args: &[VarId],
|
||||
locals: &HashMap<VarId, JoinValue>,
|
||||
) -> Result<Vec<JoinValue>, JoinRuntimeError> {
|
||||
args.iter().map(|v| read_var(locals, *v)).collect()
|
||||
}
|
||||
|
||||
fn as_bool(value: &JoinValue) -> Result<bool, JoinRuntimeError> {
|
||||
match value {
|
||||
JoinValue::Bool(b) => Ok(*b),
|
||||
JoinValue::Int(i) => Ok(*i != 0),
|
||||
JoinValue::Unit => Ok(false),
|
||||
other => Err(JoinRuntimeError::new(format!(
|
||||
"Expected bool-compatible value, got {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_int(value: &JoinValue) -> Result<i64, JoinRuntimeError> {
|
||||
match value {
|
||||
JoinValue::Int(i) => Ok(*i),
|
||||
other => Err(JoinRuntimeError::new(format!(
|
||||
"Expected int, got {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_str(value: &JoinValue) -> Result<String, JoinRuntimeError> {
|
||||
match value {
|
||||
JoinValue::Str(s) => Ok(s.clone()),
|
||||
other => Err(JoinRuntimeError::new(format!(
|
||||
"Expected string, got {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,7 @@ pub mod value_id;
|
||||
pub mod value_kind; // Phase 26-A: ValueId型安全化
|
||||
pub mod query; // Phase 26-G: MIR read/write/CFGビュー (MirQuery)
|
||||
pub mod join_ir; // Phase 26-H: 関数正規化IR(JoinIR)
|
||||
pub mod join_ir_runner; // Phase 27.2: JoinIR 実行器(実験用)
|
||||
pub mod verification;
|
||||
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
||||
|
||||
@ -59,6 +60,7 @@ pub use value_id::{LocalId, ValueId, ValueIdGenerator};
|
||||
pub use value_kind::{MirValueKind, TypedValueId}; // Phase 26-A: ValueId型安全化
|
||||
pub use verification::MirVerifier;
|
||||
pub use verification_types::VerificationError;
|
||||
pub use join_ir_runner::{run_joinir_function, JoinRuntimeError, JoinValue};
|
||||
// Phase 15 control flow utilities (段階的根治戦略)
|
||||
pub use utils::{
|
||||
capture_actual_predecessor_and_jump, collect_phi_incoming_if_reachable,
|
||||
|
||||
148
src/tests/joinir_runner_min.rs
Normal file
148
src/tests/joinir_runner_min.rs
Normal file
@ -0,0 +1,148 @@
|
||||
// JoinIR 実験ランナーの A/B 比較テスト(skip_ws / trim_min)
|
||||
//
|
||||
// 目的:
|
||||
// - JoinIR を実際に実行し、既存 VM の結果と一致することを確認する
|
||||
// - Phase 27.2 のブリッジ実装を env トグル付きで検証する
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::backend::VM;
|
||||
use crate::mir::join_ir::{lower_funcscanner_trim_to_joinir, lower_skip_ws_to_joinir, JoinFuncId};
|
||||
use crate::mir::join_ir_runner::{run_joinir_function, JoinValue};
|
||||
use crate::mir::MirCompiler;
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
fn require_experiment_toggle() -> bool {
|
||||
if std::env::var("NYASH_JOINIR_EXPERIMENT")
|
||||
.ok()
|
||||
.as_deref()
|
||||
!= Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"[joinir/runner] NYASH_JOINIR_EXPERIMENT=1 not set, skipping experimental runner test"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn joinir_runner_minimal_skip_ws_executes() {
|
||||
if !require_experiment_toggle() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::env::set_var("NYASH_PARSER_STAGE3", "1");
|
||||
std::env::set_var("HAKO_PARSER_STAGE3", "1");
|
||||
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
|
||||
// 無限ループ検出のため、実験テストではステップ上限を小さめに設定しておく。
|
||||
// 0 は「上限なし」なので、ここでは明示的な上限を使う。
|
||||
std::env::set_var("NYASH_VM_MAX_STEPS", "100000");
|
||||
|
||||
let src = std::fs::read_to_string("apps/tests/minimal_ssa_skip_ws.hako")
|
||||
.expect("failed to read minimal_ssa_skip_ws.hako");
|
||||
let runner = r#"
|
||||
static box Runner {
|
||||
main(args) {
|
||||
return Main.skip(" abc")
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let full_src = format!("{src}\n{runner}");
|
||||
|
||||
let ast: ASTNode =
|
||||
NyashParser::parse_from_string(&full_src).expect("skip_ws: parse failed");
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let compiled = mc.compile(ast).expect("skip_ws: MIR compile failed");
|
||||
|
||||
std::env::set_var("NYASH_ENTRY", "Runner.main");
|
||||
let mut vm = VM::new();
|
||||
let vm_out = vm
|
||||
.execute_module(&compiled.module)
|
||||
.expect("skip_ws: VM execution failed");
|
||||
let vm_result = vm_out.to_string_box().value;
|
||||
std::env::remove_var("NYASH_ENTRY");
|
||||
|
||||
let join_module =
|
||||
lower_skip_ws_to_joinir(&compiled.module).expect("lower_skip_ws_to_joinir failed");
|
||||
let join_result = run_joinir_function(
|
||||
&join_module,
|
||||
JoinFuncId::new(0),
|
||||
&[JoinValue::Str(" abc".to_string())],
|
||||
)
|
||||
.expect("JoinIR runner failed for skip_ws");
|
||||
|
||||
assert_eq!(vm_result, "3", "VM expected to skip 3 leading spaces");
|
||||
match join_result {
|
||||
JoinValue::Int(v) => assert_eq!(v, 3, "JoinIR runner skip_ws result mismatch"),
|
||||
other => panic!("JoinIR runner returned non-int value: {:?}", other),
|
||||
}
|
||||
|
||||
std::env::remove_var("NYASH_PARSER_STAGE3");
|
||||
std::env::remove_var("HAKO_PARSER_STAGE3");
|
||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||
std::env::remove_var("NYASH_VM_MAX_STEPS");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn joinir_runner_funcscanner_trim_executes() {
|
||||
if !require_experiment_toggle() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::env::set_var("NYASH_PARSER_STAGE3", "1");
|
||||
std::env::set_var("HAKO_PARSER_STAGE3", "1");
|
||||
std::env::set_var("NYASH_ENABLE_USING", "1");
|
||||
std::env::set_var("HAKO_ENABLE_USING", "1");
|
||||
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
|
||||
// 上と同様、無限ループ検出用にステップ上限を明示しておく。
|
||||
std::env::set_var("NYASH_VM_MAX_STEPS", "100000");
|
||||
|
||||
let func_scanner_src = include_str!("../../lang/src/compiler/entry/func_scanner.hako");
|
||||
let test_src = std::fs::read_to_string("lang/src/compiler/tests/funcscanner_trim_min.hako")
|
||||
.expect("failed to read funcscanner_trim_min.hako");
|
||||
let runner = r#"
|
||||
static box Runner {
|
||||
main(args) {
|
||||
return FuncScannerBox.trim(" abc ")
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let full_src = format!("{func_scanner_src}\n{test_src}\n{runner}");
|
||||
|
||||
let ast: ASTNode =
|
||||
NyashParser::parse_from_string(&full_src).expect("trim_min: parse failed");
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let compiled = mc.compile(ast).expect("trim_min: MIR compile failed");
|
||||
|
||||
std::env::set_var("NYASH_ENTRY", "Runner.main");
|
||||
let mut vm = VM::new();
|
||||
let vm_out = vm
|
||||
.execute_module(&compiled.module)
|
||||
.expect("trim_min: VM execution failed");
|
||||
let vm_result = vm_out.to_string_box().value;
|
||||
std::env::remove_var("NYASH_ENTRY");
|
||||
|
||||
let join_module = lower_funcscanner_trim_to_joinir(&compiled.module)
|
||||
.expect("lower_funcscanner_trim_to_joinir failed");
|
||||
let join_result = run_joinir_function(
|
||||
&join_module,
|
||||
JoinFuncId::new(0),
|
||||
&[JoinValue::Str(" abc ".to_string())],
|
||||
)
|
||||
.expect("JoinIR runner failed for trim");
|
||||
|
||||
assert_eq!(vm_result, "abc", "VM trim_min should return stripped text");
|
||||
match join_result {
|
||||
JoinValue::Str(s) => assert_eq!(s, "abc", "JoinIR runner trim result mismatch"),
|
||||
other => panic!("JoinIR runner returned non-string value: {:?}", other),
|
||||
}
|
||||
|
||||
std::env::remove_var("NYASH_PARSER_STAGE3");
|
||||
std::env::remove_var("HAKO_PARSER_STAGE3");
|
||||
std::env::remove_var("NYASH_ENABLE_USING");
|
||||
std::env::remove_var("HAKO_ENABLE_USING");
|
||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||
std::env::remove_var("NYASH_VM_MAX_STEPS");
|
||||
}
|
||||
@ -68,6 +68,7 @@ fn mir_joinir_min_manual_construction() {
|
||||
func: loop_step_id,
|
||||
args: vec![i_init],
|
||||
k_next: Some(k_exit_id),
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(main_func);
|
||||
@ -102,6 +103,7 @@ fn mir_joinir_min_manual_construction() {
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: k_exit_id,
|
||||
args: vec![i_param],
|
||||
cond: Some(cmp_result),
|
||||
});
|
||||
|
||||
// i_plus_1 = i + 1
|
||||
|
||||
@ -14,6 +14,7 @@ pub mod mir_funcscanner_ssa;
|
||||
pub mod mir_joinir_min; // Phase 26-H: JoinIR型定義妥当性確認
|
||||
pub mod mir_joinir_skip_ws; // Phase 27.0: minimal_ssa_skip_ws JoinIR変換
|
||||
pub mod mir_joinir_funcscanner_trim; // Phase 27.1: FuncScannerBox.trim JoinIR変換
|
||||
pub mod joinir_runner_min; // Phase 27.2: JoinIR 実行器 A/B 比較テスト
|
||||
pub mod mir_locals_ssa;
|
||||
pub mod mir_loopform_conditional_reassign;
|
||||
pub mod mir_loopform_exit_phi;
|
||||
|
||||
Reference in New Issue
Block a user