Files
hakorune/src/box_factory/builtin.rs
Moe Charm 3e8b75f4de fix: Kilo/CHIP-8アプリエラー修正 - toInteger, substring, レガシーBox削除
## 修正内容
1. **toIntegerメソッド実装** (#125)
   - StringBoxにtoInteger()メソッド追加
   - box_trait::IntegerBoxを返すよう統一(レガシーboxes::IntegerBox削除)

2. **substringメソッド実装**
   - StringBoxにsubstring(start, end)メソッド追加
   - Kiloエディタで必要な文字列操作を完全サポート

3. **レガシーコード削除**
   - src/boxes/mod.rsから重複StringBox/IntegerBox/BoolBoxエクスポート削除
   - 全てbox_trait実装に統一

4. **プラグインドキュメント整理**
   - 古い仕様書に「理想案・未実装」「将来構想」明記
   - 実装ベースの正確な仕様書作成
   - migration-guide.md追加

## テスト結果
-  Kiloエディタ: 完全動作確認("Enhanced Kilo Editor test complete")
-  toInteger()の乗算: 正常動作
-  substring(): 正常動作

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-20 14:13:47 +09:00

308 lines
11 KiB
Rust

/*!
* Builtin Box Factory
*
* Handles creation of all built-in Box types (StringBox, IntegerBox, etc.)
* Replaces the 600+ line match statement with clean factory pattern
*/
use super::BoxFactory;
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox};
use crate::interpreter::RuntimeError;
use crate::boxes::*;
use std::collections::HashMap;
type BoxCreator = Box<dyn Fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> + Send + Sync>;
/// Factory for all built-in Box types
pub struct BuiltinBoxFactory {
/// Map of Box type names to their creation functions
creators: HashMap<String, BoxCreator>,
}
impl BuiltinBoxFactory {
/// Create a new factory with all built-in types registered
pub fn new() -> Self {
let mut factory = Self {
creators: HashMap::new(),
};
// Register all built-in Box types
factory.register_basic_types();
factory.register_container_types();
factory.register_utility_types();
factory.register_io_types();
#[cfg(not(target_arch = "wasm32"))]
factory.register_native_types();
#[cfg(target_arch = "wasm32")]
factory.register_wasm_types();
factory
}
/// Register basic data types
fn register_basic_types(&mut self) {
// StringBox
self.register("StringBox", |args| {
let value = match args.get(0) {
Some(arg) => arg.to_string_box().value,
None => String::new(),
};
// Return StringBox directly without InstanceBox wrapper
Ok(Box::new(StringBox::new(value)) as Box<dyn NyashBox>)
});
// IntegerBox
self.register("IntegerBox", |args| {
let value = match args.get(0) {
Some(arg) => {
// Try direct downcast first
if let Some(int_box) = arg.as_any().downcast_ref::<IntegerBox>() {
int_box.value
} else {
// Parse from string
arg.to_string_box().value.parse::<i64>()
.map_err(|_| RuntimeError::TypeError {
message: format!("Cannot convert '{}' to integer", arg.to_string_box().value),
})?
}
},
None => 0,
};
Ok(Box::new(IntegerBox::new(value)))
});
// BoolBox
self.register("BoolBox", |args| {
let value = match args.get(0) {
Some(arg) => {
if let Some(bool_box) = arg.as_any().downcast_ref::<BoolBox>() {
bool_box.value
} else {
match arg.to_string_box().value.to_lowercase().as_str() {
"true" => true,
"false" => false,
_ => return Err(RuntimeError::TypeError {
message: format!("Cannot convert '{}' to boolean", arg.to_string_box().value),
}),
}
}
},
None => false,
};
Ok(Box::new(BoolBox::new(value)))
});
// FloatBox
self.register("FloatBox", |args| {
let value = match args.get(0) {
Some(arg) => {
if let Some(float_box) = arg.as_any().downcast_ref::<FloatBox>() {
float_box.value
} else if let Some(int_box) = arg.as_any().downcast_ref::<IntegerBox>() {
int_box.value as f64
} else {
arg.to_string_box().value.parse::<f64>()
.map_err(|_| RuntimeError::TypeError {
message: format!("Cannot convert '{}' to float", arg.to_string_box().value),
})?
}
},
None => 0.0,
};
Ok(Box::new(FloatBox::new(value)))
});
// NullBox
self.register("NullBox", |_args| {
Ok(Box::new(NullBox::new()))
});
}
/// Register container types
fn register_container_types(&mut self) {
// ArrayBox
self.register("ArrayBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ArrayBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(ArrayBox::new()))
});
// MapBox
self.register("MapBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("MapBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(MapBox::new()))
});
// ResultBox
self.register("ResultBox", |args| {
if args.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("ResultBox constructor expects 1 argument, got {}", args.len()),
});
}
let value = args[0].clone_box();
Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(value)))
});
}
/// Register utility types
fn register_utility_types(&mut self) {
// MathBox
self.register("MathBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("MathBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(MathBox::new()))
});
// RandomBox
self.register("RandomBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("RandomBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(RandomBox::new()))
});
// TimeBox
self.register("TimeBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("TimeBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(TimeBox::new()))
});
// DebugBox
self.register("DebugBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("DebugBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(DebugBox::new()))
});
}
/// Register I/O types
fn register_io_types(&mut self) {
// ConsoleBox
self.register("ConsoleBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ConsoleBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(ConsoleBox::new()))
});
// SoundBox
self.register("SoundBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("SoundBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(SoundBox::new()))
});
}
/// Register native-only types
#[cfg(not(target_arch = "wasm32"))]
fn register_native_types(&mut self) {
// DateTimeBox
self.register("DateTimeBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("DateTimeBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(DateTimeBox::now()))
});
// Additional native types can be registered here
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
{
self.register("EguiBox", |args| {
if !args.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("EguiBox constructor expects 0 arguments, got {}", args.len()),
});
}
Ok(Box::new(crate::boxes::EguiBox::new()))
});
}
}
/// Register WASM-specific types
#[cfg(target_arch = "wasm32")]
fn register_wasm_types(&mut self) {
// WebDisplayBox
self.register("WebDisplayBox", |args| {
if args.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("WebDisplayBox constructor expects 1 argument (element_id), got {}", args.len()),
});
}
if let Some(id_str) = args[0].as_any().downcast_ref::<StringBox>() {
Ok(Box::new(crate::boxes::WebDisplayBox::new(id_str.value.clone())))
} else {
Err(RuntimeError::TypeError {
message: "WebDisplayBox constructor requires string element_id argument".to_string(),
})
}
});
// Additional WASM types can be registered here
}
/// Register a Box creator function
fn register<F>(&mut self, name: &str, creator: F)
where
F: Fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> + Send + Sync + 'static,
{
self.creators.insert(name.to_string(), Box::new(creator));
}
}
impl BoxFactory for BuiltinBoxFactory {
fn create_box(
&self,
name: &str,
args: &[Box<dyn NyashBox>],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
if let Some(creator) = self.creators.get(name) {
creator(args)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown built-in Box type: {}", name),
})
}
}
fn box_types(&self) -> Vec<&str> {
self.creators.keys().map(|s| s.as_str()).collect()
}
}
/// Declarative macro for registering multiple Box types at once
#[macro_export]
macro_rules! register_builtins {
($factory:expr, $($box_name:literal => $creator_fn:expr),* $(,)?) => {
$(
$factory.register($box_name, $creator_fn);
)*
};
}