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:
nyash-codex
2025-11-24 08:01:56 +09:00
parent 03cb0a49c7
commit a8555e67d5
2 changed files with 40 additions and 21 deletions

View File

@ -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)))
}