diff --git a/src/boxes/array/mod.rs b/src/boxes/array/mod.rs index 7d6a2d67..0e724f95 100644 --- a/src/boxes/array/mod.rs +++ b/src/boxes/array/mod.rs @@ -135,6 +135,100 @@ impl ArrayBox { Box::new(StringBox::new("Error: join() requires string separator")) } } + + /// 配列をソート(昇順) + pub fn sort(&self) -> Box { + let mut items = self.items.lock().unwrap(); + + // Numeric values first, then string values + items.sort_by(|a, b| { + use std::cmp::Ordering; + + // Try to compare as numbers first + if let (Some(a_int), Some(b_int)) = ( + a.as_any().downcast_ref::(), + b.as_any().downcast_ref::() + ) { + return a_int.value.cmp(&b_int.value); + } + + // Try FloatBox comparison + if let (Some(a_float), Some(b_float)) = ( + a.as_any().downcast_ref::(), + b.as_any().downcast_ref::() + ) { + return a_float.value.partial_cmp(&b_float.value).unwrap_or(Ordering::Equal); + } + + // Mixed numeric types + if let (Some(a_int), Some(b_float)) = ( + a.as_any().downcast_ref::(), + b.as_any().downcast_ref::() + ) { + return (a_int.value as f64).partial_cmp(&b_float.value).unwrap_or(Ordering::Equal); + } + + if let (Some(a_float), Some(b_int)) = ( + a.as_any().downcast_ref::(), + b.as_any().downcast_ref::() + ) { + return a_float.value.partial_cmp(&(b_int.value as f64)).unwrap_or(Ordering::Equal); + } + + // Fall back to string comparison + let a_str = a.to_string_box().value; + let b_str = b.to_string_box().value; + a_str.cmp(&b_str) + }); + + Box::new(StringBox::new("ok")) + } + + /// 配列を反転 + pub fn reverse(&self) -> Box { + let mut items = self.items.lock().unwrap(); + items.reverse(); + Box::new(StringBox::new("ok")) + } + + /// 部分配列を取得 + pub fn slice(&self, start: Box, end: Box) -> Box { + let items = self.items.lock().unwrap(); + + // Extract start and end indices + let start_idx = if let Some(start_int) = start.as_any().downcast_ref::() { + if start_int.value < 0 { + 0 + } else { + start_int.value as usize + } + } else { + return Box::new(StringBox::new("Error: slice() start index must be an integer")); + }; + + let end_idx = if let Some(end_int) = end.as_any().downcast_ref::() { + if end_int.value < 0 { + items.len() + } else { + (end_int.value as usize).min(items.len()) + } + } else { + return Box::new(StringBox::new("Error: slice() end index must be an integer")); + }; + + // Validate indices + if start_idx > items.len() || start_idx > end_idx { + return Box::new(ArrayBox::new()); + } + + // Create slice + let slice_items: Vec> = items[start_idx..end_idx] + .iter() + .map(|item| item.clone_box()) + .collect(); + + Box::new(ArrayBox::new_with_elements(slice_items)) + } } impl BoxCore for ArrayBox { diff --git a/src/interpreter/methods/collection_methods.rs b/src/interpreter/methods/collection_methods.rs index 6544a712..3891c764 100644 --- a/src/interpreter/methods/collection_methods.rs +++ b/src/interpreter/methods/collection_methods.rs @@ -126,6 +126,32 @@ impl NyashInterpreter { } Ok(Box::new(array_box.to_string_box())) } + "sort" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("sort() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(array_box.sort()) + } + "reverse" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("reverse() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(array_box.reverse()) + } + "slice" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("slice() expects 2 arguments (start, end), got {}", arguments.len()), + }); + } + let start_value = self.execute_expression(&arguments[0])?; + let end_value = self.execute_expression(&arguments[1])?; + Ok(array_box.slice(start_value, end_value)) + } _ => { Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for ArrayBox", method), diff --git a/test_array_improvements.nyash b/test_array_improvements.nyash new file mode 100644 index 00000000..42949faa --- /dev/null +++ b/test_array_improvements.nyash @@ -0,0 +1,81 @@ +// test_array_improvements.nyash - ArrayBox Phase 2 improvements test +// Testing: sort(), reverse(), indexOf(), slice() methods + +print("📦 Testing ArrayBox improvements...") + +// Basic array creation and setup +local arr, result, sliceResult + +print("=== Setup: Creating test array ===") +arr = new ArrayBox() +arr.push(3) +arr.push(1) +arr.push(4) +arr.push(1) +arr.push(5) +print("Original array: " + arr.toString()) + +print("\n=== Test 1: sort() method ===") +arr.sort() +print("After sort(): " + arr.toString()) +// Expected: [1, 1, 3, 4, 5] + +print("\n=== Test 2: reverse() method ===") +arr.reverse() +print("After reverse(): " + arr.toString()) +// Expected: [5, 4, 3, 1, 1] + +print("\n=== Test 3: indexOf() method ===") +result = arr.indexOf(4) +print("indexOf(4): " + result.toString()) +// Expected: 1 + +result = arr.indexOf(1) +print("indexOf(1): " + result.toString()) +// Expected: 3 (first occurrence from current order) + +result = arr.indexOf(999) +print("indexOf(999): " + result.toString()) +// Expected: -1 (not found) + +print("\n=== Test 4: slice() method ===") +sliceResult = arr.slice(1, 4) +print("slice(1, 4): " + sliceResult.toString()) +// Expected: [4, 3, 1] (indices 1, 2, 3) + +sliceResult = arr.slice(0, 2) +print("slice(0, 2): " + sliceResult.toString()) +// Expected: [5, 4] (indices 0, 1) + +sliceResult = arr.slice(2, 10) // End beyond array +print("slice(2, 10): " + sliceResult.toString()) +// Expected: [3, 1, 1] (indices 2 to end) + +print("\n=== Test 5: Mixed types sorting ===") +local mixedArr +mixedArr = new ArrayBox() +mixedArr.push("banana") +mixedArr.push(2) +mixedArr.push("apple") +mixedArr.push(1) +mixedArr.push("cherry") +print("Mixed array before sort: " + mixedArr.toString()) + +mixedArr.sort() +print("Mixed array after sort: " + mixedArr.toString()) +// Expected: numbers first (1, 2), then strings alphabetically + +print("\n=== Test 6: FloatBox integration ===") +local floatArr +floatArr = new ArrayBox() +floatArr.push(new FloatBox(3.14)) +floatArr.push(1) +floatArr.push(new FloatBox(2.71)) +floatArr.push(4) +print("Float array before sort: " + floatArr.toString()) + +floatArr.sort() +print("Float array after sort: " + floatArr.toString()) +// Expected: [1, 2.71, 3.14, 4] + +print("\n✅ ArrayBox Phase 2 improvements tests completed!") \ No newline at end of file