builder+vm: unify method calls via emit_unified_call; add RouterPolicy trace; finalize LocalSSA/BlockSchedule guards; docs + selfhost quickstart

- Unify standard method calls to emit_unified_call; route via RouterPolicy and apply rewrite::{special,known} at a single entry.\n- Stabilize emit-time invariants: LocalSSA finalize + BlockSchedule PHI→Copy→Call ordering; metadata propagation on copies.\n- Known rewrite default ON (userbox only, strict guards) with opt-out flag NYASH_REWRITE_KNOWN_DEFAULT=0.\n- Expand TypeAnnotation whitelist (is_digit_char/is_hex_digit_char/is_alpha_char/Map.has).\n- Docs: unified-method-resolution design note; Quick Reference normalization note; selfhosting/quickstart.\n- Tools: add tools/selfhost_smoke.sh (dev-only).\n- Keep behavior unchanged for Unknown/core/user-instance via BoxCall fallback; all tests green (quick/integration).
This commit is contained in:
nyash-codex
2025-09-28 20:38:09 +09:00
parent e442e5f612
commit dd65cf7e4c
60 changed files with 2523 additions and 471 deletions

View File

@ -79,6 +79,33 @@ pub(super) fn try_handle_string_box(
if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(Box::new(crate::box_trait::StringBox::new(new_s)))) ; }
return Ok(true);
}
"is_digit_char" => {
// Accept either 0-arg (use first char of receiver) or 1-arg (string/char to test)
let ch_opt = if args.is_empty() {
sb.value.chars().next()
} else if args.len() == 1 {
let s = this.reg_load(args[0])?.to_string();
s.chars().next()
} else {
return Err(VMError::InvalidInstruction("is_digit_char expects 0 or 1 arg".into()));
};
let is_digit = ch_opt.map(|c| c.is_ascii_digit()).unwrap_or(false);
if let Some(d) = dst { this.regs.insert(d, VMValue::Bool(is_digit)); }
return Ok(true);
}
"is_hex_digit_char" => {
let ch_opt = if args.is_empty() {
sb.value.chars().next()
} else if args.len() == 1 {
let s = this.reg_load(args[0])?.to_string();
s.chars().next()
} else {
return Err(VMError::InvalidInstruction("is_hex_digit_char expects 0 or 1 arg".into()));
};
let is_hex = ch_opt.map(|c| c.is_ascii_hexdigit()).unwrap_or(false);
if let Some(d) = dst { this.regs.insert(d, VMValue::Bool(is_hex)); }
return Ok(true);
}
_ => {}
}
}

View File

@ -28,7 +28,24 @@ impl MirInterpreter {
Callee::Global(func_name) => self.execute_global_function(func_name, args),
Callee::Method { box_name: _, method, receiver, certainty: _, } => {
if let Some(recv_id) = receiver {
let recv_val = self.reg_load(*recv_id)?;
// Primary: load receiver by id. Dev fallback: if undefined and env allows,
// use args[0] as a surrogate receiver (builder localization gap workaround).
let recv_val = match self.reg_load(*recv_id) {
Ok(v) => v,
Err(e) => {
let tolerate = std::env::var("NYASH_VM_RECV_ARG_FALLBACK").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
if tolerate {
if let Some(a0) = args.get(0) {
self.reg_load(*a0)?
} else {
return Err(e);
}
} else {
return Err(e);
}
}
};
let dev_trace = std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1");
// Fast bridge for builtin boxes (Array) and common methods.
// Preserve legacy semantics when plugins are absent.

View File

@ -36,6 +36,11 @@ impl MirInterpreter {
keys.join(", ")
);
}
// Dev-time safety valve: tolerate undefined registers as Void when enabled
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
if tolerate {
return Ok(VMValue::Void);
}
Err(VMError::InvalidValue(format!(
"use of undefined value {:?}",
id
@ -64,8 +69,7 @@ impl MirInterpreter {
use BinaryOp::*;
use VMValue::*;
// Dev-time: normalize BoxRef(VoidBox) → VMValue::Void when tolerance is enabled or in --dev mode.
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1")
|| std::env::var("NYASH_DEV").ok().as_deref() == Some("1");
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
let (a, b) = if tolerate {
let norm = |v: VMValue| -> VMValue {
if let VMValue::BoxRef(bx) = &v {
@ -131,8 +135,7 @@ impl MirInterpreter {
use CompareOp::*;
use VMValue::*;
// Dev-time: normalize BoxRef(VoidBox) → VMValue::Void when tolerance is enabled or in --dev.
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1")
|| std::env::var("NYASH_DEV").ok().as_deref() == Some("1");
let tolerate = std::env::var("NYASH_VM_TOLERATE_VOID").ok().as_deref() == Some("1");
let (a, b) = if tolerate {
let norm = |v: VMValue| -> VMValue {
if let VMValue::BoxRef(bx) = &v {