diff --git a/src/boxes/array/mod.rs b/src/boxes/array/mod.rs index 00760d3d..7762b9de 100644 --- a/src/boxes/array/mod.rs +++ b/src/boxes/array/mod.rs @@ -1,34 +1,270 @@ -//! ArrayBox 📦 - 配列・リスト操作(両者一致!) -// Nyashの箱システムによる配列・リスト操作を提供します。 -// 参考: 既存Boxの設計思想 +/*! 📦 ArrayBox - 配列・リスト操作Box + * + * ## 📝 概要 + * 順序付きコレクションを扱うためのBox。 + * JavaScript風の配列操作APIで直感的なデータ管理が可能。 + * + * ## 🛠️ 利用可能メソッド + * - `push(item)` - 要素を末尾に追加 + * - `pop()` - 末尾の要素を削除して返す + * - `get(index)` - 指定インデックスの要素を取得 + * - `set(index, value)` - 指定インデックスに要素を設定 + * - `length()` - 配列の長さを取得 + * - `remove(index)` - 指定インデックスの要素を削除 + * - `indexOf(item)` - 要素のインデックスを検索 + * - `contains(item)` - 要素が含まれているか確認 + * - `clear()` - すべての要素を削除 + * - `join(separator)` - 文字列として結合 + * + * ## 💡 使用例 + * ```nyash + * local arr, item + * arr = new ArrayBox() + * + * // 要素の追加 + * arr.push("Apple") + * arr.push("Banana") + * arr.push("Cherry") + * + * // 要素へのアクセス + * print(arr.get(0)) // "Apple" + * print(arr.length()) // 3 + * + * // 要素の削除 + * item = arr.pop() // "Cherry" + * arr.remove(0) // "Apple"削除 + * + * // 文字列結合 + * print(arr.join(", ")) // "Banana" + * ``` + * + * ## 🎮 実用例 - TodoList + * ```nyash + * static box TodoList { + * init { items, console } + * + * main() { + * me.items = new ArrayBox() + * me.console = new ConsoleBox() + * + * me.addTask("Nyash開発") + * me.addTask("ドキュメント作成") + * me.addTask("テスト実行") + * + * me.showTasks() + * } + * + * addTask(task) { + * me.items.push(task) + * me.console.log("✅ タスク追加: " + task) + * } + * + * showTasks() { + * me.console.log("=== Todo List ===") + * local i + * i = 0 + * loop(i < me.items.length()) { + * me.console.log((i + 1) + ". " + me.items.get(i)) + * i = i + 1 + * } + * } + * } + * ``` + * + * ## ⚠️ 注意 + * - インデックスは0から開始 + * - 範囲外のインデックスアクセスはNullBoxを返す + * - 異なる型の要素を混在可能(Everything is Box) + */ +use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; +use crate::boxes::null_box::NullBox; +use std::any::Any; +use std::fmt::{Debug, Display}; +use std::sync::{Arc, Mutex}; + +#[derive(Debug, Clone)] pub struct ArrayBox { - pub items: Vec>, + items: Arc>>>, + id: u64, } impl ArrayBox { - /// 新しいArrayBoxを作成 pub fn new() -> Self { - ArrayBox { items: Vec::new() } + static mut COUNTER: u64 = 0; + let id = unsafe { + COUNTER += 1; + COUNTER + }; + + ArrayBox { + items: Arc::new(Mutex::new(Vec::new())), + id, + } } - /// 要素を追加 - pub fn push(&mut self, item: Box) { - self.items.push(item); + + /// 要素を末尾に追加 + pub fn push(&self, item: Box) -> Box { + self.items.lock().unwrap().push(item); + Box::new(StringBox::new("ok")) } + + /// 末尾の要素を削除して返す + pub fn pop(&self) -> Box { + match self.items.lock().unwrap().pop() { + Some(item) => item, + None => Box::new(NullBox::new()), + } + } + /// 要素数を取得 - pub fn len(&self) -> usize { - self.items.len() + pub fn length(&self) -> Box { + Box::new(IntegerBox::new(self.items.lock().unwrap().len() as i64)) } - /// 要素を取得 - pub fn get(&self, index: usize) -> Option<&Box> { - self.items.get(index) - } - /// 要素を削除 - pub fn remove(&mut self, index: usize) -> Option> { - if index < self.items.len() { - Some(self.items.remove(index)) + + /// 指定インデックスの要素を取得 + pub fn get(&self, index: Box) -> Box { + if let Some(idx_box) = index.as_any().downcast_ref::() { + let idx = idx_box.value as usize; + let items = self.items.lock().unwrap(); + match items.get(idx) { + Some(item) => item.clone_box(), + None => Box::new(NullBox::new()), + } } else { - None + Box::new(StringBox::new("Error: get() requires integer index")) + } + } + + /// 指定インデックスに要素を設定 + pub fn set(&self, index: Box, value: Box) -> Box { + if let Some(idx_box) = index.as_any().downcast_ref::() { + let idx = idx_box.value as usize; + let mut items = self.items.lock().unwrap(); + if idx < items.len() { + items[idx] = value; + Box::new(StringBox::new("ok")) + } else { + Box::new(StringBox::new("Error: index out of bounds")) + } + } else { + Box::new(StringBox::new("Error: set() requires integer index")) + } + } + + /// 指定インデックスの要素を削除 + pub fn remove(&self, index: Box) -> Box { + if let Some(idx_box) = index.as_any().downcast_ref::() { + let idx = idx_box.value as usize; + let mut items = self.items.lock().unwrap(); + if idx < items.len() { + items.remove(idx) + } else { + Box::new(NullBox::new()) + } + } else { + Box::new(StringBox::new("Error: remove() requires integer index")) + } + } + + /// 要素のインデックスを検索 + pub fn indexOf(&self, item: Box) -> Box { + let items = self.items.lock().unwrap(); + for (i, element) in items.iter().enumerate() { + if element.equals(item.as_ref()).value { + return Box::new(IntegerBox::new(i as i64)); + } + } + Box::new(IntegerBox::new(-1)) + } + + /// 要素が含まれているか確認 + pub fn contains(&self, item: Box) -> Box { + let items = self.items.lock().unwrap(); + for element in items.iter() { + if element.equals(item.as_ref()).value { + return Box::new(BoolBox::new(true)); + } + } + Box::new(BoolBox::new(false)) + } + + /// すべての要素を削除 + pub fn clear(&self) -> Box { + self.items.lock().unwrap().clear(); + Box::new(StringBox::new("ok")) + } + + /// 文字列として結合 + pub fn join(&self, separator: Box) -> Box { + if let Some(sep_box) = separator.as_any().downcast_ref::() { + let items = self.items.lock().unwrap(); + let parts: Vec = items + .iter() + .map(|item| item.to_string_box().value) + .collect(); + Box::new(StringBox::new(parts.join(&sep_box.value))) + } else { + Box::new(StringBox::new("Error: join() requires string separator")) } } } + +impl NyashBox for ArrayBox { + fn type_name(&self) -> &'static str { + "ArrayBox" + } + + fn to_string_box(&self) -> StringBox { + let items = self.items.lock().unwrap(); + let elements: Vec = items + .iter() + .map(|item| item.to_string_box().value) + .collect(); + StringBox::new(format!("[{}]", elements.join(", "))) + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(other_array) = other.as_any().downcast_ref::() { + let self_items = self.items.lock().unwrap(); + let other_items = other_array.items.lock().unwrap(); + + if self_items.len() != other_items.len() { + return BoolBox::new(false); + } + + for (a, b) in self_items.iter().zip(other_items.iter()) { + if !a.equals(&**b).value { + return BoolBox::new(false); + } + } + + BoolBox::new(true) + } else { + BoolBox::new(false) + } + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn box_id(&self) -> u64 { + self.id + } +} + +impl Display for ArrayBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let items = self.items.lock().unwrap(); + let elements: Vec = items + .iter() + .map(|item| item.to_string_box().value) + .collect(); + write!(f, "[{}]", elements.join(", ")) + } +} diff --git a/src/boxes/map_box.rs b/src/boxes/map_box.rs index 088adf55..1770173b 100644 --- a/src/boxes/map_box.rs +++ b/src/boxes/map_box.rs @@ -103,7 +103,8 @@ * - 存在しないキーの取得は "Key not found" メッセージ返却 */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, ArrayBox}; +use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; +use crate::boxes::array::ArrayBox; use std::fmt::{Debug, Display}; use std::any::Any; use std::collections::HashMap; diff --git a/src/boxes/mod.rs b/src/boxes/mod.rs index 01920945..e29af3b2 100644 --- a/src/boxes/mod.rs +++ b/src/boxes/mod.rs @@ -99,9 +99,9 @@ pub mod null_box; // pub mod intent_box_wrapper; // pub mod p2p_box; -// 今後追加予定のBox型(コメントアウト) -// pub mod array_box; -// pub use array_box::ArrayBox; +// 配列・リスト操作Box +pub mod array; +pub use array::ArrayBox; // null関数も再エクスポート pub use null_box::{NullBox, null}; diff --git a/src/interpreter/expressions.rs b/src/interpreter/expressions.rs index b05d8f6c..f36be539 100644 --- a/src/interpreter/expressions.rs +++ b/src/interpreter/expressions.rs @@ -8,6 +8,7 @@ use super::*; use crate::ast::UnaryOperator; +use crate::boxes::array::ArrayBox; // TODO: Fix NullBox import issue later // use crate::NullBox; @@ -326,6 +327,8 @@ impl NyashInterpreter { return self.execute_array_method(array_box, method, arguments); } + // TODO: 以下のBoxはまだ実装されていない + /* // FileBox method calls if let Some(file_box) = obj_value.as_any().downcast_ref::() { return self.execute_file_method(file_box, method, arguments); @@ -345,6 +348,7 @@ impl NyashInterpreter { if let Some(channel_box) = obj_value.as_any().downcast_ref::() { return self.execute_channel_method(channel_box, method, arguments); } + */ // MathBox method calls if let Some(math_box) = obj_value.as_any().downcast_ref::() { diff --git a/src/interpreter/methods/collection_methods.rs b/src/interpreter/methods/collection_methods.rs index ef784407..6544a712 100644 --- a/src/interpreter/methods/collection_methods.rs +++ b/src/interpreter/methods/collection_methods.rs @@ -8,7 +8,8 @@ */ use super::super::*; -use crate::box_trait::{StringBox, IntegerBox, ArrayBox, NyashBox, VoidBox, BoolBox}; +use crate::box_trait::{StringBox, IntegerBox, NyashBox, BoolBox}; +use crate::boxes::array::ArrayBox; use crate::boxes::map_box::MapBox; impl NyashInterpreter { @@ -48,17 +49,7 @@ impl NyashInterpreter { }); } let index_value = self.execute_expression(&arguments[0])?; - if let Some(index_int) = index_value.as_any().downcast_ref::() { - if let Some(element) = array_box.get(index_int.value as usize) { - Ok(element) - } else { - Ok(Box::new(StringBox::new("Index out of bounds"))) - } - } else { - Err(RuntimeError::TypeError { - message: "get() requires integer index".to_string(), - }) - } + Ok(array_box.get(index_value)) } "set" => { if arguments.len() != 2 { @@ -68,18 +59,43 @@ impl NyashInterpreter { } let index_value = self.execute_expression(&arguments[0])?; let element_value = self.execute_expression(&arguments[1])?; - if let Some(index_int) = index_value.as_any().downcast_ref::() { - match array_box.set(index_int.value as usize, element_value) { - Ok(()) => Ok(Box::new(VoidBox::new())), - Err(msg) => Ok(Box::new(StringBox::new(&msg))), - } - } else { - Err(RuntimeError::TypeError { - message: "set() requires integer index".to_string(), - }) - } + Ok(array_box.set(index_value, element_value)) + } + "remove" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("remove() expects 1 argument, got {}", arguments.len()), + }); + } + let index_value = self.execute_expression(&arguments[0])?; + Ok(array_box.remove(index_value)) + } + "indexOf" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("indexOf() expects 1 argument, got {}", arguments.len()), + }); + } + let element = self.execute_expression(&arguments[0])?; + Ok(array_box.indexOf(element)) + } + "contains" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("contains() expects 1 argument, got {}", arguments.len()), + }); + } + let element = self.execute_expression(&arguments[0])?; + Ok(array_box.contains(element)) + } + "clear" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("clear() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(array_box.clear()) } - // Note: indexOf, contains, clear, reverse, slice methods not implemented in ArrayBox yet "join" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { @@ -87,13 +103,7 @@ impl NyashInterpreter { }); } let delimiter_value = self.execute_expression(&arguments[0])?; - if let Some(delimiter_str) = delimiter_value.as_any().downcast_ref::() { - Ok(array_box.join(&delimiter_str.value)) - } else { - Err(RuntimeError::TypeError { - message: "join() requires string delimiter".to_string(), - }) - } + Ok(array_box.join(delimiter_value)) } "isEmpty" => { if !arguments.is_empty() { diff --git a/src/interpreter/objects.rs b/src/interpreter/objects.rs index a8bd6933..e15b43c4 100644 --- a/src/interpreter/objects.rs +++ b/src/interpreter/objects.rs @@ -9,6 +9,7 @@ use super::*; use crate::boxes::null_box::NullBox; use crate::boxes::console_box::ConsoleBox; +use crate::boxes::array::ArrayBox; // use crate::boxes::intent_box_wrapper::IntentBoxWrapper; use std::sync::Arc; @@ -29,6 +30,8 @@ impl NyashInterpreter { // 🌍 革命的実装:Environment tracking廃止 return Ok(array_box); } + // TODO: 以下のBoxはまだ実装されていない + /* "FileBox" => { // FileBoxは引数1個(ファイルパス)で作成 if arguments.len() != 1 { @@ -59,6 +62,7 @@ impl NyashInterpreter { // 🌍 革命的実装:Environment tracking廃止 return Ok(result_box); } + */ "ErrorBox" => { // ErrorBoxは引数2個(エラータイプ、メッセージ)で作成 if arguments.len() != 2 { @@ -619,9 +623,9 @@ impl NyashInterpreter { // 基本的なビルトイン型 let is_builtin = matches!(type_name, "IntegerBox" | "StringBox" | "BoolBox" | "ArrayBox" | "MapBox" | - "FileBox" | "ResultBox" | "FutureBox" | "ChannelBox" | "MathBox" | + "MathBox" | "TimeBox" | "DateTimeBox" | "TimerBox" | "RandomBox" | "SoundBox" | - "DebugBox" | "MethodBox" | "NullBox" | "ConsoleBox" | "FloatBox" + "DebugBox" | "MethodBox" | "NullBox" | "ConsoleBox" ); // Web専用Box(WASM環境のみ) diff --git a/test_array_box.nyash b/test_array_box.nyash new file mode 100644 index 00000000..f88a0450 --- /dev/null +++ b/test_array_box.nyash @@ -0,0 +1,74 @@ +// ArrayBox実装テスト +print("=== ArrayBox実装テスト ===") + +// 1. ArrayBoxの作成 +print("\n1. ArrayBoxの作成:") +local arr +arr = new ArrayBox() +print("ArrayBox created: " + arr.toString()) +// print("Type: " + arr.type_name()) // type_nameはArrayBoxのメソッドではない +print("Initial length: " + arr.length()) + +// 2. 要素の追加(push) +print("\n2. 要素の追加:") +arr.push("Apple") +arr.push("Banana") +arr.push("Cherry") +arr.push(42) +arr.push(true) +print("After push: " + arr.toString()) +print("Length: " + arr.length()) + +// 3. 要素の取得(get) +print("\n3. 要素の取得:") +print("arr.get(0) = " + arr.get(0)) +print("arr.get(1) = " + arr.get(1)) +print("arr.get(3) = " + arr.get(3)) +print("arr.get(10) = " + arr.get(10)) // 範囲外 + +// 4. 要素の削除(pop) +print("\n4. 要素の削除:") +local popped +popped = arr.pop() +print("Popped: " + popped) +print("After pop: " + arr.toString()) +print("Length: " + arr.length()) + +// 5. インデックス検索(indexOf) +print("\n5. インデックス検索:") +print("indexOf('Apple') = " + arr.indexOf("Apple")) +print("indexOf('Banana') = " + arr.indexOf("Banana")) +print("indexOf('NotExist') = " + arr.indexOf("NotExist")) + +// 6. 要素の確認(contains) +print("\n6. 要素の確認:") +print("contains('Apple') = " + arr.contains("Apple")) +print("contains(42) = " + arr.contains(42)) +print("contains('NotExist') = " + arr.contains("NotExist")) + +// 7. 文字列結合(join) +print("\n7. 文字列結合:") +print("join(', ') = " + arr.join(", ")) +print("join(' - ') = " + arr.join(" - ")) + +// 8. 要素の設定(set) +print("\n8. 要素の設定:") +arr.set(1, "Orange") +print("After set(1, 'Orange'): " + arr.toString()) + +// 9. 要素の削除(remove) +print("\n9. 要素の削除:") +local removed +removed = arr.remove(2) +print("Removed: " + removed) +print("After remove(2): " + arr.toString()) +print("Length: " + arr.length()) + +// 10. 配列のクリア(clear) +print("\n10. 配列のクリア:") +arr.clear() +print("After clear: " + arr.toString()) +print("Length: " + arr.length()) +print("isEmpty: " + arr.isEmpty()) + +print("\n=== ArrayBoxテスト完了! ===") \ No newline at end of file diff --git a/test_array_simple.nyash b/test_array_simple.nyash new file mode 100644 index 00000000..c25fecf5 --- /dev/null +++ b/test_array_simple.nyash @@ -0,0 +1,23 @@ +// ArrayBox簡単なテスト +print("=== ArrayBox簡単なテスト ===") + +local arr +arr = new ArrayBox() +print("Created ArrayBox") + +// 要素を追加 +arr.push("Hello") +arr.push("World") +print("Added elements") + +// 長さを確認 +print("Length: " + arr.length()) + +// 配列の内容を表示 +print("Array: " + arr.toString()) + +// 要素を取得 +print("First element: " + arr.get(0)) +print("Second element: " + arr.get(1)) + +print("\n=== Test complete! ===") \ No newline at end of file