mir: implement proper short-circuit lowering (&&/||) via branch+phi; vm: add NYASH_VM_TRACE exec/phi logs and reg_load diagnostics; vm-fallback: minimal Void guards (push/get_position/line/column), MapBox.birth no-op; smokes: filter builtin Array/Map plugin notices; docs: CURRENT_TASK updated

This commit is contained in:
Selfhosting Dev
2025-09-26 03:30:59 +09:00
parent 041cef875a
commit fd56b8049a
45 changed files with 3022 additions and 204 deletions

View File

@ -92,12 +92,52 @@ impl MirInterpreter {
method: &str,
args: &[ValueId],
) -> Result<(), VMError> {
// Graceful void guard for common short-circuit patterns in user code
// e.g., `A or not last.is_eof()` should not crash when last is absent.
match self.reg_load(box_val)? {
VMValue::Void => {
match method {
"is_eof" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Bool(false)); } return Ok(()); }
"length" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(0)); } return Ok(()); }
"substring" => { if let Some(d) = dst { self.regs.insert(d, VMValue::String(String::new())); } return Ok(()); }
"push" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Void); } return Ok(()); }
"get_position" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(0)); } return Ok(()); }
"get_line" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(1)); } return Ok(()); }
"get_column" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(1)); } return Ok(()); }
_ => {}
}
}
VMValue::BoxRef(ref b) => {
if b.as_any().downcast_ref::<crate::box_trait::VoidBox>().is_some() {
match method {
"is_eof" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Bool(false)); } return Ok(()); }
"length" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(0)); } return Ok(()); }
"substring" => { if let Some(d) = dst { self.regs.insert(d, VMValue::String(String::new())); } return Ok(()); }
"push" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Void); } return Ok(()); }
"get_position" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(0)); } return Ok(()); }
"get_line" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(1)); } return Ok(()); }
"get_column" => { if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(1)); } return Ok(()); }
_ => {}
}
}
}
_ => {}
}
if self.try_handle_object_fields(dst, box_val, method, args)? {
return Ok(());
}
if self.try_handle_instance_box(dst, box_val, method, args)? {
return Ok(());
}
if self.try_handle_string_box(dst, box_val, method, args)? {
return Ok(());
}
if self.try_handle_array_box(dst, box_val, method, args)? {
return Ok(());
}
if self.try_handle_map_box(dst, box_val, method, args)? {
return Ok(());
}
self.invoke_plugin_box(dst, box_val, method, args)
}
@ -149,6 +189,78 @@ impl MirInterpreter {
}
}
fn try_handle_map_box(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<bool, VMError> {
let recv = self.reg_load(box_val)?;
let recv_box_any: Box<dyn NyashBox> = match recv.clone() {
VMValue::BoxRef(b) => b.share_box(),
other => other.to_nyash_box(),
};
if let Some(mb) = recv_box_any
.as_any()
.downcast_ref::<crate::boxes::map_box::MapBox>()
{
match method {
"birth" => {
// No-op constructor init for MapBox
if let Some(d) = dst { self.regs.insert(d, VMValue::Void); }
return Ok(true);
}
"set" => {
if args.len() != 2 { return Err(VMError::InvalidInstruction("MapBox.set expects 2 args".into())); }
let k = self.reg_load(args[0])?.to_nyash_box();
let v = self.reg_load(args[1])?.to_nyash_box();
let ret = mb.set(k, v);
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"get" => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("MapBox.get expects 1 arg".into())); }
let k = self.reg_load(args[0])?.to_nyash_box();
let ret = mb.get(k);
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"has" => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("MapBox.has expects 1 arg".into())); }
let k = self.reg_load(args[0])?.to_nyash_box();
let ret = mb.has(k);
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"delete" => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("MapBox.delete expects 1 arg".into())); }
let k = self.reg_load(args[0])?.to_nyash_box();
let ret = mb.delete(k);
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"size" => {
let ret = mb.size();
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"keys" => {
let ret = mb.keys();
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"values" => {
let ret = mb.values();
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
_ => {}
}
}
Ok(false)
}
fn try_handle_string_box(
&mut self,
dst: Option<ValueId>,
@ -173,6 +285,29 @@ impl MirInterpreter {
}
return Ok(true);
}
"substring" => {
if args.len() != 2 {
return Err(VMError::InvalidInstruction(
"substring expects 2 args (start, end)".into(),
));
}
let s_idx = self.reg_load(args[0])?.as_integer().unwrap_or(0);
let e_idx = self.reg_load(args[1])?.as_integer().unwrap_or(0);
let len = sb.value.chars().count() as i64;
let start = s_idx.max(0).min(len) as usize;
let end = e_idx.max(start as i64).min(len) as usize;
let chars: Vec<char> = sb.value.chars().collect();
let sub: String = chars[start..end].iter().collect();
if let Some(d) = dst {
self.regs.insert(
d,
VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(
sub,
))),
);
}
return Ok(true);
}
"concat" => {
if args.len() != 1 {
return Err(VMError::InvalidInstruction("concat expects 1 arg".into()));
@ -195,6 +330,93 @@ impl MirInterpreter {
Ok(false)
}
fn try_handle_instance_box(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<bool, VMError> {
let recv_vm = self.reg_load(box_val)?;
let recv_box_any: Box<dyn NyashBox> = match recv_vm.clone() {
VMValue::BoxRef(b) => b.share_box(),
other => other.to_nyash_box(),
};
if let Some(inst) = recv_box_any.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
// Resolve lowered method function: "Class.method/arity"
let fname = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len()));
if let Some(func) = self.functions.get(&fname).cloned() {
// Build argv: me + args
let mut argv: Vec<VMValue> = Vec::with_capacity(1 + args.len());
argv.push(recv_vm.clone());
for a in args {
argv.push(self.reg_load(*a)?);
}
let ret = self.exec_function_inner(&func, Some(&argv))?;
if let Some(d) = dst {
self.regs.insert(d, ret);
}
return Ok(true);
}
}
Ok(false)
}
fn try_handle_array_box(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<bool, VMError> {
let recv = self.reg_load(box_val)?;
let recv_box_any: Box<dyn NyashBox> = match recv.clone() {
VMValue::BoxRef(b) => b.share_box(),
other => other.to_nyash_box(),
};
if let Some(ab) = recv_box_any
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
match method {
"birth" => {
// No-op constructor init
if let Some(d) = dst { self.regs.insert(d, VMValue::Void); }
return Ok(true);
}
"push" => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("push expects 1 arg".into())); }
let val = self.reg_load(args[0])?.to_nyash_box();
let _ = ab.push(val);
if let Some(d) = dst { self.regs.insert(d, VMValue::Void); }
return Ok(true);
}
"len" | "length" | "size" => {
let ret = ab.length();
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"get" => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("get expects 1 arg".into())); }
let idx = self.reg_load(args[0])?.to_nyash_box();
let ret = ab.get(idx);
if let Some(d) = dst { self.regs.insert(d, VMValue::from_nyash_box(ret)); }
return Ok(true);
}
"set" => {
if args.len() != 2 { return Err(VMError::InvalidInstruction("set expects 2 args".into())); }
let idx = self.reg_load(args[0])?.to_nyash_box();
let val = self.reg_load(args[1])?.to_nyash_box();
let _ = ab.set(idx, val);
if let Some(d) = dst { self.regs.insert(d, VMValue::Void); }
return Ok(true);
}
_ => {}
}
}
Ok(false)
}
fn invoke_plugin_box(
&mut self,
dst: Option<ValueId>,