Phase 10.7/10.5c: include cycle detection (VM/Interpreter), minimal pyc IR→Nyash, String unification bridge (VM partial), add core plugins: RegexBox/EncodingBox/TOMLBox/PathBox + examples; wire nyash.toml; begin String interop for internal vs plugin boxes; update CURRENT_TASK.md

This commit is contained in:
Moe Charm
2025-08-30 23:47:08 +09:00
parent c13d9c045e
commit 4ae92cfb56
39 changed files with 3217 additions and 69 deletions

View File

@ -1215,6 +1215,42 @@ impl VM {
return Ok(ControlFlow::Continue);
}
}
// Fallback: support common methods on internal StringBox without requiring PluginBox receiver
if let VMValue::BoxRef(ref bx) = recv {
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
match method {
"length" => {
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(sb.value.len() as i64)); }
return Ok(ControlFlow::Continue);
}
"is_empty" | "isEmpty" => {
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Bool(sb.value.is_empty())); }
return Ok(ControlFlow::Continue);
}
"charCodeAt" => {
let idx_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::Integer(0) };
let idx = match idx_v { VMValue::Integer(i) => i.max(0) as usize, _ => 0 };
let code = sb.value.chars().nth(idx).map(|c| c as u32 as i64).unwrap_or(0);
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(code)); }
return Ok(ControlFlow::Continue);
}
"concat" => {
let rhs_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::String(String::new()) };
let rhs_s = match rhs_v {
VMValue::String(s) => s,
VMValue::BoxRef(br) => br.to_string_box().value,
_ => rhs_v.to_string(),
};
let mut new_s = sb.value.clone();
new_s.push_str(&rhs_s);
let out = Box::new(crate::box_trait::StringBox::new(new_s));
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::BoxRef(std::sync::Arc::from(out as Box<dyn crate::box_trait::NyashBox>))); }
return Ok(ControlFlow::Continue);
}
_ => {}
}
}
}
Err(VMError::InvalidInstruction(format!("PluginInvoke requires PluginBox receiver; method={} got {:?}", method, recv)))
}
}

View File

@ -213,6 +213,33 @@ impl VM {
li.type_name(), ri.type_name(), li.to_string_box().value, ri.to_string_box().value
);
}
// String-like comparison: internal StringBox or Plugin StringBox
fn boxref_to_string(b: &dyn crate::box_trait::NyashBox) -> Option<String> {
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
return Some(sb.value.clone());
}
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let s_opt: Option<String> = {
if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) {
if let Some(vb) = val_opt {
if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() {
Some(sbb.value.clone())
} else { None }
} else { None }
} else { None }
} else { None }
};
if s_opt.is_some() { return s_opt; }
}
}
None
}
if let (Some(ls), Some(rs)) = (boxref_to_string(li.as_ref()), boxref_to_string(ri.as_ref())) {
return Ok(match op { CompareOp::Eq => ls == rs, CompareOp::Ne => ls != rs, CompareOp::Lt => ls < rs, CompareOp::Le => ls <= rs, CompareOp::Gt => ls > rs, CompareOp::Ge => ls >= rs });
}
// Try integer comparisons via downcast or parse fallback
let l_opt = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value))
@ -225,6 +252,43 @@ impl VM {
}
Err(VMError::TypeError(format!("[BoxRef-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
}
// Mixed String vs BoxRef (string-like)
(VMValue::String(ls), VMValue::BoxRef(ri)) => {
let rs_opt = if let Some(sb) = ri.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb.value.clone()) } else {
if let Some(pb) = ri.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let tmp = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) {
if let Some(vb) = val_opt {
if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sbb.value.clone()) } else { None }
} else { None }
} else { None }
} else { None };
tmp
} else { None }
} else { None }
};
if let Some(rs) = rs_opt { return Ok(match op { CompareOp::Eq => *ls == rs, CompareOp::Ne => *ls != rs, CompareOp::Lt => *ls < rs, CompareOp::Le => *ls <= rs, CompareOp::Gt => *ls > rs, CompareOp::Ge => *ls >= rs }); }
Err(VMError::TypeError(format!("[String-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
}
(VMValue::BoxRef(li), VMValue::String(rs)) => {
let ls_opt = if let Some(sb) = li.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb.value.clone()) } else {
if let Some(pb) = li.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let tmp = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) {
if let Some(vb) = val_opt { if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sbb.value.clone()) } else { None } } else { None }
} else { None }
} else { None };
tmp
} else { None }
} else { None }
};
if let Some(ls) = ls_opt { return Ok(match op { CompareOp::Eq => ls == *rs, CompareOp::Ne => ls != *rs, CompareOp::Lt => ls < *rs, CompareOp::Le => ls <= *rs, CompareOp::Gt => ls > *rs, CompareOp::Ge => ls >= *rs }); }
Err(VMError::TypeError(format!("[BoxRef-String] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
}
// Mixed Integer (BoxRef vs Integer)
(VMValue::BoxRef(li), VMValue::Integer(r)) => {
let l_opt = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)