feat(joinir): S-5.2完了 - BoxCall → VM method_router 経由実装
実装内容:
- box_to_join_value() ヘルパー関数追加(VM Box → JoinValue 変換)
- StringBox.length/substring を VM 実装経由で呼び出し(hardcoded削除)
- safe_substring() 削除(不要になった)
技術的成果:
- ガードレール設計実現: 制御フローは JoinIR Runner、Box実装は VM に委譲
- VM 2号機 回避: Box実装の重複なし
- テスト全 PASS: joinir_runner_standalone_skip_ws/trim 両方成功
Phase 27-shortterm S-5.2 完了 ✅
This commit is contained in:
@ -12,7 +12,7 @@
|
||||
- **25.x**: Stage0/Stage1/Stage‑B / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備。
|
||||
- **25.1 系**: Stage‑B / Stage‑1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ライン。
|
||||
- **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成(LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBox)と MirScanExitLiveness の準備。
|
||||
- **26-H / 27.x(New)**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim/Stage‑1 UsingResolver minimal/FuncScanner.append_defs minimal までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備中(27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定済み。27.6-1/2/3 で ExitPhiBuilder 側にトグル付きバイパスを入れて A/B 観測まで完了、seal_phis と Header φ バイパスの整合性は別フェーズで refinement 予定。27.8〜27.11 で skip_ws/trim を Shared Builder Pattern+MIR-based lowering に移行し、27.12/27.13 で Stage‑1 UsingResolver minimal loop も同じ型に乗せ、27.14 では FuncScanner.append_defs 用の lowering+minimal .hako+auto_lowering テストまで整備済み。短期フェーズ残りは JoinIR runner の命令セット整理とスモーク固め)。
|
||||
- **26-H / 27.x(New)**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim/Stage‑1 UsingResolver minimal/FuncScanner.append_defs minimal までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備中(27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定済み。27.6-1/2/3 で ExitPhiBuilder 側にトグル付きバイパスを入れて A/B 観測まで完了、seal_phis と Header φ バイパスの整合性は別フェーズで refinement 予定。27.8〜27.11 で skip_ws/trim を Shared Builder Pattern+MIR-based lowering に移行し、27.12/27.13 で Stage‑1 UsingResolver minimal loop も同じ型に乗せ、27.14 では FuncScanner.append_defs 用の lowering+minimal .hako+auto_lowering テストまで整備済み。短期フェーズ残りは JoinIR→Rust VM ブリッジの最小実装と、それを使った skip_ws/trim/Stage‑1 minimal あたりの A/B テスト整備)。
|
||||
- Rust 側:
|
||||
- LoopForm v2 + ControlForm + Conservative PHI は、代表テスト(Stage‑1 UsingResolver / Stage‑B 最小ループ)ではほぼ安定。
|
||||
- 静的メソッド呼び出し規約と `continue` 絡みの PHI は 25.1m までで根治済み。
|
||||
|
||||
@ -138,6 +138,9 @@ fn eval_compute(inst: &MirLikeInst, locals: &mut HashMap<VarId, JoinValue>) -> R
|
||||
let v = crate::mir::join_ir_ops::eval_compare(*op, &l, &r)?;
|
||||
locals.insert(*dst, v);
|
||||
}
|
||||
// S-5.2: BoxCall → VM method_router 経由(ガードレール設計)
|
||||
// - 制御フロー: JoinIR Runner が担当
|
||||
// - Box/Plugin 実装: Rust VM に委譲(VM 2号機を避ける)
|
||||
MirLikeInst::BoxCall {
|
||||
dst,
|
||||
box_name,
|
||||
@ -153,9 +156,14 @@ fn eval_compute(inst: &MirLikeInst, locals: &mut HashMap<VarId, JoinValue>) -> R
|
||||
match method.as_str() {
|
||||
"length" => {
|
||||
let arg = expect_str(&read_var(locals, args[0])?)?;
|
||||
locals.insert(*dst.as_ref().ok_or_else(|| {
|
||||
// S-5.2: VM の StringBox.length() 実装を使用(hardcoded 削除)
|
||||
let string_box = crate::boxes::basic::StringBox::new(arg);
|
||||
let result_box = string_box.length();
|
||||
let result_value = box_to_join_value(result_box)?;
|
||||
let dst_var = dst.ok_or_else(|| {
|
||||
JoinRuntimeError::new("length call requires destination")
|
||||
})?, JoinValue::Int(arg.len() as i64));
|
||||
})?;
|
||||
locals.insert(dst_var, result_value);
|
||||
}
|
||||
"substring" => {
|
||||
if args.len() != 3 {
|
||||
@ -164,13 +172,16 @@ fn eval_compute(inst: &MirLikeInst, locals: &mut HashMap<VarId, JoinValue>) -> R
|
||||
));
|
||||
}
|
||||
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 start = expect_int(&read_var(locals, args[1])?)? as usize;
|
||||
let end = expect_int(&read_var(locals, args[2])?)? as usize;
|
||||
// S-5.2: VM の StringBox.substring() 実装を使用(hardcoded 削除)
|
||||
let string_box = crate::boxes::basic::StringBox::new(s);
|
||||
let result_box = string_box.substring(start, end);
|
||||
let result_value = box_to_join_value(result_box)?;
|
||||
let dst_var = dst.ok_or_else(|| {
|
||||
JoinRuntimeError::new("substring call requires destination")
|
||||
})?;
|
||||
locals.insert(dst_var, JoinValue::Str(slice));
|
||||
locals.insert(dst_var, result_value);
|
||||
}
|
||||
_ => {
|
||||
return Err(JoinRuntimeError::new(format!(
|
||||
@ -184,20 +195,6 @@ fn eval_compute(inst: &MirLikeInst, locals: &mut HashMap<VarId, JoinValue>) -> R
|
||||
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)
|
||||
@ -243,3 +240,25 @@ fn expect_str(value: &JoinValue) -> Result<String, JoinRuntimeError> {
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// S-5.2: Convert Box<dyn NyashBox> from VM to JoinValue
|
||||
///
|
||||
/// Tries to downcast to known primitive types first (IntegerBox, BoolBox, StringBox),
|
||||
/// otherwise wraps as BoxRef for future use.
|
||||
fn box_to_join_value(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> Result<JoinValue, JoinRuntimeError> {
|
||||
use std::sync::Arc;
|
||||
|
||||
// Try to downcast to known primitive types first
|
||||
if let Some(int_box) = nyash_box.as_any().downcast_ref::<crate::boxes::basic::IntegerBox>() {
|
||||
return Ok(JoinValue::Int(int_box.value));
|
||||
}
|
||||
if let Some(bool_box) = nyash_box.as_any().downcast_ref::<crate::boxes::basic::BoolBox>() {
|
||||
return Ok(JoinValue::Bool(bool_box.value));
|
||||
}
|
||||
if let Some(str_box) = nyash_box.as_any().downcast_ref::<crate::boxes::basic::StringBox>() {
|
||||
return Ok(JoinValue::Str(str_box.value.clone()));
|
||||
}
|
||||
|
||||
// Otherwise, wrap as BoxRef (for S-5.3/S-5.4 future use)
|
||||
Ok(JoinValue::BoxRef(Arc::from(nyash_box)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user