🎉 Phase 11.8/12.7: MIR Core-13 完全実装 + 糖衣構文ドキュメント更新
主要な変更: - MIR Core-13命令セット確定(Load/Store削除の革命的設計) - Const, BinOp, Compare(値・計算) - Jump, Branch, Return, Phi(制御) - Call, BoxCall, ExternCall(呼び出し) - TypeOp, Safepoint, Barrier(メタ) - Phase 12.7糖衣構文ドキュメント整理(超圧縮重視、可逆変換保証) - MIRビルダーのモジュール分割完了 - vtableテストスイート拡充 - AI協調開発ツール追加(並列リファクタリング支援) 詳細: - src/mir/instruction_introspection.rs: core13_instruction_names()追加 - MIRビルダー分割: decls.rs, exprs_*.rs, fields.rs - plugin_loader_v2: errors.rs, host_bridge.rs分離 - 論文用データ: mir13-final.md作成 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -55,7 +55,12 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
|
||||
MirInstruction::TypeOp { dst, op, value, ty } => vm.execute_typeop(*dst, op, *value, ty),
|
||||
|
||||
// Control flow
|
||||
MirInstruction::Return { value } => vm.execute_return(*value),
|
||||
MirInstruction::Return { value } => {
|
||||
if crate::config::env::vm_vt_trace() {
|
||||
if let Some(v) = value { eprintln!("[VT] Dispatch Return val_id={}", v.to_usize()); } else { eprintln!("[VT] Dispatch Return void"); }
|
||||
}
|
||||
vm.execute_return(*value)
|
||||
},
|
||||
MirInstruction::Jump { target } => vm.execute_jump(*target),
|
||||
MirInstruction::Branch { condition, then_bb, else_bb } => vm.execute_branch(*condition, *then_bb, *else_bb),
|
||||
MirInstruction::Phi { dst, inputs } => vm.execute_phi(*dst, inputs),
|
||||
|
||||
@ -6,6 +6,7 @@ pub mod vm;
|
||||
pub mod vm_phi;
|
||||
pub mod vm_instructions;
|
||||
pub mod vm_values;
|
||||
pub mod vm_types;
|
||||
pub mod vm_boxcall;
|
||||
pub mod vm_stats;
|
||||
// Phase 9.78h: VM split scaffolding (control_flow/dispatch/frame)
|
||||
|
||||
@ -29,156 +29,8 @@ use super::control_flow;
|
||||
// #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
// use crate::runtime::plugin_loader_v2::PluginLoaderV2;
|
||||
|
||||
/// VM execution error
|
||||
#[derive(Debug)]
|
||||
pub enum VMError {
|
||||
InvalidValue(String),
|
||||
InvalidInstruction(String),
|
||||
InvalidBasicBlock(String),
|
||||
DivisionByZero,
|
||||
StackUnderflow,
|
||||
TypeError(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VMError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VMError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg),
|
||||
VMError::InvalidInstruction(msg) => write!(f, "Invalid instruction: {}", msg),
|
||||
VMError::InvalidBasicBlock(msg) => write!(f, "Invalid basic block: {}", msg),
|
||||
VMError::DivisionByZero => write!(f, "Division by zero"),
|
||||
VMError::StackUnderflow => write!(f, "Stack underflow"),
|
||||
VMError::TypeError(msg) => write!(f, "Type error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for VMError {}
|
||||
|
||||
/// VM value representation
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VMValue {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Future(crate::boxes::future::FutureBox),
|
||||
Void,
|
||||
// Phase 9.78a: Add BoxRef for complex Box types
|
||||
BoxRef(Arc<dyn NyashBox>),
|
||||
}
|
||||
|
||||
// Manual PartialEq implementation to avoid requiring PartialEq on FutureBox
|
||||
impl PartialEq for VMValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(VMValue::Integer(a), VMValue::Integer(b)) => a == b,
|
||||
(VMValue::Float(a), VMValue::Float(b)) => a == b,
|
||||
(VMValue::Bool(a), VMValue::Bool(b)) => a == b,
|
||||
(VMValue::String(a), VMValue::String(b)) => a == b,
|
||||
(VMValue::Void, VMValue::Void) => true,
|
||||
// Future equality semantics are not defined; treat distinct futures as not equal
|
||||
(VMValue::Future(_), VMValue::Future(_)) => false,
|
||||
// BoxRef equality by reference
|
||||
(VMValue::BoxRef(_), VMValue::BoxRef(_)) => false,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VMValue {
|
||||
/// Convert to NyashBox for output
|
||||
pub fn to_nyash_box(&self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
VMValue::Integer(i) => Box::new(IntegerBox::new(*i)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::FloatBox::new(*f)),
|
||||
VMValue::Bool(b) => Box::new(BoolBox::new(*b)),
|
||||
VMValue::String(s) => Box::new(StringBox::new(s)),
|
||||
VMValue::Future(f) => Box::new(f.clone()),
|
||||
VMValue::Void => Box::new(VoidBox::new()),
|
||||
// BoxRef returns a shared handle (do NOT birth a new instance)
|
||||
VMValue::BoxRef(arc_box) => arc_box.share_box(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Get string representation for printing
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
VMValue::Integer(i) => i.to_string(),
|
||||
VMValue::Float(f) => f.to_string(),
|
||||
VMValue::Bool(b) => b.to_string(),
|
||||
VMValue::String(s) => s.clone(),
|
||||
VMValue::Future(f) => f.to_string_box().value,
|
||||
VMValue::Void => "void".to_string(),
|
||||
VMValue::BoxRef(arc_box) => arc_box.to_string_box().value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert to integer
|
||||
pub fn as_integer(&self) -> Result<i64, VMError> {
|
||||
match self {
|
||||
VMValue::Integer(i) => Ok(*i),
|
||||
_ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert to bool
|
||||
pub fn as_bool(&self) -> Result<bool, VMError> {
|
||||
match self {
|
||||
VMValue::Bool(b) => Ok(*b),
|
||||
VMValue::Integer(i) => Ok(*i != 0),
|
||||
// Pragmatic coercions for dynamic boxes
|
||||
VMValue::BoxRef(b) => {
|
||||
// BoolBox → bool
|
||||
if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() {
|
||||
return Ok(bb.value);
|
||||
}
|
||||
// IntegerBox → truthy if non-zero (legacy and new)
|
||||
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() { return Ok(ib.value != 0); }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { return Ok(ib.value != 0); }
|
||||
// VoidBox → false (nullish false)
|
||||
if b.as_any().downcast_ref::<VoidBox>().is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name())))
|
||||
}
|
||||
// Treat plain Void as false for logical contexts
|
||||
VMValue::Void => Ok(false),
|
||||
_ => Err(VMError::TypeError(format!("Expected bool, got {:?}", self))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from NyashBox to VMValue
|
||||
pub fn from_nyash_box(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> VMValue {
|
||||
// Try to downcast to known types for optimization
|
||||
if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() {
|
||||
VMValue::Integer(int_box.value)
|
||||
} else if let Some(bool_box) = nyash_box.as_any().downcast_ref::<BoolBox>() {
|
||||
VMValue::Bool(bool_box.value)
|
||||
} else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() {
|
||||
VMValue::String(string_box.value.clone())
|
||||
} else if let Some(future_box) = nyash_box.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
||||
VMValue::Future(future_box.clone())
|
||||
} else {
|
||||
// Phase 9.78a: For all other Box types (user-defined, plugin), store as BoxRef
|
||||
VMValue::BoxRef(Arc::from(nyash_box))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ConstValue> for VMValue {
|
||||
fn from(const_val: &ConstValue) -> Self {
|
||||
match const_val {
|
||||
ConstValue::Integer(i) => VMValue::Integer(*i),
|
||||
ConstValue::Float(f) => VMValue::Float(*f),
|
||||
ConstValue::Bool(b) => VMValue::Bool(*b),
|
||||
ConstValue::String(s) => VMValue::String(s.clone()),
|
||||
ConstValue::Null => VMValue::Void, // Simplified
|
||||
ConstValue::Void => VMValue::Void,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Thinned: core types moved to vm_types.rs
|
||||
pub use super::vm_types::{VMError, VMValue};
|
||||
|
||||
/// Virtual Machine state
|
||||
pub struct VM {
|
||||
|
||||
@ -223,6 +223,21 @@ impl VM {
|
||||
match method {
|
||||
"length" | "len" => { return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); }
|
||||
"toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); }
|
||||
"substring" => {
|
||||
if _args.len() >= 2 {
|
||||
let s = match _args[0].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => 0 };
|
||||
let e = match _args[1].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => string_box.value.chars().count() };
|
||||
return Ok(string_box.substring(s, e));
|
||||
}
|
||||
return Ok(Box::new(VoidBox::new()));
|
||||
}
|
||||
"concat" => {
|
||||
if let Some(arg0) = _args.get(0) {
|
||||
let out = format!("{}{}", string_box.value, arg0.to_string_box().value);
|
||||
return Ok(Box::new(StringBox::new(out)));
|
||||
}
|
||||
return Ok(Box::new(VoidBox::new()));
|
||||
}
|
||||
_ => return Ok(Box::new(VoidBox::new())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,6 +278,16 @@ impl VM {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Execute terminator if present and we haven't decided control flow yet
|
||||
if should_return.is_none() && next_block.is_none() {
|
||||
if let Some(term) = &block.terminator {
|
||||
match self.execute_instruction(term)? {
|
||||
ControlFlow::Continue => {},
|
||||
ControlFlow::Jump(target) => { next_block = Some(target); },
|
||||
ControlFlow::Return(value) => { should_return = Some(value); },
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(VMError::InvalidBasicBlock(format!(
|
||||
"Basic block {:?} not found",
|
||||
|
||||
@ -4,6 +4,48 @@ use crate::backend::vm::ControlFlow;
|
||||
use crate::backend::{VM, VMError, VMValue};
|
||||
|
||||
impl VM {
|
||||
/// Small helpers to reduce duplication in vtable stub paths.
|
||||
#[inline]
|
||||
fn vmvalue_to_box(val: &VMValue) -> Box<dyn crate::box_trait::NyashBox> {
|
||||
use crate::box_trait::{NyashBox, StringBox as SBox, IntegerBox as IBox, BoolBox as BBox};
|
||||
match val {
|
||||
VMValue::Integer(i) => Box::new(IBox::new(*i)),
|
||||
VMValue::String(s) => Box::new(SBox::new(s)),
|
||||
VMValue::Bool(b) => Box::new(BBox::new(*b)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(*f)),
|
||||
VMValue::BoxRef(bx) => bx.share_box(),
|
||||
VMValue::Future(fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn arg_as_box(&mut self, id: crate::mir::ValueId) -> Result<Box<dyn crate::box_trait::NyashBox>, VMError> {
|
||||
let v = self.get_value(id)?;
|
||||
Ok(Self::vmvalue_to_box(&v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn two_args_as_box(&mut self, a0: crate::mir::ValueId, a1: crate::mir::ValueId) -> Result<(Box<dyn crate::box_trait::NyashBox>, Box<dyn crate::box_trait::NyashBox>), VMError> {
|
||||
Ok((self.arg_as_box(a0)?, self.arg_as_box(a1)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn arg_to_string(&mut self, id: crate::mir::ValueId) -> Result<String, VMError> {
|
||||
let v = self.get_value(id)?;
|
||||
Ok(v.to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn arg_to_usize_or(&mut self, id: crate::mir::ValueId, default: usize) -> Result<usize, VMError> {
|
||||
let v = self.get_value(id)?;
|
||||
match v {
|
||||
VMValue::Integer(i) => Ok((i.max(0)) as usize),
|
||||
VMValue::String(ref s) => Ok(s.trim().parse::<i64>().map(|iv| iv.max(0) as usize).unwrap_or(default)),
|
||||
_ => Ok(v.to_string().trim().parse::<i64>().map(|iv| iv.max(0) as usize).unwrap_or(default)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute BoxCall instruction
|
||||
pub(crate) fn execute_boxcall(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, method_id: Option<u16>, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
||||
let recv = self.get_value(box_val)?;
|
||||
@ -358,6 +400,66 @@ impl VM {
|
||||
if crate::config::env::vm_vt_trace() {
|
||||
match _recv { VMValue::BoxRef(b) => eprintln!("[VT] probe recv_ty={} method={} argc={}", b.type_name(), _method, _args.len()), other => eprintln!("[VT] probe recv_prim={:?} method={} argc={}", other, _method, _args.len()), }
|
||||
}
|
||||
// Primitive String fast-path using StringBox slots
|
||||
if let VMValue::String(sv) = _recv {
|
||||
if crate::runtime::type_registry::resolve_typebox_by_name("StringBox").is_some() {
|
||||
let slot = crate::runtime::type_registry::resolve_slot_by_name("StringBox", _method, _args.len());
|
||||
match slot {
|
||||
Some(300) => { // len
|
||||
let out = VMValue::Integer(sv.len() as i64);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, out); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
Some(301) => { // substring
|
||||
if _args.len() >= 2 {
|
||||
if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) {
|
||||
let chars: Vec<char> = sv.chars().collect();
|
||||
let start = match a0 { VMValue::Integer(i) => i.max(0) as usize, _ => 0 };
|
||||
let end = match a1 { VMValue::Integer(i) => i.max(0) as usize, _ => chars.len() };
|
||||
let ss: String = chars[start.min(end)..end.min(chars.len())].iter().collect();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(ss)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(302) => { // concat
|
||||
if let Some(a0) = _args.get(0) { if let Ok(v) = self.get_value(*a0) {
|
||||
let out = format!("{}{}", sv, v.to_string());
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}}
|
||||
}
|
||||
Some(303) => { // indexOf
|
||||
if let Some(a0) = _args.get(0) { if let Ok(v) = self.get_value(*a0) {
|
||||
let needle = v.to_string();
|
||||
let pos = sv.find(&needle).map(|p| p as i64).unwrap_or(-1);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::Integer(pos)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}}
|
||||
}
|
||||
Some(304) => { // replace
|
||||
if _args.len() >= 2 { if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) {
|
||||
let out = sv.replace(&a0.to_string(), &a1.to_string());
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}}
|
||||
}
|
||||
Some(305) => { // trim
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(sv.trim().to_string())); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
Some(306) => { // toUpper
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(sv.to_uppercase())); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
Some(307) => { // toLower
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(sv.to_lowercase())); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let VMValue::BoxRef(b) = _recv {
|
||||
let ty_name = b.type_name();
|
||||
let ty_name_for_reg: std::borrow::Cow<'_, str> = if let Some(p) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { std::borrow::Cow::Owned(p.box_type.clone()) } else { std::borrow::Cow::Borrowed(ty_name) };
|
||||
@ -425,62 +527,34 @@ impl VM {
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
if matches!(slot, Some(202)) {
|
||||
if let Ok(arg_v) = self.get_value(_args[0]) {
|
||||
let key_box: Box<dyn NyashBox> = match arg_v {
|
||||
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||
VMValue::BoxRef(ref bx) => bx.share_box(),
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
if let Some(a0) = _args.get(0) { if let Ok(key_box) = self.arg_as_box(*a0) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.has"); }
|
||||
let out = map.has(key_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}}
|
||||
}
|
||||
if matches!(slot, Some(203)) {
|
||||
if let Ok(arg_v) = self.get_value(_args[0]) {
|
||||
let key_box: Box<dyn NyashBox> = match arg_v {
|
||||
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||
VMValue::BoxRef(ref bx) => bx.share_box(),
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
if let Some(a0) = _args.get(0) { if let Ok(key_box) = self.arg_as_box(*a0) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.get"); }
|
||||
let out = map.get(key_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}}
|
||||
}
|
||||
if matches!(slot, Some(204)) {
|
||||
if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) {
|
||||
if let VMValue::String(ref s) = a0 { let vb: Box<dyn NyashBox> = match a1 { VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)), VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)), VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), VMValue::BoxRef(ref bx) => bx.share_box(), VMValue::Future(ref fut) => Box::new(fut.clone()), VMValue::Void => Box::new(crate::box_trait::VoidBox::new()), }; let out = map.set(Box::new(crate::box_trait::StringBox::new(s)), vb); if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } return Some(Ok(ControlFlow::Continue)); }
|
||||
let key_box: Box<dyn NyashBox> = match a0 {
|
||||
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||
VMValue::BoxRef(ref bx) => bx.share_box(),
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
let val_box: Box<dyn NyashBox> = match a1 {
|
||||
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||
VMValue::BoxRef(ref bx) => bx.share_box(),
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
if _args.len() >= 2 {
|
||||
if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) {
|
||||
if let VMValue::String(ref s) = a0 {
|
||||
let vb = Self::vmvalue_to_box(&a1);
|
||||
let out = map.set(Box::new(crate::box_trait::StringBox::new(s)), vb);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
let key_box = Self::vmvalue_to_box(&a0);
|
||||
let val_box = Self::vmvalue_to_box(&a1);
|
||||
// Barrier: mutation
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.set");
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
@ -488,8 +562,34 @@ impl VM {
|
||||
let out = map.set(key_box, val_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches!(slot, Some(205)) { // delete/remove
|
||||
if let Some(a0) = _args.get(0) { if let Ok(arg_v) = self.get_value(*a0) {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.delete");
|
||||
let key_box = Self::vmvalue_to_box(&arg_v);
|
||||
let out = map.delete(key_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}}
|
||||
}
|
||||
if matches!(slot, Some(206)) { // keys
|
||||
let out = map.keys();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
if matches!(slot, Some(207)) { // values
|
||||
let out = map.values();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
if matches!(slot, Some(208)) { // clear
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.clear");
|
||||
let out = map.clear();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
// StringBox: len
|
||||
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
@ -500,6 +600,228 @@ impl VM {
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// substring(start, end)
|
||||
if matches!(slot, Some(301)) {
|
||||
if _args.len() >= 2 {
|
||||
let full = sb.value.chars().count();
|
||||
if let (Ok(start), Ok(end)) = (self.arg_to_usize_or(_args[0], 0), self.arg_to_usize_or(_args[1], full)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] StringBox.substring({}, {})", start, end); }
|
||||
let out = sb.substring(start, end);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// concat(other)
|
||||
if matches!(slot, Some(302)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(a0) = self.get_value(*a0_id) {
|
||||
let other = a0.to_string();
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] StringBox.concat"); }
|
||||
let out = crate::box_trait::StringBox::new(format!("{}{}", sb.value, other));
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// indexOf(search)
|
||||
if matches!(slot, Some(303)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(needle) = self.arg_to_string(*a0_id) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = sb.find(&needle);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// replace(old, new)
|
||||
if matches!(slot, Some(304)) {
|
||||
if _args.len() >= 2 {
|
||||
if let (Ok(old), Ok(newv)) = (self.arg_to_string(_args[0]), self.arg_to_string(_args[1])) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = sb.replace(&old, &newv);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// trim()
|
||||
if matches!(slot, Some(305)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = sb.trim();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// toUpper()
|
||||
if matches!(slot, Some(306)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = sb.to_upper();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// toLower()
|
||||
if matches!(slot, Some(307)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = sb.to_lower();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
// ConsoleBox: log/warn/error/clear (400-series)
|
||||
if let Some(console) = b.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
|
||||
match slot {
|
||||
Some(400) => { // log
|
||||
if let Some(a0) = _args.get(0) { if let Ok(msg) = self.arg_to_string(*a0) { console.log(&msg); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); } }
|
||||
}
|
||||
Some(401) => { // warn
|
||||
if let Some(a0) = _args.get(0) { if let Ok(msg) = self.arg_to_string(*a0) { console.warn(&msg); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); } }
|
||||
}
|
||||
Some(402) => { // error
|
||||
if let Some(a0) = _args.get(0) { if let Ok(msg) = self.arg_to_string(*a0) { console.error(&msg); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); } }
|
||||
}
|
||||
Some(403) => { // clear
|
||||
console.clear(); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// ArrayBox: len/get/set (builtin fast path via vtable slots 102/100/101)
|
||||
if let Some(arr) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
// len/length
|
||||
if matches!(slot, Some(102)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.len"); }
|
||||
let out = arr.length();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } else if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.len without dst"); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// get(index)
|
||||
if matches!(slot, Some(100)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(idx_box) = self.arg_as_box(*a0_id) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get"); }
|
||||
let out = arr.get(idx_box);
|
||||
let vm_out = VMValue::from_nyash_box(out);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get -> {}", vm_out.to_string()); }
|
||||
if let Some(dst_id) = _dst { if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get set dst={}", dst_id.to_usize()); } self.set_value(dst_id, vm_out); } else if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get without dst"); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// set(index, value)
|
||||
if matches!(slot, Some(101)) {
|
||||
if _args.len() >= 2 {
|
||||
if let (Ok(idx_box), Ok(val_box)) = (self.arg_as_box(_args[0]), self.arg_as_box(_args[1])) {
|
||||
// Mutation barrier
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.set");
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.set"); }
|
||||
let out = arr.set(idx_box, val_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } else if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.set without dst"); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// push(value)
|
||||
if matches!(slot, Some(103)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(val_box) = self.arg_as_box(*a0_id) {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.push");
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.push"); }
|
||||
let out = arr.push(val_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// pop()
|
||||
if matches!(slot, Some(104)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.pop"); }
|
||||
let out = arr.pop();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// clear()
|
||||
if matches!(slot, Some(105)) {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.clear");
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.clear"); }
|
||||
let out = arr.clear();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// contains(value)
|
||||
if matches!(slot, Some(106)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(val_box) = self.arg_as_box(*a0_id) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.contains"); }
|
||||
let out = arr.contains(val_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// indexOf(value)
|
||||
if matches!(slot, Some(107)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(val_box) = self.arg_as_box(*a0_id) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.indexOf"); }
|
||||
let out = arr.indexOf(val_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// join(sep)
|
||||
if matches!(slot, Some(108)) {
|
||||
if let Some(a0_id) = _args.get(0) {
|
||||
if let Ok(sep_box) = self.arg_as_box(*a0_id) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.join"); }
|
||||
let out = arr.join(sep_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort()
|
||||
if matches!(slot, Some(109)) {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.sort");
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.sort"); }
|
||||
let out = arr.sort();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// reverse()
|
||||
if matches!(slot, Some(110)) {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.reverse");
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.reverse"); }
|
||||
let out = arr.reverse();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
// slice(start, end)
|
||||
if matches!(slot, Some(111)) {
|
||||
if _args.len() >= 2 {
|
||||
if let (Ok(start_box), Ok(end_box)) = (self.arg_as_box(_args[0]), self.arg_as_box(_args[1])) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.slice"); }
|
||||
let out = arr.slice(start_box, end_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if crate::config::env::abi_strict() {
|
||||
let known = crate::runtime::type_registry::known_methods_for(ty_name).unwrap_or_default().join(", ");
|
||||
|
||||
@ -156,7 +156,14 @@ impl VM {
|
||||
Ok(ControlFlow::Jump(if should_branch { then_bb } else { else_bb }))
|
||||
}
|
||||
pub(crate) fn execute_return(&self, value: Option<ValueId>) -> Result<ControlFlow, VMError> {
|
||||
if let Some(val_id) = value { let return_val = self.get_value(val_id)?; Ok(ControlFlow::Return(return_val)) } else { Ok(ControlFlow::Return(VMValue::Void)) }
|
||||
if let Some(val_id) = value {
|
||||
let return_val = self.get_value(val_id)?;
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] Return id={} val={}", val_id.to_usize(), return_val.to_string()); }
|
||||
Ok(ControlFlow::Return(return_val))
|
||||
} else {
|
||||
if crate::config::env::vm_vt_trace() { eprintln!("[VT] Return void"); }
|
||||
Ok(ControlFlow::Return(VMValue::Void))
|
||||
}
|
||||
}
|
||||
pub(crate) fn execute_typeop(&mut self, dst: ValueId, op: &TypeOpKind, value: ValueId, ty: &MirType) -> Result<ControlFlow, VMError> {
|
||||
let val = self.get_value(value)?;
|
||||
|
||||
148
src/backend/vm_types.rs
Normal file
148
src/backend/vm_types.rs
Normal file
@ -0,0 +1,148 @@
|
||||
/*!
|
||||
* VM Core Types
|
||||
*
|
||||
* Purpose: Error and Value enums used by the VM backend
|
||||
* Kept separate to thin vm.rs and allow reuse across helpers.
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
|
||||
use crate::mir::ConstValue;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// VM execution error
|
||||
#[derive(Debug)]
|
||||
pub enum VMError {
|
||||
InvalidValue(String),
|
||||
InvalidInstruction(String),
|
||||
InvalidBasicBlock(String),
|
||||
DivisionByZero,
|
||||
StackUnderflow,
|
||||
TypeError(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VMError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VMError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg),
|
||||
VMError::InvalidInstruction(msg) => write!(f, "Invalid instruction: {}", msg),
|
||||
VMError::InvalidBasicBlock(msg) => write!(f, "Invalid basic block: {}", msg),
|
||||
VMError::DivisionByZero => write!(f, "Division by zero"),
|
||||
VMError::StackUnderflow => write!(f, "Stack underflow"),
|
||||
VMError::TypeError(msg) => write!(f, "Type error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for VMError {}
|
||||
|
||||
/// VM value representation
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VMValue {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Future(crate::boxes::future::FutureBox),
|
||||
Void,
|
||||
BoxRef(Arc<dyn NyashBox>),
|
||||
}
|
||||
|
||||
// Manual PartialEq implementation to avoid requiring PartialEq on FutureBox
|
||||
impl PartialEq for VMValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(VMValue::Integer(a), VMValue::Integer(b)) => a == b,
|
||||
(VMValue::Float(a), VMValue::Float(b)) => a == b,
|
||||
(VMValue::Bool(a), VMValue::Bool(b)) => a == b,
|
||||
(VMValue::String(a), VMValue::String(b)) => a == b,
|
||||
(VMValue::Void, VMValue::Void) => true,
|
||||
(VMValue::Future(_), VMValue::Future(_)) => false,
|
||||
(VMValue::BoxRef(_), VMValue::BoxRef(_)) => false,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VMValue {
|
||||
/// Convert to NyashBox for output
|
||||
pub fn to_nyash_box(&self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
VMValue::Integer(i) => Box::new(IntegerBox::new(*i)),
|
||||
VMValue::Float(f) => Box::new(crate::boxes::FloatBox::new(*f)),
|
||||
VMValue::Bool(b) => Box::new(BoolBox::new(*b)),
|
||||
VMValue::String(s) => Box::new(StringBox::new(s)),
|
||||
VMValue::Future(f) => Box::new(f.clone()),
|
||||
VMValue::Void => Box::new(VoidBox::new()),
|
||||
VMValue::BoxRef(arc_box) => arc_box.share_box(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get string representation for printing
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
VMValue::Integer(i) => i.to_string(),
|
||||
VMValue::Float(f) => f.to_string(),
|
||||
VMValue::Bool(b) => b.to_string(),
|
||||
VMValue::String(s) => s.clone(),
|
||||
VMValue::Future(f) => f.to_string_box().value,
|
||||
VMValue::Void => "void".to_string(),
|
||||
VMValue::BoxRef(arc_box) => arc_box.to_string_box().value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert to integer
|
||||
pub fn as_integer(&self) -> Result<i64, VMError> {
|
||||
match self {
|
||||
VMValue::Integer(i) => Ok(*i),
|
||||
_ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert to bool
|
||||
pub fn as_bool(&self) -> Result<bool, VMError> {
|
||||
match self {
|
||||
VMValue::Bool(b) => Ok(*b),
|
||||
VMValue::Integer(i) => Ok(*i != 0),
|
||||
// Pragmatic coercions for dynamic boxes (preserve legacy semantics)
|
||||
VMValue::BoxRef(b) => {
|
||||
if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() { return Ok(bb.value); }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() { return Ok(ib.value != 0); }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { return Ok(ib.value != 0); }
|
||||
if b.as_any().downcast_ref::<VoidBox>().is_some() { return Ok(false); }
|
||||
Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name())))
|
||||
}
|
||||
VMValue::Void => Ok(false),
|
||||
VMValue::Float(f) => Ok(*f != 0.0),
|
||||
VMValue::String(s) => Ok(!s.is_empty()),
|
||||
VMValue::Future(_) => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from NyashBox to VMValue
|
||||
pub fn from_nyash_box(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> VMValue {
|
||||
if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() {
|
||||
VMValue::Integer(int_box.value)
|
||||
} else if let Some(bool_box) = nyash_box.as_any().downcast_ref::<BoolBox>() {
|
||||
VMValue::Bool(bool_box.value)
|
||||
} else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() {
|
||||
VMValue::String(string_box.value.clone())
|
||||
} else if let Some(future_box) = nyash_box.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
||||
VMValue::Future(future_box.clone())
|
||||
} else {
|
||||
VMValue::BoxRef(Arc::from(nyash_box))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ConstValue> for VMValue {
|
||||
fn from(const_val: &ConstValue) -> Self {
|
||||
match const_val {
|
||||
ConstValue::Integer(i) => VMValue::Integer(*i),
|
||||
ConstValue::Float(f) => VMValue::Float(*f),
|
||||
ConstValue::Bool(b) => VMValue::Bool(*b),
|
||||
ConstValue::String(s) => VMValue::String(s.clone()),
|
||||
ConstValue::Null => VMValue::Void,
|
||||
ConstValue::Void => VMValue::Void,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,16 @@
|
||||
*/
|
||||
|
||||
use crate::mir::{BinaryOp, CompareOp, UnaryOp};
|
||||
use super::vm::{VM, VMError, VMValue};
|
||||
use super::vm::VM;
|
||||
use super::vm_types::{VMError, VMValue};
|
||||
|
||||
impl VM {
|
||||
/// Try to view a BoxRef as a UTF-8 string using unified semantics
|
||||
fn try_boxref_to_string(&self, b: &dyn crate::box_trait::NyashBox) -> Option<String> {
|
||||
// Avoid recursion via PluginHost<->Loader for PluginBoxV2 during VM add/string ops
|
||||
if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
return None;
|
||||
}
|
||||
crate::runtime::semantics::coerce_to_string(b)
|
||||
}
|
||||
/// Execute binary operation
|
||||
|
||||
Reference in New Issue
Block a user