diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 4b78f680..447c407e 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -60,6 +60,15 @@ tools/ci_check_golden.sh # 代表ケースのMIR含有チェック - ただし、VM実行時に `And` が `execute_binop` の短絡ルートに入らず、`execute_binary_op` の汎用経路で `Type error: Unsupported binary operation: And ...` が発生。 - 兆候: `NYASH_VM_DEBUG_ANDOR` のデバッグ出力が出ない=`execute_binop` に入っていない、または別実装ルートを通過している可能性。 - 仮説: 古いVM経路の残存/リンク切替、もしくは実行時に別ビルド/別profileが使用されている。 + +### 🆕 進捗(2025-08-26 早朝) +- and/or 真理値強制の最終化: `VMValue::as_bool()` を拡張(`BoxRef(IntegerBox)`→非0でtrue、`Void`→false)。 +- テスト追加: `local_tests/and_or_truthy_vm.nyash`(期待: `false,true,false,true,false`)。緑を確認。 +- 基本ボックス統一(第一弾): `IntegerBox` を正典に一本化。 + - `src/boxes/integer_box.rs` は `pub use crate::box_trait::IntegerBox;` に置換し、実体の二重化を排除。 +- 既知の未解決: ループ比較で `BoxRef(IntegerBox) < BoxRef(IntegerBox)` が TypeError。 + - 対応中: 比較前に i64 へ正規化するフォールバックをVMに実装(downcast→toString→parse)。 + - 80/20ポリシー: 数値にパース可能なら比較継続、失敗時のみTypeError。 ### 🎯 次の優先タスク @@ -73,6 +82,7 @@ tools/ci_check_golden.sh # 代表ケースのMIR含有チェック - バイナリ一致確認: `strings` によるシグネチャ(デバッグ文字列)含有の照合で実行バイナリを同定。 - 代替経路の洗い出し: `src/` 全体で `execute_binop`/`And`/`Unsupported binary operation` を再走査し、影響箇所を一掃。 - 修正後、`local_tests/and_or_vm.nyash` で `false/true` の出力を確認。 + - ループ比較の犯人退治: `Compare` 直前で `BoxRef(IntegerBox)` を確実に i64 正規化(downcast→toString→parse フォールバック)。 2. **MIR26命令対応** - TypeOp/WeakRef/Barrierのプリンタ拡張 - スナップショット整備(extern_call/loop/boxcall/typeop_mixed 追加済) @@ -121,6 +131,11 @@ tools/ci_check_golden.sh # 代表ケースのMIR含有チェック - ResultBox移行TODOを追加(`docs/development/current/RESULTBOX_MIGRATION_TODO.md`)。 - VM: 旧`box_trait::ResultBox`扱いのデプリケーション警告を最小抑制(完全移行までの暫定)。 +### ✅ 小タスク完了(2025-08-26 早朝) +- VM: `as_bool()` の拡張(IntegerBox/ Void を真理値化)。 +- テスト: `and_or_truthy_vm.nyash` 追加。 +- 基本ボックス統一(第一弾): `IntegerBox` 実体の一本化(re-export)。 + ### ▶ リファクタリング開始(控えめ) - 方針: 肥大化防止のため`src/backend/vm.rs`を2分割のみ実施。 - `vm_values.rs`: `execute_binary_op`/`execute_unary_op`/`execute_compare_op` を移動。 diff --git a/err.txt b/err.txt new file mode 100644 index 00000000..0d6c3be2 --- /dev/null +++ b/err.txt @@ -0,0 +1,16 @@ +🔍 DEBUG: Initializing v2 plugin system +[PluginLoaderV2] nyash_plugin_init rc=0 for libnyash_counter_plugin.so +Net plugin: LOG_ON=false, LOG_PATH=net_plugin.log +[PluginLoaderV2] nyash_plugin_init rc=0 for libnyash_net_plugin.so +[FileBox] Plugin initialized +[PluginLoaderV2] nyash_plugin_init rc=0 for libnyash_filebox_plugin.so + 📦 Registering plugin provider for CounterBox + 📦 Registering plugin provider for HttpServerBox + 📦 Registering plugin provider for HttpClientBox + 📦 Registering plugin provider for HttpResponseBox + 📦 Registering plugin provider for HttpRequestBox + 📦 Registering plugin provider for SocketServerBox + 📦 Registering plugin provider for SocketClientBox + 📦 Registering plugin provider for SocketConnBox + 📦 Registering plugin provider for FileBox +❌ VM execution error: Type error: Unsupported comparison: Lt on BoxRef(IntegerBox { value: 0, base: BoxBase { id: 3, parent_type_id: None } }) and BoxRef(IntegerBox { value: 3, base: BoxBase { id: 7, parent_type_id: None } }) diff --git a/out.txt b/out.txt new file mode 100644 index 00000000..9ead3720 --- /dev/null +++ b/out.txt @@ -0,0 +1,3 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/simple_loop_test.nyash 🚀 diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 70a4ce06..99a40f15 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -132,12 +132,17 @@ impl VMValue { if let Some(bb) = b.as_any().downcast_ref::() { return Ok(bb.value); } + // IntegerBox → truthy if non-zero (legacy and new) + if let Some(ib) = b.as_any().downcast_ref::() { return Ok(ib.value != 0); } + if let Some(ib) = b.as_any().downcast_ref::() { return Ok(ib.value != 0); } // VoidBox → false (nullish false) if b.as_any().downcast_ref::().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))), } } @@ -441,8 +446,21 @@ impl VM { MirInstruction::UnaryOp { dst, op, operand } => self.execute_unaryop(*dst, op, *operand), - MirInstruction::Compare { dst, op, lhs, rhs } => - self.execute_compare(*dst, op, *lhs, *rhs), + MirInstruction::Compare { dst, op, lhs, rhs } => { + // Fast path: compare IntegerBox values by unboxing to i64 + if let (Ok(lv), Ok(rv)) = (self.get_value(*lhs), self.get_value(*rhs)) { + if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) { + if lb.type_name() == "IntegerBox" && rb.type_name() == "IntegerBox" { + let li = if let Some(ib) = lb.as_any().downcast_ref::() { ib.value } else { lb.to_string_box().value.parse::().unwrap_or(0) }; + let ri = if let Some(ib) = rb.as_any().downcast_ref::() { ib.value } else { rb.to_string_box().value.parse::().unwrap_or(0) }; + let out = match op { crate::mir::CompareOp::Eq => li == ri, crate::mir::CompareOp::Ne => li != ri, crate::mir::CompareOp::Lt => li < ri, crate::mir::CompareOp::Le => li <= ri, crate::mir::CompareOp::Gt => li > ri, crate::mir::CompareOp::Ge => li >= ri }; + self.set_value(*dst, VMValue::Bool(out)); + return Ok(ControlFlow::Continue); + } + } + } + self.execute_compare(*dst, op, *lhs, *rhs) + }, // I/O operations MirInstruction::Print { value, .. } => diff --git a/src/backend/vm_instructions.rs b/src/backend/vm_instructions.rs index 22afaafc..60fd822d 100644 --- a/src/backend/vm_instructions.rs +++ b/src/backend/vm_instructions.rs @@ -58,8 +58,44 @@ impl VM { /// Execute a comparison instruction pub(super) fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result { - let left = self.get_value(lhs)?; - let right = self.get_value(rhs)?; + let mut left = self.get_value(lhs)?; + let mut right = self.get_value(rhs)?; + + // Canonicalize BoxRef(IntegerBox) -> VMValue::Integer(i64) + left = match left { + VMValue::BoxRef(b) if b.type_name() == "IntegerBox" => { + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { + eprintln!("[VM] Coerce left BoxRef(IntegerBox) -> Integer"); + } + // Try downcast then parse fallback + if let Some(ib) = b.as_any().downcast_ref::() { + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] left downcast ok: {}", ib.value); } + VMValue::Integer(ib.value) + } else { + let s = b.to_string_box().value; + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] left downcast fail; parse {}", s); } + match s.parse::() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } + } + } + other => other, + }; + right = match right { + VMValue::BoxRef(b) if b.type_name() == "IntegerBox" => { + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { + eprintln!("[VM] Coerce right BoxRef(IntegerBox) -> Integer"); + } + if let Some(ib) = b.as_any().downcast_ref::() { + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] right downcast ok: {}", ib.value); } + VMValue::Integer(ib.value) + } else { + let s = b.to_string_box().value; + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] right downcast fail; parse {}", s); } + match s.parse::() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } + } + } + other => other, + }; + let result = self.execute_compare_op(op, &left, &right)?; self.set_value(dst, VMValue::Bool(result)); Ok(ControlFlow::Continue) @@ -161,15 +197,14 @@ impl VM { /// Execute Phi instruction pub(super) fn execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result { - // For now, just use the first input since we don't track previous BB in this refactored version - // TODO: Track previous basic block for proper phi node resolution - if let Some((_, val_id)) = inputs.first() { - let value = self.get_value(*val_id)?; - self.set_value(dst, value); - Ok(ControlFlow::Continue) - } else { - Err(VMError::InvalidInstruction("Phi node has no inputs".to_string())) - } + // Minimal correct phi: select input based on previous_block via LoopExecutor + let selected = self.loop_executor.execute_phi( + dst, + inputs, + |id| self.get_value(id), + )?; + self.set_value(dst, selected); + Ok(ControlFlow::Continue) } /// Execute Load/Store instructions diff --git a/src/backend/vm_values.rs b/src/backend/vm_values.rs index 8b232bcb..cca79b81 100644 --- a/src/backend/vm_values.rs +++ b/src/backend/vm_values.rs @@ -63,10 +63,22 @@ impl VM { 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())) } }, - // Arithmetic with BoxRef(IntegerBox) - (VMValue::BoxRef(li), VMValue::BoxRef(ri)) if li.as_any().downcast_ref::().is_some() && ri.as_any().downcast_ref::().is_some() => { - let l = li.as_any().downcast_ref::().unwrap().value; - let r = ri.as_any().downcast_ref::().unwrap().value; + // Arithmetic with BoxRef(IntegerBox) — support both legacy and new IntegerBox + (VMValue::BoxRef(li), VMValue::BoxRef(ri)) if { + li.as_any().downcast_ref::().is_some() + || li.as_any().downcast_ref::().is_some() + } && { + ri.as_any().downcast_ref::().is_some() + || ri.as_any().downcast_ref::().is_some() + } => { + let l = li.as_any().downcast_ref::() + .map(|x| x.value) + .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + .unwrap(); + let r = ri.as_any().downcast_ref::() + .map(|x| x.value) + .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + .unwrap(); let res = match op { BinaryOp::Add => l + r, BinaryOp::Sub => l - r, @@ -78,8 +90,12 @@ impl VM { } // Mixed Integer forms - (VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::().is_some() => { - let l = li.as_any().downcast_ref::().unwrap().value; + (VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::().is_some() + || li.as_any().downcast_ref::().is_some() => { + let l = li.as_any().downcast_ref::() + .map(|x| x.value) + .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + .unwrap(); let res = match op { BinaryOp::Add => l + *r, BinaryOp::Sub => l - *r, @@ -89,8 +105,12 @@ impl VM { }; Ok(VMValue::Integer(res)) } - (VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::().is_some() => { - let r = ri.as_any().downcast_ref::().unwrap().value; + (VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::().is_some() + || ri.as_any().downcast_ref::().is_some() => { + let r = ri.as_any().downcast_ref::() + .map(|x| x.value) + .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + .unwrap(); let res = match op { BinaryOp::Add => *l + r, BinaryOp::Sub => *l - r, @@ -139,7 +159,50 @@ impl VM { (VMValue::Integer(l), VMValue::Integer(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }), (VMValue::Float(l), VMValue::Float(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }), (VMValue::String(l), VMValue::String(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }), - _ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))), + + // BoxRef(IntegerBox) comparisons (homogeneous) + (VMValue::BoxRef(li), VMValue::BoxRef(ri)) => { + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { + eprintln!( + "[VM] compare BoxRef vs BoxRef: left_type={}, right_type={}, left_str={}, right_str={}", + li.type_name(), ri.type_name(), li.to_string_box().value, ri.to_string_box().value + ); + } + // Try integer comparisons via downcast or parse fallback + let l_opt = li.as_any().downcast_ref::().map(|x| x.value) + .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| li.to_string_box().value.parse::().ok()); + let r_opt = ri.as_any().downcast_ref::().map(|x| x.value) + .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| ri.to_string_box().value.parse::().ok()); + if let (Some(l), Some(r)) = (l_opt, r_opt) { + return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }); + } + Err(VMError::TypeError(format!("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::().map(|x| x.value) + .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| li.to_string_box().value.parse::().ok()); + if let Some(l) = l_opt { return Ok(match op { CompareOp::Eq => l == *r, CompareOp::Ne => l != *r, CompareOp::Lt => l < *r, CompareOp::Le => l <= *r, CompareOp::Gt => l > *r, CompareOp::Ge => l >= *r }); } + Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) + } + (VMValue::Integer(l), VMValue::BoxRef(ri)) => { + let r_opt = ri.as_any().downcast_ref::().map(|x| x.value) + .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| ri.to_string_box().value.parse::().ok()); + if let Some(r) = r_opt { return Ok(match op { CompareOp::Eq => *l == r, CompareOp::Ne => *l != r, CompareOp::Lt => *l < r, CompareOp::Le => *l <= r, CompareOp::Gt => *l > r, CompareOp::Ge => *l >= r }); } + Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) + } + _ => { + if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { + let lty = match left { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) }; + let rty = match right { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) }; + eprintln!("[VM] compare default arm: op={:?}, left={}, right={}", op, lty, rty); + } + Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) + }, } } } diff --git a/src/boxes/integer_box.rs b/src/boxes/integer_box.rs index 65e9e966..6337fbda 100644 --- a/src/boxes/integer_box.rs +++ b/src/boxes/integer_box.rs @@ -1,119 +1,6 @@ -/*! 🔢 IntegerBox - 整数計算Box - * - * ## 📝 概要 - * 64ビット符号付き整数を扱うためのBox。 - * JavaScript Number型のように直感的な数値操作が可能。 - * - * ## 🛠️ 利用可能メソッド - * - `toString()` - 文字列変換 - * - `add(other)` - 加算 (演算子: +) - * - `subtract(other)` - 減算 (演算子: -) - * - `multiply(other)` - 乗算 (演算子: *) - * - `divide(other)` - 除算 (演算子: /) - * - `modulo(other)` - 余り計算 (演算子: %) - * - `equals(other)` - 等価比較 (演算子: ==) - * - `abs()` - 絶対値 - * - `min(other)` - 最小値 - * - `max(other)` - 最大値 - * - * ## 💡 使用例 - * ```nyash - * local num, result, text - * num = 42 - * - * result = num + 8 // 50 - * result = num * 2 // 84 - * result = num / 3 // 14 (整数除算) - * text = num.toString() // "42" - * - * // メソッド呼び出し形式も可能 - * result = num.add(10) // 52 - * result = num.multiply(3) // 126 - * ``` - * - * ## ⚠️ 注意 - * - ゼロ除算は実行時エラー - * - オーバーフロー時は標準i64の動作に従う - * - 小数点以下は切り捨て(整数除算) - */ +// Basic Box Unification: re-export canonical IntegerBox +// Canonical implementation lives in `crate::box_trait::IntegerBox`. +// This module re-exports it so both `crate::box_trait::IntegerBox` and +// `crate::boxes::integer_box::IntegerBox` refer to the same runtime type. -use crate::box_trait::{NyashBox, BoxCore, BoxBase}; -use std::any::Any; -use std::fmt::Display; - -/// Integer values in Nyash - 64-bit signed integers -#[derive(Debug, Clone, PartialEq)] -pub struct IntegerBox { - pub value: i64, - base: BoxBase, -} - -impl IntegerBox { - pub fn new(value: i64) -> Self { - Self { - value, - base: BoxBase::new(), - } - } - - pub fn zero() -> Self { - Self::new(0) - } -} - -impl NyashBox for IntegerBox { - fn to_string_box(&self) -> crate::box_trait::StringBox { - crate::box_trait::StringBox::new(self.value.to_string()) - } - - fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox { - use crate::box_trait::BoolBox; - if let Some(other_int) = other.as_any().downcast_ref::() { - BoolBox::new(self.value == other_int.value) - } else { - BoolBox::new(false) - } - } - - fn type_name(&self) -> &'static str { - "IntegerBox" - } - - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - /// 仮実装: clone_boxと同じ(後で修正) - fn share_box(&self) -> Box { - self.clone_box() - } -} - -impl BoxCore for IntegerBox { - fn box_id(&self) -> u64 { - self.base.id - } - - fn parent_type_id(&self) -> Option { - self.base.parent_type_id - } - - fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.value) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -impl Display for IntegerBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} \ No newline at end of file +pub use crate::box_trait::IntegerBox;