diff --git a/src/box_trait.rs b/src/box_trait.rs index 921f08d0..459f3eba 100644 --- a/src/box_trait.rs +++ b/src/box_trait.rs @@ -7,9 +7,7 @@ */ use std::any::Any; -use std::fmt::{Debug, Display}; -use std::fs; -use std::path::Path; +use std::fmt::Debug; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; @@ -158,612 +156,23 @@ pub trait NyashBox: BoxCore + Debug { // fn get_type_box(&self) -> std::sync::Arc; } -// ===== Basic Box Types ===== +// ===== Basic Box Types (Re-exported from basic module) ===== -/// String values in Nyash - immutable and owned -#[derive(Debug, Clone, PartialEq)] -pub struct StringBox { - pub value: String, - base: BoxBase, -} +// Re-export all basic box types from the dedicated basic module +pub use crate::boxes::basic::{ + BoolBox, ErrorBox, FileBox, IntegerBox, StringBox, VoidBox, +}; -impl StringBox { - pub fn new(value: impl Into) -> Self { - Self { - value: value.into(), - base: BoxBase::new(), - } - } - pub fn empty() -> Self { - Self::new("") - } - // ===== String Methods for Nyash ===== - /// Split string by delimiter and return ArrayBox - pub fn split(&self, delimiter: &str) -> Box { - let parts: Vec = self.value.split(delimiter).map(|s| s.to_string()).collect(); - let array_elements: Vec> = parts - .into_iter() - .map(|s| Box::new(StringBox::new(s)) as Box) - .collect(); - Box::new(ArrayBox::new_with_elements(array_elements)) - } - /// Find substring and return position (or -1 if not found) - pub fn find(&self, search: &str) -> Box { - match self.value.find(search) { - Some(pos) => Box::new(IntegerBox::new(pos as i64)), - None => Box::new(IntegerBox::new(-1)), - } - } - /// Replace all occurrences of old with new - pub fn replace(&self, old: &str, new: &str) -> Box { - Box::new(StringBox::new(self.value.replace(old, new))) - } - - /// Trim whitespace from both ends - pub fn trim(&self) -> Box { - Box::new(StringBox::new(self.value.trim())) - } - - /// Convert to uppercase - pub fn to_upper(&self) -> Box { - Box::new(StringBox::new(self.value.to_uppercase())) - } - - /// Convert to lowercase - pub fn to_lower(&self) -> Box { - Box::new(StringBox::new(self.value.to_lowercase())) - } - - /// Check if string contains substring - pub fn contains(&self, search: &str) -> Box { - Box::new(BoolBox::new(self.value.contains(search))) - } - - /// Check if string starts with prefix - pub fn starts_with(&self, prefix: &str) -> Box { - Box::new(BoolBox::new(self.value.starts_with(prefix))) - } - - /// Check if string ends with suffix - pub fn ends_with(&self, suffix: &str) -> Box { - Box::new(BoolBox::new(self.value.ends_with(suffix))) - } - - /// Join array elements using this string as delimiter - pub fn join(&self, array_box: Box) -> Box { - if let Some(array) = array_box.as_any().downcast_ref::() { - let strings: Vec = array - .items - .read() - .unwrap() - .iter() - .map(|element| element.to_string_box().value) - .collect(); - Box::new(StringBox::new(strings.join(&self.value))) - } else { - // If not an ArrayBox, treat as single element - Box::new(StringBox::new(array_box.to_string_box().value)) - } - } - - /// Get string length - /// - /// Env gate: NYASH_STR_CP=1 → count Unicode scalar values (chars), - /// otherwise use UTF-8 byte length (legacy/default). - pub fn length(&self) -> Box { - let use_cp = std::env::var("NYASH_STR_CP").ok().as_deref() == Some("1"); - let n = if use_cp { - self.value.chars().count() as i64 - } else { - self.value.len() as i64 - }; - Box::new(IntegerBox::new(n)) - } - - /// Convert string to integer (parse as i64) - pub fn to_integer(&self) -> Box { - match self.value.trim().parse::() { - Ok(n) => Box::new(IntegerBox::new(n)), - Err(_) => { - // If parsing fails, return 0 (JavaScript-like behavior) - Box::new(IntegerBox::new(0)) - } - } - } - - /// Get character at index - pub fn get(&self, index: usize) -> Option> { - if let Some(ch) = self.value.chars().nth(index) { - Some(Box::new(StringBox::new(ch.to_string()))) - } else { - None - } - } - - /// Get substring from start to end (exclusive) - pub fn substring(&self, start: usize, end: usize) -> Box { - let chars: Vec = self.value.chars().collect(); - let actual_end = end.min(chars.len()); - let actual_start = start.min(actual_end); - let substring: String = chars[actual_start..actual_end].iter().collect(); - Box::new(StringBox::new(substring)) - } -} - -impl BoxCore for StringBox { - 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 NyashBox for StringBox { - fn to_string_box(&self) -> StringBox { - self.clone() - } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(other_string) = other.as_any().downcast_ref::() { - BoolBox::new(self.value == other_string.value) - } else { - BoolBox::new(false) - } - } - - fn type_name(&self) -> &'static str { - "StringBox" - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - /// 仮実装: clone_boxと同じ(後で修正) - fn share_box(&self) -> Box { - self.clone_box() - } -} - -impl Display for StringBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} - -/// 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 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 NyashBox for IntegerBox { - fn to_string_box(&self) -> StringBox { - StringBox::new(self.value.to_string()) - } - - fn equals(&self, other: &dyn NyashBox) -> 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 Display for IntegerBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} - -/// Boolean values in Nyash - true/false -#[derive(Debug, Clone, PartialEq)] -pub struct BoolBox { - pub value: bool, - base: BoxBase, -} - -impl BoolBox { - pub fn new(value: bool) -> Self { - Self { - value, - base: BoxBase::new(), - } - } - - pub fn true_box() -> Self { - Self::new(true) - } - - pub fn false_box() -> Self { - Self::new(false) - } -} - -impl BoxCore for BoolBox { - 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, "{}", if self.value { "true" } else { "false" }) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -impl NyashBox for BoolBox { - fn to_string_box(&self) -> StringBox { - StringBox::new(if self.value { "true" } else { "false" }) - } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(other_bool) = other.as_any().downcast_ref::() { - BoolBox::new(self.value == other_bool.value) - } else { - BoolBox::new(false) - } - } - - fn type_name(&self) -> &'static str { - "BoolBox" - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - /// 仮実装: clone_boxと同じ(後で修正) - fn share_box(&self) -> Box { - self.clone_box() - } -} - -impl Display for BoolBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} - -/// Void/null values in Nyash - represents empty or null results -#[derive(Debug, Clone, PartialEq)] -pub struct VoidBox { - base: BoxBase, -} - -impl VoidBox { - pub fn new() -> Self { - Self { - base: BoxBase::new(), - } - } -} - -impl Default for VoidBox { - fn default() -> Self { - Self::new() - } -} - -impl BoxCore for VoidBox { - 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, "void") - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -impl NyashBox for VoidBox { - fn to_string_box(&self) -> StringBox { - StringBox::new("void") - } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - BoolBox::new(other.as_any().is::()) - } - - fn type_name(&self) -> &'static str { - "VoidBox" - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - /// 仮実装: clone_boxと同じ(後で修正) - fn share_box(&self) -> Box { - self.clone_box() - } -} - -impl Display for VoidBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} +// Old Box implementations have been moved to separate files // ArrayBox is now defined in boxes::array module pub use crate::boxes::array::ArrayBox; -/// File values in Nyash - file system operations -#[derive(Debug, Clone)] -pub struct FileBox { - pub path: String, - base: BoxBase, -} - -impl FileBox { - pub fn new(path: impl Into) -> Self { - Self { - path: path.into(), - base: BoxBase::new(), - } - } - - // ===== File Methods for Nyash ===== - - /// Read file contents as string - pub fn read(&self) -> Box { - match fs::read_to_string(&self.path) { - Ok(content) => Box::new(StringBox::new(content)), - Err(_) => Box::new(VoidBox::new()), // Return void on error for now - } - } - - /// Write content to file - pub fn write(&self, content: Box) -> Box { - let content_str = content.to_string_box().value; - match fs::write(&self.path, content_str) { - Ok(_) => Box::new(BoolBox::new(true)), - Err(_) => Box::new(BoolBox::new(false)), - } - } - - /// Check if file exists - pub fn exists(&self) -> Box { - Box::new(BoolBox::new(Path::new(&self.path).exists())) - } - - /// Delete file - pub fn delete(&self) -> Box { - match fs::remove_file(&self.path) { - Ok(_) => Box::new(BoolBox::new(true)), - Err(_) => Box::new(BoolBox::new(false)), - } - } - - /// Copy file to destination - pub fn copy(&self, dest_path: &str) -> Box { - match fs::copy(&self.path, dest_path) { - Ok(_) => Box::new(BoolBox::new(true)), - Err(_) => Box::new(BoolBox::new(false)), - } - } -} - -impl BoxCore for FileBox { - 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.path) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -impl NyashBox for FileBox { - fn to_string_box(&self) -> StringBox { - StringBox::new(format!("", self.path)) - } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(other_file) = other.as_any().downcast_ref::() { - BoolBox::new(self.path == other_file.path) - } else { - BoolBox::new(false) - } - } - - fn type_name(&self) -> &'static str { - "FileBox" - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - /// 仮実装: clone_boxと同じ(後で修正) - fn share_box(&self) -> Box { - self.clone_box() - } -} - -impl Display for FileBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} - -/// Error values in Nyash - represents error information -#[derive(Debug, Clone)] -pub struct ErrorBox { - pub error_type: String, - pub message: String, - base: BoxBase, -} - -impl ErrorBox { - pub fn new(error_type: impl Into, message: impl Into) -> Self { - Self { - error_type: error_type.into(), - message: message.into(), - base: BoxBase::new(), - } - } -} - -impl BoxCore for ErrorBox { - 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.error_type, self.message) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -impl NyashBox for ErrorBox { - fn to_string_box(&self) -> StringBox { - StringBox::new(format!("{}: {}", self.error_type, self.message)) - } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(other_error) = other.as_any().downcast_ref::() { - BoolBox::new( - self.error_type == other_error.error_type && self.message == other_error.message, - ) - } else { - BoolBox::new(false) - } - } - - fn type_name(&self) -> &'static str { - "ErrorBox" - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - /// 仮実装: clone_boxと同じ(後で修正) - fn share_box(&self) -> Box { - self.clone_box() - } -} - -impl Display for ErrorBox { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_box(f) - } -} - // FutureBox is now implemented in src/boxes/future/mod.rs using RwLock pattern // and re-exported from src/boxes/mod.rs as both NyashFutureBox and FutureBox diff --git a/src/boxes/basic/bool_box.rs b/src/boxes/basic/bool_box.rs new file mode 100644 index 00000000..b1804060 --- /dev/null +++ b/src/boxes/basic/bool_box.rs @@ -0,0 +1,86 @@ +//! BoolBox - Boolean values in Nyash +//! +//! Implements the core BoolBox type for true/false values. + +use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox}; +use std::any::Any; +use std::fmt::{Debug, Display}; + +/// Boolean values in Nyash - true/false +#[derive(Debug, Clone, PartialEq)] +pub struct BoolBox { + pub value: bool, + base: BoxBase, +} + +impl BoolBox { + pub fn new(value: bool) -> Self { + Self { + value, + base: BoxBase::new(), + } + } + + pub fn true_box() -> Self { + Self::new(true) + } + + pub fn false_box() -> Self { + Self::new(false) + } +} + +impl BoxCore for BoolBox { + 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, "{}", if self.value { "true" } else { "false" }) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +impl NyashBox for BoolBox { + fn to_string_box(&self) -> StringBox { + StringBox::new(if self.value { "true" } else { "false" }) + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(other_bool) = other.as_any().downcast_ref::() { + BoolBox::new(self.value == other_bool.value) + } else { + BoolBox::new(false) + } + } + + fn type_name(&self) -> &'static str { + "BoolBox" + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + /// 仮実装: clone_boxと同じ(後で修正) + fn share_box(&self) -> Box { + self.clone_box() + } +} + +impl Display for BoolBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_box(f) + } +} \ No newline at end of file diff --git a/src/boxes/basic/error_box.rs b/src/boxes/basic/error_box.rs new file mode 100644 index 00000000..7e87bfeb --- /dev/null +++ b/src/boxes/basic/error_box.rs @@ -0,0 +1,82 @@ +//! ErrorBox - Error handling in Nyash +//! +//! Implements the ErrorBox type for representing error information. + +use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox}; +use std::any::Any; +use std::fmt::{Debug, Display}; + +/// Error values in Nyash - represents error information +#[derive(Debug, Clone)] +pub struct ErrorBox { + pub error_type: String, + pub message: String, + base: BoxBase, +} + +impl ErrorBox { + pub fn new(error_type: impl Into, message: impl Into) -> Self { + Self { + error_type: error_type.into(), + message: message.into(), + base: BoxBase::new(), + } + } +} + +impl BoxCore for ErrorBox { + 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.error_type, self.message) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +impl NyashBox for ErrorBox { + fn to_string_box(&self) -> StringBox { + StringBox::new(format!("{}: {}", self.error_type, self.message)) + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(other_error) = other.as_any().downcast_ref::() { + BoolBox::new( + self.error_type == other_error.error_type && self.message == other_error.message, + ) + } else { + BoolBox::new(false) + } + } + + fn type_name(&self) -> &'static str { + "ErrorBox" + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + /// 仮実装: clone_boxと同じ(後で修正) + fn share_box(&self) -> Box { + self.clone_box() + } +} + +impl Display for ErrorBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_box(f) + } +} \ No newline at end of file diff --git a/src/boxes/basic/file_box.rs b/src/boxes/basic/file_box.rs new file mode 100644 index 00000000..99f81837 --- /dev/null +++ b/src/boxes/basic/file_box.rs @@ -0,0 +1,120 @@ +//! FileBox - File system operations in Nyash +//! +//! Implements the FileBox type with file I/O operations. + +use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, VoidBox}; +use std::any::Any; +use std::fmt::{Debug, Display}; +use std::fs; +use std::path::Path; + +/// File values in Nyash - file system operations +#[derive(Debug, Clone)] +pub struct FileBox { + pub path: String, + base: BoxBase, +} + +impl FileBox { + pub fn new(path: impl Into) -> Self { + Self { + path: path.into(), + base: BoxBase::new(), + } + } + + // ===== File Methods for Nyash ===== + + /// Read file contents as string + pub fn read(&self) -> Box { + match fs::read_to_string(&self.path) { + Ok(content) => Box::new(StringBox::new(content)), + Err(_) => Box::new(VoidBox::new()), // Return void on error for now + } + } + + /// Write content to file + pub fn write(&self, content: Box) -> Box { + let content_str = content.to_string_box().value; + match fs::write(&self.path, content_str) { + Ok(_) => Box::new(BoolBox::new(true)), + Err(_) => Box::new(BoolBox::new(false)), + } + } + + /// Check if file exists + pub fn exists(&self) -> Box { + Box::new(BoolBox::new(Path::new(&self.path).exists())) + } + + /// Delete file + pub fn delete(&self) -> Box { + match fs::remove_file(&self.path) { + Ok(_) => Box::new(BoolBox::new(true)), + Err(_) => Box::new(BoolBox::new(false)), + } + } + + /// Copy file to destination + pub fn copy(&self, dest_path: &str) -> Box { + match fs::copy(&self.path, dest_path) { + Ok(_) => Box::new(BoolBox::new(true)), + Err(_) => Box::new(BoolBox::new(false)), + } + } +} + +impl BoxCore for FileBox { + 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.path) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +impl NyashBox for FileBox { + fn to_string_box(&self) -> StringBox { + StringBox::new(format!("", self.path)) + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(other_file) = other.as_any().downcast_ref::() { + BoolBox::new(self.path == other_file.path) + } else { + BoolBox::new(false) + } + } + + fn type_name(&self) -> &'static str { + "FileBox" + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + /// 仮実装: clone_boxと同じ(後で修正) + fn share_box(&self) -> Box { + self.clone_box() + } +} + +impl Display for FileBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_box(f) + } +} \ No newline at end of file diff --git a/src/boxes/basic/integer_box.rs b/src/boxes/basic/integer_box.rs new file mode 100644 index 00000000..b8cbe4bf --- /dev/null +++ b/src/boxes/basic/integer_box.rs @@ -0,0 +1,82 @@ +//! IntegerBox - Integer values in Nyash +//! +//! Implements the core IntegerBox type for 64-bit signed integers. + +use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox}; +use std::any::Any; +use std::fmt::{Debug, 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 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 NyashBox for IntegerBox { + fn to_string_box(&self) -> StringBox { + StringBox::new(self.value.to_string()) + } + + fn equals(&self, other: &dyn NyashBox) -> 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 Display for IntegerBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_box(f) + } +} \ No newline at end of file diff --git a/src/boxes/basic/mod.rs b/src/boxes/basic/mod.rs new file mode 100644 index 00000000..cfe89aad --- /dev/null +++ b/src/boxes/basic/mod.rs @@ -0,0 +1,20 @@ +//! Basic box implementations +//! +//! This module contains the core basic Box types that implement the +//! fundamental data types in Nyash: String, Integer, Boolean, Void, File, and Error. + +// Individual basic box implementations +mod string_box; +mod integer_box; +mod bool_box; +mod void_box; +mod file_box; +mod error_box; + +// Re-export all basic box types +pub use string_box::StringBox; +pub use integer_box::IntegerBox; +pub use bool_box::BoolBox; +pub use void_box::VoidBox; +pub use file_box::FileBox; +pub use error_box::ErrorBox; \ No newline at end of file diff --git a/src/boxes/basic/string_box.rs b/src/boxes/basic/string_box.rs new file mode 100644 index 00000000..7d9a58c2 --- /dev/null +++ b/src/boxes/basic/string_box.rs @@ -0,0 +1,205 @@ +//! StringBox - String values in Nyash +//! +//! Implements the core StringBox type with all string manipulation methods. + +use crate::box_trait::{NyashBox, BoxCore, BoxBase}; +use crate::boxes::ArrayBox; +use std::any::Any; +use std::fmt::{Debug, Display}; + +/// String values in Nyash - UTF-8 strings with rich method support +#[derive(Debug, Clone, PartialEq)] +pub struct StringBox { + pub value: String, + base: BoxBase, +} + +impl StringBox { + pub fn new(value: impl Into) -> Self { + Self { + value: value.into(), + base: BoxBase::new(), + } + } + + pub fn empty() -> Self { + Self::new("") + } + + // ===== String Methods for Nyash ===== + + /// Split string by delimiter and return ArrayBox + pub fn split(&self, delimiter: &str) -> Box { + let parts: Vec = self.value.split(delimiter).map(|s| s.to_string()).collect(); + let array_elements: Vec> = parts + .into_iter() + .map(|s| Box::new(StringBox::new(s)) as Box) + .collect(); + Box::new(ArrayBox::new_with_elements(array_elements)) + } + + /// Find substring and return position (or -1 if not found) + pub fn find(&self, search: &str) -> Box { + use crate::box_trait::IntegerBox; + match self.value.find(search) { + Some(pos) => Box::new(IntegerBox::new(pos as i64)), + None => Box::new(IntegerBox::new(-1)), + } + } + + /// Replace all occurrences of old with new + pub fn replace(&self, old: &str, new: &str) -> Box { + Box::new(StringBox::new(self.value.replace(old, new))) + } + + /// Trim whitespace from both ends + pub fn trim(&self) -> Box { + Box::new(StringBox::new(self.value.trim())) + } + + /// Convert to uppercase + pub fn to_upper(&self) -> Box { + Box::new(StringBox::new(self.value.to_uppercase())) + } + + /// Convert to lowercase + pub fn to_lower(&self) -> Box { + Box::new(StringBox::new(self.value.to_lowercase())) + } + + /// Check if string contains substring + pub fn contains(&self, search: &str) -> Box { + use crate::box_trait::BoolBox; + Box::new(BoolBox::new(self.value.contains(search))) + } + + /// Check if string starts with prefix + pub fn starts_with(&self, prefix: &str) -> Box { + use crate::box_trait::BoolBox; + Box::new(BoolBox::new(self.value.starts_with(prefix))) + } + + /// Check if string ends with suffix + pub fn ends_with(&self, suffix: &str) -> Box { + use crate::box_trait::BoolBox; + Box::new(BoolBox::new(self.value.ends_with(suffix))) + } + + /// Join array elements using this string as delimiter + pub fn join(&self, array_box: Box) -> Box { + if let Some(array) = array_box.as_any().downcast_ref::() { + let strings: Vec = array + .items + .read() + .unwrap() + .iter() + .map(|element| element.to_string_box().value) + .collect(); + Box::new(StringBox::new(strings.join(&self.value))) + } else { + // If not an ArrayBox, treat as single element + Box::new(StringBox::new(array_box.to_string_box().value)) + } + } + + /// Get string length + /// + /// Env gate: NYASH_STR_CP=1 → count Unicode scalar values (chars), + /// otherwise use UTF-8 byte length (legacy/default). + pub fn length(&self) -> Box { + use crate::box_trait::IntegerBox; + let use_cp = std::env::var("NYASH_STR_CP").ok().as_deref() == Some("1"); + let n = if use_cp { + self.value.chars().count() as i64 + } else { + self.value.len() as i64 + }; + Box::new(IntegerBox::new(n)) + } + + /// Convert string to integer (parse as i64) + pub fn to_integer(&self) -> Box { + use crate::box_trait::IntegerBox; + match self.value.trim().parse::() { + Ok(n) => Box::new(IntegerBox::new(n)), + Err(_) => { + // If parsing fails, return 0 (JavaScript-like behavior) + Box::new(IntegerBox::new(0)) + } + } + } + + /// Get character at index + pub fn get(&self, index: usize) -> Option> { + if let Some(ch) = self.value.chars().nth(index) { + Some(Box::new(StringBox::new(ch.to_string()))) + } else { + None + } + } + + /// Get substring from start to end (exclusive) + pub fn substring(&self, start: usize, end: usize) -> Box { + let chars: Vec = self.value.chars().collect(); + let actual_end = end.min(chars.len()); + let actual_start = start.min(actual_end); + let substring: String = chars[actual_start..actual_end].iter().collect(); + Box::new(StringBox::new(substring)) + } +} + +impl BoxCore for StringBox { + 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 NyashBox for StringBox { + fn to_string_box(&self) -> StringBox { + self.clone() + } + + fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox { + use crate::box_trait::BoolBox; + if let Some(other_string) = other.as_any().downcast_ref::() { + BoolBox::new(self.value == other_string.value) + } else { + BoolBox::new(false) + } + } + + fn type_name(&self) -> &'static str { + "StringBox" + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + /// 仮実装: clone_boxと同じ(後で修正) + fn share_box(&self) -> Box { + self.clone_box() + } +} + +impl Display for StringBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_box(f) + } +} \ No newline at end of file diff --git a/src/boxes/basic/void_box.rs b/src/boxes/basic/void_box.rs new file mode 100644 index 00000000..2de8e87d --- /dev/null +++ b/src/boxes/basic/void_box.rs @@ -0,0 +1,78 @@ +//! VoidBox - Void/null values in Nyash +//! +//! Implements the core VoidBox type representing empty or null results. + +use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox}; +use std::any::Any; +use std::fmt::{Debug, Display}; + +/// Void/null values in Nyash - represents empty or null results +#[derive(Debug, Clone, PartialEq)] +pub struct VoidBox { + base: BoxBase, +} + +impl VoidBox { + pub fn new() -> Self { + Self { + base: BoxBase::new(), + } + } +} + +impl Default for VoidBox { + fn default() -> Self { + Self::new() + } +} + +impl BoxCore for VoidBox { + 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, "void") + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +impl NyashBox for VoidBox { + fn to_string_box(&self) -> StringBox { + StringBox::new("void") + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + + fn type_name(&self) -> &'static str { + "VoidBox" + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + /// 仮実装: clone_boxと同じ(後で修正) + fn share_box(&self) -> Box { + self.clone_box() + } +} + +impl Display for VoidBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_box(f) + } +} \ No newline at end of file diff --git a/src/boxes/mod.rs b/src/boxes/mod.rs index 8712daee..8ef75de1 100644 --- a/src/boxes/mod.rs +++ b/src/boxes/mod.rs @@ -53,6 +53,9 @@ #![allow(non_snake_case)] // 各Boxモジュールを宣言 +// 🎯 Phase 3リファクタリング: 基本Box実装を分離したモジュール +pub mod basic; + pub mod bool_box; pub mod debug_box; pub mod integer_box; diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index c436ee19..1f502b8b 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -302,587 +302,16 @@ pub enum MirInstruction { } -impl MirInstruction { - /// Get the effect mask for this instruction - pub fn effects(&self) -> EffectMask { - if let Some(eff) = inst_meta::effects_via_meta(self) { - return eff; - } - match self { - // Pure operations - MirInstruction::Const { .. } - | MirInstruction::BinOp { .. } - | MirInstruction::UnaryOp { .. } - | MirInstruction::Compare { .. } - | MirInstruction::Cast { .. } - | MirInstruction::TypeOp { .. } - | MirInstruction::Copy { .. } - | MirInstruction::Phi { .. } - | MirInstruction::TypeCheck { .. } - | MirInstruction::Nop => EffectMask::PURE, - - // Memory operations - MirInstruction::Load { .. } => EffectMask::READ, - MirInstruction::Store { .. } | MirInstruction::ArraySet { .. } => EffectMask::WRITE, - MirInstruction::ArrayGet { .. } => EffectMask::READ, - - // Function calls use provided effect mask - MirInstruction::Call { effects, .. } - | MirInstruction::BoxCall { effects, .. } - | MirInstruction::PluginInvoke { effects, .. } => *effects, - - // Control flow (pure but affects execution) - MirInstruction::Branch { .. } - | MirInstruction::Jump { .. } - | MirInstruction::Return { .. } => EffectMask::PURE, - - // Box creation may allocate - MirInstruction::NewBox { .. } => EffectMask::PURE.add(Effect::Alloc), - - // Debug has debug effect - MirInstruction::Debug { .. } => EffectMask::PURE.add(Effect::Debug), - - // Print has external write effect - MirInstruction::Print { effects, .. } => *effects, - - // Phase 5: Control flow & exception handling - MirInstruction::Throw { effects, .. } => *effects, - MirInstruction::Catch { .. } => EffectMask::CONTROL, // Handler setup affects control handling - MirInstruction::Safepoint => EffectMask::PURE, // No-op for now - - // Phase 6: Box reference operations - MirInstruction::RefNew { .. } => EffectMask::PURE, // Creating reference is pure - MirInstruction::RefGet { .. } => EffectMask::READ, // Reading field has read effects - MirInstruction::RefSet { .. } => EffectMask::WRITE, // Writing field has write effects - MirInstruction::WeakNew { .. } => EffectMask::PURE, // Creating weak ref is pure - MirInstruction::WeakLoad { .. } => EffectMask::READ, // Loading weak ref has read effects - MirInstruction::BarrierRead { .. } => EffectMask::READ.add(Effect::Barrier), // Memory barrier with read - MirInstruction::BarrierWrite { .. } => EffectMask::WRITE.add(Effect::Barrier), // Memory barrier with write - // PoC unified ops mirror legacy effects - MirInstruction::WeakRef { op, .. } => match op { - WeakRefOp::New => EffectMask::PURE, - WeakRefOp::Load => EffectMask::READ, - }, - MirInstruction::Barrier { op, .. } => match op { - BarrierOp::Read => EffectMask::READ.add(Effect::Barrier), - BarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier), - }, - - // Phase 7: Async/Future Operations - MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate - MirInstruction::FutureSet { .. } => EffectMask::WRITE, // Setting future has write effects - MirInstruction::Await { .. } => EffectMask::READ.add(Effect::Async), // Await blocks and reads - - // Phase 9.7: External Function Calls - MirInstruction::ExternCall { effects, .. } => *effects, // Use provided effect mask - // Function value construction: treat as pure with allocation - MirInstruction::NewClosure { .. } => EffectMask::PURE.add(Effect::Alloc), - } - } - - /// Get the destination ValueId if this instruction produces a value - pub fn dst_value(&self) -> Option { - if let Some(dst) = inst_meta::dst_via_meta(self) { - return Some(dst); - } - match self { - MirInstruction::Const { dst, .. } - | MirInstruction::BinOp { dst, .. } - | MirInstruction::UnaryOp { dst, .. } - | MirInstruction::Compare { dst, .. } - | MirInstruction::Load { dst, .. } - | MirInstruction::Phi { dst, .. } - | MirInstruction::NewBox { dst, .. } - | MirInstruction::TypeCheck { dst, .. } - | MirInstruction::Cast { dst, .. } - | MirInstruction::TypeOp { dst, .. } - | MirInstruction::ArrayGet { dst, .. } - | MirInstruction::Copy { dst, .. } - | MirInstruction::RefNew { dst, .. } - | MirInstruction::RefGet { dst, .. } - | MirInstruction::WeakNew { dst, .. } - | MirInstruction::WeakLoad { dst, .. } - | MirInstruction::WeakRef { dst, .. } - | MirInstruction::FutureNew { dst, .. } - | MirInstruction::Await { dst, .. } => Some(*dst), - MirInstruction::NewClosure { dst, .. } => Some(*dst), - - MirInstruction::Call { dst, .. } - | MirInstruction::BoxCall { dst, .. } - | MirInstruction::PluginInvoke { dst, .. } - | MirInstruction::ExternCall { dst, .. } => *dst, - - MirInstruction::Store { .. } - | MirInstruction::Branch { .. } - | MirInstruction::Jump { .. } - | MirInstruction::Return { .. } - | MirInstruction::ArraySet { .. } - | MirInstruction::Debug { .. } - | MirInstruction::Print { .. } - | MirInstruction::Throw { .. } - | MirInstruction::RefSet { .. } - | MirInstruction::BarrierRead { .. } - | MirInstruction::BarrierWrite { .. } - | MirInstruction::Barrier { .. } - | MirInstruction::FutureSet { .. } - | MirInstruction::Safepoint - | MirInstruction::Nop => None, - - MirInstruction::Catch { - exception_value, .. - } => Some(*exception_value), - } - } - - /// Get all ValueIds used by this instruction - pub fn used_values(&self) -> Vec { - if let Some(used) = inst_meta::used_via_meta(self) { - return used; - } - match self { - MirInstruction::Const { .. } | MirInstruction::Jump { .. } | MirInstruction::Nop => { - Vec::new() - } - - MirInstruction::UnaryOp { operand, .. } - | MirInstruction::Load { ptr: operand, .. } - | MirInstruction::TypeCheck { value: operand, .. } - | MirInstruction::Cast { value: operand, .. } - | MirInstruction::TypeOp { value: operand, .. } - | MirInstruction::Copy { src: operand, .. } - | MirInstruction::Debug { value: operand, .. } - | MirInstruction::Print { value: operand, .. } => vec![*operand], - - MirInstruction::BinOp { lhs, rhs, .. } - | MirInstruction::Compare { lhs, rhs, .. } - | MirInstruction::Store { - value: lhs, - ptr: rhs, - .. - } => vec![*lhs, *rhs], - - MirInstruction::ArrayGet { array, index, .. } => vec![*array, *index], - - MirInstruction::ArraySet { - array, - index, - value, - } => vec![*array, *index, *value], - - MirInstruction::Branch { condition, .. } => vec![*condition], - - MirInstruction::Return { value } => value.map(|v| vec![v]).unwrap_or_default(), - - MirInstruction::Call { func, args, .. } => { - let mut used = vec![*func]; - used.extend(args); - used - } - MirInstruction::NewClosure { captures, me, .. } => { - let mut used: Vec = Vec::new(); - used.extend(captures.iter().map(|(_, v)| *v)); - if let Some(m) = me { - used.push(*m); - } - used - } - - MirInstruction::BoxCall { box_val, args, .. } - | MirInstruction::PluginInvoke { box_val, args, .. } => { - let mut used = vec![*box_val]; - used.extend(args); - used - } - - MirInstruction::NewBox { args, .. } => args.clone(), - - MirInstruction::Phi { inputs, .. } => inputs.iter().map(|(_, value)| *value).collect(), - - // Phase 5: Control flow & exception handling - MirInstruction::Throw { exception, .. } => vec![*exception], - MirInstruction::Catch { .. } => Vec::new(), // Handler setup doesn't use values - MirInstruction::Safepoint => Vec::new(), - - // Phase 6: Box reference operations - MirInstruction::RefNew { box_val, .. } => vec![*box_val], - MirInstruction::RefGet { reference, .. } => vec![*reference], - MirInstruction::RefSet { - reference, value, .. - } => vec![*reference, *value], - MirInstruction::WeakNew { box_val, .. } => vec![*box_val], - MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref], - MirInstruction::BarrierRead { ptr } => vec![*ptr], - MirInstruction::BarrierWrite { ptr } => vec![*ptr], - MirInstruction::WeakRef { value, .. } => vec![*value], - MirInstruction::Barrier { ptr, .. } => vec![*ptr], - - // Phase 7: Async/Future Operations - MirInstruction::FutureNew { value, .. } => vec![*value], - MirInstruction::FutureSet { future, value } => vec![*future, *value], - MirInstruction::Await { future, .. } => vec![*future], - - // Phase 9.7: External Function Calls - MirInstruction::ExternCall { args, .. } => args.clone(), - } - } -} - - -impl ConstValue { - /* - /// Convert to NyashValue - pub fn to_nyash_value(&self) -> NyashValue { - match self { - ConstValue::Integer(n) => NyashValue::new_integer(*n), - ConstValue::Float(f) => NyashValue::new_float(*f), - ConstValue::Bool(b) => NyashValue::new_bool(*b), - ConstValue::String(s) => NyashValue::new_string(s.clone()), - ConstValue::Null => NyashValue::new_null(), - ConstValue::Void => NyashValue::new_void(), - } - } - - /// Create from NyashValue - pub fn from_nyash_value(value: &NyashValue) -> Option { - match value { - NyashValue::Integer(n) => Some(ConstValue::Integer(*n)), - NyashValue::Float(f) => Some(ConstValue::Float(*f)), - NyashValue::Bool(b) => Some(ConstValue::Bool(*b)), - NyashValue::String(s) => Some(ConstValue::String(s.clone())), - NyashValue::Null => Some(ConstValue::Null), - NyashValue::Void => Some(ConstValue::Void), - _ => None, // Collections and Boxes can't be constants - } - } - */ -} - -impl fmt::Display for MirInstruction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - MirInstruction::Const { dst, value } => { - write!(f, "{} = const {}", dst, value) - } - MirInstruction::BinOp { dst, op, lhs, rhs } => { - write!(f, "{} = {} {:?} {}", dst, lhs, op, rhs) - } - MirInstruction::UnaryOp { dst, op, operand } => { - write!(f, "{} = {:?} {}", dst, op, operand) - } - MirInstruction::Compare { dst, op, lhs, rhs } => { - write!(f, "{} = {} {:?} {}", dst, lhs, op, rhs) - } - MirInstruction::Load { dst, ptr } => { - write!(f, "{} = load {}", dst, ptr) - } - MirInstruction::Store { value, ptr } => { - write!(f, "store {} -> {}", value, ptr) - } - MirInstruction::Call { - dst, - func, - callee: _, // TODO: Use callee for type-safe resolution display - args, - effects, - } => { - if let Some(dst) = dst { - write!( - f, - "{} = call {}({}); effects: {}", - dst, - func, - args.iter() - .map(|v| format!("{}", v)) - .collect::>() - .join(", "), - effects - ) - } else { - write!( - f, - "call {}({}); effects: {}", - func, - args.iter() - .map(|v| format!("{}", v)) - .collect::>() - .join(", "), - effects - ) - } - } - MirInstruction::PluginInvoke { - dst, - box_val, - method, - args, - effects: _, - } => { - if let Some(dst) = dst { - write!( - f, - "{} = plugin_invoke {}.{}({})", - dst, - box_val, - method, - args.iter() - .map(|v| format!("{}", v)) - .collect::>() - .join(", ") - ) - } else { - write!( - f, - "plugin_invoke {}.{}({})", - box_val, - method, - args.iter() - .map(|v| format!("{}", v)) - .collect::>() - .join(", ") - ) - } - } - MirInstruction::Return { value } => { - if let Some(value) = value { - write!(f, "ret {}", value) - } else { - write!(f, "ret void") - } - } - MirInstruction::ExternCall { - dst, - iface_name, - method_name, - args, - effects, - } => { - if let Some(dst) = dst { - write!( - f, - "{} = extern_call {}.{}({}); effects: {}", - dst, - iface_name, - method_name, - args.iter() - .map(|v| format!("{}", v)) - .collect::>() - .join(", "), - effects - ) - } else { - write!( - f, - "extern_call {}.{}({}); effects: {}", - iface_name, - method_name, - args.iter() - .map(|v| format!("{}", v)) - .collect::>() - .join(", "), - effects - ) - } - } - _ => write!(f, "{:?}", self), // Fallback for other instructions - } - } -} +// Method implementations have been moved to src/mir/instruction/methods.rs +#[path = "instruction/methods.rs"] +mod methods; +// Display implementation has been moved to src/mir/instruction/display.rs +#[path = "instruction/display.rs"] +mod display; +// Tests have been moved to src/mir/instruction/tests.rs for better organization #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_const_instruction() { - let dst = ValueId::new(0); - let inst = MirInstruction::Const { - dst, - value: ConstValue::Integer(42), - }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert!(inst.used_values().is_empty()); - assert!(inst.effects().is_pure()); - } - - #[test] - fn test_binop_instruction() { - let dst = ValueId::new(0); - let lhs = ValueId::new(1); - let rhs = ValueId::new(2); - - let inst = MirInstruction::BinOp { - dst, - op: BinaryOp::Add, - lhs, - rhs, - }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![lhs, rhs]); - assert!(inst.effects().is_pure()); - } - - #[test] - fn test_call_instruction() { - let dst = ValueId::new(0); - let func = ValueId::new(1); - let arg1 = ValueId::new(2); - let arg2 = ValueId::new(3); - - let inst = MirInstruction::Call { - dst: Some(dst), - func, - args: vec![arg1, arg2], - effects: EffectMask::IO, - }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![func, arg1, arg2]); - assert_eq!(inst.effects(), EffectMask::IO); - } - - /* - #[test] - fn test_const_value_conversion() { - let const_val = ConstValue::Integer(42); - let nyash_val = const_val.to_nyash_value(); - - assert_eq!(nyash_val, NyashValue::new_integer(42)); - - let back = ConstValue::from_nyash_value(&nyash_val).unwrap(); - assert_eq!(back, const_val); - } - */ - - #[test] - fn test_ref_new_instruction() { - let dst = ValueId::new(0); - let box_val = ValueId::new(1); - let inst = MirInstruction::RefNew { dst, box_val }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![box_val]); - assert!(inst.effects().is_pure()); - } - - #[test] - fn test_ref_get_instruction() { - let dst = ValueId::new(0); - let reference = ValueId::new(1); - let field = "name".to_string(); - let inst = MirInstruction::RefGet { - dst, - reference, - field, - }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![reference]); - assert!(!inst.effects().is_pure()); - assert!(inst - .effects() - .contains(super::super::effect::Effect::ReadHeap)); - } - - #[test] - fn test_ref_set_instruction() { - let reference = ValueId::new(0); - let field = "value".to_string(); - let value = ValueId::new(1); - let inst = MirInstruction::RefSet { - reference, - field, - value, - }; - - assert_eq!(inst.dst_value(), None); - assert_eq!(inst.used_values(), vec![reference, value]); - assert!(!inst.effects().is_pure()); - assert!(inst - .effects() - .contains(super::super::effect::Effect::WriteHeap)); - } - - #[test] - fn test_weak_new_instruction() { - let dst = ValueId::new(0); - let box_val = ValueId::new(1); - let inst = MirInstruction::WeakNew { dst, box_val }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![box_val]); - assert!(inst.effects().is_pure()); - } - - #[test] - fn test_weak_load_instruction() { - let dst = ValueId::new(0); - let weak_ref = ValueId::new(1); - let inst = MirInstruction::WeakLoad { dst, weak_ref }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![weak_ref]); - assert!(!inst.effects().is_pure()); - assert!(inst - .effects() - .contains(super::super::effect::Effect::ReadHeap)); - } - - #[test] - fn test_barrier_instructions() { - let ptr = ValueId::new(0); - - let read_barrier = MirInstruction::BarrierRead { ptr }; - assert_eq!(read_barrier.dst_value(), None); - assert_eq!(read_barrier.used_values(), vec![ptr]); - assert!(read_barrier - .effects() - .contains(super::super::effect::Effect::Barrier)); - assert!(read_barrier - .effects() - .contains(super::super::effect::Effect::ReadHeap)); - - let write_barrier = MirInstruction::BarrierWrite { ptr }; - assert_eq!(write_barrier.dst_value(), None); - assert_eq!(write_barrier.used_values(), vec![ptr]); - assert!(write_barrier - .effects() - .contains(super::super::effect::Effect::Barrier)); - assert!(write_barrier - .effects() - .contains(super::super::effect::Effect::WriteHeap)); - } - - #[test] - fn test_extern_call_instruction() { - let dst = ValueId::new(0); - let arg1 = ValueId::new(1); - let arg2 = ValueId::new(2); - let inst = MirInstruction::ExternCall { - dst: Some(dst), - iface_name: "env.console".to_string(), - method_name: "log".to_string(), - args: vec![arg1, arg2], - effects: super::super::effect::EffectMask::IO, - }; - - assert_eq!(inst.dst_value(), Some(dst)); - assert_eq!(inst.used_values(), vec![arg1, arg2]); - assert_eq!(inst.effects(), super::super::effect::EffectMask::IO); - - // Test void extern call - let void_inst = MirInstruction::ExternCall { - dst: None, - iface_name: "env.canvas".to_string(), - method_name: "fillRect".to_string(), - args: vec![arg1], - effects: super::super::effect::EffectMask::IO, - }; - - assert_eq!(void_inst.dst_value(), None); - assert_eq!(void_inst.used_values(), vec![arg1]); - } -} +#[path = "instruction/tests.rs"] +mod tests; diff --git a/src/mir/instruction/display.rs b/src/mir/instruction/display.rs new file mode 100644 index 00000000..f51d0689 --- /dev/null +++ b/src/mir/instruction/display.rs @@ -0,0 +1,137 @@ +//! Display implementation for MIR Instructions +//! +//! Provides human-readable string representation of MIR instructions for debugging and analysis. + +use crate::mir::instruction::MirInstruction; +use std::fmt; + +impl fmt::Display for MirInstruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MirInstruction::Const { dst, value } => { + write!(f, "{} = const {}", dst, value) + } + MirInstruction::BinOp { dst, op, lhs, rhs } => { + write!(f, "{} = {} {:?} {}", dst, lhs, op, rhs) + } + MirInstruction::UnaryOp { dst, op, operand } => { + write!(f, "{} = {:?} {}", dst, op, operand) + } + MirInstruction::Compare { dst, op, lhs, rhs } => { + write!(f, "{} = {} {:?} {}", dst, lhs, op, rhs) + } + MirInstruction::Load { dst, ptr } => { + write!(f, "{} = load {}", dst, ptr) + } + MirInstruction::Store { value, ptr } => { + write!(f, "store {} -> {}", value, ptr) + } + MirInstruction::Call { + dst, + func, + callee: _, // TODO: Use callee for type-safe resolution display + args, + effects, + } => { + if let Some(dst) = dst { + write!( + f, + "{} = call {}({}); effects: {}", + dst, + func, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) + } else { + write!( + f, + "call {}({}); effects: {}", + func, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) + } + } + MirInstruction::PluginInvoke { + dst, + box_val, + method, + args, + effects: _, + } => { + if let Some(dst) = dst { + write!( + f, + "{} = plugin_invoke {}.{}({})", + dst, + box_val, + method, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", ") + ) + } else { + write!( + f, + "plugin_invoke {}.{}({})", + box_val, + method, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", ") + ) + } + } + MirInstruction::Return { value } => { + if let Some(value) = value { + write!(f, "ret {}", value) + } else { + write!(f, "ret void") + } + } + MirInstruction::ExternCall { + dst, + iface_name, + method_name, + args, + effects, + } => { + if let Some(dst) = dst { + write!( + f, + "{} = extern_call {}.{}({}); effects: {}", + dst, + iface_name, + method_name, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) + } else { + write!( + f, + "extern_call {}.{}({}); effects: {}", + iface_name, + method_name, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) + } + } + _ => write!(f, "{:?}", self), // Fallback for other instructions + } + } +} \ No newline at end of file diff --git a/src/mir/instruction/methods.rs b/src/mir/instruction/methods.rs new file mode 100644 index 00000000..c2a868aa --- /dev/null +++ b/src/mir/instruction/methods.rs @@ -0,0 +1,266 @@ +//! Method implementations for MIR Instructions +//! +//! Contains utility methods for MIR instruction analysis including: +//! - Effect tracking (effects()) +//! - Destination value extraction (dst_value()) +//! - Used values collection (used_values()) + +use super::super::{Effect, EffectMask, ValueId}; +use crate::mir::instruction::MirInstruction; +use crate::mir::instruction_kinds as inst_meta; +use crate::mir::types::{BarrierOp, ConstValue, WeakRefOp}; + +impl MirInstruction { + /// Get the effect mask for this instruction + pub fn effects(&self) -> EffectMask { + if let Some(eff) = inst_meta::effects_via_meta(self) { + return eff; + } + match self { + // Pure operations + MirInstruction::Const { .. } + | MirInstruction::BinOp { .. } + | MirInstruction::UnaryOp { .. } + | MirInstruction::Compare { .. } + | MirInstruction::Cast { .. } + | MirInstruction::TypeOp { .. } + | MirInstruction::Copy { .. } + | MirInstruction::Phi { .. } + | MirInstruction::TypeCheck { .. } + | MirInstruction::Nop => EffectMask::PURE, + + // Memory operations + MirInstruction::Load { .. } => EffectMask::READ, + MirInstruction::Store { .. } | MirInstruction::ArraySet { .. } => EffectMask::WRITE, + MirInstruction::ArrayGet { .. } => EffectMask::READ, + + // Function calls use provided effect mask + MirInstruction::Call { effects, .. } + | MirInstruction::BoxCall { effects, .. } + | MirInstruction::PluginInvoke { effects, .. } => *effects, + + // Control flow (pure but affects execution) + MirInstruction::Branch { .. } + | MirInstruction::Jump { .. } + | MirInstruction::Return { .. } => EffectMask::PURE, + + // Box creation may allocate + MirInstruction::NewBox { .. } => EffectMask::PURE.add(Effect::Alloc), + + // Debug has debug effect + MirInstruction::Debug { .. } => EffectMask::PURE.add(Effect::Debug), + + // Print has external write effect + MirInstruction::Print { effects, .. } => *effects, + + // Phase 5: Control flow & exception handling + MirInstruction::Throw { effects, .. } => *effects, + MirInstruction::Catch { .. } => EffectMask::CONTROL, // Handler setup affects control handling + MirInstruction::Safepoint => EffectMask::PURE, // No-op for now + + // Phase 6: Box reference operations + MirInstruction::RefNew { .. } => EffectMask::PURE, // Creating reference is pure + MirInstruction::RefGet { .. } => EffectMask::READ, // Reading field has read effects + MirInstruction::RefSet { .. } => EffectMask::WRITE, // Writing field has write effects + MirInstruction::WeakNew { .. } => EffectMask::PURE, // Creating weak ref is pure + MirInstruction::WeakLoad { .. } => EffectMask::READ, // Loading weak ref has read effects + MirInstruction::BarrierRead { .. } => EffectMask::READ.add(Effect::Barrier), // Memory barrier with read + MirInstruction::BarrierWrite { .. } => EffectMask::WRITE.add(Effect::Barrier), // Memory barrier with write + // PoC unified ops mirror legacy effects + MirInstruction::WeakRef { op, .. } => match op { + WeakRefOp::New => EffectMask::PURE, + WeakRefOp::Load => EffectMask::READ, + }, + MirInstruction::Barrier { op, .. } => match op { + BarrierOp::Read => EffectMask::READ.add(Effect::Barrier), + BarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier), + }, + + // Phase 7: Async/Future Operations + MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate + MirInstruction::FutureSet { .. } => EffectMask::WRITE, // Setting future has write effects + MirInstruction::Await { .. } => EffectMask::READ.add(Effect::Async), // Await blocks and reads + + // Phase 9.7: External Function Calls + MirInstruction::ExternCall { effects, .. } => *effects, // Use provided effect mask + // Function value construction: treat as pure with allocation + MirInstruction::NewClosure { .. } => EffectMask::PURE.add(Effect::Alloc), + } + } + + /// Get the destination ValueId if this instruction produces a value + pub fn dst_value(&self) -> Option { + if let Some(dst) = inst_meta::dst_via_meta(self) { + return Some(dst); + } + match self { + MirInstruction::Const { dst, .. } + | MirInstruction::BinOp { dst, .. } + | MirInstruction::UnaryOp { dst, .. } + | MirInstruction::Compare { dst, .. } + | MirInstruction::Load { dst, .. } + | MirInstruction::Phi { dst, .. } + | MirInstruction::NewBox { dst, .. } + | MirInstruction::TypeCheck { dst, .. } + | MirInstruction::Cast { dst, .. } + | MirInstruction::TypeOp { dst, .. } + | MirInstruction::ArrayGet { dst, .. } + | MirInstruction::Copy { dst, .. } + | MirInstruction::RefNew { dst, .. } + | MirInstruction::RefGet { dst, .. } + | MirInstruction::WeakNew { dst, .. } + | MirInstruction::WeakLoad { dst, .. } + | MirInstruction::WeakRef { dst, .. } + | MirInstruction::FutureNew { dst, .. } + | MirInstruction::Await { dst, .. } => Some(*dst), + MirInstruction::NewClosure { dst, .. } => Some(*dst), + + MirInstruction::Call { dst, .. } + | MirInstruction::BoxCall { dst, .. } + | MirInstruction::PluginInvoke { dst, .. } + | MirInstruction::ExternCall { dst, .. } => *dst, + + MirInstruction::Store { .. } + | MirInstruction::Branch { .. } + | MirInstruction::Jump { .. } + | MirInstruction::Return { .. } + | MirInstruction::ArraySet { .. } + | MirInstruction::Debug { .. } + | MirInstruction::Print { .. } + | MirInstruction::Throw { .. } + | MirInstruction::RefSet { .. } + | MirInstruction::BarrierRead { .. } + | MirInstruction::BarrierWrite { .. } + | MirInstruction::Barrier { .. } + | MirInstruction::FutureSet { .. } + | MirInstruction::Safepoint + | MirInstruction::Nop => None, + + MirInstruction::Catch { + exception_value, .. + } => Some(*exception_value), + } + } + + /// Get all ValueIds used by this instruction + pub fn used_values(&self) -> Vec { + if let Some(used) = inst_meta::used_via_meta(self) { + return used; + } + match self { + MirInstruction::Const { .. } | MirInstruction::Jump { .. } | MirInstruction::Nop => { + Vec::new() + } + + MirInstruction::UnaryOp { operand, .. } + | MirInstruction::Load { ptr: operand, .. } + | MirInstruction::TypeCheck { value: operand, .. } + | MirInstruction::Cast { value: operand, .. } + | MirInstruction::TypeOp { value: operand, .. } + | MirInstruction::Copy { src: operand, .. } + | MirInstruction::Debug { value: operand, .. } + | MirInstruction::Print { value: operand, .. } => vec![*operand], + + MirInstruction::BinOp { lhs, rhs, .. } + | MirInstruction::Compare { lhs, rhs, .. } + | MirInstruction::Store { + value: lhs, + ptr: rhs, + .. + } => vec![*lhs, *rhs], + + MirInstruction::ArrayGet { array, index, .. } => vec![*array, *index], + + MirInstruction::ArraySet { + array, + index, + value, + } => vec![*array, *index, *value], + + MirInstruction::Branch { condition, .. } => vec![*condition], + + MirInstruction::Return { value } => value.map(|v| vec![v]).unwrap_or_default(), + + MirInstruction::Call { func, args, .. } => { + let mut used = vec![*func]; + used.extend(args); + used + } + MirInstruction::NewClosure { captures, me, .. } => { + let mut used: Vec = Vec::new(); + used.extend(captures.iter().map(|(_, v)| *v)); + if let Some(m) = me { + used.push(*m); + } + used + } + + MirInstruction::BoxCall { box_val, args, .. } + | MirInstruction::PluginInvoke { box_val, args, .. } => { + let mut used = vec![*box_val]; + used.extend(args); + used + } + + MirInstruction::NewBox { args, .. } => args.clone(), + + MirInstruction::Phi { inputs, .. } => inputs.iter().map(|(_, value)| *value).collect(), + + // Phase 5: Control flow & exception handling + MirInstruction::Throw { exception, .. } => vec![*exception], + MirInstruction::Catch { .. } => Vec::new(), // Handler setup doesn't use values + MirInstruction::Safepoint => Vec::new(), + + // Phase 6: Box reference operations + MirInstruction::RefNew { box_val, .. } => vec![*box_val], + MirInstruction::RefGet { reference, .. } => vec![*reference], + MirInstruction::RefSet { + reference, value, .. + } => vec![*reference, *value], + MirInstruction::WeakNew { box_val, .. } => vec![*box_val], + MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref], + MirInstruction::BarrierRead { ptr } => vec![*ptr], + MirInstruction::BarrierWrite { ptr } => vec![*ptr], + MirInstruction::WeakRef { value, .. } => vec![*value], + MirInstruction::Barrier { ptr, .. } => vec![*ptr], + + // Phase 7: Async/Future Operations + MirInstruction::FutureNew { value, .. } => vec![*value], + MirInstruction::FutureSet { future, value } => vec![*future, *value], + MirInstruction::Await { future, .. } => vec![*future], + + // Phase 9.7: External Function Calls + MirInstruction::ExternCall { args, .. } => args.clone(), + } + } +} + + +impl ConstValue { + /* + /// Convert to NyashValue + pub fn to_nyash_value(&self) -> NyashValue { + match self { + ConstValue::Integer(n) => NyashValue::new_integer(*n), + ConstValue::Float(f) => NyashValue::new_float(*f), + ConstValue::Bool(b) => NyashValue::new_bool(*b), + ConstValue::String(s) => NyashValue::new_string(s.clone()), + ConstValue::Null => NyashValue::new_null(), + ConstValue::Void => NyashValue::new_void(), + } + } + + /// Create from NyashValue + pub fn from_nyash_value(value: &NyashValue) -> Option { + match value { + NyashValue::Integer(n) => Some(ConstValue::Integer(*n)), + NyashValue::Float(f) => Some(ConstValue::Float(*f)), + NyashValue::Bool(b) => Some(ConstValue::Bool(*b)), + NyashValue::String(s) => Some(ConstValue::String(s.clone())), + NyashValue::Null => Some(ConstValue::Null), + NyashValue::Void => Some(ConstValue::Void), + _ => None, // Collections and Boxes can't be constants + } + } + */ +} \ No newline at end of file diff --git a/src/mir/instruction/tests.rs b/src/mir/instruction/tests.rs new file mode 100644 index 00000000..9b6e5e6a --- /dev/null +++ b/src/mir/instruction/tests.rs @@ -0,0 +1,199 @@ +//! Tests for MIR Instructions +//! +//! Comprehensive test suite for all MIR instruction types and their methods. + +use super::super::{Effect, EffectMask, ValueId}; +use super::MirInstruction; +use crate::mir::types::{BinaryOp, ConstValue}; + +#[test] +fn test_const_instruction() { + let dst = ValueId::new(0); + let inst = MirInstruction::Const { + dst, + value: ConstValue::Integer(42), + }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert!(inst.used_values().is_empty()); + assert!(inst.effects().is_pure()); +} + +#[test] +fn test_binop_instruction() { + let dst = ValueId::new(0); + let lhs = ValueId::new(1); + let rhs = ValueId::new(2); + + let inst = MirInstruction::BinOp { + dst, + op: BinaryOp::Add, + lhs, + rhs, + }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![lhs, rhs]); + assert!(inst.effects().is_pure()); +} + +#[test] +fn test_call_instruction() { + let dst = ValueId::new(0); + let func = ValueId::new(1); + let arg1 = ValueId::new(2); + let arg2 = ValueId::new(3); + + let inst = MirInstruction::Call { + dst: Some(dst), + func, + args: vec![arg1, arg2], + effects: EffectMask::IO, + }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![func, arg1, arg2]); + assert_eq!(inst.effects(), EffectMask::IO); +} + +/* +#[test] +fn test_const_value_conversion() { + let const_val = ConstValue::Integer(42); + let nyash_val = const_val.to_nyash_value(); + + assert_eq!(nyash_val, NyashValue::new_integer(42)); + + let back = ConstValue::from_nyash_value(&nyash_val).unwrap(); + assert_eq!(back, const_val); +} +*/ + +#[test] +fn test_ref_new_instruction() { + let dst = ValueId::new(0); + let box_val = ValueId::new(1); + let inst = MirInstruction::RefNew { dst, box_val }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![box_val]); + assert!(inst.effects().is_pure()); +} + +#[test] +fn test_ref_get_instruction() { + let dst = ValueId::new(0); + let reference = ValueId::new(1); + let field = "name".to_string(); + let inst = MirInstruction::RefGet { + dst, + reference, + field, + }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![reference]); + assert!(!inst.effects().is_pure()); + assert!(inst + .effects() + .contains(super::super::effect::Effect::ReadHeap)); +} + +#[test] +fn test_ref_set_instruction() { + let reference = ValueId::new(0); + let field = "value".to_string(); + let value = ValueId::new(1); + let inst = MirInstruction::RefSet { + reference, + field, + value, + }; + + assert_eq!(inst.dst_value(), None); + assert_eq!(inst.used_values(), vec![reference, value]); + assert!(!inst.effects().is_pure()); + assert!(inst + .effects() + .contains(super::super::effect::Effect::WriteHeap)); +} + +#[test] +fn test_weak_new_instruction() { + let dst = ValueId::new(0); + let box_val = ValueId::new(1); + let inst = MirInstruction::WeakNew { dst, box_val }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![box_val]); + assert!(inst.effects().is_pure()); +} + +#[test] +fn test_weak_load_instruction() { + let dst = ValueId::new(0); + let weak_ref = ValueId::new(1); + let inst = MirInstruction::WeakLoad { dst, weak_ref }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![weak_ref]); + assert!(!inst.effects().is_pure()); + assert!(inst + .effects() + .contains(super::super::effect::Effect::ReadHeap)); +} + +#[test] +fn test_barrier_instructions() { + let ptr = ValueId::new(0); + + let read_barrier = MirInstruction::BarrierRead { ptr }; + assert_eq!(read_barrier.dst_value(), None); + assert_eq!(read_barrier.used_values(), vec![ptr]); + assert!(read_barrier + .effects() + .contains(super::super::effect::Effect::Barrier)); + assert!(read_barrier + .effects() + .contains(super::super::effect::Effect::ReadHeap)); + + let write_barrier = MirInstruction::BarrierWrite { ptr }; + assert_eq!(write_barrier.dst_value(), None); + assert_eq!(write_barrier.used_values(), vec![ptr]); + assert!(write_barrier + .effects() + .contains(super::super::effect::Effect::Barrier)); + assert!(write_barrier + .effects() + .contains(super::super::effect::Effect::WriteHeap)); +} + +#[test] +fn test_extern_call_instruction() { + let dst = ValueId::new(0); + let arg1 = ValueId::new(1); + let arg2 = ValueId::new(2); + let inst = MirInstruction::ExternCall { + dst: Some(dst), + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![arg1, arg2], + effects: super::super::effect::EffectMask::IO, + }; + + assert_eq!(inst.dst_value(), Some(dst)); + assert_eq!(inst.used_values(), vec![arg1, arg2]); + assert_eq!(inst.effects(), super::super::effect::EffectMask::IO); + + // Test void extern call + let void_inst = MirInstruction::ExternCall { + dst: None, + iface_name: "env.canvas".to_string(), + method_name: "fillRect".to_string(), + args: vec![arg1], + effects: super::super::effect::EffectMask::IO, + }; + + assert_eq!(void_inst.dst_value(), None); + assert_eq!(void_inst.used_values(), vec![arg1]); +} \ No newline at end of file