From 750543be7c0beda62394fd7667d3e8a770835063 Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Sun, 10 Aug 2025 13:03:42 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20refactor:=20=E3=81=99=E3=81=B9?= =?UTF-8?q?=E3=81=A6=E3=81=AEBox=E3=82=92Arc=E3=83=91=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=83=B3=E3=81=A7=E7=B5=B1=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CopilotのBox実装を私たちのArcパターンで統一: - BufferBox: Arc>>で内部可変性を実現 - FileBox: Arc>でファイルハンドル管理 - JSONBox: Arc>でJSON値を保持 - HttpClientBox: Arc>でHTTPクライアント管理 - StreamBox: Arcでストリームバッファと位置を管理 - RegexBox: Arcで軽量ラッパー実装 各Boxに実用的なメソッドも追加: - BufferBox: write, read, readAll, clear, length, append, slice - FileBox: read, write, exists, delete, copy - JSONBox: parse, stringify, get, set, has, keys - HttpClientBox: get, post, put, delete, request - StreamBox: write, read, position, length, reset - RegexBox: test, find, findAll, replace, split interpreterに新Box用のメソッド実行を追加: - data_methods.rs: BufferBox, JSONBox, RegexBox - network_methods.rs: HttpClientBox, StreamBox これでコードベース全体が一貫性のあるArcパターンで統一されました! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/boxes/buffer/mod.rs | 140 ++++++++++++-- src/boxes/file/mod.rs | 62 ++++++- src/boxes/future/mod.rs | 18 +- src/boxes/http/mod.rs | 116 +++++++++++- src/boxes/json/mod.rs | 178 +++++++++++++++++- src/boxes/regex/mod.rs | 62 ++++++- src/boxes/result/mod.rs | 33 ++-- src/boxes/stream/mod.rs | 130 ++++++++----- src/interpreter/expressions.rs | 26 +++ src/interpreter/methods/data_methods.rs | 204 +++++++++++++++++++++ src/interpreter/methods/mod.rs | 6 +- src/interpreter/methods/network_methods.rs | 124 +++++++++++++ 12 files changed, 989 insertions(+), 110 deletions(-) create mode 100644 src/interpreter/methods/data_methods.rs create mode 100644 src/interpreter/methods/network_methods.rs diff --git a/src/boxes/buffer/mod.rs b/src/boxes/buffer/mod.rs index 0b0a5b5b..5e7f0080 100644 --- a/src/boxes/buffer/mod.rs +++ b/src/boxes/buffer/mod.rs @@ -1,13 +1,42 @@ -//! BufferBox 📊 - バイナリデータ処理 -// Nyashの箱システムによるバイナリデータ処理を提供します。 -// 参考: 既存Boxの設計思想 +/*! 📊 BufferBox - バイナリデータ処理Box + * + * ## 📝 概要 + * バイナリデータの読み書きを扱うBox。 + * ファイル操作、ネットワーク通信、画像処理などで使用。 + * + * ## 🛠️ 利用可能メソッド + * - `write(data)` - バイトデータ書き込み + * - `read(count)` - 指定バイト数読み取り + * - `readAll()` - 全データ読み取り + * - `clear()` - バッファクリア + * - `length()` - データサイズ取得 + * - `append(buffer)` - 他のBufferを追加 + * - `slice(start, end)` - 部分データ取得 + * + * ## 💡 使用例 + * ```nyash + * local buffer + * buffer = new BufferBox() + * + * // データ書き込み + * buffer.write([72, 101, 108, 108, 111]) // "Hello" + * print("Size: " + buffer.length()) + * + * // データ読み取り + * local data + * data = buffer.readAll() + * ``` + */ -use crate::box_trait::{NyashBox, StringBox, BoolBox}; +use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox}; +use crate::boxes::array::ArrayBox; use std::any::Any; +use std::sync::{Arc, Mutex}; +use std::fmt::{Debug, Display}; #[derive(Debug, Clone)] pub struct BufferBox { - pub data: Vec, + data: Arc>>, id: u64, } @@ -19,7 +48,7 @@ impl BufferBox { COUNTER }; BufferBox { - data: Vec::new(), + data: Arc::new(Mutex::new(Vec::new())), id, } } @@ -30,15 +59,101 @@ impl BufferBox { COUNTER += 1; COUNTER }; - BufferBox { data, id } + BufferBox { + data: Arc::new(Mutex::new(data)), + id, + } } - pub fn len(&self) -> usize { - self.data.len() + /// データを書き込む + pub fn write(&self, data: Box) -> Box { + // ArrayBoxから変換 + if let Some(array_box) = data.as_any().downcast_ref::() { + let mut buffer = self.data.lock().unwrap(); + let items = array_box.items.lock().unwrap(); + for item in items.iter() { + if let Some(int_box) = item.as_any().downcast_ref::() { + if int_box.value >= 0 && int_box.value <= 255 { + buffer.push(int_box.value as u8); + } + } + } + Box::new(IntegerBox::new(buffer.len() as i64)) + } else { + Box::new(StringBox::new("Error: write() requires ArrayBox of integers")) + } } - pub fn as_slice(&self) -> &[u8] { - &self.data + /// すべてのデータを読み取る + pub fn readAll(&self) -> Box { + let buffer = self.data.lock().unwrap(); + let array = ArrayBox::new(); + for &byte in buffer.iter() { + array.push(Box::new(IntegerBox::new(byte as i64))); + } + Box::new(array) + } + + /// 指定バイト数読み取る + pub fn read(&self, count: Box) -> Box { + if let Some(count_int) = count.as_any().downcast_ref::() { + let mut buffer = self.data.lock().unwrap(); + let count = count_int.value.min(buffer.len() as i64) as usize; + let array = ArrayBox::new(); + + // 先頭からcount個取り出す + let bytes: Vec = buffer.drain(0..count).collect(); + for byte in bytes { + array.push(Box::new(IntegerBox::new(byte as i64))); + } + Box::new(array) + } else { + Box::new(StringBox::new("Error: read() requires integer count")) + } + } + + /// バッファをクリア + pub fn clear(&self) -> Box { + self.data.lock().unwrap().clear(); + Box::new(StringBox::new("ok")) + } + + /// データサイズを取得 + pub fn length(&self) -> Box { + Box::new(IntegerBox::new(self.data.lock().unwrap().len() as i64)) + } + + /// 他のBufferBoxを追加 + pub fn append(&self, other: Box) -> Box { + if let Some(other_buffer) = other.as_any().downcast_ref::() { + let mut self_data = self.data.lock().unwrap(); + let other_data = other_buffer.data.lock().unwrap(); + self_data.extend_from_slice(&other_data); + Box::new(IntegerBox::new(self_data.len() as i64)) + } else { + Box::new(StringBox::new("Error: append() requires BufferBox")) + } + } + + /// 部分データ取得 + pub fn slice(&self, start: Box, end: Box) -> Box { + if let (Some(start_int), Some(end_int)) = ( + start.as_any().downcast_ref::(), + end.as_any().downcast_ref::() + ) { + let data = self.data.lock().unwrap(); + let start = (start_int.value as usize).min(data.len()); + let end = (end_int.value as usize).min(data.len()); + + if start <= end { + let slice_data = data[start..end].to_vec(); + Box::new(BufferBox::from_vec(slice_data)) + } else { + Box::new(StringBox::new("Error: invalid slice range")) + } + } else { + Box::new(StringBox::new("Error: slice() requires integer indices")) + } } } @@ -48,7 +163,8 @@ impl NyashBox for BufferBox { } fn to_string_box(&self) -> StringBox { - StringBox::new(format!("BufferBox({} bytes)", self.data.len())) + let data = self.data.lock().unwrap(); + StringBox::new(format!("BufferBox({} bytes)", data.len())) } fn as_any(&self) -> &dyn Any { diff --git a/src/boxes/file/mod.rs b/src/boxes/file/mod.rs index 4198a25e..a71aef2f 100644 --- a/src/boxes/file/mod.rs +++ b/src/boxes/file/mod.rs @@ -6,11 +6,12 @@ use crate::box_trait::{NyashBox, StringBox, BoolBox}; use std::any::Any; use std::fs::{File, OpenOptions}; use std::io::{Read, Write, Result}; +use std::sync::{Arc, Mutex}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FileBox { - pub file: File, - pub path: String, + file: Arc>, + path: Arc, id: u64, } @@ -23,20 +24,61 @@ impl FileBox { }; let file = OpenOptions::new().read(true).write(true).create(true).open(path)?; Ok(FileBox { - file, - path: path.to_string(), + file: Arc::new(Mutex::new(file)), + path: Arc::new(path.to_string()), id, }) } - pub fn read_to_string(&mut self) -> Result { + pub fn read_to_string(&self) -> Result { + let mut file = self.file.lock().unwrap(); let mut s = String::new(); - self.file.read_to_string(&mut s)?; + file.read_to_string(&mut s)?; Ok(s) } - pub fn write_all(&mut self, buf: &[u8]) -> Result<()> { - self.file.write_all(buf) + pub fn write_all(&self, buf: &[u8]) -> Result<()> { + let mut file = self.file.lock().unwrap(); + file.write_all(buf) + } + + /// ファイルの内容を読み取る + pub fn read(&self) -> Box { + match self.read_to_string() { + Ok(content) => Box::new(StringBox::new(&content)), + Err(e) => Box::new(StringBox::new(&format!("Error reading file: {}", e))), + } + } + + /// ファイルに内容を書き込む + pub fn write(&self, content: Box) -> Box { + let content_str = content.to_string_box().value; + match self.write_all(content_str.as_bytes()) { + Ok(()) => Box::new(StringBox::new("ok")), + Err(e) => Box::new(StringBox::new(&format!("Error writing file: {}", e))), + } + } + + /// ファイルが存在するかチェック + pub fn exists(&self) -> Box { + use std::path::Path; + Box::new(BoolBox::new(Path::new(&**self.path).exists())) + } + + /// ファイルを削除 + pub fn delete(&self) -> Box { + match std::fs::remove_file(&**self.path) { + Ok(()) => Box::new(StringBox::new("ok")), + Err(e) => Box::new(StringBox::new(&format!("Error deleting file: {}", e))), + } + } + + /// ファイルをコピー + pub fn copy(&self, dest: &str) -> Box { + match std::fs::copy(&**self.path, dest) { + Ok(_) => Box::new(StringBox::new("ok")), + Err(e) => Box::new(StringBox::new(&format!("Error copying file: {}", e))), + } } } @@ -67,7 +109,7 @@ impl NyashBox for FileBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_file) = other.as_any().downcast_ref::() { - BoolBox::new(self.path == other_file.path) + BoolBox::new(*self.path == *other_file.path) } else { BoolBox::new(false) } diff --git a/src/boxes/future/mod.rs b/src/boxes/future/mod.rs index d8ee112d..67338bac 100644 --- a/src/boxes/future/mod.rs +++ b/src/boxes/future/mod.rs @@ -109,18 +109,12 @@ impl NyashBox for NyashFutureBox { } } -// Keep the original generic FutureBox for compatibility -pub struct FutureBox { - pub future: Pin + Send>>, -} +// Export NyashFutureBox as FutureBox for consistency +pub type FutureBox = NyashFutureBox; -impl FutureBox { - pub fn new(fut: F) -> Self - where - F: Future + Send + 'static, - { - FutureBox { - future: Box::pin(fut), - } +impl FutureBox { + /// wait_and_get()の実装 - await演算子で使用 + pub fn wait_and_get(&self) -> Result, String> { + Ok(self.get()) } } diff --git a/src/boxes/http/mod.rs b/src/boxes/http/mod.rs index 663aedc9..139f69d6 100644 --- a/src/boxes/http/mod.rs +++ b/src/boxes/http/mod.rs @@ -3,13 +3,15 @@ // 参考: 既存Boxの設計思想 use crate::box_trait::{NyashBox, StringBox, BoolBox}; +use crate::boxes::map_box::MapBox; use std::any::Any; +use std::sync::{Arc, Mutex}; use reqwest::blocking::Client; use reqwest::Result; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HttpClientBox { - pub client: Client, + client: Arc>, id: u64, } @@ -21,21 +23,123 @@ impl HttpClientBox { COUNTER }; HttpClientBox { - client: Client::new(), + client: Arc::new(Mutex::new(Client::new())), id, } } pub fn get(&self, url: &str) -> Result { - let res = self.client.get(url).send()?.text()?; + let client = self.client.lock().unwrap(); + let res = client.get(url).send()?.text()?; Ok(res) } + + /// HTTP GETリクエスト + pub fn http_get(&self, url: Box) -> Box { + let url_str = url.to_string_box().value; + match self.get(&url_str) { + Ok(response) => Box::new(StringBox::new(&response)), + Err(e) => Box::new(StringBox::new(&format!("Error in HTTP GET: {}", e))), + } + } + + /// HTTP POSTリクエスト + pub fn post(&self, url: Box, body: Box) -> Box { + let url_str = url.to_string_box().value; + let body_str = body.to_string_box().value; + + let client = self.client.lock().unwrap(); + match client.post(&url_str).body(body_str).send() { + Ok(response) => { + match response.text() { + Ok(text) => Box::new(StringBox::new(&text)), + Err(e) => Box::new(StringBox::new(&format!("Error reading response: {}", e))), + } + }, + Err(e) => Box::new(StringBox::new(&format!("Error in HTTP POST: {}", e))), + } + } + + /// HTTP PUT リクエスト + pub fn put(&self, url: Box, body: Box) -> Box { + let url_str = url.to_string_box().value; + let body_str = body.to_string_box().value; + + let client = self.client.lock().unwrap(); + match client.put(&url_str).body(body_str).send() { + Ok(response) => { + match response.text() { + Ok(text) => Box::new(StringBox::new(&text)), + Err(e) => Box::new(StringBox::new(&format!("Error reading response: {}", e))), + } + }, + Err(e) => Box::new(StringBox::new(&format!("Error in HTTP PUT: {}", e))), + } + } + + /// HTTP DELETE リクエスト + pub fn delete(&self, url: Box) -> Box { + let url_str = url.to_string_box().value; + + let client = self.client.lock().unwrap(); + match client.delete(&url_str).send() { + Ok(response) => { + match response.text() { + Ok(text) => Box::new(StringBox::new(&text)), + Err(e) => Box::new(StringBox::new(&format!("Error reading response: {}", e))), + } + }, + Err(e) => Box::new(StringBox::new(&format!("Error in HTTP DELETE: {}", e))), + } + } + + /// ヘッダー付きHTTPリクエスト + pub fn request(&self, method: Box, url: Box, options: Box) -> Box { + let method_str = method.to_string_box().value.to_uppercase(); + let url_str = url.to_string_box().value; + + // optionsはMapBoxと仮定 + if let Some(map_box) = options.as_any().downcast_ref::() { + let client = self.client.lock().unwrap(); + let mut request = match method_str.as_str() { + "GET" => client.get(&url_str), + "POST" => client.post(&url_str), + "PUT" => client.put(&url_str), + "DELETE" => client.delete(&url_str), + _ => return Box::new(StringBox::new(&format!("Unsupported HTTP method: {}", method_str))), + }; + + // ヘッダー設定 + if let Some(headers_box) = map_box.get(Box::new(StringBox::new("headers"))).as_any().downcast_ref::() { + let headers_map = headers_box.map.lock().unwrap(); + for (key, value) in headers_map.iter() { + request = request.header(key, value.to_string_box().value); + } + } + + // ボディ設定 + if let Some(body_box) = map_box.get(Box::new(StringBox::new("body"))).as_any().downcast_ref::() { + request = request.body(body_box.value.clone()); + } + + match request.send() { + Ok(response) => { + match response.text() { + Ok(text) => Box::new(StringBox::new(&text)), + Err(e) => Box::new(StringBox::new(&format!("Error reading response: {}", e))), + } + }, + Err(e) => Box::new(StringBox::new(&format!("Error in HTTP request: {}", e))), + } + } else { + Box::new(StringBox::new("Error: options must be a MapBox")) + } + } } impl NyashBox for HttpClientBox { fn clone_box(&self) -> Box { - // Create a new client instance since Client doesn't implement Clone in a straightforward way - Box::new(HttpClientBox::new()) + Box::new(self.clone()) } fn to_string_box(&self) -> StringBox { diff --git a/src/boxes/json/mod.rs b/src/boxes/json/mod.rs index 8241c10b..e064663b 100644 --- a/src/boxes/json/mod.rs +++ b/src/boxes/json/mod.rs @@ -2,13 +2,16 @@ // Nyashの箱システムによるJSON解析・生成を提供します。 // 参考: 既存Boxの設計思想 -use crate::box_trait::{NyashBox, StringBox, BoolBox}; +use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox}; +use crate::boxes::array::ArrayBox; +use crate::boxes::map_box::MapBox; use std::any::Any; +use std::sync::{Arc, Mutex}; use serde_json::{Value, Error}; #[derive(Debug, Clone)] pub struct JSONBox { - pub value: Value, + value: Arc>, id: u64, } @@ -20,7 +23,10 @@ impl JSONBox { COUNTER }; let value = serde_json::from_str(s)?; - Ok(JSONBox { value, id }) + Ok(JSONBox { + value: Arc::new(Mutex::new(value)), + id + }) } pub fn new(value: Value) -> Self { @@ -29,11 +35,96 @@ impl JSONBox { COUNTER += 1; COUNTER }; - JSONBox { value, id } + JSONBox { + value: Arc::new(Mutex::new(value)), + id + } } pub fn to_string(&self) -> String { - self.value.to_string() + let value = self.value.lock().unwrap(); + value.to_string() + } + + /// JSONパース + pub fn parse(data: Box) -> Box { + let json_str = data.to_string_box().value; + match JSONBox::from_str(&json_str) { + Ok(json_box) => Box::new(json_box), + Err(e) => Box::new(StringBox::new(&format!("Error parsing JSON: {}", e))), + } + } + + /// JSON文字列化 + pub fn stringify(&self) -> Box { + Box::new(StringBox::new(&self.to_string())) + } + + /// 値取得 + pub fn get(&self, key: Box) -> Box { + let key_str = key.to_string_box().value; + let value = self.value.lock().unwrap(); + + if let Some(obj) = value.as_object() { + if let Some(val) = obj.get(&key_str) { + json_value_to_nyash_box(val) + } else { + Box::new(crate::boxes::null_box::NullBox::new()) + } + } else if let Some(arr) = value.as_array() { + if let Ok(index) = key_str.parse::() { + if let Some(val) = arr.get(index) { + json_value_to_nyash_box(val) + } else { + Box::new(crate::boxes::null_box::NullBox::new()) + } + } else { + Box::new(crate::boxes::null_box::NullBox::new()) + } + } else { + Box::new(crate::boxes::null_box::NullBox::new()) + } + } + + /// 値設定 + pub fn set(&self, key: Box, new_value: Box) -> Box { + let key_str = key.to_string_box().value; + let mut value = self.value.lock().unwrap(); + + let json_value = nyash_box_to_json_value(new_value); + + if let Some(obj) = value.as_object_mut() { + obj.insert(key_str, json_value); + Box::new(StringBox::new("ok")) + } else { + Box::new(StringBox::new("Error: JSONBox is not an object")) + } + } + + /// キー存在チェック + pub fn has(&self, key: Box) -> Box { + let key_str = key.to_string_box().value; + let value = self.value.lock().unwrap(); + + if let Some(obj) = value.as_object() { + Box::new(BoolBox::new(obj.contains_key(&key_str))) + } else { + Box::new(BoolBox::new(false)) + } + } + + /// すべてのキーを取得 + pub fn keys(&self) -> Box { + let value = self.value.lock().unwrap(); + let array = ArrayBox::new(); + + if let Some(obj) = value.as_object() { + for key in obj.keys() { + array.push(Box::new(StringBox::new(key))); + } + } + + Box::new(array) } } @@ -43,7 +134,8 @@ impl NyashBox for JSONBox { } fn to_string_box(&self) -> StringBox { - StringBox::new(self.value.to_string()) + let value = self.value.lock().unwrap(); + StringBox::new(value.to_string()) } fn as_any(&self) -> &dyn Any { @@ -60,9 +152,81 @@ impl NyashBox for JSONBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_json) = other.as_any().downcast_ref::() { - BoolBox::new(self.value == other_json.value) + let self_value = self.value.lock().unwrap(); + let other_value = other_json.value.lock().unwrap(); + BoolBox::new(*self_value == *other_value) } else { BoolBox::new(false) } } } + +/// JSON Value を NyashBox に変換 +fn json_value_to_nyash_box(value: &Value) -> Box { + match value { + Value::Null => Box::new(crate::boxes::null_box::NullBox::new()), + Value::Bool(b) => Box::new(BoolBox::new(*b)), + Value::Number(n) => { + if let Some(i) = n.as_i64() { + Box::new(IntegerBox::new(i)) + } else if let Some(f) = n.as_f64() { + Box::new(crate::box_trait::FloatBox::new(f)) + } else { + Box::new(StringBox::new(&n.to_string())) + } + } + Value::String(s) => Box::new(StringBox::new(s)), + Value::Array(arr) => { + let array_box = ArrayBox::new(); + for item in arr { + array_box.push(json_value_to_nyash_box(item)); + } + Box::new(array_box) + } + Value::Object(obj) => { + let map_box = MapBox::new(); + for (key, val) in obj { + map_box.set( + Box::new(StringBox::new(key)), + json_value_to_nyash_box(val) + ); + } + Box::new(map_box) + } + } +} + +/// NyashBox を JSON Value に変換 +fn nyash_box_to_json_value(value: Box) -> Value { + if value.as_any().downcast_ref::().is_some() { + Value::Null + } else if let Some(bool_box) = value.as_any().downcast_ref::() { + Value::Bool(bool_box.value) + } else if let Some(int_box) = value.as_any().downcast_ref::() { + Value::Number(serde_json::Number::from(int_box.value)) + } else if let Some(float_box) = value.as_any().downcast_ref::() { + if let Some(n) = serde_json::Number::from_f64(float_box.value) { + Value::Number(n) + } else { + Value::String(float_box.value.to_string()) + } + } else if let Some(string_box) = value.as_any().downcast_ref::() { + Value::String(string_box.value.clone()) + } else if let Some(array_box) = value.as_any().downcast_ref::() { + let items = array_box.items.lock().unwrap(); + let arr: Vec = items.iter() + .map(|item| nyash_box_to_json_value(item.clone_box())) + .collect(); + Value::Array(arr) + } else if let Some(map_box) = value.as_any().downcast_ref::() { + let map = map_box.map.lock().unwrap(); + let mut obj = serde_json::Map::new(); + for (key, val) in map.iter() { + obj.insert(key.clone(), nyash_box_to_json_value(val.clone_box())); + } + Value::Object(obj) + } else { + // その他の型は文字列に変換 + Value::String(value.to_string_box().value) + } +} diff --git a/src/boxes/regex/mod.rs b/src/boxes/regex/mod.rs index eabd13d3..37851777 100644 --- a/src/boxes/regex/mod.rs +++ b/src/boxes/regex/mod.rs @@ -4,14 +4,16 @@ use regex::Regex; use crate::box_trait::{NyashBox, StringBox, BoolBox}; +use crate::boxes::array::ArrayBox; use std::any::Any; +use std::sync::{Arc, Mutex}; use std::fmt::Debug; #[derive(Debug, Clone)] pub struct RegexBox { - pub regex: Regex, + regex: Arc, + pattern: Arc, id: u64, - pattern: String, } impl RegexBox { @@ -23,9 +25,9 @@ impl RegexBox { COUNTER }; Ok(RegexBox { - regex, + regex: Arc::new(regex), + pattern: Arc::new(pattern.to_string()), id, - pattern: pattern.to_string(), }) } pub fn is_match(&self, text: &str) -> bool { @@ -34,6 +36,54 @@ impl RegexBox { pub fn pattern(&self) -> &str { &self.pattern } + + /// パターンマッチテスト + pub fn test(&self, text: Box) -> Box { + let text_str = text.to_string_box().value; + Box::new(BoolBox::new(self.is_match(&text_str))) + } + + /// マッチ箇所を検索 + pub fn find(&self, text: Box) -> Box { + let text_str = text.to_string_box().value; + if let Some(mat) = self.regex.find(&text_str) { + Box::new(StringBox::new(mat.as_str())) + } else { + Box::new(crate::boxes::null_box::NullBox::new()) + } + } + + /// すべてのマッチを検索 + pub fn find_all(&self, text: Box) -> Box { + let text_str = text.to_string_box().value; + let array = ArrayBox::new(); + + for mat in self.regex.find_iter(&text_str) { + array.push(Box::new(StringBox::new(mat.as_str()))); + } + + Box::new(array) + } + + /// 文字列置換 + pub fn replace(&self, text: Box, replacement: Box) -> Box { + let text_str = text.to_string_box().value; + let replacement_str = replacement.to_string_box().value; + let result = self.regex.replace_all(&text_str, replacement_str.as_str()); + Box::new(StringBox::new(&result)) + } + + /// 文字列分割 + pub fn split(&self, text: Box) -> Box { + let text_str = text.to_string_box().value; + let array = ArrayBox::new(); + + for part in self.regex.split(&text_str) { + array.push(Box::new(StringBox::new(part))); + } + + Box::new(array) + } } impl NyashBox for RegexBox { @@ -42,7 +92,7 @@ impl NyashBox for RegexBox { } fn to_string_box(&self) -> StringBox { - StringBox::new(format!("RegexBox({})", self.pattern)) + StringBox::new(format!("RegexBox({})", **self.pattern)) } fn as_any(&self) -> &dyn Any { @@ -59,7 +109,7 @@ impl NyashBox for RegexBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_regex) = other.as_any().downcast_ref::() { - BoolBox::new(self.pattern == other_regex.pattern) + BoolBox::new(**self.pattern == **other_regex.pattern) } else { BoolBox::new(false) } diff --git a/src/boxes/result/mod.rs b/src/boxes/result/mod.rs index 8f311074..6787e026 100644 --- a/src/boxes/result/mod.rs +++ b/src/boxes/result/mod.rs @@ -80,23 +80,28 @@ impl NyashBox for NyashResultBox { } } -// Keep the original generic ResultBox for compatibility -pub enum ResultBox { - Ok(T), - Err(E), -} +// Export NyashResultBox as ResultBox for compatibility +pub type ResultBox = NyashResultBox; -impl ResultBox { - pub fn is_ok(&self) -> bool { - matches!(self, ResultBox::Ok(_)) +impl ResultBox { + /// is_ok()の実装 + pub fn is_ok(&self) -> Box { + Box::new(BoolBox::new(matches!(self, NyashResultBox::Ok(_)))) } - pub fn is_err(&self) -> bool { - matches!(self, ResultBox::Err(_)) - } - pub fn unwrap(self) -> T { + + /// getValue()の実装 - Ok値を取得 + pub fn get_value(&self) -> Box { match self { - ResultBox::Ok(val) => val, - ResultBox::Err(_) => panic!("called `unwrap()` on an `Err` value"), + NyashResultBox::Ok(val) => val.clone_box(), + NyashResultBox::Err(_) => Box::new(StringBox::new("Error: Result is Err")), + } + } + + /// getError()の実装 - Err値を取得 + pub fn get_error(&self) -> Box { + match self { + NyashResultBox::Ok(_) => Box::new(StringBox::new("Error: Result is Ok")), + NyashResultBox::Err(err) => err.clone_box(), } } } diff --git a/src/boxes/stream/mod.rs b/src/boxes/stream/mod.rs index 2484d748..5be95c2c 100644 --- a/src/boxes/stream/mod.rs +++ b/src/boxes/stream/mod.rs @@ -2,14 +2,16 @@ // Nyashの箱システムによるストリーミング処理を提供します。 // 参考: 既存Boxの設計思想 -use crate::box_trait::{NyashBox, StringBox, BoolBox}; +use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox}; +use crate::boxes::buffer::BufferBox; use std::any::Any; +use std::sync::{Arc, Mutex}; use std::io::{Read, Write, Result}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NyashStreamBox { - pub buffer: Vec, - pub position: usize, + buffer: Arc>>, + position: Arc>, id: u64, } @@ -21,8 +23,8 @@ impl NyashStreamBox { COUNTER }; NyashStreamBox { - buffer: Vec::new(), - position: 0, + buffer: Arc::new(Mutex::new(Vec::new())), + position: Arc::new(Mutex::new(0)), id, } } @@ -34,54 +36,109 @@ impl NyashStreamBox { COUNTER }; NyashStreamBox { - buffer: data, - position: 0, + buffer: Arc::new(Mutex::new(data)), + position: Arc::new(Mutex::new(0)), id, } } - pub fn read(&mut self, buf: &mut [u8]) -> Result { - let available = self.buffer.len().saturating_sub(self.position); + pub fn read(&self, buf: &mut [u8]) -> Result { + let buffer = self.buffer.lock().unwrap(); + let mut position = self.position.lock().unwrap(); + + let available = buffer.len().saturating_sub(*position); let to_read = buf.len().min(available); if to_read == 0 { return Ok(0); } - buf[..to_read].copy_from_slice(&self.buffer[self.position..self.position + to_read]); - self.position += to_read; + buf[..to_read].copy_from_slice(&buffer[*position..*position + to_read]); + *position += to_read; Ok(to_read) } - pub fn write(&mut self, buf: &[u8]) -> Result<()> { - self.buffer.extend_from_slice(buf); + pub fn write(&self, buf: &[u8]) -> Result<()> { + let mut buffer = self.buffer.lock().unwrap(); + buffer.extend_from_slice(buf); Ok(()) } pub fn len(&self) -> usize { - self.buffer.len() + self.buffer.lock().unwrap().len() } pub fn position(&self) -> usize { - self.position + *self.position.lock().unwrap() } - pub fn reset(&mut self) { - self.position = 0; + pub fn reset(&self) { + *self.position.lock().unwrap() = 0; + } + + /// ストリームに書き込み + pub fn stream_write(&self, data: Box) -> Box { + // BufferBoxから変換 + if let Some(buffer_box) = data.as_any().downcast_ref::() { + let buffer_data = buffer_box.data.lock().unwrap(); + match self.write(&buffer_data) { + Ok(()) => Box::new(StringBox::new("ok")), + Err(e) => Box::new(StringBox::new(&format!("Error writing to stream: {}", e))), + } + } else if let Some(string_box) = data.as_any().downcast_ref::() { + match self.write(string_box.value.as_bytes()) { + Ok(()) => Box::new(StringBox::new("ok")), + Err(e) => Box::new(StringBox::new(&format!("Error writing to stream: {}", e))), + } + } else { + Box::new(StringBox::new("Error: write() requires BufferBox or StringBox")) + } + } + + /// ストリームから読み込み + pub fn stream_read(&self, count: Box) -> Box { + if let Some(count_int) = count.as_any().downcast_ref::() { + let count_val = count_int.value as usize; + let mut buf = vec![0u8; count_val]; + + match self.read(&mut buf) { + Ok(bytes_read) => { + buf.truncate(bytes_read); + Box::new(BufferBox::from_vec(buf)) + }, + Err(e) => Box::new(StringBox::new(&format!("Error reading from stream: {}", e))), + } + } else { + Box::new(StringBox::new("Error: read() requires integer count")) + } + } + + /// 現在位置を取得 + pub fn get_position(&self) -> Box { + Box::new(IntegerBox::new(self.position() as i64)) + } + + /// バッファサイズを取得 + pub fn get_length(&self) -> Box { + Box::new(IntegerBox::new(self.len() as i64)) + } + + /// ストリームをリセット + pub fn stream_reset(&self) -> Box { + self.reset(); + Box::new(StringBox::new("ok")) } } impl NyashBox for NyashStreamBox { fn clone_box(&self) -> Box { - Box::new(NyashStreamBox { - buffer: self.buffer.clone(), - position: self.position, - id: self.id, - }) + Box::new(self.clone()) } fn to_string_box(&self) -> StringBox { - StringBox::new(format!("NyashStreamBox({} bytes, pos: {})", self.buffer.len(), self.position)) + let buffer = self.buffer.lock().unwrap(); + let position = self.position.lock().unwrap(); + StringBox::new(format!("NyashStreamBox({} bytes, pos: {})", buffer.len(), *position)) } fn as_any(&self) -> &dyn Any { @@ -98,27 +155,16 @@ impl NyashBox for NyashStreamBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_stream) = other.as_any().downcast_ref::() { - BoolBox::new(self.buffer == other_stream.buffer && self.position == other_stream.position) + let self_buffer = self.buffer.lock().unwrap(); + let self_position = self.position.lock().unwrap(); + let other_buffer = other_stream.buffer.lock().unwrap(); + let other_position = other_stream.position.lock().unwrap(); + BoolBox::new(*self_buffer == *other_buffer && *self_position == *other_position) } else { BoolBox::new(false) } } } -// Keep the original generic StreamBox for compatibility -pub struct StreamBox { - pub reader: R, - pub writer: W, -} - -impl StreamBox { - pub fn new(reader: R, writer: W) -> Self { - StreamBox { reader, writer } - } - pub fn read(&mut self, buf: &mut [u8]) -> Result { - self.reader.read(buf) - } - pub fn write(&mut self, buf: &[u8]) -> Result<()> { - self.writer.write_all(buf) - } -} +// Export NyashStreamBox as StreamBox for consistency +pub type StreamBox = NyashStreamBox; diff --git a/src/interpreter/expressions.rs b/src/interpreter/expressions.rs index b05d8f6c..93d60cac 100644 --- a/src/interpreter/expressions.rs +++ b/src/interpreter/expressions.rs @@ -8,6 +8,7 @@ use super::*; use crate::ast::UnaryOperator; +use crate::boxes::{BufferBox, JSONBox, HttpClientBox, StreamBox, RegexBox}; // TODO: Fix NullBox import issue later // use crate::NullBox; @@ -326,6 +327,11 @@ impl NyashInterpreter { return self.execute_array_method(array_box, method, arguments); } + // BufferBox method calls + if let Some(buffer_box) = obj_value.as_any().downcast_ref::() { + return self.execute_buffer_method(buffer_box, method, arguments); + } + // FileBox method calls if let Some(file_box) = obj_value.as_any().downcast_ref::() { return self.execute_file_method(file_box, method, arguments); @@ -346,6 +352,26 @@ impl NyashInterpreter { return self.execute_channel_method(channel_box, method, arguments); } + // JSONBox method calls + if let Some(json_box) = obj_value.as_any().downcast_ref::() { + return self.execute_json_method(json_box, method, arguments); + } + + // HttpClientBox method calls + if let Some(http_box) = obj_value.as_any().downcast_ref::() { + return self.execute_http_method(http_box, method, arguments); + } + + // StreamBox method calls + if let Some(stream_box) = obj_value.as_any().downcast_ref::() { + return self.execute_stream_method(stream_box, method, arguments); + } + + // RegexBox method calls + if let Some(regex_box) = obj_value.as_any().downcast_ref::() { + return self.execute_regex_method(regex_box, method, arguments); + } + // MathBox method calls if let Some(math_box) = obj_value.as_any().downcast_ref::() { return self.execute_math_method(math_box, method, arguments); diff --git a/src/interpreter/methods/data_methods.rs b/src/interpreter/methods/data_methods.rs new file mode 100644 index 00000000..749cc994 --- /dev/null +++ b/src/interpreter/methods/data_methods.rs @@ -0,0 +1,204 @@ +/*! + * Data Processing Box Methods Module + * + * Contains method implementations for data processing Box types: + * - BufferBox (execute_buffer_method) - Binary data operations + * - JSONBox (execute_json_method) - JSON parsing and manipulation + * - RegexBox (execute_regex_method) - Regular expression operations + */ + +use super::super::*; +use crate::box_trait::{NyashBox, StringBox, IntegerBox}; +use crate::boxes::{BufferBox, JSONBox, RegexBox}; + +impl NyashInterpreter { + /// BufferBoxのメソッド呼び出しを実行 + pub(in crate::interpreter) fn execute_buffer_method(&mut self, buffer_box: &BufferBox, method: &str, arguments: &[ASTNode]) + -> Result, RuntimeError> { + match method { + "write" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("write() expects 1 argument, got {}", arguments.len()), + }); + } + let data = self.execute_expression(&arguments[0])?; + Ok(buffer_box.write(data)) + } + "readAll" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("readAll() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(buffer_box.readAll()) + } + "read" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("read() expects 1 argument, got {}", arguments.len()), + }); + } + let count = self.execute_expression(&arguments[0])?; + Ok(buffer_box.read(count)) + } + "clear" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("clear() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(buffer_box.clear()) + } + "length" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("length() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(buffer_box.length()) + } + "append" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("append() expects 1 argument, got {}", arguments.len()), + }); + } + let other = self.execute_expression(&arguments[0])?; + Ok(buffer_box.append(other)) + } + "slice" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("slice() expects 2 arguments, got {}", arguments.len()), + }); + } + let start = self.execute_expression(&arguments[0])?; + let end = self.execute_expression(&arguments[1])?; + Ok(buffer_box.slice(start, end)) + } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for BufferBox", method), + }) + } + } + + /// JSONBoxのメソッド呼び出しを実行 + pub(in crate::interpreter) fn execute_json_method(&mut self, json_box: &JSONBox, method: &str, arguments: &[ASTNode]) + -> Result, RuntimeError> { + match method { + "parse" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("parse() expects 1 argument, got {}", arguments.len()), + }); + } + let data = self.execute_expression(&arguments[0])?; + Ok(JSONBox::parse(data)) + } + "stringify" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("stringify() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(json_box.stringify()) + } + "get" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("get() expects 1 argument, got {}", arguments.len()), + }); + } + let key = self.execute_expression(&arguments[0])?; + Ok(json_box.get(key)) + } + "set" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("set() expects 2 arguments, got {}", arguments.len()), + }); + } + let key = self.execute_expression(&arguments[0])?; + let value = self.execute_expression(&arguments[1])?; + Ok(json_box.set(key, value)) + } + "has" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("has() expects 1 argument, got {}", arguments.len()), + }); + } + let key = self.execute_expression(&arguments[0])?; + Ok(json_box.has(key)) + } + "keys" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("keys() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(json_box.keys()) + } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for JSONBox", method), + }) + } + } + + /// RegexBoxのメソッド呼び出しを実行 + pub(in crate::interpreter) fn execute_regex_method(&mut self, regex_box: &RegexBox, method: &str, arguments: &[ASTNode]) + -> Result, RuntimeError> { + match method { + "test" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("test() expects 1 argument, got {}", arguments.len()), + }); + } + let text = self.execute_expression(&arguments[0])?; + Ok(regex_box.test(text)) + } + "find" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("find() expects 1 argument, got {}", arguments.len()), + }); + } + let text = self.execute_expression(&arguments[0])?; + Ok(regex_box.find(text)) + } + "findAll" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("findAll() expects 1 argument, got {}", arguments.len()), + }); + } + let text = self.execute_expression(&arguments[0])?; + Ok(regex_box.find_all(text)) + } + "replace" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("replace() expects 2 arguments, got {}", arguments.len()), + }); + } + let text = self.execute_expression(&arguments[0])?; + let replacement = self.execute_expression(&arguments[1])?; + Ok(regex_box.replace(text, replacement)) + } + "split" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("split() expects 1 argument, got {}", arguments.len()), + }); + } + let text = self.execute_expression(&arguments[0])?; + Ok(regex_box.split(text)) + } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for RegexBox", method), + }) + } + } +} \ No newline at end of file diff --git a/src/interpreter/methods/mod.rs b/src/interpreter/methods/mod.rs index 57e910f8..366b7988 100644 --- a/src/interpreter/methods/mod.rs +++ b/src/interpreter/methods/mod.rs @@ -19,8 +19,12 @@ pub mod basic_methods; // StringBox, IntegerBox, BoolBox, FloatBox pub mod collection_methods; // ArrayBox, MapBox pub mod io_methods; // FileBox, ResultBox +pub mod data_methods; // BufferBox, JSONBox, RegexBox +pub mod network_methods; // HttpClientBox, StreamBox // Re-export methods for easy access pub use basic_methods::*; pub use collection_methods::*; -pub use io_methods::*; \ No newline at end of file +pub use io_methods::*; +pub use data_methods::*; +pub use network_methods::*; \ No newline at end of file diff --git a/src/interpreter/methods/network_methods.rs b/src/interpreter/methods/network_methods.rs new file mode 100644 index 00000000..9095ea21 --- /dev/null +++ b/src/interpreter/methods/network_methods.rs @@ -0,0 +1,124 @@ +/*! + * Network and Communication Box Methods Module + * + * Contains method implementations for network-related Box types: + * - HttpClientBox (execute_http_method) - HTTP client operations + * - StreamBox (execute_stream_method) - Stream processing operations + */ + +use super::super::*; +use crate::box_trait::{NyashBox, StringBox}; +use crate::boxes::{HttpClientBox, StreamBox}; + +impl NyashInterpreter { + /// HttpClientBoxのメソッド呼び出しを実行 + pub(in crate::interpreter) fn execute_http_method(&mut self, http_box: &HttpClientBox, method: &str, arguments: &[ASTNode]) + -> Result, RuntimeError> { + match method { + "get" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("get() expects 1 argument, got {}", arguments.len()), + }); + } + let url = self.execute_expression(&arguments[0])?; + Ok(http_box.http_get(url)) + } + "post" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("post() expects 2 arguments, got {}", arguments.len()), + }); + } + let url = self.execute_expression(&arguments[0])?; + let body = self.execute_expression(&arguments[1])?; + Ok(http_box.post(url, body)) + } + "put" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("put() expects 2 arguments, got {}", arguments.len()), + }); + } + let url = self.execute_expression(&arguments[0])?; + let body = self.execute_expression(&arguments[1])?; + Ok(http_box.put(url, body)) + } + "delete" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("delete() expects 1 argument, got {}", arguments.len()), + }); + } + let url = self.execute_expression(&arguments[0])?; + Ok(http_box.delete(url)) + } + "request" => { + if arguments.len() != 3 { + return Err(RuntimeError::InvalidOperation { + message: format!("request() expects 3 arguments, got {}", arguments.len()), + }); + } + let method_arg = self.execute_expression(&arguments[0])?; + let url = self.execute_expression(&arguments[1])?; + let options = self.execute_expression(&arguments[2])?; + Ok(http_box.request(method_arg, url, options)) + } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for HttpClientBox", method), + }) + } + } + + /// StreamBoxのメソッド呼び出しを実行 + pub(in crate::interpreter) fn execute_stream_method(&mut self, stream_box: &StreamBox, method: &str, arguments: &[ASTNode]) + -> Result, RuntimeError> { + match method { + "write" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("write() expects 1 argument, got {}", arguments.len()), + }); + } + let data = self.execute_expression(&arguments[0])?; + Ok(stream_box.stream_write(data)) + } + "read" => { + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("read() expects 1 argument, got {}", arguments.len()), + }); + } + let count = self.execute_expression(&arguments[0])?; + Ok(stream_box.stream_read(count)) + } + "position" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("position() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(stream_box.get_position()) + } + "length" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("length() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(stream_box.get_length()) + } + "reset" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("reset() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(stream_box.stream_reset()) + } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for StreamBox", method), + }) + } + } +} \ No newline at end of file