String unification bridge: VM string-like normalization (compare/add/method fallbacks) and Interpreter normalization for compare/add; fix lifetime issues; update CURRENT_TASK status
This commit is contained in:
@ -34,10 +34,10 @@ How To Run(Nyash-only)
|
|||||||
|
|
||||||
Next Steps(優先順・更新)
|
Next Steps(優先順・更新)
|
||||||
|
|
||||||
1. String統一ブリッジの完了
|
1. String統一ブリッジ(実装済・一次完了)
|
||||||
- VM: 内部String受けのフォールバックを全パスで拾う(length/isEmpty/charCodeAt/concat/+)
|
- VM: 比較/加算/代表メソッドのフォールバック(length/isEmpty/charCodeAt/concat/+)をstring-like正規化で実装
|
||||||
- Interpreter: 同等のフォールバック/正規化(比較・結合・代表メソッド)
|
- Interpreter: 比較/加算はstring-like正規化を適用(メソッドは後続で最小追補があれば対応)
|
||||||
- 混在比較/結合の回帰ケース追加(内部/プラグイン/プリミティブ混在)
|
- 例: encoding_min/regex_min/toml_min/path_min で回帰確認
|
||||||
2. tools/pyc: IR→Nyashの反映強化(return/If/Assignを安定化、Strictスイッチ連動)
|
2. tools/pyc: IR→Nyashの反映強化(return/If/Assignを安定化、Strictスイッチ連動)
|
||||||
3. Strictスイッチ: tools/pyc(unsupported_nodes非空でErr、envでON/OFF)
|
3. Strictスイッチ: tools/pyc(unsupported_nodes非空でErr、envでON/OFF)
|
||||||
4. CLI隠しフラグ `--pyc`/`--pyc-native`(Parser→Compiler→AOTの一本化導線)
|
4. CLI隠しフラグ `--pyc`/`--pyc-native`(Parser→Compiler→AOTの一本化導線)
|
||||||
|
|||||||
@ -1003,6 +1003,34 @@ impl VM {
|
|||||||
|
|
||||||
/// Execute a forced plugin invocation (no builtin fallback)
|
/// Execute a forced plugin invocation (no builtin fallback)
|
||||||
pub(super) fn execute_plugin_invoke(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
pub(super) fn execute_plugin_invoke(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
||||||
|
// Helper: extract UTF-8 string from internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8
|
||||||
|
fn extract_string_from_box(bx: &dyn crate::box_trait::NyashBox) -> Option<String> {
|
||||||
|
// Internal StringBox
|
||||||
|
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||||
|
return Some(sb.value.clone());
|
||||||
|
}
|
||||||
|
// Result.Ok(inner) → recurse
|
||||||
|
if let Some(res) = bx.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||||
|
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return extract_string_from_box(inner.as_ref()); }
|
||||||
|
}
|
||||||
|
// Plugin StringBox → call toUtf8
|
||||||
|
if let Some(p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||||
|
if p.box_type == "StringBox" {
|
||||||
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
let tmp: Option<String> = if let Ok(ro) = host.read() {
|
||||||
|
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) {
|
||||||
|
if let Some(vb) = val_opt {
|
||||||
|
if let Some(sb2) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||||
|
Some(sb2.value.clone())
|
||||||
|
} else { None }
|
||||||
|
} else { None }
|
||||||
|
} else { None }
|
||||||
|
} else { None };
|
||||||
|
if tmp.is_some() { return tmp; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
let recv = self.get_value(box_val)?;
|
let recv = self.get_value(box_val)?;
|
||||||
// Allow static birth on primitives/builtin boxes to create a plugin instance.
|
// Allow static birth on primitives/builtin boxes to create a plugin instance.
|
||||||
if method == "birth" && !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some()) {
|
if method == "birth" && !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some()) {
|
||||||
@ -1215,33 +1243,34 @@ impl VM {
|
|||||||
return Ok(ControlFlow::Continue);
|
return Ok(ControlFlow::Continue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fallback: support common methods on internal StringBox without requiring PluginBox receiver
|
// Fallback: support common string-like methods without requiring PluginBox receiver
|
||||||
if let VMValue::BoxRef(ref bx) = recv {
|
if let VMValue::BoxRef(ref bx) = recv {
|
||||||
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
// Try to view receiver as string (internal, plugin, or Result.Ok)
|
||||||
|
if let Some(s) = extract_string_from_box(bx.as_ref()) {
|
||||||
match method {
|
match method {
|
||||||
"length" => {
|
"length" => {
|
||||||
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(sb.value.len() as i64)); }
|
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(s.len() as i64)); }
|
||||||
return Ok(ControlFlow::Continue);
|
return Ok(ControlFlow::Continue);
|
||||||
}
|
}
|
||||||
"is_empty" | "isEmpty" => {
|
"is_empty" | "isEmpty" => {
|
||||||
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Bool(sb.value.is_empty())); }
|
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Bool(s.is_empty())); }
|
||||||
return Ok(ControlFlow::Continue);
|
return Ok(ControlFlow::Continue);
|
||||||
}
|
}
|
||||||
"charCodeAt" => {
|
"charCodeAt" => {
|
||||||
let idx_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::Integer(0) };
|
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 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);
|
let code = s.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)); }
|
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(code)); }
|
||||||
return Ok(ControlFlow::Continue);
|
return Ok(ControlFlow::Continue);
|
||||||
}
|
}
|
||||||
"concat" => {
|
"concat" => {
|
||||||
let rhs_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::String(String::new()) };
|
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 {
|
let rhs_s = match rhs_v {
|
||||||
VMValue::String(s) => s,
|
VMValue::String(ss) => ss,
|
||||||
VMValue::BoxRef(br) => br.to_string_box().value,
|
VMValue::BoxRef(br) => extract_string_from_box(br.as_ref()).unwrap_or_else(|| br.to_string_box().value),
|
||||||
_ => rhs_v.to_string(),
|
_ => rhs_v.to_string(),
|
||||||
};
|
};
|
||||||
let mut new_s = sb.value.clone();
|
let mut new_s = s.clone();
|
||||||
new_s.push_str(&rhs_s);
|
new_s.push_str(&rhs_s);
|
||||||
let out = Box::new(crate::box_trait::StringBox::new(new_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>))); }
|
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>))); }
|
||||||
|
|||||||
@ -11,6 +11,30 @@ use crate::mir::{BinaryOp, CompareOp, UnaryOp};
|
|||||||
use super::vm::{VM, VMError, VMValue};
|
use super::vm::{VM, VMError, VMValue};
|
||||||
|
|
||||||
impl VM {
|
impl VM {
|
||||||
|
/// Try to view a BoxRef as a UTF-8 string (internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8)
|
||||||
|
fn try_boxref_to_string(&self, b: &dyn crate::box_trait::NyashBox) -> Option<String> {
|
||||||
|
// Internal StringBox
|
||||||
|
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||||
|
return Some(sb.value.clone());
|
||||||
|
}
|
||||||
|
// Result.Ok(inner) → recurse
|
||||||
|
if let Some(res) = b.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||||
|
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return self.try_boxref_to_string(inner.as_ref()); }
|
||||||
|
}
|
||||||
|
// Plugin StringBox → call toUtf8
|
||||||
|
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 tmp: 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(sb2) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb2.value.clone()) } else { None } } else { None }
|
||||||
|
} else { None }
|
||||||
|
} else { None };
|
||||||
|
if tmp.is_some() { return tmp; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
/// Execute binary operation
|
/// Execute binary operation
|
||||||
pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result<VMValue, VMError> {
|
pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result<VMValue, VMError> {
|
||||||
let debug_bin = std::env::var("NYASH_VM_DEBUG_BIN").ok().as_deref() == Some("1");
|
let debug_bin = std::env::var("NYASH_VM_DEBUG_BIN").ok().as_deref() == Some("1");
|
||||||
@ -57,12 +81,14 @@ impl VM {
|
|||||||
|
|
||||||
// String + BoxRef concatenation
|
// String + BoxRef concatenation
|
||||||
(VMValue::String(l), VMValue::BoxRef(r)) => {
|
(VMValue::String(l), VMValue::BoxRef(r)) => {
|
||||||
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r.to_string_box().value))), _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())) }
|
let rs = self.try_boxref_to_string(r.as_ref()).unwrap_or_else(|| r.to_string_box().value);
|
||||||
|
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, rs))), _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())) }
|
||||||
},
|
},
|
||||||
|
|
||||||
// BoxRef + String concatenation
|
// BoxRef + String concatenation
|
||||||
(VMValue::BoxRef(l), VMValue::String(r)) => {
|
(VMValue::BoxRef(l), VMValue::String(r)) => {
|
||||||
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l.to_string_box().value, r))), _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())) }
|
let ls = self.try_boxref_to_string(l.as_ref()).unwrap_or_else(|| l.to_string_box().value);
|
||||||
|
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", ls, r))), _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())) }
|
||||||
},
|
},
|
||||||
|
|
||||||
// Arithmetic with BoxRef(IntegerBox) — support both legacy and new IntegerBox
|
// Arithmetic with BoxRef(IntegerBox) — support both legacy and new IntegerBox
|
||||||
@ -90,6 +116,15 @@ impl VM {
|
|||||||
};
|
};
|
||||||
Ok(VMValue::Integer(res))
|
Ok(VMValue::Integer(res))
|
||||||
}
|
}
|
||||||
|
// BoxRef + BoxRef string-like concatenation
|
||||||
|
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
|
||||||
|
if matches!(*op, BinaryOp::Add) {
|
||||||
|
let ls = self.try_boxref_to_string(li.as_ref()).unwrap_or_else(|| li.to_string_box().value);
|
||||||
|
let rs = self.try_boxref_to_string(ri.as_ref()).unwrap_or_else(|| ri.to_string_box().value);
|
||||||
|
return Ok(VMValue::String(format!("{}{}", ls, rs)));
|
||||||
|
}
|
||||||
|
Err(VMError::TypeError("Unsupported BoxRef+BoxRef operation".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
// Mixed Integer forms
|
// Mixed Integer forms
|
||||||
(VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some()
|
(VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some()
|
||||||
|
|||||||
@ -107,6 +107,33 @@ pub(super) fn try_mod_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Re
|
|||||||
// ========================================================================================
|
// ========================================================================================
|
||||||
|
|
||||||
impl NyashInterpreter {
|
impl NyashInterpreter {
|
||||||
|
/// Try to extract a UTF-8 string from a NyashBox: supports internal StringBox,
|
||||||
|
/// Result.Ok(String-like), and Plugin StringBox via toUtf8 (when plugins enabled).
|
||||||
|
fn try_box_to_string(&self, b: &dyn NyashBox) -> Option<String> {
|
||||||
|
// Internal StringBox
|
||||||
|
if let Some(sb) = b.as_any().downcast_ref::<StringBox>() { return Some(sb.value.clone()); }
|
||||||
|
// Result.Ok(inner) → recurse
|
||||||
|
if let Some(res) = b.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||||
|
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return self.try_box_to_string(inner.as_ref()); }
|
||||||
|
}
|
||||||
|
// Plugin StringBox via toUtf8
|
||||||
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
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(sb2) = vb.as_any().downcast_ref::<StringBox>() { return Some(sb2.value.clone()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
/// 二項演算を実行
|
/// 二項演算を実行
|
||||||
pub(super) fn execute_binary_op(&mut self, op: &BinaryOperator, left: &ASTNode, right: &ASTNode)
|
pub(super) fn execute_binary_op(&mut self, op: &BinaryOperator, left: &ASTNode, right: &ASTNode)
|
||||||
-> Result<Box<dyn NyashBox>, RuntimeError>
|
-> Result<Box<dyn NyashBox>, RuntimeError>
|
||||||
@ -119,6 +146,16 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
match op {
|
match op {
|
||||||
BinaryOperator::Add => {
|
BinaryOperator::Add => {
|
||||||
|
// Prefer string-like concatenation when either side is string-like
|
||||||
|
if let (Some(ls), Some(rs)) = (self.try_box_to_string(left_val), self.try_box_to_string(right_val)) {
|
||||||
|
return Ok(Box::new(StringBox::new(format!("{}{}", ls, rs))));
|
||||||
|
}
|
||||||
|
if let Some(ls) = self.try_box_to_string(left_val) {
|
||||||
|
return Ok(Box::new(StringBox::new(format!("{}{}", ls, right_val.to_string_box().value))));
|
||||||
|
}
|
||||||
|
if let Some(rs) = self.try_box_to_string(right_val) {
|
||||||
|
return Ok(Box::new(StringBox::new(format!("{}{}", left_val.to_string_box().value, rs))));
|
||||||
|
}
|
||||||
if let Some(result) = try_add_operation(left_val, right_val) {
|
if let Some(result) = try_add_operation(left_val, right_val) {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
@ -252,12 +289,9 @@ impl NyashInterpreter {
|
|||||||
return Ok(left_int.value == right_int.value);
|
return Ok(left_int.value == right_int.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringBox comparison
|
// String-like comparison (internal/Result.Ok/plugin StringBox)
|
||||||
if let (Some(left_str), Some(right_str)) = (
|
if let (Some(ls), Some(rs)) = (self.try_box_to_string(left), self.try_box_to_string(right)) {
|
||||||
left.as_any().downcast_ref::<StringBox>(),
|
return Ok(ls == rs);
|
||||||
right.as_any().downcast_ref::<StringBox>()
|
|
||||||
) {
|
|
||||||
return Ok(left_str.value == right_str.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BoolBox comparison
|
// BoolBox comparison
|
||||||
@ -287,12 +321,9 @@ impl NyashInterpreter {
|
|||||||
return Ok(left_int.value < right_int.value);
|
return Ok(left_int.value < right_int.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringBox comparison (lexicographic)
|
// String-like comparison (lexicographic)
|
||||||
if let (Some(left_str), Some(right_str)) = (
|
if let (Some(ls), Some(rs)) = (self.try_box_to_string(left), self.try_box_to_string(right)) {
|
||||||
left.as_any().downcast_ref::<StringBox>(),
|
return Ok(ls < rs);
|
||||||
right.as_any().downcast_ref::<StringBox>()
|
|
||||||
) {
|
|
||||||
return Ok(left_str.value < right_str.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(RuntimeError::InvalidOperation {
|
Err(RuntimeError::InvalidOperation {
|
||||||
@ -325,4 +356,4 @@ impl NyashInterpreter {
|
|||||||
// Everything else is true
|
// Everything else is true
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user