chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt

This commit is contained in:
Selfhosting Dev
2025-09-17 07:43:07 +09:00
parent fcf8ce1f3c
commit adbb0201a9
385 changed files with 35622 additions and 15004 deletions

View File

@ -1,25 +1,55 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct AotCompilerBox { base: BoxBase }
pub struct AotCompilerBox {
base: BoxBase,
}
impl AotCompilerBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
impl AotCompilerBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for AotCompilerBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AotCompilerBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AotCompilerBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for AotCompilerBox {
fn to_string_box(&self) -> StringBox { StringBox::new("AotCompilerBox".to_string()) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotCompilerBox>()) }
fn type_name(&self) -> &'static str { "AotCompilerBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox {
StringBox::new("AotCompilerBox".to_string())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<AotCompilerBox>())
}
fn type_name(&self) -> &'static str {
"AotCompilerBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
impl AotCompilerBox {
@ -28,26 +58,32 @@ impl AotCompilerBox {
pub fn compile(&self, file: &str, out: &str) -> Box<dyn NyashBox> {
let mut cmd = match std::env::current_exe() {
Ok(p) => std::process::Command::new(p),
Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e)))
Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e))),
};
// Propagate relevant envs (AOT/JIT observe)
let c = cmd.arg("--backend").arg("vm") // ensures runner path
.arg("--compile-native")
.arg("-o").arg(out)
.arg(file)
.envs(std::env::vars());
let c = cmd
.arg("--backend")
.arg("vm") // ensures runner path
.arg("--compile-native")
.arg("-o")
.arg(out)
.arg(file)
.envs(std::env::vars());
match c.output() {
Ok(o) => {
let mut s = String::new();
s.push_str(&String::from_utf8_lossy(&o.stdout));
s.push_str(&String::from_utf8_lossy(&o.stderr));
if !o.status.success() {
s = format!("AOT FAILED (code={}):\n{}", o.status.code().unwrap_or(-1), s);
s = format!(
"AOT FAILED (code={}):\n{}",
o.status.code().unwrap_or(-1),
s
);
}
Box::new(StringBox::new(s))
}
Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e)))
Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e))),
}
}
}

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
@ -10,44 +10,104 @@ pub struct AotConfigBox {
pub plugin_paths: Option<String>,
}
impl AotConfigBox { pub fn new() -> Self { Self { base: BoxBase::new(), output_file: None, emit_obj_out: None, plugin_paths: None } } }
impl AotConfigBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
output_file: None,
emit_obj_out: None,
plugin_paths: None,
}
}
}
impl BoxCore for AotConfigBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AotConfigBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AotConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for AotConfigBox {
fn to_string_box(&self) -> StringBox { self.summary() }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotConfigBox>()) }
fn type_name(&self) -> &'static str { "AotConfigBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), output_file: self.output_file.clone(), emit_obj_out: self.emit_obj_out.clone(), plugin_paths: self.plugin_paths.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox {
self.summary()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<AotConfigBox>())
}
fn type_name(&self) -> &'static str {
"AotConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
output_file: self.output_file.clone(),
emit_obj_out: self.emit_obj_out.clone(),
plugin_paths: self.plugin_paths.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
impl AotConfigBox {
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> { self.output_file = Some(path.to_string()); Box::new(VoidBox::new()) }
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> { self.emit_obj_out = Some(path.to_string()); Box::new(VoidBox::new()) }
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> { self.plugin_paths = Some(paths.to_string()); Box::new(VoidBox::new()) }
pub fn clear(&mut self) -> Box<dyn NyashBox> { self.output_file = None; self.emit_obj_out = None; self.plugin_paths = None; Box::new(VoidBox::new()) }
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> {
self.output_file = Some(path.to_string());
Box::new(VoidBox::new())
}
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> {
self.emit_obj_out = Some(path.to_string());
Box::new(VoidBox::new())
}
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> {
self.plugin_paths = Some(paths.to_string());
Box::new(VoidBox::new())
}
pub fn clear(&mut self) -> Box<dyn NyashBox> {
self.output_file = None;
self.emit_obj_out = None;
self.plugin_paths = None;
Box::new(VoidBox::new())
}
/// Apply staged config to environment for CLI/runner consumption
pub fn apply(&self) -> Box<dyn NyashBox> {
if let Some(p) = &self.output_file { std::env::set_var("NYASH_AOT_OUT", p); }
if let Some(p) = &self.emit_obj_out { std::env::set_var("NYASH_AOT_OBJECT_OUT", p); }
if let Some(p) = &self.plugin_paths { std::env::set_var("NYASH_PLUGIN_PATHS", p); }
if let Some(p) = &self.output_file {
std::env::set_var("NYASH_AOT_OUT", p);
}
if let Some(p) = &self.emit_obj_out {
std::env::set_var("NYASH_AOT_OBJECT_OUT", p);
}
if let Some(p) = &self.plugin_paths {
std::env::set_var("NYASH_PLUGIN_PATHS", p);
}
Box::new(VoidBox::new())
}
pub fn summary(&self) -> StringBox {
let s = format!(
"output={} obj_out={} plugin_paths={}",
self.output_file.clone().unwrap_or_else(|| "<none>".to_string()),
self.emit_obj_out.clone().unwrap_or_else(|| "<none>".to_string()),
self.plugin_paths.clone().unwrap_or_else(|| "<none>".to_string()),
self.output_file
.clone()
.unwrap_or_else(|| "<none>".to_string()),
self.emit_obj_out
.clone()
.unwrap_or_else(|| "<none>".to_string()),
self.plugin_paths
.clone()
.unwrap_or_else(|| "<none>".to_string()),
);
StringBox::new(s)
}

View File

@ -2,39 +2,39 @@
// Nyashの箱システムによる配列・リスト操作を提供します。
// RwLockパターンで内部可変性を実現Phase 9.75-B Arc<Mutex>削除)
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use std::any::Any;
use std::sync::{Arc, RwLock};
use std::fmt::Display;
use std::sync::{Arc, RwLock};
pub struct ArrayBox {
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加
base: BoxBase,
}
impl ArrayBox {
/// 新しいArrayBoxを作成
pub fn new() -> Self {
ArrayBox {
items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
ArrayBox {
items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
base: BoxBase::new(),
}
}
/// 要素を持つArrayBoxを作成
pub fn new_with_elements(elements: Vec<Box<dyn NyashBox>>) -> Self {
ArrayBox {
items: Arc::new(RwLock::new(elements)), // Arc::new追加
ArrayBox {
items: Arc::new(RwLock::new(elements)), // Arc::new追加
base: BoxBase::new(),
}
}
/// 要素を追加
pub fn push(&self, item: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
self.items.write().unwrap().push(item);
Box::new(StringBox::new("ok"))
}
/// 最後の要素を取り出す
pub fn pop(&self) -> Box<dyn NyashBox> {
match self.items.write().unwrap().pop() {
@ -42,7 +42,7 @@ impl ArrayBox {
None => Box::new(crate::boxes::null_box::NullBox::new()),
}
}
/// 要素数を取得
pub fn length(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.items.read().unwrap().len() as i64))
@ -52,7 +52,7 @@ impl ArrayBox {
pub fn len(&self) -> usize {
self.items.read().unwrap().len()
}
/// インデックスで要素を取得
pub fn get(&self, index: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
@ -61,7 +61,11 @@ impl ArrayBox {
match items.get(idx) {
Some(item) => {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if item
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return item.share_box();
}
item.clone_box()
@ -72,7 +76,7 @@ impl ArrayBox {
Box::new(StringBox::new("Error: get() requires integer index"))
}
}
/// インデックスで要素を設定
pub fn set(&self, index: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
@ -92,7 +96,7 @@ impl ArrayBox {
Box::new(StringBox::new("Error: set() requires integer index"))
}
}
/// 要素を削除
pub fn remove(&self, index: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
@ -107,7 +111,7 @@ impl ArrayBox {
Box::new(StringBox::new("Error: remove() requires integer index"))
}
}
/// 指定された値のインデックスを検索
pub fn indexOf(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let items = self.items.read().unwrap();
@ -118,7 +122,7 @@ impl ArrayBox {
}
Box::new(IntegerBox::new(-1))
}
/// 指定された値が含まれているか確認
pub fn contains(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let items = self.items.read().unwrap();
@ -129,13 +133,13 @@ impl ArrayBox {
}
Box::new(BoolBox::new(false))
}
/// 配列を空にする
pub fn clear(&self) -> Box<dyn NyashBox> {
self.items.write().unwrap().clear();
Box::new(StringBox::new("ok"))
}
/// 文字列結合
pub fn join(&self, delimiter: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(sep_box) = delimiter.as_any().downcast_ref::<StringBox>() {
@ -149,66 +153,78 @@ impl ArrayBox {
Box::new(StringBox::new("Error: join() requires string separator"))
}
}
/// 配列をソート(昇順)
pub fn sort(&self) -> Box<dyn NyashBox> {
let mut items = self.items.write().unwrap();
// Numeric values first, then string values
items.sort_by(|a, b| {
use std::cmp::Ordering;
// Try to compare as numbers first
if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>()
b.as_any().downcast_ref::<IntegerBox>(),
) {
return a_int.value.cmp(&b_int.value);
}
// Try FloatBox comparison
if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(),
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>()
a.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
b.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
) {
return a_float.value.partial_cmp(&b_float.value).unwrap_or(Ordering::Equal);
return a_float
.value
.partial_cmp(&b_float.value)
.unwrap_or(Ordering::Equal);
}
// Mixed numeric types
if let (Some(a_int), Some(b_float)) = (
a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>()
b.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
) {
return (a_int.value as f64).partial_cmp(&b_float.value).unwrap_or(Ordering::Equal);
return (a_int.value as f64)
.partial_cmp(&b_float.value)
.unwrap_or(Ordering::Equal);
}
if let (Some(a_float), Some(b_int)) = (
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(),
b.as_any().downcast_ref::<IntegerBox>()
a.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
b.as_any().downcast_ref::<IntegerBox>(),
) {
return a_float.value.partial_cmp(&(b_int.value as f64)).unwrap_or(Ordering::Equal);
return a_float
.value
.partial_cmp(&(b_int.value as f64))
.unwrap_or(Ordering::Equal);
}
// Fall back to string comparison
let a_str = a.to_string_box().value;
let b_str = b.to_string_box().value;
a_str.cmp(&b_str)
});
Box::new(StringBox::new("ok"))
}
/// 配列を反転
pub fn reverse(&self) -> Box<dyn NyashBox> {
let mut items = self.items.write().unwrap();
items.reverse();
Box::new(StringBox::new("ok"))
}
/// 部分配列を取得
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let items = self.items.read().unwrap();
// Extract start and end indices
let start_idx = if let Some(start_int) = start.as_any().downcast_ref::<IntegerBox>() {
if start_int.value < 0 {
@ -217,9 +233,11 @@ impl ArrayBox {
start_int.value as usize
}
} else {
return Box::new(StringBox::new("Error: slice() start index must be an integer"));
return Box::new(StringBox::new(
"Error: slice() start index must be an integer",
));
};
let end_idx = if let Some(end_int) = end.as_any().downcast_ref::<IntegerBox>() {
if end_int.value < 0 {
items.len()
@ -227,26 +245,32 @@ impl ArrayBox {
(end_int.value as usize).min(items.len())
}
} else {
return Box::new(StringBox::new("Error: slice() end index must be an integer"));
return Box::new(StringBox::new(
"Error: slice() end index must be an integer",
));
};
// Validate indices
if start_idx > items.len() || start_idx > end_idx {
return Box::new(ArrayBox::new());
}
// Create slice
let slice_items: Vec<Box<dyn NyashBox>> = items[start_idx..end_idx]
.iter()
.map(|item| {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if item
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return item.share_box();
}
item.clone_box()
})
.collect();
Box::new(ArrayBox::new_with_elements(slice_items))
}
}
@ -256,18 +280,23 @@ impl Clone for ArrayBox {
fn clone(&self) -> Self {
// ディープコピー(独立インスタンス)
let items_guard = self.items.read().unwrap();
let cloned_items: Vec<Box<dyn NyashBox>> = items_guard.iter()
let cloned_items: Vec<Box<dyn NyashBox>> = items_guard
.iter()
.map(|item| {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if item
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return item.share_box();
}
item.clone_box()
}) // 要素もディープコピー(ハンドルは共有)
}) // 要素もディープコピー(ハンドルは共有)
.collect();
ArrayBox {
items: Arc::new(RwLock::new(cloned_items)), // 新しいArc
items: Arc::new(RwLock::new(cloned_items)), // 新しいArc
base: BoxBase::new(),
}
}
@ -277,23 +306,24 @@ impl BoxCore for ArrayBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let items = self.items.read().unwrap();
let strings: Vec<String> = items.iter()
let strings: Vec<String> = items
.iter()
.map(|item| item.to_string_box().value)
.collect();
write!(f, "[{}]", strings.join(", "))
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -306,49 +336,50 @@ impl Display for ArrayBox {
}
impl NyashBox for ArrayBox {
fn is_identity(&self) -> bool { true }
fn is_identity(&self) -> bool {
true
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 🎯 状態共有の核心実装
fn share_box(&self) -> Box<dyn NyashBox> {
let new_instance = ArrayBox {
items: Arc::clone(&self.items), // Arcクローンで状態共有
base: BoxBase::new(), // 新しいID
items: Arc::clone(&self.items), // Arcクローンで状態共有
base: BoxBase::new(), // 新しいID
};
Box::new(new_instance)
}
fn to_string_box(&self) -> StringBox {
let items = self.items.read().unwrap();
let strings: Vec<String> = items.iter()
let strings: Vec<String> = items
.iter()
.map(|item| item.to_string_box().value)
.collect();
StringBox::new(format!("[{}]", strings.join(", ")))
}
fn type_name(&self) -> &'static str {
"ArrayBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_array) = other.as_any().downcast_ref::<ArrayBox>() {
let self_items = self.items.read().unwrap();
let other_items = other_array.items.read().unwrap();
if self_items.len() != other_items.len() {
return BoolBox::new(false);
}
for (a, b) in self_items.iter().zip(other_items.iter()) {
if !a.equals(b.as_ref()).value {
return BoolBox::new(false);
}
}
BoolBox::new(true)
} else {
BoolBox::new(false)

View File

@ -1,56 +1,56 @@
/*!
* AudioBox - 音声再生・合成Box
*
*
* ## 📝 概要
* Web Audio APIを使用してブラウザでの音声再生、
* 合成、エフェクト処理を統一的に管理するBox。
* ゲーム、音楽アプリ、オーディオビジュアライザー開発に最適。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 🔊 基本再生
* - `loadAudio(url)` - 音声ファイル読み込み
* - `play()` - 再生開始
* - `pause()` - 一時停止
* - `stop()` - 停止
* - `setVolume(volume)` - 音量設定 (0.0-1.0)
*
*
* ### 🎵 音声合成
* - `createTone(frequency, duration)` - 純音生成
* - `createNoise(type, duration)` - ノイズ生成
* - `createBeep()` - システム音
*
*
* ### 📊 解析・ビジュアライザー
* - `getFrequencyData()` - 周波数解析データ取得
* - `getWaveformData()` - 波形データ取得
* - `getVolume()` - 現在の音量レベル
*
*
* ### 🎛️ エフェクト
* - `addReverb(room)` - リバーブエフェクト
* - `addFilter(type, frequency)` - フィルター適用
* - `addDistortion(amount)` - ディストーション
*
*
* ## 💡 使用例
* ```nyash
* local audio, visualizer
* audio = new AudioBox()
*
*
* // 効果音再生
* audio.loadAudio("sounds/explosion.wav")
* audio.setVolume(0.7)
* audio.play()
*
*
* // 音声合成
* audio.createTone(440, 1000) // A4音を1秒
* audio.createBeep() // システム音
*
*
* // オーディオビジュアライザー
* local freqData = audio.getFrequencyData()
* // freqDataを使用してcanvasに描画
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
@ -58,8 +58,8 @@ use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{
AudioContext, AudioBuffer, AudioBufferSourceNode, GainNode,
AnalyserNode, AudioDestinationNode, PeriodicWave, OscillatorNode
AnalyserNode, AudioBuffer, AudioBufferSourceNode, AudioContext, AudioDestinationNode, GainNode,
OscillatorNode, PeriodicWave,
};
/// 音声管理Box
@ -82,7 +82,7 @@ impl AudioBox {
/// 音量を設定 (0.0 - 1.0)
pub fn set_volume(&mut self, volume: f64) {
self.volume = volume.max(0.0).min(1.0);
#[cfg(target_arch = "wasm32")]
{
if let Some(gain) = &self.gain_node {
@ -104,21 +104,24 @@ impl AudioBox {
if let Ok(gain) = context.create_gain() {
// 周波数設定
oscillator.frequency().set_value(frequency as f32);
// 音量設定
gain.gain().set_value(self.volume as f32);
// ノード接続
oscillator.connect_with_audio_node(&gain).unwrap_or_default();
gain.connect_with_audio_node(&context.destination()).unwrap_or_default();
oscillator
.connect_with_audio_node(&gain)
.unwrap_or_default();
gain.connect_with_audio_node(&context.destination())
.unwrap_or_default();
// 再生
let start_time = context.current_time();
let end_time = start_time + duration_ms / 1000.0;
oscillator.start_with_when(start_time).unwrap_or_default();
oscillator.stop_with_when(end_time).unwrap_or_default();
return true;
}
}
@ -138,7 +141,7 @@ impl AudioBox {
if let Some(context) = &self.context {
let sample_rate = context.sample_rate() as usize;
let length = ((duration_ms / 1000.0) * sample_rate as f64) as u32;
if let Ok(buffer) = context.create_buffer(1, length, sample_rate as f32) {
if let Ok(channel_data) = buffer.get_channel_data(0) {
// ホワイトノイズデータ生成
@ -146,15 +149,16 @@ impl AudioBox {
let noise = (js_sys::Math::random() - 0.5) * 2.0; // -1.0 to 1.0
channel_data.set_index(i, noise as f32);
}
if let Ok(source) = context.create_buffer_source() {
source.set_buffer(Some(&buffer));
if let Ok(gain) = context.create_gain() {
gain.gain().set_value(self.volume as f32);
source.connect_with_audio_node(&gain).unwrap_or_default();
gain.connect_with_audio_node(&context.destination()).unwrap_or_default();
gain.connect_with_audio_node(&context.destination())
.unwrap_or_default();
source.start().unwrap_or_default();
return true;
}
@ -171,7 +175,7 @@ impl AudioBox {
if let Some(analyser) = &self.analyser_node {
let buffer_length = analyser.frequency_bin_count() as usize;
let mut data_array = vec![0u8; buffer_length];
// 周波数データを取得
analyser.get_byte_frequency_data(&mut data_array);
return data_array;
@ -185,7 +189,7 @@ impl AudioBox {
if let Some(analyser) = &self.analyser_node {
let buffer_length = analyser.fft_size() as usize;
let mut data_array = vec![0u8; buffer_length];
// 時間領域データを取得
analyser.get_byte_time_domain_data(&mut data_array);
return data_array;
@ -201,7 +205,10 @@ impl AudioBox {
#[cfg(not(target_arch = "wasm32"))]
/// Non-WASM環境用のダミー実装
pub fn create_tone(&self, frequency: f64, duration: f64) -> bool {
println!("AudioBox: Playing tone {}Hz for {}ms (simulated)", frequency, duration);
println!(
"AudioBox: Playing tone {}Hz for {}ms (simulated)",
frequency, duration
);
true
}
@ -220,13 +227,17 @@ impl AudioBox {
#[cfg(not(target_arch = "wasm32"))]
pub fn get_frequency_data(&self) -> Vec<u8> {
// シミュレーション用のダミーデータ
(0..64).map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8).collect()
(0..64)
.map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8)
.collect()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_waveform_data(&self) -> Vec<u8> {
// シミュレーション用のダミーデータ
(0..128).map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8).collect()
(0..128)
.map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8)
.collect()
}
/// オーディオコンテキストの状態を確認
@ -260,19 +271,23 @@ impl BoxCore for AudioBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "AudioBox(volume={:.2}, playing={})", self.volume, self.is_playing)
write!(
f,
"AudioBox(volume={:.2}, playing={})",
self.volume, self.is_playing
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -282,20 +297,23 @@ impl NyashBox for AudioBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("AudioBox(volume={:.2}, playing={})", self.volume, self.is_playing))
StringBox::new(format!(
"AudioBox(volume={:.2}, playing={})",
self.volume, self.is_playing
))
}
fn type_name(&self) -> &'static str {
"AudioBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_audio) = other.as_any().downcast_ref::<AudioBox>() {
BoolBox::new(self.base.id == other_audio.base.id)
@ -309,4 +327,4 @@ impl std::fmt::Display for AudioBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,44 +1,44 @@
/*! ✅ BoolBox - 真偽値Box
*
*
* ## 📝 概要
* true/false値を扱うためのBox。
* JavaScript Boolean型のように直感的な論理演算が可能。
*
*
* ## 🛠️ 利用可能メソッド
* - `toString()` - 文字列変換 ("true" / "false")
* - `not()` - 論理NOT (演算子: not)
* - `and(other)` - 論理AND (演算子: and)
* - `or(other)` - 論理OR (演算子: or)
* - `equals(other)` - 等価比較 (演算子: ==)
*
*
* ## 💡 使用例
* ```nyash
* local flag, result, text
* flag = true
*
*
* result = not flag // false
* result = flag and true // true
* result = flag or false // true
* text = flag.toString() // "true"
*
*
* // 条件分岐での利用
* if (flag) {
* print("Flag is true!")
* }
* ```
*
*
* ## 🔄 型変換
* - 数値への変換: true → 1, false → 0
* - 文字列への変換: "true" / "false"
* - 空文字・null・0は false として扱われる
*
*
* ## ⚡ 論理演算子実装済み
* - `not condition` - NOT演算子
* - `a and b` - AND演算子
* - `a or b` - OR演算子
*/
use crate::box_trait::{NyashBox, BoxCore, BoxBase};
use crate::box_trait::{BoxBase, BoxCore, NyashBox};
use std::any::Any;
use std::fmt::Display;
@ -51,16 +51,16 @@ pub struct BoolBox {
impl BoolBox {
pub fn new(value: bool) -> Self {
Self {
value,
Self {
value,
base: BoxBase::new(),
}
}
pub fn true_box() -> Self {
Self::new(true)
}
pub fn false_box() -> Self {
Self::new(false)
}
@ -70,7 +70,7 @@ impl NyashBox for BoolBox {
fn to_string_box(&self) -> crate::box_trait::StringBox {
crate::box_trait::StringBox::new(if self.value { "true" } else { "false" })
}
fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox {
if let Some(other_bool) = other.as_any().downcast_ref::<BoolBox>() {
crate::box_trait::BoolBox::new(self.value == other_bool.value)
@ -78,16 +78,15 @@ impl NyashBox for BoolBox {
crate::box_trait::BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"BoolBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -98,19 +97,19 @@ impl BoxCore for BoolBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
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
}
@ -120,4 +119,4 @@ impl Display for BoolBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,9 +1,9 @@
/*! 📊 BufferBox - バイナリデータ処理Box
*
*
* ## 📝 概要
* バイナリデータの読み書きを扱うBox。
* ファイル操作、ネットワーク通信、画像処理などで使用。
*
*
* ## 🛠️ 利用可能メソッド
* - `write(data)` - バイトデータ書き込み
* - `read(count)` - 指定バイト数読み取り
@ -12,37 +12,37 @@
* - `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, IntegerBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::array::ArrayBox;
use std::any::Any;
use std::sync::{Arc, RwLock}; // Arc追加
use std::fmt::Display;
use std::sync::{Arc, RwLock}; // Arc追加
pub struct BufferBox {
data: Arc<RwLock<Vec<u8>>>, // Arc追加
data: Arc<RwLock<Vec<u8>>>, // Arc追加
base: BoxBase,
}
impl BufferBox {
pub fn new() -> Self {
BufferBox {
data: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
BufferBox {
data: Arc::new(RwLock::new(Vec::new())), // Arc::new追加
base: BoxBase::new(),
}
}
@ -56,18 +56,21 @@ impl BufferBox {
pub fn len(&self) -> usize {
self.data.read().unwrap().len()
}
pub fn from_vec(data: Vec<u8>) -> Self {
BufferBox {
data: Arc::new(RwLock::new(data)), // Arc::new追加
BufferBox {
data: Arc::new(RwLock::new(data)), // Arc::new追加
base: BoxBase::new(),
}
}
/// データを書き込む
pub fn write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// ArrayBoxから変換 - use crate::boxes::array::ArrayBox directly
if let Some(array_box) = data.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
if let Some(array_box) = data
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
let mut buffer = self.data.write().unwrap();
let items = array_box.items.read().unwrap();
for item in items.iter() {
@ -80,10 +83,13 @@ impl BufferBox {
Box::new(IntegerBox::new(buffer.len() as i64))
} else {
let type_name = data.type_name();
Box::new(StringBox::new(&format!("Error: write() requires ArrayBox of integers, got {}", type_name)))
Box::new(StringBox::new(&format!(
"Error: write() requires ArrayBox of integers, got {}",
type_name
)))
}
}
/// すべてのデータを読み取る
pub fn readAll(&self) -> Box<dyn NyashBox> {
let buffer = self.data.read().unwrap();
@ -93,14 +99,14 @@ impl BufferBox {
}
Box::new(array)
}
/// 指定バイト数読み取る
pub fn read(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
let mut buffer = self.data.write().unwrap();
let count = count_int.value.min(buffer.len() as i64) as usize;
let array = ArrayBox::new();
// 先頭からcount個取り出す
let bytes: Vec<u8> = buffer.drain(0..count).collect();
for byte in bytes {
@ -111,18 +117,18 @@ impl BufferBox {
Box::new(StringBox::new("Error: read() requires integer count"))
}
}
/// バッファをクリア
pub fn clear(&self) -> Box<dyn NyashBox> {
self.data.write().unwrap().clear();
Box::new(StringBox::new("ok"))
}
/// データサイズを取得
pub fn length(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64))
}
/// 他のBufferBoxを追加
pub fn append(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
@ -134,17 +140,17 @@ impl BufferBox {
Box::new(StringBox::new("Error: append() requires BufferBox"))
}
}
/// 部分データ取得
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(start_int), Some(end_int)) = (
start.as_any().downcast_ref::<IntegerBox>(),
end.as_any().downcast_ref::<IntegerBox>()
end.as_any().downcast_ref::<IntegerBox>(),
) {
let data = self.data.read().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))
@ -155,7 +161,7 @@ impl BufferBox {
Box::new(StringBox::new("Error: slice() requires integer indices"))
}
}
/// ⭐ Phase 10: Zero-copy detection - check if buffer is shared with another buffer
pub fn is_shared_with(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
@ -167,17 +173,17 @@ impl BufferBox {
Box::new(BoolBox::new(false))
}
}
/// ⭐ Phase 10: Share reference - create a zero-copy shared reference to this buffer's data
pub fn share_reference(&self, _data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// Create a new BufferBox that shares the same Arc as this buffer
let shared_buffer = BufferBox {
data: Arc::clone(&self.data), // Share THIS buffer's data
base: BoxBase::new(), // New ID but shared data
base: BoxBase::new(), // New ID but shared data
};
Box::new(shared_buffer)
}
/// ⭐ Phase 10: Memory footprint - get current memory usage in bytes
pub fn memory_footprint(&self) -> Box<dyn NyashBox> {
let data = self.data.read().unwrap();
@ -192,7 +198,7 @@ impl Clone for BufferBox {
// ディープコピー(独立インスタンス)
let data_guard = self.data.read().unwrap();
BufferBox {
data: Arc::new(RwLock::new(data_guard.clone())), // 新しいArc
data: Arc::new(RwLock::new(data_guard.clone())), // 新しいArc
base: BoxBase::new(),
}
}
@ -202,20 +208,20 @@ impl BoxCore for BufferBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let data = self.data.read().unwrap();
write!(f, "BufferBox({} bytes)", data.len())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -231,12 +237,12 @@ impl NyashBox for BufferBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 🎯 状态共享的核心实现
/// 🎯 状态共享的核心实现
fn share_box(&self) -> Box<dyn NyashBox> {
let new_instance = BufferBox {
data: Arc::clone(&self.data), // Arcクローンで状態共有
base: BoxBase::new(), // 新しいID
data: Arc::clone(&self.data), // Arcクローンで状態共有
base: BoxBase::new(), // 新しいID
};
Box::new(new_instance)
}
@ -246,12 +252,10 @@ impl NyashBox for BufferBox {
StringBox::new(format!("BufferBox({} bytes)", data.len()))
}
fn type_name(&self) -> &'static str {
"BufferBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
// RwLock内容を比較
@ -264,7 +268,7 @@ impl NyashBox for BufferBox {
}
}
// Debug implementation for BufferBox
// Debug implementation for BufferBox
impl std::fmt::Debug for BufferBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let data = self.data.read().unwrap();

View File

@ -1,45 +1,45 @@
/*!
* CanvasEventBox - Canvas入力イベント管理Box
*
*
* ## 📝 概要
* HTML5 Canvasでのマウス・タッチ・キーボードイベントを
* Nyashから利用可能にするBox。ゲーム開発、インタラクティブ
* アプリケーション開発に必須の入力機能を提供。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 🖱️ マウスイベント
* - `onMouseDown(callback)` - マウスボタン押下
* - `onMouseUp(callback)` - マウスボタン離上
* - `onMouseMove(callback)` - マウス移動
* - `onMouseClick(callback)` - マウスクリック
* - `onMouseWheel(callback)` - マウスホイール
*
*
* ### 👆 タッチイベント
* - `onTouchStart(callback)` - タッチ開始
* - `onTouchMove(callback)` - タッチ移動
* - `onTouchEnd(callback)` - タッチ終了
*
*
* ### ⌨️ キーボードイベント
* - `onKeyDown(callback)` - キー押下
* - `onKeyUp(callback)` - キー離上
*
*
* ### 📊 座標取得
* - `getMouseX()` - 現在のマウスX座標
* - `getMouseY()` - 現在のマウスY座標
* - `isPressed(button)` - ボタン押下状態確認
*
*
* ## 💡 使用例
* ```nyash
* local events, canvas
* events = new CanvasEventBox("game-canvas")
* canvas = new WebCanvasBox("game-canvas", 800, 600)
*
*
* // マウスクリックで円を描画
* events.onMouseClick(function(x, y) {
* canvas.fillCircle(x, y, 10, "red")
* })
*
*
* // ドラッグで線を描画
* local isDrawing = false
* events.onMouseDown(function(x, y) {
@ -47,31 +47,28 @@
* canvas.beginPath()
* canvas.moveTo(x, y)
* })
*
*
* events.onMouseMove(function(x, y) {
* if (isDrawing) {
* canvas.lineTo(x, y)
* canvas.stroke("black", 2)
* }
* })
*
*
* events.onMouseUp(function() {
* isDrawing = false
* })
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{
HtmlCanvasElement, MouseEvent, TouchEvent, KeyboardEvent,
EventTarget, Element
};
use web_sys::{Element, EventTarget, HtmlCanvasElement, KeyboardEvent, MouseEvent, TouchEvent};
/// Canvas入力イベント管理Box
#[derive(Debug, Clone)]
@ -140,7 +137,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
canvas
.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
.unwrap_or_default();
closure.forget(); // メモリリークを防ぐため通常は適切な管理が必要
}
@ -154,7 +152,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
canvas
.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
.unwrap_or_default();
closure.forget();
}
@ -168,7 +167,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
canvas
.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
.unwrap_or_default();
closure.forget();
}
@ -182,7 +182,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
canvas
.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
.unwrap_or_default();
closure.forget();
}
@ -196,7 +197,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(TouchEvent)>);
canvas.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
canvas
.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
.unwrap_or_default();
closure.forget();
}
@ -210,7 +212,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(KeyboardEvent)>);
window.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
window
.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
.unwrap_or_default();
closure.forget();
}
@ -252,19 +255,19 @@ impl BoxCore for CanvasEventBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "CanvasEventBox({})", self.canvas_id)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -274,7 +277,7 @@ impl NyashBox for CanvasEventBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -287,7 +290,7 @@ impl NyashBox for CanvasEventBox {
fn type_name(&self) -> &'static str {
"CanvasEventBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_events) = other.as_any().downcast_ref::<CanvasEventBox>() {
BoolBox::new(self.base.id == other_events.base.id)
@ -301,4 +304,4 @@ impl std::fmt::Display for CanvasEventBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,29 +1,29 @@
/*!
* CanvasLoopBox - アニメーションループ管理Box
*
*
* ## 📝 概要
* ゲームや動的コンテンツのためのアニメーションループを
* 管理するBox。requestAnimationFrame、フレームレート制御、
* ループ状態管理を統一的に提供。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 🎮 ループ制御
* - `start(callback)` - アニメーションループ開始
* - `stop()` - アニメーションループ停止
* - `pause()` - アニメーションループ一時停止
* - `resume()` - アニメーションループ再開
*
*
* ### 📊 フレーム情報
* - `getFPS()` - 現在のFPS取得
* - `getFrameCount()` - 総フレーム数取得
* - `getDeltaTime()` - 前フレームからの経過時間
* - `setTargetFPS(fps)` - 目標FPS設定
*
*
* ### ⏱️ 時間管理
* - `getElapsedTime()` - ループ開始からの経過時間
* - `reset()` - タイマーリセット
*
*
* ## 💡 使用例
* ```nyash
* local loop, canvas, ball_x, ball_y
@ -31,7 +31,7 @@
* canvas = new WebCanvasBox("game-canvas", 800, 600)
* ball_x = 400
* ball_y = 300
*
*
* // ゲームループ
* loop.start(function(deltaTime) {
* // 更新処理
@ -47,7 +47,7 @@
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use crate::boxes::TimerBox;
use std::any::Any;
@ -75,7 +75,7 @@ impl CanvasLoopBox {
pub fn new() -> Self {
let timer = TimerBox::new();
let current_time = timer.now();
Self {
base: BoxBase::new(),
is_running: false,
@ -108,12 +108,16 @@ impl CanvasLoopBox {
// アニメーションフレーム用のクロージャを作成
let closure = Closure::wrap(Box::new(move |time: f64| {
// ここでフレーム処理を実行
callback.call1(&JsValue::NULL, &JsValue::from_f64(time)).unwrap_or_default();
callback
.call1(&JsValue::NULL, &JsValue::from_f64(time))
.unwrap_or_default();
}) as Box<dyn FnMut(f64)>);
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref());
let id = self
.timer
.request_animation_frame(closure.as_ref().unchecked_ref());
self.animation_id = Some(id);
closure.forget(); // クロージャの所有権を手放す
}
@ -147,9 +151,9 @@ impl CanvasLoopBox {
if !self.is_running || self.is_paused {
return;
}
self.is_paused = true;
#[cfg(target_arch = "wasm32")]
{
if let Some(id) = self.animation_id {
@ -170,12 +174,16 @@ impl CanvasLoopBox {
self.last_frame_time = self.timer.now(); // 時間をリセット
let closure = Closure::wrap(Box::new(move |time: f64| {
callback.call1(&JsValue::NULL, &JsValue::from_f64(time)).unwrap_or_default();
callback
.call1(&JsValue::NULL, &JsValue::from_f64(time))
.unwrap_or_default();
}) as Box<dyn FnMut(f64)>);
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref());
let id = self
.timer
.request_animation_frame(closure.as_ref().unchecked_ref());
self.animation_id = Some(id);
closure.forget();
}
@ -262,19 +270,23 @@ impl BoxCore for CanvasLoopBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "CanvasLoopBox(running={}, fps={:.1})", self.is_running, self.fps)
write!(
f,
"CanvasLoopBox(running={}, fps={:.1})",
self.is_running, self.fps
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -284,20 +296,23 @@ impl NyashBox for CanvasLoopBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("CanvasLoopBox(running={}, fps={:.1})", self.is_running, self.fps))
StringBox::new(format!(
"CanvasLoopBox(running={}, fps={:.1})",
self.is_running, self.fps
))
}
fn type_name(&self) -> &'static str {
"CanvasLoopBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_loop) = other.as_any().downcast_ref::<CanvasLoopBox>() {
BoolBox::new(self.base.id == other_loop.base.id)
@ -311,4 +326,4 @@ impl std::fmt::Display for CanvasLoopBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,35 +1,35 @@
/*! 📟 ConsoleBox - コンソール出力Box
*
*
* ## 📝 概要
* Webブラウザのコンソール機能を統合したBox。
* WASM環境ではブラウザコンソール、ネイティブ環境では標準出力。
*
*
* ## 🛠️ 利用可能メソッド
* - `log(message)` - 通常のメッセージ出力
* - `warn(message)` - 警告メッセージ出力
* - `error(message)` - エラーメッセージ出力
* - `clear()` - コンソール画面クリア
*
*
* ## 💡 使用例
* ```nyash
* local console
* console = new ConsoleBox()
*
*
* console.log("Hello, Nyash!") // 通常ログ
* console.warn("This is a warning") // 警告
* console.error("Something went wrong") // エラー
* console.clear() // クリア
*
*
* // デバッグ用途
* local value
* value = 42
* console.log("Debug: value = " + value.toString())
* ```
*
*
* ## 🌐 環境別動作
* - **WASM環境**: ブラウザの開発者ツールコンソールに出力
* - **ネイティブ環境**: ターミナル標準出力にプレフィックス付きで出力
*
*
* ## 🔍 デバッグ活用
* ```nyash
* // エラーハンドリング
@ -37,7 +37,7 @@
* console.error("Critical error occurred!")
* return null
* }
*
*
* // 実行トレース
* console.log("Function start")
* // 処理...
@ -45,7 +45,7 @@
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::fmt::Display;
@ -59,24 +59,26 @@ pub struct ConsoleBox {
#[cfg(target_arch = "wasm32")]
impl ConsoleBox {
pub fn new() -> Self {
Self { base: BoxBase::new() }
Self {
base: BoxBase::new(),
}
}
/// Log messages to browser console
pub fn log(&self, message: &str) {
web_sys::console::log_1(&message.into());
}
/// Log warning to browser console
pub fn warn(&self, message: &str) {
web_sys::console::warn_1(&message.into());
}
/// Log error to browser console
pub fn error(&self, message: &str) {
web_sys::console::error_1(&message.into());
}
/// Clear browser console
pub fn clear(&self) {
web_sys::console::clear();
@ -88,19 +90,19 @@ impl BoxCore for ConsoleBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[ConsoleBox - Browser Console Interface]")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -111,27 +113,26 @@ impl NyashBox for ConsoleBox {
fn to_string_box(&self) -> StringBox {
StringBox::new("[ConsoleBox - Browser Console Interface]")
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<ConsoleBox>())
}
fn type_name(&self) -> &'static str {
"ConsoleBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
// Non-WASM版 - モックアップ実装
// Non-WASM版 - モックアップ実装
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Clone)]
pub struct ConsoleBox {
@ -141,22 +142,24 @@ pub struct ConsoleBox {
#[cfg(not(target_arch = "wasm32"))]
impl ConsoleBox {
pub fn new() -> Self {
Self { base: BoxBase::new() }
Self {
base: BoxBase::new(),
}
}
/// Mock log method for non-WASM environments
pub fn log(&self, message: &str) {
println!("[Console LOG] {}", message);
}
pub fn warn(&self, message: &str) {
println!("[Console WARN] {}", message);
}
pub fn error(&self, message: &str) {
println!("[Console ERROR] {}", message);
}
pub fn clear(&self) {
println!("[Console CLEAR]");
}
@ -167,19 +170,19 @@ impl BoxCore for ConsoleBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[ConsoleBox - Mock Implementation]")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -190,27 +193,25 @@ impl NyashBox for ConsoleBox {
fn to_string_box(&self) -> StringBox {
StringBox::new("[ConsoleBox - Mock Implementation]")
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<ConsoleBox>())
}
fn type_name(&self) -> &'static str {
"ConsoleBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
// Display implementations for both WASM and non-WASM versions
#[cfg(target_arch = "wasm32")]
impl Display for ConsoleBox {

View File

@ -1,57 +1,57 @@
/*! 🔍 DebugBox - デバッグ支援Box
*
*
* ## 📝 概要
* プロフェッショナル開発向けデバッグ機能を提供するBox。
* メモリ使用量監視、実行トレース、ブレークポイントなど
* 高度なデバッグ機能を完備。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 🎯 基本デバッグ
* - `startTracking()` - デバッグ追跡開始
* - `stopTracking()` - デバッグ追跡停止
* - `trackBox(box, name)` - 特定Boxを追跡対象に追加
* - `watch(box, name)` - リアルタイム監視
* - `clear()` - 全デバッグ情報クリア
*
*
* ### 📊 レポート・分析
* - `dumpAll()` - 全追跡データダンプ
* - `memoryReport()` - メモリ使用量レポート
* - `showCallStack()` - 関数呼び出しスタック表示
* - `saveToFile(filename)` - デバッグ情報をファイル保存
*
*
* ### 🎮 高度機能
* - `setBreakpoint(function)` - ブレークポイント設定
* - `traceCall(function, args)` - 関数呼び出しトレース
* - `isTracking()` - 追跡状態確認
* - `getTrackedCount()` - 追跡中Box数取得
*
*
* ## 💡 使用例
* ```nyash
* local debug, user, product
* debug = new DebugBox()
*
*
* // デバッグ開始
* debug.startTracking()
*
*
* // オブジェクトを追跡
* user = new User("Alice", 25)
* debug.trackBox(user, "user_alice")
*
*
* product = new Product("Book", 1500)
* debug.trackBox(product, "book_product")
*
*
* // リアルタイム監視
* debug.watch(user.age, "user_age")
*
*
* // レポート生成
* print(debug.memoryReport())
* print(debug.dumpAll())
*
*
* // ファイルに保存
* debug.saveToFile("debug_report.txt")
* ```
*
*
* ## 🎮 実用例 - パフォーマンス診断
* ```nyash
* static box PerformanceTest {
@ -76,13 +76,13 @@
* }
* }
* ```
*
*
* ## ⚡ ベストプラクティス
* ```nyash
* // エラーハンドリング付きデバッグ
* local debug
* debug = new DebugBox()
*
*
* try {
* debug.startTracking()
* // 問題のあるコード
@ -92,20 +92,20 @@
* print("Debug info saved to error_dump.txt")
* }
* ```
*
*
* ## ⚠️ 注意
* - 本格運用時はtrackingを無効にしてパフォーマンス向上
* - 大量データ追跡時はメモリ消費に注意
* - call stackは直近100件まで自動保持
*/
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use crate::instance_v2::InstanceBox;
use crate::interpreter::RuntimeError;
use chrono::Local;
use std::any::Any;
use std::collections::HashMap;
use std::sync::RwLock;
use chrono::Local;
use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, BoolBox, VoidBox};
use crate::interpreter::RuntimeError;
use crate::instance_v2::InstanceBox;
use std::any::Any;
#[derive(Debug)]
pub struct DebugBox {
@ -156,23 +156,27 @@ impl DebugBox {
Ok(Box::new(VoidBox::new()))
}
pub fn track_box(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
pub fn track_box(
&self,
box_value: &dyn NyashBox,
name: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let enabled = self.tracking_enabled.read().unwrap();
if !*enabled {
return Ok(Box::new(VoidBox::new()));
}
let mut tracked = self.tracked_boxes.write().unwrap();
let info = TrackedBoxInfo {
box_type: box_value.type_name().to_string(),
created_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
fields: self.get_box_fields(box_value),
value_repr: box_value.to_string_box().value,
};
tracked.insert(name.to_string(), info);
Ok(Box::new(VoidBox::new()))
}
@ -192,9 +196,12 @@ impl DebugBox {
pub fn dump_all(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.read().unwrap();
let mut output = String::from("=== Box State Dump ===\n");
output.push_str(&format!("Time: {}\n", Local::now().format("%Y-%m-%d %H:%M:%S")));
output.push_str(&format!(
"Time: {}\n",
Local::now().format("%Y-%m-%d %H:%M:%S")
));
output.push_str(&format!("Total tracked boxes: {}\n\n", tracked.len()));
for (name, info) in tracked.iter() {
output.push_str(&format!("Box: {}\n", name));
output.push_str(&format!(" Type: {}\n", info.box_type));
@ -203,28 +210,31 @@ impl DebugBox {
output.push_str(&format!(" Value: {}\n", info.value_repr));
output.push_str("\n");
}
Ok(Box::new(StringBox::new(output)))
}
pub fn save_to_file(&self, filename: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let dump_result = self.dump_all()?;
let content = dump_result.to_string_box().value;
// Write to file using std::fs
std::fs::write(filename, content)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to write debug file: {}", e),
})?;
std::fs::write(filename, content).map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to write debug file: {}", e),
})?;
println!("[DEBUG] Saved debug info to {}", filename);
Ok(Box::new(VoidBox::new()))
}
pub fn watch(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
pub fn watch(
&self,
box_value: &dyn NyashBox,
name: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value_str = box_value.to_string_box().value;
let type_name = box_value.type_name();
println!("[DEBUG] Watching {} ({}): {}", name, type_name, value_str);
Ok(Box::new(VoidBox::new()))
}
@ -233,18 +243,18 @@ impl DebugBox {
let tracked = self.tracked_boxes.read().unwrap();
let mut report = String::from("=== Memory Report ===\n");
report.push_str(&format!("Tracked boxes: {}\n", tracked.len()));
// Count by type
let mut type_counts: HashMap<String, usize> = HashMap::new();
for info in tracked.values() {
*type_counts.entry(info.box_type.clone()).or_insert(0) += 1;
}
report.push_str("\nBoxes by type:\n");
for (box_type, count) in type_counts.iter() {
report.push_str(&format!(" {}: {}\n", box_type, count));
}
Ok(Box::new(StringBox::new(report)))
}
@ -256,45 +266,50 @@ impl DebugBox {
Ok(Box::new(VoidBox::new()))
}
pub fn trace_call(&self, function_name: &str, args: Vec<String>) -> Result<Box<dyn NyashBox>, RuntimeError> {
pub fn trace_call(
&self,
function_name: &str,
args: Vec<String>,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut stack = self.call_stack.write().unwrap();
stack.push(CallInfo {
function_name: function_name.to_string(),
args,
timestamp: Local::now().format("%H:%M:%S.%3f").to_string(),
});
// Keep only last 100 calls to prevent memory issues
if stack.len() > 100 {
stack.remove(0);
}
Ok(Box::new(VoidBox::new()))
}
pub fn show_call_stack(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let stack = self.call_stack.read().unwrap();
let mut output = String::from("=== Call Stack ===\n");
for (i, call) in stack.iter().enumerate() {
output.push_str(&format!("{}: [{}] {}({})\n",
i,
call.timestamp,
output.push_str(&format!(
"{}: [{}] {}({})\n",
i,
call.timestamp,
call.function_name,
call.args.join(", ")
));
}
Ok(Box::new(StringBox::new(output)))
}
pub fn clear(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut tracked = self.tracked_boxes.write().unwrap();
tracked.clear();
let mut stack = self.call_stack.write().unwrap();
stack.clear();
println!("[DEBUG] Cleared all debug information");
Ok(Box::new(VoidBox::new()))
}
@ -306,13 +321,18 @@ impl DebugBox {
pub fn get_tracked_count(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.read().unwrap();
Ok(Box::new(crate::box_trait::IntegerBox::new(tracked.len() as i64)))
Ok(Box::new(crate::box_trait::IntegerBox::new(
tracked.len() as i64
)))
}
// --- Phase 1: JIT/Plugin shim tracing ---
pub fn trace_plugin_calls(&self, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
crate::jit::shim_trace::set_enabled(on);
println!("[DEBUG] JIT shim trace: {}", if on {"ENABLED"} else {"DISABLED"});
println!(
"[DEBUG] JIT shim trace: {}",
if on { "ENABLED" } else { "DISABLED" }
);
Ok(Box::new(VoidBox::new()))
}
@ -329,7 +349,7 @@ impl Clone for DebugBox {
let breakpoints = self.breakpoints.read().unwrap();
let call_stack = self.call_stack.read().unwrap();
let tracking_enabled = self.tracking_enabled.read().unwrap();
DebugBox {
base: BoxBase::new(), // New unique ID for cloned instance
tracking_enabled: RwLock::new(*tracking_enabled),
@ -345,20 +365,20 @@ impl BoxCore for DebugBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let tracked = self.tracked_boxes.read().unwrap();
write!(f, "DebugBox[{} tracked]", tracked.len())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -377,7 +397,7 @@ impl NyashBox for DebugBox {
let tracked = self.tracked_boxes.read().unwrap();
StringBox::new(format!("DebugBox[{} tracked]", tracked.len()))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_debug) = other.as_any().downcast_ref::<DebugBox>() {
BoolBox::new(self.base.id == other_debug.base.id)
@ -385,19 +405,17 @@ impl NyashBox for DebugBox {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"DebugBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
@ -20,13 +20,19 @@ impl DebugConfigBox {
Self {
base: BoxBase::new(),
jit_events: std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1"),
jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1"),
jit_events_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1"),
jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref()
== Some("1"),
jit_events_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref()
== Some("1"),
jit_stats: std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1"),
jit_stats_json: std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1"),
jit_dump: std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1"),
jit_dot_path: std::env::var("NYASH_JIT_DOT").ok().filter(|s| !s.is_empty()),
jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH").ok().filter(|s| !s.is_empty()),
jit_dot_path: std::env::var("NYASH_JIT_DOT")
.ok()
.filter(|s| !s.is_empty()),
jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH")
.ok()
.filter(|s| !s.is_empty()),
}
}
@ -38,7 +44,7 @@ impl DebugConfigBox {
"jit_stats" => self.jit_stats = on,
"jit_stats_json" => self.jit_stats_json = on,
"jit_dump" => self.jit_dump = on,
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
}
Box::new(VoidBox::new())
}
@ -47,7 +53,7 @@ impl DebugConfigBox {
match name {
"jit_dot" | "jit_dot_path" => self.jit_dot_path = Some(value.to_string()),
"jit_events_path" => self.jit_events_path = Some(value.to_string()),
_ => return Box::new(StringBox::new(format!("Unknown path: {}", name)))
_ => return Box::new(StringBox::new(format!("Unknown path: {}", name))),
}
Box::new(VoidBox::new())
}
@ -75,17 +81,29 @@ impl DebugConfigBox {
}
pub fn apply(&self) -> Box<dyn NyashBox> {
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } };
let setb = |k: &str, v: bool| {
if v {
std::env::set_var(k, "1");
} else {
std::env::remove_var(k);
}
};
setb("NYASH_JIT_EVENTS", self.jit_events);
setb("NYASH_JIT_EVENTS_COMPILE", self.jit_events_compile);
setb("NYASH_JIT_EVENTS_RUNTIME", self.jit_events_runtime);
setb("NYASH_JIT_STATS", self.jit_stats);
setb("NYASH_JIT_STATS_JSON", self.jit_stats_json);
setb("NYASH_JIT_DUMP", self.jit_dump);
if let Some(p) = &self.jit_dot_path { std::env::set_var("NYASH_JIT_DOT", p); }
else { std::env::remove_var("NYASH_JIT_DOT"); }
if let Some(p) = &self.jit_events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); }
else { std::env::remove_var("NYASH_JIT_EVENTS_PATH"); }
if let Some(p) = &self.jit_dot_path {
std::env::set_var("NYASH_JIT_DOT", p);
} else {
std::env::remove_var("NYASH_JIT_DOT");
}
if let Some(p) = &self.jit_events_path {
std::env::set_var("NYASH_JIT_EVENTS_PATH", p);
} else {
std::env::remove_var("NYASH_JIT_EVENTS_PATH");
}
// If any events are enabled and threshold is not set, default to 1 so lowering runs early
if (self.jit_events || self.jit_events_compile || self.jit_events_runtime)
&& std::env::var("NYASH_JIT_THRESHOLD").is_err()
@ -108,17 +126,40 @@ impl DebugConfigBox {
}
impl BoxCore for DebugConfigBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "DebugConfigBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DebugConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for DebugConfigBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<DebugConfigBox>()) }
fn type_name(&self) -> &'static str { "DebugConfigBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), ..self.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new("DebugConfigBox".to_string()) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<DebugConfigBox>())
}
fn type_name(&self) -> &'static str {
"DebugConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
..self.clone()
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new("DebugConfigBox".to_string())
}
}

View File

@ -3,11 +3,11 @@
/*! 🖼️ EguiBox - デスクトップGUIアプリBox
* Everything is Box哲学によるGUIフレームワーク統合
* 「なんでもBoxにできる」化け物言語の第一歩
*
*
* ## 📝 概要
* Rustの人気GUI框架eframeを使ったネイティブデスクトップアプリ作成。
* Nyashコードから直接GUI操作が可能
*
*
* ## 🛠️ 利用可能メソッド
* - `setTitle(title)` - ウィンドウタイトル設定
* - `setSize(width, height)` - ウィンドウサイズ設定
@ -15,7 +15,7 @@
* - `addText(text)` - テキスト表示追加
* - `addButton(label)` - ボタン追加
* - `close()` - ウィンドウ閉じる
*
*
* ## 💡 使用例
* ```nyash
* // 基本的なGUIアプリ
@ -27,20 +27,20 @@
* app.addButton("Click Me")
* app.run() // GUIアプリ開始
* ```
*
*
* ## ⚠️ 注意
* - デスクトップ環境でのみ利用可能WASM環境では無効
* - `run()`はブロッキング動作(アプリ終了まで制御を返さない)
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use crate::interpreter::RuntimeError;
use eframe::{self, egui, epaint::Vec2};
use std::any::Any;
use std::sync::{Arc, RwLock};
use eframe::{self, egui, epaint::Vec2};
/// EguiBox - GUI アプリケーションを包むBox
///
///
/// # 使用例
/// ```nyash
/// app = new EguiBox()
@ -89,16 +89,16 @@ impl EguiBox {
update_fn: None,
}
}
/// アプリケーション状態を設定
pub fn set_app_state<T: Any + Send + Sync + 'static>(&mut self, state: T) {
*self.app_state.write().unwrap() = Box::new(state);
}
/// 更新関数を設定
pub fn set_update_fn<F>(&mut self, f: F)
where
F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static
pub fn set_update_fn<F>(&mut self, f: F)
where
F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static,
{
self.update_fn = Some(Arc::new(f));
}
@ -122,19 +122,23 @@ impl BoxCore for EguiBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y)
write!(
f,
"EguiBox('{}', {}x{})",
self.title, self.size.x, self.size.y
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -148,21 +152,21 @@ impl std::fmt::Display for EguiBox {
impl NyashBox for EguiBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(
format!("EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y)
)
StringBox::new(format!(
"EguiBox('{}', {}x{})",
self.title, self.size.x, self.size.y
))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_egui) = other.as_any().downcast_ref::<EguiBox>() {
BoolBox::new(self.title == other_egui.title && self.size == other_egui.size)
@ -170,11 +174,10 @@ impl NyashBox for EguiBox {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"EguiBox"
}
}
// EguiBoxのメソッド実装実際にはインタープリターから呼ばれない
@ -187,28 +190,24 @@ impl EguiBox {
// we would need a more sophisticated state sharing mechanism
let app_state = Arc::new(RwLock::new(Box::new(()) as Box<dyn Any + Send + Sync>));
drop(state_snapshot);
let update_fn = Arc::clone(update_fn);
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size(self.size)
.with_title(&self.title),
..Default::default()
};
let app = NyashApp {
app_state,
update_fn,
};
// 注意: これはブロッキング呼び出し
let _ = eframe::run_native(
&self.title,
options,
Box::new(|_cc| Ok(Box::new(app))),
);
let _ = eframe::run_native(&self.title, options, Box::new(|_cc| Ok(Box::new(app))));
Ok(())
} else {
Err(RuntimeError::InvalidOperation {
@ -221,18 +220,18 @@ impl EguiBox {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_egui_box_creation() {
let gui = EguiBox::new();
assert_eq!(gui.title, "Nyash GUI Application");
assert_eq!(gui.size, Vec2::new(800.0, 600.0));
}
#[test]
fn test_egui_box_to_string() {
let gui = EguiBox::new();
let s = gui.to_string_box();
assert_eq!(s.value, "EguiBox('Nyash GUI Application', 800x600)");
}
}
}

View File

@ -2,10 +2,10 @@
// Nyashの箱システムによるファイル入出力を提供します。
// 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Result};
use std::io::{Read, Result, Write};
use std::sync::RwLock;
#[derive(Debug)]
@ -38,8 +38,12 @@ impl FileBox {
Err(_) => {
// Fallback: create with empty file handle - only for dispatch
use std::fs::OpenOptions;
let file = OpenOptions::new().create(true).write(true).read(true)
.open("/dev/null").unwrap_or_else(|_| File::open("/dev/null").unwrap());
let file = OpenOptions::new()
.create(true)
.write(true)
.read(true)
.open("/dev/null")
.unwrap_or_else(|_| File::open("/dev/null").unwrap());
FileBox {
file: RwLock::new(file),
path: String::new(),
@ -48,28 +52,32 @@ impl FileBox {
}
}
}
pub fn open(path: &str) -> Result<Self> {
let file = OpenOptions::new().read(true).write(true).create(true).open(path)?;
Ok(FileBox {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)?;
Ok(FileBox {
file: RwLock::new(file),
path: path.to_string(),
base: BoxBase::new(),
})
}
pub fn read_to_string(&self) -> Result<String> {
let mut file = self.file.write().unwrap();
let mut s = String::new();
file.read_to_string(&mut s)?;
Ok(s)
}
pub fn write_all(&self, buf: &[u8]) -> Result<()> {
let mut file = self.file.write().unwrap();
file.write_all(buf)
}
/// ファイルの内容を読み取る
pub fn read(&self) -> Box<dyn NyashBox> {
match self.read_to_string() {
@ -77,7 +85,7 @@ impl FileBox {
Err(e) => Box::new(StringBox::new(&format!("Error reading file: {}", e))),
}
}
/// ファイルに内容を書き込む
pub fn write(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let content_str = content.to_string_box().value;
@ -86,13 +94,13 @@ impl FileBox {
Err(e) => Box::new(StringBox::new(&format!("Error writing file: {}", e))),
}
}
/// ファイルが存在するかチェック
pub fn exists(&self) -> Box<dyn NyashBox> {
use std::path::Path;
Box::new(BoolBox::new(Path::new(&self.path).exists()))
}
/// ファイルを削除
pub fn delete(&self) -> Box<dyn NyashBox> {
match std::fs::remove_file(&self.path) {
@ -100,7 +108,7 @@ impl FileBox {
Err(e) => Box::new(StringBox::new(&format!("Error deleting file: {}", e))),
}
}
/// ファイルをコピー
pub fn copy(&self, dest: &str) -> Box<dyn NyashBox> {
match std::fs::copy(&self.path, dest) {
@ -114,19 +122,19 @@ impl BoxCore for FileBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "FileBox({})", self.path)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -137,10 +145,10 @@ impl NyashBox for FileBox {
// Note: Cannot truly clone a File handle, so create a new one to the same path
match FileBox::open(&self.path) {
Ok(new_file) => Box::new(new_file),
Err(_) => Box::new(crate::box_trait::VoidBox::new()) // Return void on error
Err(_) => Box::new(crate::box_trait::VoidBox::new()), // Return void on error
}
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -150,12 +158,10 @@ impl NyashBox for FileBox {
StringBox::new(format!("FileBox({})", self.path))
}
fn type_name(&self) -> &'static str {
"FileBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() {
BoolBox::new(self.path == other_file.path)

View File

@ -1,8 +1,8 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::ast::ASTNode;
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::collections::HashMap;
use std::sync::Weak;
use std::any::Any;
#[derive(Debug)]
pub struct ClosureEnv {
@ -11,7 +11,12 @@ pub struct ClosureEnv {
}
impl ClosureEnv {
pub fn new() -> Self { Self { me_value: None, captures: HashMap::new() } }
pub fn new() -> Self {
Self {
me_value: None,
captures: HashMap::new(),
}
}
}
#[derive(Debug)]
@ -24,32 +29,70 @@ pub struct FunctionBox {
impl FunctionBox {
pub fn new(params: Vec<String>, body: Vec<ASTNode>) -> Self {
Self { params, body, env: ClosureEnv::new(), base: BoxBase::new() }
Self {
params,
body,
env: ClosureEnv::new(),
base: BoxBase::new(),
}
}
pub fn with_env(params: Vec<String>, body: Vec<ASTNode>, env: ClosureEnv) -> Self {
Self { params, body, env, base: BoxBase::new() }
Self {
params,
body,
env,
base: BoxBase::new(),
}
}
}
impl BoxCore for FunctionBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FunctionBox(params={}, body={})", self.params.len(), self.body.len())
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"FunctionBox(params={}, body={})",
self.params.len(),
self.body.len()
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
impl NyashBox for FunctionBox {
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FunctionBox(params={}, captures={}, body={})", self.params.len(), self.env.captures.len(), self.body.len())) }
fn type_name(&self) -> &'static str { "FunctionBox" }
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!(
"FunctionBox(params={}, captures={}, body={})",
self.params.len(),
self.env.captures.len(),
self.body.len()
))
}
fn type_name(&self) -> &'static str {
"FunctionBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(o) = other.as_any().downcast_ref::<FunctionBox>() {
BoolBox::new(self.box_id() == o.box_id())
} else { BoolBox::new(false) }
} else {
BoolBox::new(false)
}
}
}
@ -57,13 +100,20 @@ impl Clone for ClosureEnv {
fn clone(&self) -> Self {
let me_value = self.me_value.as_ref().map(|w| Weak::clone(w));
let mut captures: HashMap<String, Box<dyn NyashBox>> = HashMap::new();
for (k, v) in &self.captures { captures.insert(k.clone(), v.clone_box()); }
for (k, v) in &self.captures {
captures.insert(k.clone(), v.clone_box());
}
Self { me_value, captures }
}
}
impl Clone for FunctionBox {
fn clone(&self) -> Self {
Self { params: self.params.clone(), body: self.body.clone(), env: self.env.clone(), base: BoxBase::new() }
Self {
params: self.params.clone(),
body: self.body.clone(),
env: self.env.clone(),
base: BoxBase::new(),
}
}
}

View File

@ -2,9 +2,9 @@
// Nyashの箱システムによる非同期処理の基盤を提供します。
// 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::sync::{Mutex, Condvar, Arc, Weak};
use std::sync::{Arc, Condvar, Mutex, Weak};
#[derive(Debug)]
pub struct NyashFutureBox {
@ -33,7 +33,10 @@ pub struct FutureWeak {
impl Clone for NyashFutureBox {
fn clone(&self) -> Self {
Self { inner: self.inner.clone(), base: BoxBase::new() }
Self {
inner: self.inner.clone(),
base: BoxBase::new(),
}
}
}
@ -41,13 +44,16 @@ impl NyashFutureBox {
pub fn new() -> Self {
Self {
inner: Arc::new(Inner {
state: Mutex::new(FutureState { result: None, ready: false }),
state: Mutex::new(FutureState {
result: None,
ready: false,
}),
cv: Condvar::new(),
}),
base: BoxBase::new(),
}
}
/// Set the result of the future
pub fn set_result(&self, value: Box<dyn NyashBox>) {
let mut st = self.inner.state.lock().unwrap();
@ -55,7 +61,7 @@ impl NyashFutureBox {
st.ready = true;
self.inner.cv.notify_all();
}
/// Get the result (blocks until ready)
pub fn get(&self) -> Box<dyn NyashBox> {
let mut st = self.inner.state.lock().unwrap();
@ -64,21 +70,25 @@ impl NyashFutureBox {
}
st.result.as_ref().unwrap().clone_box()
}
/// Check if the future is ready
pub fn ready(&self) -> bool {
self.inner.state.lock().unwrap().ready
}
/// Create a non-owning weak handle to this Future's state
pub fn downgrade(&self) -> FutureWeak { FutureWeak { inner: Arc::downgrade(&self.inner) } }
pub fn downgrade(&self) -> FutureWeak {
FutureWeak {
inner: Arc::downgrade(&self.inner),
}
}
}
impl NyashBox for NyashFutureBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -98,12 +108,10 @@ impl NyashBox for NyashFutureBox {
}
}
fn type_name(&self) -> &'static str {
"NyashFutureBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_future) = other.as_any().downcast_ref::<NyashFutureBox>() {
BoolBox::new(self.base.id == other_future.base.id)
@ -117,7 +125,7 @@ impl BoxCore for NyashFutureBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -135,11 +143,11 @@ impl BoxCore for NyashFutureBox {
write!(f, "Future(pending)")
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -164,6 +172,8 @@ impl FutureBox {
impl FutureWeak {
/// Try to upgrade and check readiness
pub(crate) fn is_ready(&self) -> Option<bool> {
self.inner.upgrade().map(|arc| arc.state.lock().unwrap().ready)
self.inner
.upgrade()
.map(|arc| arc.state.lock().unwrap().ready)
}
}

View File

@ -1,8 +1,13 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct GcConfigBox { base: BoxBase, counting: bool, trace: bool, barrier_strict: bool }
pub struct GcConfigBox {
base: BoxBase,
counting: bool,
trace: bool,
barrier_strict: bool,
}
impl GcConfigBox {
pub fn new() -> Self {
@ -18,7 +23,7 @@ impl GcConfigBox {
"counting" => self.counting = on,
"trace" => self.trace = on,
"barrier_strict" | "strict" => self.barrier_strict = on,
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
}
Box::new(VoidBox::new())
}
@ -32,31 +37,59 @@ impl GcConfigBox {
Box::new(BoolBox::new(v))
}
pub fn apply(&self) -> Box<dyn NyashBox> {
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } };
let setb = |k: &str, v: bool| {
if v {
std::env::set_var(k, "1");
} else {
std::env::remove_var(k);
}
};
setb("NYASH_GC_COUNTING", self.counting);
setb("NYASH_GC_TRACE", self.trace);
setb("NYASH_GC_BARRIER_STRICT", self.barrier_strict);
Box::new(VoidBox::new())
}
pub fn summary(&self) -> Box<dyn NyashBox> {
let s = format!("counting={} trace={} barrier_strict={}", self.counting, self.trace, self.barrier_strict);
let s = format!(
"counting={} trace={} barrier_strict={}",
self.counting, self.trace, self.barrier_strict
);
Box::new(StringBox::new(s))
}
}
impl BoxCore for GcConfigBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "GcConfigBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GcConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for GcConfigBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<GcConfigBox>()) }
fn type_name(&self) -> &'static str { "GcConfigBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<GcConfigBox>())
}
fn type_name(&self) -> &'static str {
"GcConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(self.summary().to_string_box().value)
}
}

View File

@ -1,11 +1,11 @@
//! HttpClientBox 🌐 - HTTP通信
// Nyashの箱システムによるHTTP通信を提供します。
// 参考: 既存Boxの設計思想
//
//
// NOTE: HTTPサポートは現在開発中です。
// reqwestクレートの依存関係のため、一時的に無効化されています。
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[derive(Debug, Clone)]
@ -15,33 +15,38 @@ pub struct HttpClientBox {
impl HttpClientBox {
pub fn new() -> Self {
HttpClientBox {
base: BoxBase::new()
HttpClientBox {
base: BoxBase::new(),
}
}
/// HTTP GETリクエストスタブ
pub fn http_get(&self, _url: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
Box::new(StringBox::new("HTTP support is currently disabled"))
}
/// HTTP POSTリクエストスタブ
pub fn post(&self, _url: Box<dyn NyashBox>, _body: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
Box::new(StringBox::new("HTTP support is currently disabled"))
}
/// HTTP PUT リクエスト(スタブ)
pub fn put(&self, _url: Box<dyn NyashBox>, _body: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
Box::new(StringBox::new("HTTP support is currently disabled"))
}
/// HTTP DELETE リクエスト(スタブ)
pub fn delete(&self, _url: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
Box::new(StringBox::new("HTTP support is currently disabled"))
}
/// ヘッダー付きHTTPリクエストスタブ
pub fn request(&self, _method: Box<dyn NyashBox>, _url: Box<dyn NyashBox>, _options: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn request(
&self,
_method: Box<dyn NyashBox>,
_url: Box<dyn NyashBox>,
_options: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
Box::new(StringBox::new("HTTP support is currently disabled"))
}
}
@ -50,7 +55,7 @@ impl NyashBox for HttpClientBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -60,12 +65,10 @@ impl NyashBox for HttpClientBox {
StringBox::new(format!("HttpClientBox(id: {})", self.base.id))
}
fn type_name(&self) -> &'static str {
"HttpClientBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_http) = other.as_any().downcast_ref::<HttpClientBox>() {
BoolBox::new(self.base.id == other_http.base.id)
@ -79,7 +82,7 @@ impl BoxCore for HttpClientBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -87,11 +90,11 @@ impl BoxCore for HttpClientBox {
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HttpClientBox(id: {})", self.base.id)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -101,4 +104,4 @@ impl std::fmt::Display for HttpClientBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,36 +1,36 @@
/*! 📬 HTTPRequestBox & HTTPResponseBox - HTTP メッセージ処理
*
*
* ## 📝 概要
* HTTP/1.1 プロトコルのリクエスト・レスポンス処理を提供するBox群
* SocketBox と連携して完全なHTTPサーバー・クライアント機能を実現
*
*
* ## 🛠️ HTTPRequestBox - リクエスト処理
* ### HTTP Method & URL
* - `getMethod()` - HTTP メソッド取得 (GET, POST, etc.)
* - `getPath()` - URL パス取得
* - `getQueryString()` - クエリ文字列取得
*
*
* ### Headers
* - `getHeader(name)` - 特定ヘッダー取得
* - `getAllHeaders()` - 全ヘッダー取得MapBox
* - `hasHeader(name)` - ヘッダー存在確認
*
*
* ### Body & Content
* - `getBody()` - リクエストボディ取得
* - `getContentType()` - Content-Type取得
* - `getContentLength()` - Content-Length取得
*
*
* ## 🛠️ HTTPResponseBox - レスポンス生成
* ### Status & Headers
* - `setStatus(code, message)` - ステータス設定
* - `setHeader(name, value)` - ヘッダー設定
* - `setContentType(type)` - Content-Type設定
*
*
* ### Body & Output
* - `setBody(content)` - レスポンスボディ設定
* - `appendBody(content)` - ボディ追加
* - `toHttpString()` - HTTP形式文字列生成
*
*
* ## 💡 使用例
* ```nyash
* // Request parsing
@ -38,7 +38,7 @@
* local request = HTTPRequestBox.parse(rawRequest)
* print("Method: " + request.getMethod())
* print("Path: " + request.getPath())
*
*
* // Response generation
* local response = new HTTPResponseBox()
* response.setStatus(200, "OK")
@ -48,7 +48,7 @@
* ```
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::MapBox;
use std::any::Any;
use std::collections::HashMap;
@ -77,32 +77,32 @@ impl HTTPRequestBox {
http_version: "HTTP/1.1".to_string(),
}
}
/// 生のHTTPリクエスト文字列を解析
pub fn parse(raw_request: Box<dyn NyashBox>) -> Self {
let request_str = raw_request.to_string_box().value;
let mut request = HTTPRequestBox::new();
let lines: Vec<&str> = request_str.lines().collect();
if lines.is_empty() {
return request;
}
// Parse request line: "GET /path HTTP/1.1"
let request_line_parts: Vec<&str> = lines[0].split_whitespace().collect();
if request_line_parts.len() >= 3 {
request.method = request_line_parts[0].to_string();
// Split path and query string
let url_parts: Vec<&str> = request_line_parts[1].splitn(2, '?').collect();
request.path = url_parts[0].to_string();
if url_parts.len() > 1 {
request.query_string = url_parts[1].to_string();
}
request.http_version = request_line_parts[2].to_string();
}
// Parse headers
let mut header_end = 1;
for (i, line) in lines.iter().enumerate().skip(1) {
@ -110,37 +110,37 @@ impl HTTPRequestBox {
header_end = i + 1;
break;
}
if let Some(colon_pos) = line.find(':') {
let name = line[..colon_pos].trim().to_lowercase();
let value = line[colon_pos + 1..].trim().to_string();
request.headers.insert(name, value);
}
}
// Parse body (everything after headers)
if header_end < lines.len() {
request.body = lines[header_end..].join("\n");
}
request
}
/// HTTP メソッド取得
pub fn get_method(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.method.clone()))
}
/// URL パス取得
pub fn get_path(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.path.clone()))
}
/// クエリ文字列取得
pub fn get_query_string(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.query_string.clone()))
}
/// 特定ヘッダー取得
pub fn get_header(&self, name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let header_name = name.to_string_box().value.to_lowercase();
@ -149,7 +149,7 @@ impl HTTPRequestBox {
None => Box::new(StringBox::new("".to_string())),
}
}
/// 全ヘッダー取得MapBox形式
pub fn get_all_headers(&self) -> Box<dyn NyashBox> {
let headers_map = MapBox::new();
@ -160,31 +160,29 @@ impl HTTPRequestBox {
}
Box::new(headers_map)
}
/// ヘッダー存在確認
pub fn has_header(&self, name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let header_name = name.to_string_box().value.to_lowercase();
Box::new(BoolBox::new(self.headers.contains_key(&header_name)))
}
/// リクエストボディ取得
pub fn get_body(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.body.clone()))
}
/// Content-Type取得
pub fn get_content_type(&self) -> Box<dyn NyashBox> {
self.get_header(Box::new(StringBox::new("content-type".to_string())))
}
/// Content-Length取得
pub fn get_content_length(&self) -> Box<dyn NyashBox> {
match self.headers.get("content-length") {
Some(length_str) => {
match length_str.parse::<i64>() {
Ok(length) => Box::new(IntegerBox::new(length)),
Err(_) => Box::new(IntegerBox::new(0)),
}
Some(length_str) => match length_str.parse::<i64>() {
Ok(length) => Box::new(IntegerBox::new(length)),
Err(_) => Box::new(IntegerBox::new(0)),
},
None => Box::new(IntegerBox::new(0)),
}
@ -195,15 +193,19 @@ impl NyashBox for HTTPRequestBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("HTTPRequest({} {} - {} headers)",
self.method, self.path, self.headers.len()))
StringBox::new(format!(
"HTTPRequest({} {} - {} headers)",
self.method,
self.path,
self.headers.len()
))
}
fn type_name(&self) -> &'static str {
@ -223,20 +225,25 @@ impl BoxCore for HTTPRequestBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HTTPRequest({} {} - {} headers)",
self.method, self.path, self.headers.len())
write!(
f,
"HTTPRequest({} {} - {} headers)",
self.method,
self.path,
self.headers.len()
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -270,105 +277,123 @@ impl HTTPResponseBox {
http_version: "HTTP/1.1".to_string(),
}
}
/// ステータスコード・メッセージ設定
pub fn set_status(&self, code: Box<dyn NyashBox>, message: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn set_status(
&self,
code: Box<dyn NyashBox>,
message: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
// Note: This would need interior mutability for actual mutation
// For now, this is a placeholder for the API structure
let _code_val = code.to_string_box().value.parse::<i32>().unwrap_or(200);
let _message_val = message.to_string_box().value;
// TODO: Use RefCell or similar for interior mutability
Box::new(BoolBox::new(true))
}
/// ヘッダー設定
pub fn set_header(&self, name: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn set_header(
&self,
name: Box<dyn NyashBox>,
value: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
let _name_str = name.to_string_box().value;
let _value_str = value.to_string_box().value;
// TODO: Use RefCell for interior mutability
Box::new(BoolBox::new(true))
}
/// Content-Type設定
pub fn set_content_type(&self, content_type: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let content_type_str = content_type.to_string_box().value;
self.set_header(
Box::new(StringBox::new("Content-Type".to_string())),
Box::new(StringBox::new(content_type_str))
Box::new(StringBox::new(content_type_str)),
)
}
/// レスポンスボディ設定
pub fn set_body(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let _content_str = content.to_string_box().value;
// TODO: Use RefCell for interior mutability
Box::new(BoolBox::new(true))
}
/// ボディ追加
pub fn append_body(&self, content: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let _content_str = content.to_string_box().value;
// TODO: Use RefCell for interior mutability
Box::new(BoolBox::new(true))
}
/// HTTP形式文字列生成
pub fn to_http_string(&self) -> Box<dyn NyashBox> {
let mut response = String::new();
// Status line
response.push_str(&format!("{} {} {}\r\n",
self.http_version, self.status_code, self.status_message));
response.push_str(&format!(
"{} {} {}\r\n",
self.http_version, self.status_code, self.status_message
));
// Headers
for (name, value) in &self.headers {
response.push_str(&format!("{}: {}\r\n", name, value));
}
// Content-Length if not already set
if !self.headers.contains_key("content-length") && !self.body.is_empty() {
response.push_str(&format!("Content-Length: {}\r\n", self.body.len()));
}
// Empty line before body
response.push_str("\r\n");
// Body
response.push_str(&self.body);
Box::new(StringBox::new(response))
}
/// Quick HTML response creation
pub fn create_html_response(content: Box<dyn NyashBox>) -> Self {
let mut response = HTTPResponseBox::new();
response.status_code = 200;
response.status_message = "OK".to_string();
response.headers.insert("Content-Type".to_string(), "text/html; charset=utf-8".to_string());
response.headers.insert(
"Content-Type".to_string(),
"text/html; charset=utf-8".to_string(),
);
response.body = content.to_string_box().value;
response
}
/// Quick JSON response creation
pub fn create_json_response(content: Box<dyn NyashBox>) -> Self {
let mut response = HTTPResponseBox::new();
response.status_code = 200;
response.status_message = "OK".to_string();
response.headers.insert("Content-Type".to_string(), "application/json".to_string());
response
.headers
.insert("Content-Type".to_string(), "application/json".to_string());
response.body = content.to_string_box().value;
response
}
/// Quick 404 response creation
pub fn create_404_response() -> Self {
let mut response = HTTPResponseBox::new();
response.status_code = 404;
response.status_message = "Not Found".to_string();
response.headers.insert("Content-Type".to_string(), "text/html; charset=utf-8".to_string());
response.headers.insert(
"Content-Type".to_string(),
"text/html; charset=utf-8".to_string(),
);
response.body = "<html><body><h1>404 - Not Found</h1></body></html>".to_string();
response
}
@ -378,15 +403,19 @@ impl NyashBox for HTTPResponseBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("HTTPResponse({} {} - {} bytes)",
self.status_code, self.status_message, self.body.len()))
StringBox::new(format!(
"HTTPResponse({} {} - {} bytes)",
self.status_code,
self.status_message,
self.body.len()
))
}
fn type_name(&self) -> &'static str {
@ -406,20 +435,25 @@ impl BoxCore for HTTPResponseBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HTTPResponse({} {} - {} bytes)",
self.status_code, self.status_message, self.body.len())
write!(
f,
"HTTPResponse({} {} - {} bytes)",
self.status_code,
self.status_message,
self.body.len()
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -429,4 +463,4 @@ impl std::fmt::Display for HTTPResponseBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,51 +1,51 @@
/*! 🌐 HTTPServerBox - HTTP サーバー実装
*
*
* ## 📝 概要
* TCP SocketBox を基盤とした高性能 HTTP/1.1 サーバー
* 並行処理・ルーティング・ミドルウェア対応で実用アプリケーション開発可能
*
*
* ## 🛠️ 利用可能メソッド
* ### Server Management
* - `bind(address, port)` - サーバーアドレス bind
* - `listen(backlog)` - 接続待機開始
* - `start()` - HTTP サーバー開始(ブロッキング)
* - `stop()` - サーバー停止
*
*
* ### Routing & Handlers
* - `route(path, handler)` - ルート・ハンドラー登録
* - `get(path, handler)` - GET ルート登録
* - `post(path, handler)` - POST ルート登録
* - `put(path, handler)` - PUT ルート登録
* - `delete(path, handler)` - DELETE ルート登録
*
*
* ### Middleware & Configuration
* - `use(middleware)` - ミドルウェア登録
* - `setStaticPath(path)` - 静的ファイル配信設定
* - `setTimeout(seconds)` - リクエストタイムアウト設定
*
*
* ## 💡 使用例
* ```nyash
* // HTTP Server creation
* local server = new HTTPServerBox()
* server.bind("0.0.0.0", 8080)
*
*
* // Route handlers
* server.get("/", APIHandler.home)
* server.get("/api/status", APIHandler.status)
* server.post("/api/users", APIHandler.createUser)
*
*
* // Start server (blocking)
* print("🚀 Server starting on port 8080...")
* server.start()
* ```
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use crate::boxes::SocketBox;
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox};
use crate::boxes::SocketBox;
use std::any::Any;
use std::sync::RwLock;
use std::collections::HashMap;
use std::sync::RwLock;
use std::thread;
/// HTTP サーバーを提供するBox
@ -66,26 +66,29 @@ impl Clone for HTTPServerBox {
// State-preserving clone implementation following PR #87 pattern
let socket_guard = self.socket.read().unwrap();
let socket_val = socket_guard.as_ref().map(|s| s.clone());
let routes_guard = self.routes.read().unwrap();
let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter()
let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard
.iter()
.map(|(k, v)| (k.clone(), v.clone_box()))
.collect();
let middleware_guard = self.middleware.read().unwrap();
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard.iter()
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard
.iter()
.map(|item| item.clone_box())
.collect();
let running_val = *self.running.read().unwrap();
let static_path_val = self.static_path.read().unwrap().clone();
let timeout_val = *self.timeout_seconds.read().unwrap();
let connections_guard = self.active_connections.read().unwrap();
let connections_val: Vec<Box<dyn NyashBox>> = connections_guard.iter()
let connections_val: Vec<Box<dyn NyashBox>> = connections_guard
.iter()
.map(|item| item.clone_box())
.collect();
Self {
base: BoxBase::new(), // New unique ID for clone
socket: RwLock::new(socket_val),
@ -112,39 +115,43 @@ impl HTTPServerBox {
active_connections: RwLock::new(Vec::new()),
}
}
/// サーバーアドレスにバインド
pub fn bind(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let socket = SocketBox::new();
let bind_result = socket.bind(address, port);
if bind_result.to_string_box().value == "true" {
match self.socket.write() {
Ok(mut socket_guard) => {
*socket_guard = Some(socket);
Box::new(BoolBox::new(true))
},
Err(_) => {
Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string()))
}
Err(_) => Box::new(StringBox::new(
"Error: Failed to acquire socket lock".to_string(),
)),
}
} else {
Box::new(BoolBox::new(false))
}
}
/// 接続待機開始
pub fn listen(&self, _backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let socket_guard = match self.socket.read() {
Ok(guard) => guard,
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())),
Err(_) => {
return Box::new(StringBox::new(
"Error: Failed to acquire socket lock".to_string(),
))
}
};
if let Some(ref _socket) = *socket_guard {
// For HTTPServerBox, if we have a socket stored, it means bind() was successful
// and the socket should be in listening state. TcpListener::bind already puts
// the socket in listening state, so we just need to verify it's working.
// Try to access the stored listener directly (this is a simplified check)
// In a real implementation, we'd store the listener state separately
Box::new(BoolBox::new(true))
@ -152,27 +159,35 @@ impl HTTPServerBox {
Box::new(BoolBox::new(false))
}
}
/// HTTP サーバー開始(メインループ)
pub fn start(&self) -> Box<dyn NyashBox> {
// Set running state
match self.running.write() {
Ok(mut running) => *running = true,
Err(_) => return Box::new(StringBox::new("Error: Failed to set running state".to_string())),
Err(_) => {
return Box::new(StringBox::new(
"Error: Failed to set running state".to_string(),
))
}
};
let socket_guard = match self.socket.read() {
Ok(guard) => guard,
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())),
Err(_) => {
return Box::new(StringBox::new(
"Error: Failed to acquire socket lock".to_string(),
))
}
};
if let Some(ref socket) = *socket_guard {
// Clone socket for the server loop
let server_socket = socket.clone();
drop(socket_guard);
println!("🚀 HTTP Server starting...");
// Main server loop - need to handle RwLock references carefully for threading
loop {
// Check if server should stop
@ -180,53 +195,54 @@ impl HTTPServerBox {
Ok(running_guard) => *running_guard,
Err(_) => break, // Exit loop if we can't check running state
};
if !should_continue {
break;
}
// Accept new connection
let client_result = server_socket.accept();
// Check if we got a valid client connection
let client_socket = match client_result.as_any().downcast_ref::<SocketBox>() {
Some(socket) => socket.clone(),
None => continue, // Skip invalid connections
};
// Add to active connections (with error handling)
if let Ok(mut connections) = self.active_connections.write() {
connections.push(Box::new(client_socket.clone()));
}
// Handle client in separate thread (simulate nowait)
// For RwLock pattern, we need to pass the data needed for the thread
let routes_snapshot = match self.routes.read() {
Ok(routes_guard) => {
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter()
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard
.iter()
.map(|(k, v)| (k.clone(), v.clone_box()))
.collect();
routes_clone
},
}
Err(_) => continue, // Skip this connection if we can't read routes
};
thread::spawn(move || {
Self::handle_client_request_with_routes(client_socket, routes_snapshot);
// Note: Connection cleanup is handled separately to avoid complex lifetime issues
});
}
Box::new(BoolBox::new(true))
} else {
Box::new(BoolBox::new(false))
}
}
/// サーバー停止
pub fn stop(&self) -> Box<dyn NyashBox> {
*self.running.write().unwrap() = false;
// Close all active connections
let mut connections = self.active_connections.write().unwrap();
for connection in connections.iter() {
@ -235,128 +251,128 @@ impl HTTPServerBox {
}
}
connections.clear();
// Close server socket
if let Some(ref socket) = *self.socket.read().unwrap() {
let _ = socket.close();
}
println!("🛑 HTTP Server stopped");
Box::new(BoolBox::new(true))
}
/// ルート・ハンドラー登録
pub fn route(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("ANY {}", path_str);
self.routes.write().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// GET ルート登録
pub fn get(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("GET {}", path_str);
self.routes.write().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// POST ルート登録
pub fn post(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("POST {}", path_str);
self.routes.write().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// PUT ルート登録
pub fn put(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("PUT {}", path_str);
self.routes.write().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// DELETE ルート登録
pub fn delete(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("DELETE {}", path_str);
self.routes.write().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// 静的ファイル配信パス設定
pub fn set_static_path(&self, path: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
*self.static_path.write().unwrap() = Some(path_str);
Box::new(BoolBox::new(true))
}
/// リクエストタイムアウト設定
pub fn set_timeout(&self, seconds: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let timeout_val = seconds.to_string_box().value.parse::<u64>().unwrap_or(30);
*self.timeout_seconds.write().unwrap() = timeout_val;
Box::new(BoolBox::new(true))
}
/// クライアントリクエスト処理(内部メソッド)
fn handle_client_request_with_routes(
client_socket: SocketBox,
routes: HashMap<String, Box<dyn NyashBox>>
client_socket: SocketBox,
routes: HashMap<String, Box<dyn NyashBox>>,
) {
// Read HTTP request
let raw_request = client_socket.read_http_request();
let request_str = raw_request.to_string_box().value;
if request_str.trim().is_empty() {
let _ = client_socket.close();
return;
}
// Parse HTTP request
let request = HTTPRequestBox::parse(raw_request);
let method = request.get_method().to_string_box().value;
let path = request.get_path().to_string_box().value;
println!("📬 {} {}", method, path);
// Find matching route
let route_key = format!("{} {}", method, path);
let fallback_key = format!("ANY {}", path);
let response = if let Some(_handler) = routes.get(&route_key) {
// Found specific method route
// TODO: Actual handler invocation would need method calling infrastructure
HTTPResponseBox::create_json_response(
Box::new(StringBox::new(r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#))
)
HTTPResponseBox::create_json_response(Box::new(StringBox::new(
r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#,
)))
} else if let Some(_handler) = routes.get(&fallback_key) {
// Found generic route
HTTPResponseBox::create_json_response(
Box::new(StringBox::new(r#"{"message": "Generic route found"}"#))
)
HTTPResponseBox::create_json_response(Box::new(StringBox::new(
r#"{"message": "Generic route found"}"#,
)))
} else {
// No route found - 404
HTTPResponseBox::create_404_response()
};
// Send response
let response_str = response.to_http_string();
let _ = client_socket.write(response_str);
let _ = client_socket.close();
}
/// アクティブ接続数取得
pub fn get_active_connections(&self) -> Box<dyn NyashBox> {
let connections = self.active_connections.read().unwrap();
Box::new(IntegerBox::new(connections.len() as i64))
}
/// サーバー状態取得
pub fn is_running(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(*self.running.read().unwrap()))
@ -367,7 +383,7 @@ impl NyashBox for HTTPServerBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -377,9 +393,9 @@ impl NyashBox for HTTPServerBox {
let running = *self.running.read().unwrap();
let routes_count = self.routes.read().unwrap().len();
let connections_count = self.active_connections.read().unwrap().len();
StringBox::new(format!(
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
self.base.id, running, routes_count, connections_count
))
}
@ -401,7 +417,7 @@ impl BoxCore for HTTPServerBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -410,15 +426,18 @@ impl BoxCore for HTTPServerBox {
let running = *self.running.read().unwrap();
let routes_count = self.routes.read().unwrap().len();
let connections_count = self.active_connections.read().unwrap().len();
write!(f, "HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
self.base.id, running, routes_count, connections_count)
write!(
f,
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
self.base.id, running, routes_count, connections_count
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -436,4 +455,4 @@ impl Drop for HTTPServerBox {
// Ensure server is stopped and resources are cleaned up
let _ = self.stop();
}
}
}

View File

@ -1,29 +1,29 @@
/*! 📦 IntentBox - Structured Message Box
*
*
* ## 📝 概要
* IntentBoxは構造化メッセージを表現するBoxです。
* P2P通信において、メッセージの種類(name)と内容(payload)を
* 明確に分離して管理します。
*
*
* ## 🏗️ 設計
* - **name**: メッセージの種類 ("chat.message", "file.share"等)
* - **payload**: JSON形式の任意データ
* - **Arc<Mutex>**: 他のBoxと統一されたメモリ管理パターン
*
*
* ## 🛠️ 利用可能メソッド
* - `new(name, payload)` - 構造化メッセージを作成
* - `getName()` - メッセージ名を取得
* - `getPayload()` - ペイロードを取得
* - `setPayload(data)` - ペイロードを更新
*
*
* ## 💡 使用例
* ```nyash
* // チャットメッセージ
* local msg = new IntentBox("chat.message", {
* text: "Hello P2P!",
* from: "alice"
* local msg = new IntentBox("chat.message", {
* text: "Hello P2P!",
* from: "alice"
* })
*
*
* // ファイル共有メッセージ
* local file_msg = new IntentBox("file.share", {
* filename: "document.pdf",
@ -32,10 +32,10 @@
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::sync::RwLock;
use std::fmt::Debug;
use std::sync::RwLock;
/// IntentBox - 構造化メッセージBox (RwLock pattern)
#[derive(Debug)]
@ -51,7 +51,7 @@ impl Clone for IntentBox {
fn clone(&self) -> Self {
let name_val = self.name.read().unwrap().clone();
let payload_val = self.payload.read().unwrap().clone();
Self {
base: BoxBase::new(), // New unique ID for clone
name: RwLock::new(name_val),
@ -69,19 +69,19 @@ impl IntentBox {
payload: RwLock::new(payload),
}
}
/// メッセージ名を取得
pub fn get_name(&self) -> Box<dyn NyashBox> {
let name = self.name.read().unwrap().clone();
Box::new(StringBox::new(name))
}
/// ペイロードを取得
pub fn get_payload(&self) -> Box<dyn NyashBox> {
let payload = self.payload.read().unwrap().clone();
Box::new(StringBox::new(payload.to_string()))
}
/// ペイロードを更新
pub fn set_payload(&self, payload: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let payload_str = payload.to_string_box().value;
@ -89,8 +89,8 @@ impl IntentBox {
Ok(json_val) => {
*self.payload.write().unwrap() = json_val;
Box::new(BoolBox::new(true))
},
Err(_) => Box::new(BoolBox::new(false))
}
Err(_) => Box::new(BoolBox::new(false)),
}
}
}
@ -99,7 +99,7 @@ impl NyashBox for IntentBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -109,7 +109,7 @@ impl NyashBox for IntentBox {
let name = self.name.read().unwrap().clone();
StringBox::new(format!("IntentBox[{}]", name))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_intent) = other.as_any().downcast_ref::<IntentBox>() {
BoolBox::new(self.base.id == other_intent.base.id)
@ -117,7 +117,7 @@ impl NyashBox for IntentBox {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"IntentBox"
}
@ -127,7 +127,7 @@ impl BoxCore for IntentBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -136,11 +136,11 @@ impl BoxCore for IntentBox {
let name = self.name.read().unwrap().clone();
write!(f, "IntentBox[{}]", name)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -151,4 +151,3 @@ impl std::fmt::Display for IntentBox {
self.fmt_box(f)
}
}

View File

@ -1,6 +1,6 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, VoidBox, BoxCore, BoxBase};
use crate::jit::config::JitConfig;
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox};
use crate::interpreter::RuntimeError;
use crate::jit::config::JitConfig;
use std::any::Any;
use std::sync::RwLock;
@ -11,12 +11,19 @@ pub struct JitConfigBox {
}
impl JitConfigBox {
pub fn new() -> Self { Self { base: BoxBase::new(), config: RwLock::new(JitConfig::from_env()) } }
pub fn new() -> Self {
Self {
base: BoxBase::new(),
config: RwLock::new(JitConfig::from_env()),
}
}
/// Update internal config flags from runtime capability probe
pub fn from_runtime_probe(&self) -> Box<dyn NyashBox> {
let caps = crate::jit::config::probe_capabilities();
let mut cfg = self.config.write().unwrap();
if cfg.native_bool_abi && !caps.supports_b1_sig { cfg.native_bool_abi = false; }
if cfg.native_bool_abi && !caps.supports_b1_sig {
cfg.native_bool_abi = false;
}
Box::new(VoidBox::new())
}
pub fn set_flag(&self, name: &str, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
@ -34,7 +41,11 @@ impl JitConfigBox {
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on,
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = on,
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric = on,
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }),
_ => {
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown flag: {}", name),
})
}
}
Ok(Box::new(VoidBox::new()))
}
@ -53,19 +64,28 @@ impl JitConfigBox {
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi,
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1,
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric,
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }),
_ => {
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown flag: {}", name),
})
}
};
Ok(Box::new(BoolBox::new(b)))
}
pub fn set_threshold(&self, v: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut cfg = self.config.write().unwrap();
if v <= 0 { cfg.threshold = None; }
else { cfg.threshold = Some(v as u32); }
if v <= 0 {
cfg.threshold = None;
} else {
cfg.threshold = Some(v as u32);
}
Ok(Box::new(VoidBox::new()))
}
pub fn get_threshold(&self) -> Box<dyn NyashBox> {
let cfg = self.config.read().unwrap();
Box::new(IntegerBox::new(cfg.threshold.map(|v| v as i64).unwrap_or(0)))
Box::new(IntegerBox::new(
cfg.threshold.map(|v| v as i64).unwrap_or(0),
))
}
pub fn to_json(&self) -> Box<dyn NyashBox> {
let cfg = self.config.read().unwrap();
@ -88,20 +108,49 @@ impl JitConfigBox {
}
pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut cfg = self.config.write().unwrap();
let v: serde_json::Value = serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation { message: format!("Invalid JSON: {}", e) })?;
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) { cfg.exec = b; }
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) { cfg.stats = b; }
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) { cfg.stats_json = b; }
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) { cfg.dump = b; }
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) { cfg.threshold = Some(n as u32); }
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) { cfg.phi_min = b; }
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) { cfg.hostcall = b; }
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) { cfg.handle_debug = b; }
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) { cfg.native_f64 = b; }
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) { cfg.native_bool = b; }
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) { cfg.native_bool_abi = b; }
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) { cfg.ret_bool_b1 = b; }
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) { cfg.relax_numeric = b; }
let v: serde_json::Value =
serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation {
message: format!("Invalid JSON: {}", e),
})?;
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) {
cfg.exec = b;
}
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) {
cfg.stats = b;
}
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) {
cfg.stats_json = b;
}
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) {
cfg.dump = b;
}
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) {
cfg.threshold = Some(n as u32);
}
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) {
cfg.phi_min = b;
}
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) {
cfg.hostcall = b;
}
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) {
cfg.handle_debug = b;
}
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) {
cfg.native_f64 = b;
}
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) {
cfg.native_bool = b;
}
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) {
cfg.native_bool_abi = b;
}
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) {
cfg.ret_bool_b1 = b;
}
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) {
cfg.relax_numeric = b;
}
Ok(Box::new(VoidBox::new()))
}
pub fn apply(&self) -> Box<dyn NyashBox> {
@ -124,20 +173,41 @@ impl JitConfigBox {
}
impl BoxCore for JitConfigBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitConfigBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for JitConfigBox {
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitConfigBox>()) }
fn type_name(&self) -> &'static str { "JitConfigBox" }
fn to_string_box(&self) -> StringBox {
StringBox::new(self.summary().to_string_box().value)
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitConfigBox>())
}
fn type_name(&self) -> &'static str {
"JitConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
let cfg = self.config.read().unwrap().clone();
Box::new(JitConfigBox { base: self.base.clone(), config: RwLock::new(cfg) })
Box::new(JitConfigBox {
base: self.base.clone(),
config: RwLock::new(cfg),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
}

View File

@ -1,25 +1,55 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct JitEventsBox { base: BoxBase }
pub struct JitEventsBox {
base: BoxBase,
}
impl JitEventsBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
impl JitEventsBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitEventsBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitEventsBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitEventsBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for JitEventsBox {
fn to_string_box(&self) -> StringBox { StringBox::new("JitEventsBox") }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitEventsBox>()) }
fn type_name(&self) -> &'static str { "JitEventsBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox {
StringBox::new("JitEventsBox")
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitEventsBox>())
}
fn type_name(&self) -> &'static str {
"JitEventsBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
impl JitEventsBox {
@ -28,14 +58,17 @@ impl JitEventsBox {
Box::new(VoidBox::new())
}
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
if on { std::env::set_var("NYASH_JIT_EVENTS", "1"); }
else { std::env::remove_var("NYASH_JIT_EVENTS"); }
if on {
std::env::set_var("NYASH_JIT_EVENTS", "1");
} else {
std::env::remove_var("NYASH_JIT_EVENTS");
}
Box::new(VoidBox::new())
}
pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box<dyn NyashBox> {
let extra = serde_json::from_str::<serde_json::Value>(note_json).unwrap_or_else(|_| serde_json::json!({"note": note_json}));
let extra = serde_json::from_str::<serde_json::Value>(note_json)
.unwrap_or_else(|_| serde_json::json!({"note": note_json}));
crate::jit::events::emit(kind, function, None, None, extra);
Box::new(VoidBox::new())
}
}

View File

@ -1,17 +1,35 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct JitHostcallRegistryBox { base: BoxBase }
pub struct JitHostcallRegistryBox {
base: BoxBase,
}
impl JitHostcallRegistryBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
impl JitHostcallRegistryBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitHostcallRegistryBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitHostcallRegistryBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitHostcallRegistryBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for JitHostcallRegistryBox {
@ -20,18 +38,41 @@ impl NyashBox for JitHostcallRegistryBox {
let payload = serde_json::json!({ "readonly": ro, "mutating": mu });
StringBox::new(payload.to_string())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>()) }
fn type_name(&self) -> &'static str { "JitHostcallRegistryBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>())
}
fn type_name(&self) -> &'static str {
"JitHostcallRegistryBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
impl JitHostcallRegistryBox {
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_readonly(sym); Box::new(VoidBox::new()) }
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_mutating(sym); Box::new(VoidBox::new()) }
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv); Box::new(VoidBox::new()) }
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> {
crate::jit::hostcall_registry::add_readonly(sym);
Box::new(VoidBox::new())
}
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> {
crate::jit::hostcall_registry::add_mutating(sym);
Box::new(VoidBox::new())
}
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> {
crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv);
Box::new(VoidBox::new())
}
pub fn add_signature(&self, sym: &str, args_csv: &str, ret_str: &str) -> Box<dyn NyashBox> {
let ok = crate::jit::hostcall_registry::set_signature_csv(sym, args_csv, ret_str);
if ok { Box::new(VoidBox::new()) } else { Box::new(StringBox::new("Invalid signature")) }
if ok {
Box::new(VoidBox::new())
} else {
Box::new(StringBox::new("Invalid signature"))
}
}
}

View File

@ -1,29 +1,61 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct JitPolicyBox { base: BoxBase }
pub struct JitPolicyBox {
base: BoxBase,
}
impl JitPolicyBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
impl JitPolicyBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitPolicyBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitPolicyBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitPolicyBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for JitPolicyBox {
fn to_string_box(&self) -> StringBox {
let p = crate::jit::policy::current();
let s = format!("read_only={} whitelist={}", p.read_only, p.hostcall_whitelist.join(","));
let s = format!(
"read_only={} whitelist={}",
p.read_only,
p.hostcall_whitelist.join(",")
);
StringBox::new(s)
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitPolicyBox>()) }
fn type_name(&self) -> &'static str { "JitPolicyBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitPolicyBox>())
}
fn type_name(&self) -> &'static str {
"JitPolicyBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
// Methods (exposed via VM dispatch):
@ -32,19 +64,26 @@ impl JitPolicyBox {
let mut cur = crate::jit::policy::current();
match name {
"read_only" | "readonly" => cur.read_only = on,
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name)))
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
}
crate::jit::policy::set_current(cur);
Box::new(VoidBox::new())
}
pub fn get_flag(&self, name: &str) -> Box<dyn NyashBox> {
let cur = crate::jit::policy::current();
let v = match name { "read_only" | "readonly" => cur.read_only, _ => false };
let v = match name {
"read_only" | "readonly" => cur.read_only,
_ => false,
};
Box::new(BoolBox::new(v))
}
pub fn set_whitelist_csv(&self, csv: &str) -> Box<dyn NyashBox> {
let mut cur = crate::jit::policy::current();
cur.hostcall_whitelist = csv.split(',').map(|t| t.trim().to_string()).filter(|s| !s.is_empty()).collect();
cur.hostcall_whitelist = csv
.split(',')
.map(|t| t.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
crate::jit::policy::set_current(cur);
Box::new(VoidBox::new())
}
@ -89,9 +128,15 @@ impl JitPolicyBox {
crate::jit::r#extern::collections::SYM_ARRAY_SET_H,
crate::jit::r#extern::collections::SYM_MAP_SET_H,
];
for id in ids { if !cur.hostcall_whitelist.iter().any(|s| s == id) { cur.hostcall_whitelist.push(id.to_string()); } }
for id in ids {
if !cur.hostcall_whitelist.iter().any(|s| s == id) {
cur.hostcall_whitelist.push(id.to_string());
}
}
}
_ => {
return Box::new(StringBox::new(format!("Unknown preset: {}", name)));
}
_ => { return Box::new(StringBox::new(format!("Unknown preset: {}", name))); }
}
crate::jit::policy::set_current(cur);
Box::new(VoidBox::new())

View File

@ -1,15 +1,25 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct JitStatsBox { base: BoxBase }
pub struct JitStatsBox {
base: BoxBase,
}
impl JitStatsBox {
pub fn new() -> Self { Self { base: BoxBase::new() } }
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
pub fn to_json(&self) -> Box<dyn NyashBox> {
let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities();
let mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
let mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let payload = serde_json::json!({
"version": 1,
"abi_mode": mode,
@ -25,17 +35,37 @@ impl JitStatsBox {
}
impl BoxCore for JitStatsBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitStatsBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitStatsBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for JitStatsBox {
fn to_string_box(&self) -> StringBox { StringBox::new(self.to_json().to_string_box().value) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStatsBox>()) }
fn type_name(&self) -> &'static str { "JitStatsBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox {
StringBox::new(self.to_json().to_string_box().value)
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitStatsBox>())
}
fn type_name(&self) -> &'static str {
"JitStatsBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}

View File

@ -1,25 +1,55 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
#[derive(Debug, Clone)]
pub struct JitStrictBox { base: BoxBase }
pub struct JitStrictBox {
base: BoxBase,
}
impl JitStrictBox { pub fn new() -> Self { Self { base: BoxBase::new() } } }
impl JitStrictBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitStrictBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitStrictBox") }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitStrictBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for JitStrictBox {
fn to_string_box(&self) -> StringBox { StringBox::new("JitStrictBox".to_string()) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStrictBox>()) }
fn type_name(&self) -> &'static str { "JitStrictBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox {
StringBox::new("JitStrictBox".to_string())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitStrictBox>())
}
fn type_name(&self) -> &'static str {
"JitStrictBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
impl JitStrictBox {
@ -27,8 +57,12 @@ impl JitStrictBox {
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
if on {
std::env::set_var("NYASH_JIT_STRICT", "1");
if std::env::var("NYASH_JIT_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ONLY", "1"); }
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); }
if std::env::var("NYASH_JIT_ONLY").ok().is_none() {
std::env::set_var("NYASH_JIT_ONLY", "1");
}
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() {
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
}
} else {
std::env::remove_var("NYASH_JIT_STRICT");
}
@ -36,11 +70,19 @@ impl JitStrictBox {
}
pub fn set_only(&self, on: bool) -> Box<dyn NyashBox> {
if on { std::env::set_var("NYASH_JIT_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ONLY"); }
if on {
std::env::set_var("NYASH_JIT_ONLY", "1");
} else {
std::env::remove_var("NYASH_JIT_ONLY");
}
Box::new(VoidBox::new())
}
pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> {
if on { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY"); }
if on {
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
} else {
std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY");
}
Box::new(VoidBox::new())
}
@ -60,4 +102,3 @@ impl JitStrictBox {
Box::new(VoidBox::new())
}
}

View File

@ -2,12 +2,12 @@
// Nyashの箱システムによるJSON解析・生成を提供します。
// 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, IntegerBox};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::array::ArrayBox;
use crate::boxes::map_box::MapBox;
use serde_json::{Error, Value};
use std::any::Any;
use std::sync::RwLock;
use serde_json::{Value, Error};
#[derive(Debug)]
pub struct JSONBox {
@ -18,7 +18,7 @@ pub struct JSONBox {
impl Clone for JSONBox {
fn clone(&self) -> Self {
let value_clone = self.value.read().unwrap().clone();
Self {
value: RwLock::new(value_clone),
base: BoxBase::new(), // New unique ID for clone
@ -29,24 +29,24 @@ impl Clone for JSONBox {
impl JSONBox {
pub fn from_str(s: &str) -> Result<Self, Error> {
let value = serde_json::from_str(s)?;
Ok(JSONBox {
value: RwLock::new(value),
base: BoxBase::new()
Ok(JSONBox {
value: RwLock::new(value),
base: BoxBase::new(),
})
}
pub fn new(value: Value) -> Self {
JSONBox {
value: RwLock::new(value),
base: BoxBase::new()
JSONBox {
value: RwLock::new(value),
base: BoxBase::new(),
}
}
pub fn to_string(&self) -> String {
let value = self.value.read().unwrap();
value.to_string()
}
/// JSONパース
pub fn parse(data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let json_str = data.to_string_box().value;
@ -55,17 +55,17 @@ impl JSONBox {
Err(e) => Box::new(StringBox::new(&format!("Error parsing JSON: {}", e))),
}
}
/// JSON文字列化
pub fn stringify(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(&self.to_string()))
}
/// 値取得
pub fn get(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
let value = self.value.read().unwrap();
if let Some(obj) = value.as_object() {
if let Some(val) = obj.get(&key_str) {
json_value_to_nyash_box(val)
@ -86,14 +86,14 @@ impl JSONBox {
Box::new(crate::boxes::null_box::NullBox::new())
}
}
/// 値設定
pub fn set(&self, key: Box<dyn NyashBox>, new_value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
let mut value = self.value.write().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"))
@ -101,31 +101,31 @@ impl JSONBox {
Box::new(StringBox::new("Error: JSONBox is not an object"))
}
}
/// キー存在チェック
pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
let value = self.value.read().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<dyn NyashBox> {
let value = self.value.read().unwrap();
let array = ArrayBox::new();
if let Some(obj) = value.as_object() {
for key in obj.keys() {
// ArrayBoxのpushメソッドは&selfなので、直接呼び出し可能
let _ = array.push(Box::new(StringBox::new(key)));
}
}
Box::new(array)
}
}
@ -134,11 +134,11 @@ impl BoxCore for JSONBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let value = self.value.read().unwrap();
let json_type = match *value {
@ -148,18 +148,18 @@ impl BoxCore for JSONBox {
Value::String(_) => "string",
Value::Array(ref arr) => {
return write!(f, "JSONBox[array:{}]", arr.len());
},
}
Value::Object(ref obj) => {
return write!(f, "JSONBox[object:{}]", obj.len());
},
}
};
write!(f, "JSONBox[{}]", json_type)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -175,7 +175,7 @@ impl NyashBox for JSONBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -186,12 +186,10 @@ impl NyashBox for JSONBox {
StringBox::new(value.to_string())
}
fn type_name(&self) -> &'static str {
"JSONBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_json) = other.as_any().downcast_ref::<JSONBox>() {
let self_value = self.value.read().unwrap();
@ -230,10 +228,7 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
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)
);
map_box.set(Box::new(StringBox::new(key)), json_value_to_nyash_box(val));
}
Box::new(map_box)
}
@ -242,7 +237,11 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
/// NyashBox を JSON Value に変換
fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
if value.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
if value
.as_any()
.downcast_ref::<crate::boxes::null_box::NullBox>()
.is_some()
{
Value::Null
} else if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() {
Value::Bool(bool_box.value)
@ -259,7 +258,8 @@ fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
Value::String(string_box.value.clone())
} else if let Some(array_box) = value.as_any().downcast_ref::<ArrayBox>() {
let items = array_box.items.read().unwrap();
let arr: Vec<Value> = items.iter()
let arr: Vec<Value> = items
.iter()
.map(|item| nyash_box_to_json_value(item.clone_box()))
.collect();
Value::Array(arr)

View File

@ -1,10 +1,10 @@
/*! 🗄️ MapBox - キー値ストレージBox
*
*
* ## 📝 概要
* 高性能キー値ストレージを提供するBox。
* JavaScript Map、Python dict、C# Dictionaryと同等機能。
* 動的データ管理やキャッシュ実装に最適。
*
*
* ## 🛠️ 利用可能メソッド
* - `set(key, value)` - キー値ペア設定
* - `get(key)` - 値取得
@ -15,21 +15,21 @@
* - `values()` - 全値取得
* - `size()` - データ数取得
* - `isEmpty()` - 空判定
*
*
* ## 💡 使用例
* ```nyash
* local map, result
* map = new MapBox()
*
*
* // データ設定
* map.set("name", "Alice")
* map.set("age", 25)
* map.set("active", true)
*
*
* // データ取得
* result = map.get("name") // "Alice"
* print("User: " + result)
*
*
* // 存在確認
* if (map.has("email")) {
* print("Email: " + map.get("email"))
@ -37,7 +37,7 @@
* print("No email registered")
* }
* ```
*
*
* ## 🎮 実用例 - ゲーム設定管理
* ```nyash
* static box GameConfig {
@ -68,7 +68,7 @@
* }
* }
* ```
*
*
* ## 🔍 キャッシュ実装例
* ```nyash
* static box APICache {
@ -95,7 +95,7 @@
* }
* }
* ```
*
*
* ## ⚠️ 注意
* - キーは自動的に文字列変換される
* - スレッドセーフ (Arc<RwLock>使用)
@ -103,41 +103,45 @@
* - 存在しないキーの取得は "Key not found" メッセージ返却
*/
use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, IntegerBox, BoolBox};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::ArrayBox;
use std::fmt::{Debug, Display};
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, RwLock}; // Arc追加
use std::fmt::{Debug, Display};
use std::sync::{Arc, RwLock}; // Arc追加
/// キーバリューストアを表すBox
pub struct MapBox {
data: Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>, // Arc追加
data: Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>, // Arc追加
base: BoxBase,
}
impl MapBox {
pub fn new() -> Self {
Self {
data: Arc::new(RwLock::new(HashMap::new())), // Arc::new追加
data: Arc::new(RwLock::new(HashMap::new())), // Arc::new追加
base: BoxBase::new(),
}
}
/// 値を設定
pub fn set(&self, key: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
self.data.write().unwrap().insert(key_str.clone(), value);
Box::new(StringBox::new(&format!("Set key: {}", key_str)))
}
/// 値を取得
pub fn get(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
match self.data.read().unwrap().get(&key_str) {
Some(value) => {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return value.share_box();
}
value.clone_box()
@ -145,13 +149,15 @@ impl MapBox {
None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))),
}
}
/// キーが存在するかチェック
pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
Box::new(BoolBox::new(self.data.read().unwrap().contains_key(&key_str)))
Box::new(BoolBox::new(
self.data.read().unwrap().contains_key(&key_str),
))
}
/// キーを削除
pub fn delete(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value;
@ -160,7 +166,7 @@ impl MapBox {
None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))),
}
}
/// 全てのキーを取得
pub fn keys(&self) -> Box<dyn NyashBox> {
let keys: Vec<String> = self.data.read().unwrap().keys().cloned().collect();
@ -170,10 +176,13 @@ impl MapBox {
}
Box::new(array)
}
/// 全ての値を取得
pub fn values(&self) -> Box<dyn NyashBox> {
let values: Vec<Box<dyn NyashBox>> = self.data.read().unwrap()
let values: Vec<Box<dyn NyashBox>> = self
.data
.read()
.unwrap()
.values()
.map(|v| v.clone_box())
.collect();
@ -183,45 +192,46 @@ impl MapBox {
}
Box::new(array)
}
/// サイズを取得
pub fn size(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64))
}
/// 全てクリア
pub fn clear(&self) -> Box<dyn NyashBox> {
self.data.write().unwrap().clear();
Box::new(StringBox::new("Map cleared"))
}
/// 各要素に対して関数を実行
pub fn forEach(&self, _callback: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// 簡易実装callbackの実行はスキップ
let count = self.data.read().unwrap().len();
Box::new(StringBox::new(&format!("Iterated over {} items", count)))
}
/// JSON文字列に変換
pub fn toJSON(&self) -> Box<dyn NyashBox> {
let data = self.data.read().unwrap();
let mut json_parts = Vec::new();
for (key, value) in data.iter() {
let value_str = value.to_string_box().value;
// 値が数値の場合はそのまま、文字列の場合は引用符で囲む
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|| value.as_any().downcast_ref::<BoolBox>().is_some() {
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|| value.as_any().downcast_ref::<BoolBox>().is_some()
{
value_str
} else {
format!("\"{}\"", value_str.replace("\"", "\\\""))
};
json_parts.push(format!("\"{}\":{}", key, formatted_value));
}
Box::new(StringBox::new(&format!("{{{}}}", json_parts.join(","))))
}
/// 内部データへのアクセスJSONBox用
pub fn get_data(&self) -> &RwLock<HashMap<String, Box<dyn NyashBox>>> {
&self.data
@ -233,11 +243,12 @@ impl Clone for MapBox {
fn clone(&self) -> Self {
// ディープコピー(独立インスタンス)
let data_guard = self.data.read().unwrap();
let cloned_data: HashMap<String, Box<dyn NyashBox>> = data_guard.iter()
.map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー
let cloned_data: HashMap<String, Box<dyn NyashBox>> = data_guard
.iter()
.map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー
.collect();
MapBox {
data: Arc::new(RwLock::new(cloned_data)), // 新しいArc
data: Arc::new(RwLock::new(cloned_data)), // 新しいArc
base: BoxBase::new(),
}
}
@ -247,50 +258,51 @@ impl BoxCore for MapBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let size = self.data.read().unwrap().len();
write!(f, "MapBox(size={})", size)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for MapBox {
fn is_identity(&self) -> bool { true }
fn is_identity(&self) -> bool {
true
}
fn type_name(&self) -> &'static str {
"MapBox"
}
fn to_string_box(&self) -> StringBox {
let size = self.data.read().unwrap().len();
StringBox::new(&format!("MapBox(size={})", size))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 🎯 状態共有の核心実装
fn share_box(&self) -> Box<dyn NyashBox> {
let new_instance = MapBox {
data: Arc::clone(&self.data), // Arcクローンで状態共有
base: BoxBase::new(), // 新しいID
data: Arc::clone(&self.data), // Arcクローンで状態共有
base: BoxBase::new(), // 新しいID
};
Box::new(new_instance)
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_map) = other.as_any().downcast_ref::<MapBox>() {
// 同じインスタンスかチェック(データの共有を考慮)
@ -299,7 +311,6 @@ impl NyashBox for MapBox {
BoolBox::new(false)
}
}
}
impl Display for MapBox {

View File

@ -1,54 +1,54 @@
/*! 🧮 MathBox - 数学計算Box
*
*
* ## 📝 概要
* 高度な数学演算を提供するBox。Python mathモジュールや
* JavaScript Math オブジェクトと同様の機能を提供。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 🔢 基本計算
* - `abs(value)` - 絶対値
* - `max(a, b)` - 最大値
* - `min(a, b)` - 最小値
* - `pow(base, exp)` - 累乗 (base^exp)
* - `sqrt(value)` - 平方根
*
*
* ### 📐 三角関数
* - `sin(radians)` - 正弦
* - `cos(radians)` - 余弦
* - `tan(radians)` - 正接
*
*
* ### 📊 対数・指数関数
* - `log(value)` - 自然対数 (ln)
* - `log10(value)` - 常用対数
* - `exp(value)` - 指数関数 (e^x)
*
*
* ### 🔄 丸め関数
* - `floor(value)` - 切り下げ
* - `ceil(value)` - 切り上げ
* - `round(value)` - 四捨五入
*
*
* ### 📏 定数取得
* - `getPi()` - 円周率π (3.14159...)
* - `getE()` - 自然対数の底e (2.71828...)
*
*
* ## 💡 使用例
* ```nyash
* local math, result
* math = new MathBox()
*
*
* result = math.abs(-42) // 42
* result = math.max(10, 25) // 25
* result = math.sqrt(16) // 4.0
* result = math.pow(2, 3) // 8.0
* result = math.sin(math.getPi() / 2) // 1.0
*
*
* // 計算例
* local pi, area
* pi = math.getPi()
* area = pi * math.pow(5, 2) // 半径5の円の面積
* ```
*
*
* ## ⚠️ 注意
* - 三角関数の引数はラジアン
* - 負数の平方根・対数はエラー
@ -56,9 +56,9 @@
* - 整数演算は自動でFloatBoxに変換される場合あり
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use std::fmt::{Debug, Display};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use std::any::Any;
use std::fmt::{Debug, Display};
/// 数学演算を提供するBox
#[derive(Debug, Clone)]
@ -68,11 +68,11 @@ pub struct MathBox {
impl MathBox {
pub fn new() -> Self {
Self {
base: BoxBase::new()
Self {
base: BoxBase::new(),
}
}
/// 絶対値を計算
pub fn abs(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -83,46 +83,46 @@ impl MathBox {
Box::new(StringBox::new("Error: abs() requires numeric input"))
}
}
/// 最大値を返す
pub fn max(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>()
b.as_any().downcast_ref::<IntegerBox>(),
) {
Box::new(IntegerBox::new(a_int.value.max(b_int.value)))
} else if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<FloatBox>(),
b.as_any().downcast_ref::<FloatBox>()
b.as_any().downcast_ref::<FloatBox>(),
) {
Box::new(FloatBox::new(a_float.value.max(b_float.value)))
} else {
Box::new(StringBox::new("Error: max() requires numeric inputs"))
}
}
/// 最小値を返す
pub fn min(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>()
b.as_any().downcast_ref::<IntegerBox>(),
) {
Box::new(IntegerBox::new(a_int.value.min(b_int.value)))
} else if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<FloatBox>(),
b.as_any().downcast_ref::<FloatBox>()
b.as_any().downcast_ref::<FloatBox>(),
) {
Box::new(FloatBox::new(a_float.value.min(b_float.value)))
} else {
Box::new(StringBox::new("Error: min() requires numeric inputs"))
}
}
/// 累乗を計算
pub fn pow(&self, base: Box<dyn NyashBox>, exp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(base_int), Some(exp_int)) = (
base.as_any().downcast_ref::<IntegerBox>(),
exp.as_any().downcast_ref::<IntegerBox>()
exp.as_any().downcast_ref::<IntegerBox>(),
) {
if exp_int.value >= 0 {
let result = (base_int.value as f64).powi(exp_int.value as i32);
@ -134,7 +134,7 @@ impl MathBox {
Box::new(StringBox::new("Error: pow() requires numeric inputs"))
}
}
/// 平方根を計算
pub fn sqrt(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -153,19 +153,19 @@ impl MathBox {
Box::new(StringBox::new("Error: sqrt() requires numeric input"))
}
}
/// 円周率πを返す
#[allow(non_snake_case)]
pub fn getPi(&self) -> Box<dyn NyashBox> {
Box::new(FloatBox::new(std::f64::consts::PI))
}
/// 自然対数の底eを返す
#[allow(non_snake_case)]
pub fn getE(&self) -> Box<dyn NyashBox> {
Box::new(FloatBox::new(std::f64::consts::E))
}
/// サイン(正弦)
pub fn sin(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -176,7 +176,7 @@ impl MathBox {
Box::new(StringBox::new("Error: sin() requires numeric input"))
}
}
/// コサイン(余弦)
pub fn cos(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -187,7 +187,7 @@ impl MathBox {
Box::new(StringBox::new("Error: cos() requires numeric input"))
}
}
/// タンジェント(正接)
pub fn tan(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -198,7 +198,7 @@ impl MathBox {
Box::new(StringBox::new("Error: tan() requires numeric input"))
}
}
/// 自然対数
pub fn log(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -217,7 +217,7 @@ impl MathBox {
Box::new(StringBox::new("Error: log() requires numeric input"))
}
}
/// 常用対数底10
pub fn log10(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -236,7 +236,7 @@ impl MathBox {
Box::new(StringBox::new("Error: log10() requires numeric input"))
}
}
/// 指数関数e^x
pub fn exp(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -247,33 +247,33 @@ impl MathBox {
Box::new(StringBox::new("Error: exp() requires numeric input"))
}
}
/// 床関数(切り下げ)
pub fn floor(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(IntegerBox::new(float_box.value.floor() as i64))
} else {
Box::new(StringBox::new("Error: floor() requires numeric input"))
}
}
/// 天井関数(切り上げ)
pub fn ceil(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(IntegerBox::new(float_box.value.ceil() as i64))
} else {
Box::new(StringBox::new("Error: ceil() requires numeric input"))
}
}
/// 四捨五入
pub fn round(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Box::new(IntegerBox::new(float_box.value.round() as i64))
} else {
@ -286,19 +286,19 @@ impl BoxCore for MathBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "MathBox()")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -308,20 +308,20 @@ impl NyashBox for MathBox {
fn type_name(&self) -> &'static str {
"MathBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("MathBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_math) = other.as_any().downcast_ref::<MathBox>() {
BoolBox::new(self.box_id() == other_math.box_id())
@ -329,7 +329,6 @@ impl NyashBox for MathBox {
BoolBox::new(false)
}
}
}
impl Display for MathBox {
@ -347,9 +346,9 @@ pub struct FloatBox {
impl FloatBox {
pub fn new(value: f64) -> Self {
Self {
value,
base: BoxBase::new()
Self {
value,
base: BoxBase::new(),
}
}
}
@ -358,19 +357,19 @@ impl BoxCore for FloatBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
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
}
@ -380,20 +379,20 @@ impl NyashBox for FloatBox {
fn type_name(&self) -> &'static str {
"FloatBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&self.value.to_string())
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
BoolBox::new((self.value - other_float.value).abs() < f64::EPSILON)
@ -403,7 +402,6 @@ impl NyashBox for FloatBox {
BoolBox::new(false)
}
}
}
impl Display for FloatBox {
@ -423,19 +421,19 @@ pub struct RangeBox {
impl RangeBox {
pub fn new(start: i64, end: i64, step: i64) -> Self {
Self {
start,
end,
step,
base: BoxBase::new()
Self {
start,
end,
step,
base: BoxBase::new(),
}
}
/// イテレータとして値を生成
pub fn iter(&self) -> Vec<i64> {
let mut result = Vec::new();
let mut current = self.start;
if self.step > 0 {
while current < self.end {
result.push(current);
@ -447,7 +445,7 @@ impl RangeBox {
current += self.step;
}
}
result
}
}
@ -456,19 +454,19 @@ impl BoxCore for RangeBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Range({}, {}, {})", self.start, self.end, self.step)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -478,36 +476,38 @@ impl NyashBox for RangeBox {
fn type_name(&self) -> &'static str {
"RangeBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&format!("Range({}, {}, {})", self.start, self.end, self.step))
StringBox::new(&format!(
"Range({}, {}, {})",
self.start, self.end, self.step
))
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_range) = other.as_any().downcast_ref::<RangeBox>() {
BoolBox::new(
self.start == other_range.start &&
self.end == other_range.end &&
self.step == other_range.step
self.start == other_range.start
&& self.end == other_range.end
&& self.step == other_range.step,
)
} else {
BoolBox::new(false)
}
}
}
impl Display for RangeBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,38 +1,38 @@
/*! 🎯 Nyash Box実装モジュール
* Everything is Box哲学に基づく各Box型の実装
*
*
* ## 📦 利用可能なBox一覧
*
*
* ### 🔤 基本データ型Box
* - **StringBox**: 文字列操作 - `"Hello".length()`, `str.split(",")`
* - **IntegerBox**: 整数計算 - `42.add(8)`, `num.toString()`
* - **BoolBox**: 真偽値 - `true.not()`, `flag.toString()`
*
*
* ### 🧮 計算・ユーティリティBox
* - **MathBox**: 数学関数 - `Math.sin(x)`, `Math.random()`
* - **TimeBox**: 時間操作 - `Time.now()`, `time.format()`
* - **RandomBox**: 乱数生成 - `Random.int(10)`, `Random.choice(array)`
*
*
* ### 🖥️ システム・IO Box
* - **ConsoleBox**: コンソール出力 - `console.log()`, `console.error()`
* - **DebugBox**: デバッグ支援 - `debug.trace()`, `debug.memory()`
* - **SoundBox**: 音声再生 - `sound.beep()`, `sound.play(file)`
*
*
* ### 🗄️ コレクション・データBox
* - **MapBox**: キー値ストレージ - `map.set(key, val)`, `map.get(key)`
* - **NullBox**: NULL値表現 - `null.toString()` → "void"
*
*
* ### 🖼️ GUI・グラフィックBox
* - **EguiBox**: デスクトップGUI - `gui.setTitle()`, `gui.run()`
*
*
* ### 🌐 Web専用Box (WASM環境)
* - **WebDisplayBox**: HTML表示 - `display.show(html)`
* - **WebConsoleBox**: ブラウザコンソール - `webConsole.log()`
* - **WebCanvasBox**: Canvas描画 - `canvas.drawRect()`
*
*
* ### 🔗 通信・ネットワークBox
* - **SimpleIntentBox**: P2P通信 - `intent.send()`, `intent.on()`
*
*
* ## 💡 使用例
* ```nyash
* // 基本的な使い方
@ -40,7 +40,7 @@
* str = "Nyash"
* num = 42
* result = str.concat(" v") + num.toString()
*
*
* // GUIアプリ作成
* local app
* app = new EguiBox()
@ -53,41 +53,41 @@
#![allow(non_snake_case)]
// 各Boxモジュールを宣言
pub mod string_box;
pub mod integer_box;
pub mod bool_box;
pub mod math_box;
pub mod time_box;
pub mod debug_box;
pub mod integer_box;
pub mod math_box;
pub mod random_box;
pub mod string_box;
pub mod time_box;
// These boxes use web APIs that require special handling in WASM
pub mod aot_compiler_box;
pub mod aot_config_box;
#[cfg(not(target_arch = "wasm32"))]
pub mod timer_box;
pub mod audio_box;
#[cfg(not(target_arch = "wasm32"))]
pub mod canvas_event_box;
#[cfg(not(target_arch = "wasm32"))]
pub mod canvas_loop_box;
#[cfg(not(target_arch = "wasm32"))]
pub mod audio_box;
pub mod console_box;
pub mod debug_config_box;
pub mod function_box;
pub mod gc_config_box;
pub mod jit_config_box;
pub mod jit_events_box;
pub mod jit_hostcall_registry_box;
pub mod jit_policy_box;
pub mod jit_stats_box;
pub mod jit_strict_box;
pub mod map_box;
#[cfg(not(target_arch = "wasm32"))]
pub mod qr_box;
pub mod sound_box;
pub mod map_box;
pub mod console_box;
pub mod jit_config_box;
pub mod jit_stats_box;
pub mod jit_policy_box;
pub mod jit_events_box;
pub mod jit_strict_box;
pub mod jit_hostcall_registry_box;
pub mod debug_config_box;
pub mod gc_config_box;
pub mod aot_config_box;
pub mod aot_compiler_box;
pub mod task_group_box;
pub mod token_box;
pub mod function_box;
pub mod ref_cell_box;
pub mod sound_box;
pub mod task_group_box;
#[cfg(not(target_arch = "wasm32"))]
pub mod timer_box;
pub mod token_box;
// Web専用Box群ブラウザ環境でのみ利用可能
#[cfg(target_arch = "wasm32")]
@ -99,34 +99,34 @@ pub mod egui_box;
// 共通で使う型とトレイトを再エクスポート
// pub use string_box::StringBox; // レガシー実装、box_trait::StringBoxを使用すること
// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること
// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること
// pub use bool_box::BoolBox; // レガシー実装、box_trait::BoolBoxを使用すること
pub use math_box::{MathBox, FloatBox};
pub use time_box::{TimeBox, DateTimeBox};
pub use debug_box::DebugBox;
pub use random_box::RandomBox;
pub use aot_compiler_box::AotCompilerBox;
pub use aot_config_box::AotConfigBox;
#[cfg(not(target_arch = "wasm32"))]
pub use timer_box::TimerBox;
pub use audio_box::AudioBox;
#[cfg(not(target_arch = "wasm32"))]
pub use canvas_event_box::CanvasEventBox;
#[cfg(not(target_arch = "wasm32"))]
pub use canvas_loop_box::CanvasLoopBox;
#[cfg(not(target_arch = "wasm32"))]
pub use audio_box::AudioBox;
pub use console_box::ConsoleBox;
pub use debug_box::DebugBox;
pub use jit_config_box::JitConfigBox;
pub use jit_events_box::JitEventsBox;
pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
pub use jit_policy_box::JitPolicyBox;
pub use jit_stats_box::JitStatsBox;
pub use jit_strict_box::JitStrictBox;
pub use map_box::MapBox;
pub use math_box::{FloatBox, MathBox};
#[cfg(not(target_arch = "wasm32"))]
pub use qr_box::QRBox;
pub use random_box::RandomBox;
pub use sound_box::SoundBox;
pub use map_box::MapBox;
pub use console_box::ConsoleBox;
pub use jit_config_box::JitConfigBox;
pub use jit_stats_box::JitStatsBox;
pub use jit_policy_box::JitPolicyBox;
pub use jit_events_box::JitEventsBox;
pub use jit_strict_box::JitStrictBox;
pub use jit_hostcall_registry_box::JitHostcallRegistryBox;
pub use aot_config_box::AotConfigBox;
pub use aot_compiler_box::AotCompilerBox;
pub use task_group_box::TaskGroupBox;
pub use time_box::{DateTimeBox, TimeBox};
#[cfg(not(target_arch = "wasm32"))]
pub use timer_box::TimerBox;
pub use token_box::TokenBox;
// EguiBoxの再エクスポート非WASM環境のみ
@ -135,7 +135,7 @@ pub use egui_box::EguiBox;
// Web Box群の再エクスポートWASM環境のみ
#[cfg(target_arch = "wasm32")]
pub use web::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
pub use web::{WebCanvasBox, WebConsoleBox, WebDisplayBox};
pub mod null_box;
@ -144,35 +144,35 @@ pub mod array;
pub mod buffer;
pub mod file;
pub mod future;
pub mod json;
pub mod result;
pub mod http;
pub mod stream;
pub mod regex;
pub mod socket_box;
pub mod http_message_box;
pub mod http_server_box;
pub mod json;
pub mod regex;
pub mod result;
pub mod socket_box;
pub mod stream;
// P2P通信Box群 (NEW! - Completely rewritten)
pub mod intent_box;
pub mod p2p_box;
// null関数も再エクスポート
pub use null_box::{NullBox, null};
pub use null_box::{null, NullBox};
// High-priority Box types re-export
pub use array::ArrayBox;
pub use buffer::BufferBox;
pub use file::FileBox;
pub use future::{NyashFutureBox, FutureBox, FutureWeak};
pub use json::JSONBox;
pub use result::{NyashResultBox, ResultBox};
pub use future::{FutureBox, FutureWeak, NyashFutureBox};
pub use http::HttpClientBox;
pub use stream::{NyashStreamBox, StreamBox};
pub use regex::RegexBox;
pub use socket_box::SocketBox;
pub use http_message_box::{HTTPRequestBox, HTTPResponseBox};
pub use http_server_box::HTTPServerBox;
pub use json::JSONBox;
pub use regex::RegexBox;
pub use result::{NyashResultBox, ResultBox};
pub use socket_box::SocketBox;
pub use stream::{NyashStreamBox, StreamBox};
// P2P通信Boxの再エクスポート
pub use intent_box::IntentBox;

View File

@ -1,37 +1,37 @@
/*! 🚫 NullBox - NULL値表現Box
*
*
* ## 📝 概要
* null/void値を表現する特別なBox。
* JavaScript null、Python None、C# nullと同等の機能を提供。
* NULL安全プログラミングをサポート。
*
*
* ## 🛠️ 利用可能メソッド
* - `isNull()` - null判定 (常にtrue)
* - `isNotNull()` - 非null判定 (常にfalse)
* - `toString()` - 文字列変換 ("null")
* - `equals(other)` - 等価比較 (他のnullとのみtrue)
*
*
* ## 🛡️ 静的メソッド (null安全機能)
* - `NullBox.checkNull(value)` - 値のnull判定
* - `NullBox.checkNotNull(value)` - 値の非null判定
* - `NullBox.getOrDefault(value, default)` - null時デフォルト値取得
*
*
* ## 💡 使用例
* ```nyash
* local user, name, default_name
*
*
* // null値の作成と判定
* user = null
* if (user == null) {
* print("User is null")
* }
*
*
* // null安全な値取得
* name = getUsername() // null の可能性
* default_name = NullBox.getOrDefault(name, "Anonymous")
* print("Hello, " + default_name)
* ```
*
*
* ## 🎮 実用例 - null安全プログラミング
* ```nyash
* static box UserManager {
@ -62,12 +62,12 @@
* }
* }
* ```
*
*
* ## 🔍 デバッグ活用
* ```nyash
* local data, result
* data = fetchDataFromAPI() // null になる可能性
*
*
* // null チェック付きデバッグ
* if (NullBox.checkNull(data)) {
* print("Warning: API returned null data")
@ -76,7 +76,7 @@
* result = data.process()
* }
* ```
*
*
* ## ⚠️ 重要な特徴
* - `null == null` は常にtrue
* - `null.toString()` は "null"
@ -84,9 +84,9 @@
* - メソッド呼び出し時のnullチェックでNullPointerException防止
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use std::fmt::{Debug, Display};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::fmt::{Debug, Display};
/// null値を表現するBox
#[derive(Debug, Clone)]
@ -96,36 +96,33 @@ pub struct NullBox {
impl NullBox {
pub fn new() -> Self {
Self {
base: BoxBase::new()
Self {
base: BoxBase::new(),
}
}
/// null値かどうかを判定
pub fn is_null(&self) -> bool {
true // NullBoxは常にnull
true // NullBoxは常にnull
}
/// 値がnullでないかを判定
pub fn is_not_null(&self) -> bool {
false // NullBoxは常にnull
false // NullBoxは常にnull
}
/// 他の値がnullかどうかを判定
pub fn check_null(value: &dyn NyashBox) -> bool {
value.as_any().downcast_ref::<NullBox>().is_some()
}
/// 他の値がnullでないかを判定
pub fn check_not_null(value: &dyn NyashBox) -> bool {
!Self::check_null(value)
}
/// null安全な値の取得
pub fn get_or_default(
value: &dyn NyashBox,
default: Box<dyn NyashBox>
) -> Box<dyn NyashBox> {
pub fn get_or_default(value: &dyn NyashBox, default: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if Self::check_null(value) {
default
} else {
@ -138,19 +135,19 @@ impl BoxCore for NullBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "null")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -160,25 +157,24 @@ impl NyashBox for NullBox {
fn type_name(&self) -> &'static str {
"NullBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("null")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
// すべてのNullBoxは等しい
BoolBox::new(other.as_any().downcast_ref::<NullBox>().is_some())
}
}
impl Display for NullBox {
@ -196,7 +192,7 @@ pub fn null() -> Box<dyn NyashBox> {
mod tests {
use super::*;
use crate::box_trait::IntegerBox;
#[test]
fn test_null_creation() {
let null_box = NullBox::new();
@ -204,41 +200,41 @@ mod tests {
assert!(!null_box.is_not_null());
assert_eq!(null_box.to_string_box().value, "null");
}
#[test]
fn test_null_check() {
let null_box = null();
let int_box = Box::new(IntegerBox::new(42));
assert!(NullBox::check_null(null_box.as_ref()));
assert!(!NullBox::check_null(int_box.as_ref()));
assert!(!NullBox::check_not_null(null_box.as_ref()));
assert!(NullBox::check_not_null(int_box.as_ref()));
}
#[test]
fn test_null_equality() {
let null1 = NullBox::new();
let null2 = NullBox::new();
let int_box = IntegerBox::new(42);
assert!(null1.equals(&null2).value);
assert!(!null1.equals(&int_box).value);
}
#[test]
fn test_get_or_default() {
let null_box = null();
let default_value = Box::new(IntegerBox::new(100));
let actual_value = Box::new(IntegerBox::new(42));
// nullの場合はデフォルト値を返す
let result1 = NullBox::get_or_default(null_box.as_ref(), default_value.clone());
assert_eq!(result1.to_string_box().value, "100");
// null以外の場合は元の値を返す
let result2 = NullBox::get_or_default(actual_value.as_ref(), default_value);
assert_eq!(result2.to_string_box().value, "42");
}
}
}

View File

@ -1,49 +1,49 @@
/*! 📡 P2PBox - Modern P2P Communication Node
*
*
* ## 📝 概要
* P2PBoxは現代的なP2P通信ードを表現するBoxです。
* 新しいアーキテクチャIntentBox + MessageBus + Transportを使用し、
* 構造化メッセージによる安全で明示的な通信を実現します。
*
*
* ## 🎯 AI大会議決定事項準拠
* - **個別送信のみ**: `send(to, message)` 固定API
* - **ブロードキャスト除外**: 安全性のため完全除外
* - **明示的API**: 関数オーバーロード不採用
* - **構造化メッセージ**: IntentBox (name + payload) 使用
*
*
* ## 🛠️ 利用可能メソッド
* - `new(node_id, transport)` - ノードを作成
* - `send(to, intent)` - 特定ノードにメッセージ送信
* - `on(intent_name, handler)` - イベントリスナー登録
* - `getNodeId()` - ードID取得
* - `isReachable(node_id)` - ノード到達可能性確認
*
*
* ## 💡 使用例
* ```nyash
* // ノード作成
* local alice = new P2PBox("alice", "inprocess")
* local bob = new P2PBox("bob", "inprocess")
*
*
* // 受信ハンドラ登録
* bob.on("chat.message", function(intent, from) {
* print("From " + from + ": " + intent.payload.text)
* })
*
*
* // メッセージ送信
* local msg = new IntentBox("chat.message", { text: "Hello P2P!" })
* alice.send("bob", msg)
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use crate::boxes::result::ResultBox;
use crate::boxes::IntentBox;
use crate::method_box::MethodBox;
use crate::boxes::result::ResultBox;
use crate::transport::{Transport, InProcessTransport};
use crate::transport::{InProcessTransport, Transport};
use std::any::Any;
use std::sync::{RwLock, Arc};
use std::sync::atomic::{AtomicBool, Ordering};
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
/// P2PBox - P2P通信ード (RwLock pattern)
#[derive(Debug)]
@ -72,7 +72,7 @@ impl Clone for P2PBox {
let handlers_val = HashMap::new(); // Start fresh for cloned instance
let last_from_val = self.last_from.read().unwrap().clone();
let last_intent_val = self.last_intent_name.read().unwrap().clone();
Self {
base: BoxBase::new(), // New unique ID for clone
node_id: RwLock::new(node_id_val),
@ -93,7 +93,7 @@ pub enum TransportKind {
impl std::str::FromStr for TransportKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"inprocess" => Ok(TransportKind::InProcess),
@ -136,28 +136,38 @@ impl P2PBox {
{
if let Ok(mut t) = transport_arc_outer.write() {
let transport_arc_for_cb = Arc::clone(&transport_arc_outer);
t.register_intent_handler("sys.ping", Box::new(move |env| {
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
// Reply asynchronously to avoid deep call stacks
let to = env.from.clone();
let reply = crate::boxes::IntentBox::new("sys.pong".to_string(), serde_json::json!({}));
let transport_arc = Arc::clone(&transport_arc_for_cb);
std::thread::spawn(move || {
// slight delay to avoid lock contention and ordering races
std::thread::sleep(std::time::Duration::from_millis(3));
if let Ok(transport) = transport_arc.read() {
let _ = transport.send(&to, reply, Default::default());
t.register_intent_handler(
"sys.ping",
Box::new(move |env| {
if let Ok(mut lf) = last_from.write() {
*lf = Some(env.from.clone());
}
});
}));
if let Ok(mut li) = last_intent.write() {
*li = Some(env.intent.get_name().to_string_box().value);
}
// Reply asynchronously to avoid deep call stacks
let to = env.from.clone();
let reply = crate::boxes::IntentBox::new(
"sys.pong".to_string(),
serde_json::json!({}),
);
let transport_arc = Arc::clone(&transport_arc_for_cb);
std::thread::spawn(move || {
// slight delay to avoid lock contention and ordering races
std::thread::sleep(std::time::Duration::from_millis(3));
if let Ok(transport) = transport_arc.read() {
let _ = transport.send(&to, reply, Default::default());
}
});
}),
);
};
}
}
p2p
}
/// ードIDを取得
pub fn get_node_id(&self) -> Box<dyn NyashBox> {
let node_id = self.node_id.read().unwrap().clone();
@ -177,14 +187,17 @@ impl P2PBox {
// Register temporary transport-level handler for sys.pong
if let Ok(mut t) = self.transport.write() {
t.register_intent_handler("sys.pong", Box::new(move |env| {
if active_cb.load(Ordering::SeqCst) {
// record last receive for visibility
// Note: we cannot access self here safely; rely on tx notify only
let _ = env; // suppress unused
let _ = tx.send(());
}
}));
t.register_intent_handler(
"sys.pong",
Box::new(move |env| {
if active_cb.load(Ordering::SeqCst) {
// record last receive for visibility
// Note: we cannot access self here safely; rely on tx notify only
let _ = env; // suppress unused
let _ = tx.send(());
}
}),
);
// Send sys.ping
let ping = IntentBox::new("sys.ping".to_string(), serde_json::json!({}));
@ -199,7 +212,9 @@ impl P2PBox {
}
// Wait for pong with timeout
let ok = rx.recv_timeout(std::time::Duration::from_millis(timeout_ms)).is_ok();
let ok = rx
.recv_timeout(std::time::Duration::from_millis(timeout_ms))
.is_ok();
active.store(false, Ordering::SeqCst);
Box::new(BoolBox::new(ok))
}
@ -208,11 +223,11 @@ impl P2PBox {
pub fn ping(&self, to: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
self.ping_with_timeout(to, 300)
}
/// 特定ノードにメッセージを送信
pub fn send(&self, to: Box<dyn NyashBox>, intent: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let to_str = to.to_string_box().value;
// Extract IntentBox from the generic Box
if let Some(intent_box) = intent.as_any().downcast_ref::<IntentBox>() {
let transport = self.transport.read().unwrap();
@ -221,20 +236,34 @@ impl P2PBox {
// Minimal loopback trace without relying on transport callbacks
let self_id = self.node_id.read().unwrap().clone();
if to_str == self_id {
if let Ok(mut lf) = self.last_from.write() { *lf = Some(self_id.clone()); }
if let Ok(mut li) = self.last_intent_name.write() { *li = Some(intent_box.get_name().to_string_box().value); }
if let Ok(mut lf) = self.last_from.write() {
*lf = Some(self_id.clone());
}
if let Ok(mut li) = self.last_intent_name.write() {
*li = Some(intent_box.get_name().to_string_box().value);
}
}
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
},
Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!("{:?}", e))))),
}
Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!(
"{:?}",
e
))))),
}
} else {
Box::new(ResultBox::new_err(Box::new(StringBox::new("Second argument must be IntentBox"))))
Box::new(ResultBox::new_err(Box::new(StringBox::new(
"Second argument must be IntentBox",
))))
}
}
/// イベントハンドラーを登録
fn register_handler_internal(&self, intent_str: &str, handler: &Box<dyn NyashBox>, once: bool) -> Box<dyn NyashBox> {
fn register_handler_internal(
&self,
intent_str: &str,
handler: &Box<dyn NyashBox>,
once: bool,
) -> Box<dyn NyashBox> {
// 保存
{
let mut handlers = self.handlers.write().unwrap();
@ -245,7 +274,10 @@ impl P2PBox {
let flag = Arc::new(AtomicBool::new(true));
{
let mut flags = self.handler_flags.write().unwrap();
flags.entry(intent_str.to_string()).or_default().push(flag.clone());
flags
.entry(intent_str.to_string())
.or_default()
.push(flag.clone());
}
// once情報を記録
{
@ -265,82 +297,111 @@ impl P2PBox {
// capture flags map to allow removal on once
let flags_arc = Arc::clone(&self.handler_flags);
let intent_name_closure = intent_name.clone();
t.register_intent_handler(&intent_name, Box::new(move |env| {
if flag.load(Ordering::SeqCst) {
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
let _ = method_clone.invoke(vec![
Box::new(env.intent.clone()),
Box::new(StringBox::new(env.from.clone())),
]);
if once {
flag.store(false, Ordering::SeqCst);
if let Ok(mut flags) = flags_arc.write() {
if let Some(v) = flags.get_mut(&intent_name_closure) { v.clear(); }
t.register_intent_handler(
&intent_name,
Box::new(move |env| {
if flag.load(Ordering::SeqCst) {
if let Ok(mut lf) = last_from.write() {
*lf = Some(env.from.clone());
}
if let Ok(mut li) = last_intent.write() {
*li = Some(env.intent.get_name().to_string_box().value);
}
let _ = method_clone.invoke(vec![
Box::new(env.intent.clone()),
Box::new(StringBox::new(env.from.clone())),
]);
if once {
flag.store(false, Ordering::SeqCst);
if let Ok(mut flags) = flags_arc.write() {
if let Some(v) = flags.get_mut(&intent_name_closure) {
v.clear();
}
}
}
}
}
}));
}),
);
// FunctionBox ハンドラー(関数値)
} else if let Some(func_box) = handler.as_any().downcast_ref::<crate::boxes::function_box::FunctionBox>() {
} else if let Some(func_box) = handler
.as_any()
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
{
let func_clone = func_box.clone();
let intent_name = intent_str.to_string();
let last_from = Arc::clone(&self.last_from);
let last_intent = Arc::clone(&self.last_intent_name);
let flags_arc = Arc::clone(&self.handler_flags);
let intent_name_closure = intent_name.clone();
t.register_intent_handler(&intent_name, Box::new(move |env| {
if flag.load(Ordering::SeqCst) {
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
// 最小インタープリタで FunctionBox を実行
let mut interp = crate::interpreter::NyashInterpreter::new();
// キャプチャ注入
for (k, v) in func_clone.env.captures.iter() {
interp.declare_local_variable(k, v.clone_or_share());
}
if let Some(me_w) = &func_clone.env.me_value {
if let Some(me_arc) = me_w.upgrade() {
interp.declare_local_variable("me", (*me_arc).clone_or_share());
t.register_intent_handler(
&intent_name,
Box::new(move |env| {
if flag.load(Ordering::SeqCst) {
if let Ok(mut lf) = last_from.write() {
*lf = Some(env.from.clone());
}
if let Ok(mut li) = last_intent.write() {
*li = Some(env.intent.get_name().to_string_box().value);
}
// 最小インタープリタで FunctionBox を実行
let mut interp = crate::interpreter::NyashInterpreter::new();
// キャプチャ注入
for (k, v) in func_clone.env.captures.iter() {
interp.declare_local_variable(k, v.clone_or_share());
}
if let Some(me_w) = &func_clone.env.me_value {
if let Some(me_arc) = me_w.upgrade() {
interp.declare_local_variable("me", (*me_arc).clone_or_share());
}
}
// 引数束縛: intent, from必要数だけ
let args: Vec<Box<dyn NyashBox>> = vec![
Box::new(env.intent.clone()),
Box::new(StringBox::new(env.from.clone())),
];
for (i, p) in func_clone.params.iter().enumerate() {
if let Some(av) = args.get(i) {
interp.declare_local_variable(p, av.clone_or_share());
}
}
// 本体実行
crate::runtime::global_hooks::push_task_scope();
for st in &func_clone.body {
let _ = interp.execute_statement(st);
}
crate::runtime::global_hooks::pop_task_scope();
if once {
flag.store(false, Ordering::SeqCst);
if let Ok(mut flags) = flags_arc.write() {
if let Some(v) = flags.get_mut(&intent_name_closure) {
v.clear();
}
}
}
}
// 引数束縛: intent, from必要数だけ
let args: Vec<Box<dyn NyashBox>> = vec![
Box::new(env.intent.clone()),
Box::new(StringBox::new(env.from.clone())),
];
for (i, p) in func_clone.params.iter().enumerate() {
if let Some(av) = args.get(i) {
interp.declare_local_variable(p, av.clone_or_share());
}
}
// 本体実行
crate::runtime::global_hooks::push_task_scope();
for st in &func_clone.body {
let _ = interp.execute_statement(st);
}
crate::runtime::global_hooks::pop_task_scope();
if once {
flag.store(false, Ordering::SeqCst);
if let Ok(mut flags) = flags_arc.write() {
if let Some(v) = flags.get_mut(&intent_name_closure) { v.clear(); }
}
}
}
}));
}),
);
}
}
Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true))))
}
/// イベントハンドラーを登録
pub fn on(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn on(
&self,
intent_name: Box<dyn NyashBox>,
handler: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
let intent_str = intent_name.to_string_box().value;
self.register_handler_internal(&intent_str, &handler, false)
}
/// 一度だけのハンドラー登録
pub fn on_once(&self, intent_name: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn on_once(
&self,
intent_name: Box<dyn NyashBox>,
handler: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
let intent_str = intent_name.to_string_box().value;
self.register_handler_internal(&intent_str, &handler, true)
}
@ -350,7 +411,9 @@ impl P2PBox {
let intent_str = intent_name.to_string_box().value;
if let Ok(mut flags) = self.handler_flags.write() {
if let Some(v) = flags.get_mut(&intent_str) {
for f in v.iter() { f.store(false, Ordering::SeqCst); }
for f in v.iter() {
f.store(false, Ordering::SeqCst);
}
v.clear();
}
}
@ -364,7 +427,7 @@ impl P2PBox {
let transport = self.transport.read().unwrap();
Box::new(BoolBox::new(transport.is_reachable(&node_str)))
}
/// トランスポート種類を取得
pub fn get_transport_type(&self) -> Box<dyn NyashBox> {
let transport = self.transport.read().unwrap();
@ -396,11 +459,16 @@ impl P2PBox {
// once登録かつ直近受信が同名なら 0 を返す(自己送信の安定化用)
if let (Ok(once_map), Ok(last)) = (self.handler_once.read(), self.last_intent_name.read()) {
if let Some(true) = once_map.get(&name).copied() {
if let Some(li) = &*last { if li == &name { return Box::new(crate::box_trait::IntegerBox::new(0)); } }
if let Some(li) = &*last {
if li == &name {
return Box::new(crate::box_trait::IntegerBox::new(0));
}
}
}
}
let flags = self.handler_flags.read().unwrap();
let cnt = flags.get(&name)
let cnt = flags
.get(&name)
.map(|v| v.iter().filter(|f| f.load(Ordering::SeqCst)).count())
.unwrap_or(0);
Box::new(crate::box_trait::IntegerBox::new(cnt as i64))
@ -414,18 +482,21 @@ impl P2PBox {
/// 最後に受信したIntent名を取得ループバック検証用
pub fn get_last_intent_name(&self) -> Box<dyn NyashBox> {
let v = self.last_intent_name.read().unwrap().clone().unwrap_or_default();
let v = self
.last_intent_name
.read()
.unwrap()
.clone()
.unwrap_or_default();
Box::new(StringBox::new(v))
}
}
impl NyashBox for P2PBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
// Share underlying transport and state via Arc clones
let node_id_val = self.node_id.read().unwrap().clone();
@ -446,7 +517,7 @@ impl NyashBox for P2PBox {
let transport_type = self.transport.read().unwrap().transport_type().to_string();
StringBox::new(format!("P2PBox[{}:{}]", node_id, transport_type))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_p2p) = other.as_any().downcast_ref::<P2PBox>() {
BoolBox::new(self.base.id == other_p2p.base.id)
@ -464,7 +535,7 @@ impl BoxCore for P2PBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -474,11 +545,11 @@ impl BoxCore for P2PBox {
let transport_type = self.transport.read().unwrap().transport_type().to_string();
write!(f, "P2PBox[{}:{}]", node_id, transport_type)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -498,7 +569,10 @@ mod tests {
fn self_ping_sets_last_fields() {
let p = P2PBox::new("alice".to_string(), TransportKind::InProcess);
let intent = IntentBox::new("ping".to_string(), serde_json::json!({}));
let res = p.send(Box::new(StringBox::new("alice".to_string())), Box::new(intent));
let res = p.send(
Box::new(StringBox::new("alice".to_string())),
Box::new(intent),
);
// Ensure Ok
if let Some(r) = res.as_any().downcast_ref::<ResultBox>() {
assert!(matches!(r, ResultBox::Ok(_)));
@ -506,7 +580,10 @@ mod tests {
panic!("send did not return ResultBox");
}
assert_eq!(p.get_last_from().to_string_box().value, "alice".to_string());
assert_eq!(p.get_last_intent_name().to_string_box().value, "ping".to_string());
assert_eq!(
p.get_last_intent_name().to_string_box().value,
"ping".to_string()
);
}
/// Internal helper for tests: register raw Rust handler with optional async reply
@ -521,22 +598,29 @@ mod tests {
// Avoid deep clone (which re-registers transport). Use transport directly for reply.
let transport_arc = Arc::clone(&self.transport);
let reply_name = reply_intent.map(|s| s.to_string());
t.register_intent_handler(&intent_name, Box::new(move |env| {
if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); }
if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); }
if let Some(rn) = reply_name.clone() {
let to = env.from.clone();
let transport_arc = Arc::clone(&transport_arc);
std::thread::spawn(move || {
// slight delay to avoid lock contention
std::thread::sleep(std::time::Duration::from_millis(5));
let intent = IntentBox::new(rn, serde_json::json!({}));
if let Ok(transport) = transport_arc.read() {
let _ = transport.send(&to, intent, Default::default());
}
});
}
}));
t.register_intent_handler(
&intent_name,
Box::new(move |env| {
if let Ok(mut lf) = last_from.write() {
*lf = Some(env.from.clone());
}
if let Ok(mut li) = last_intent.write() {
*li = Some(env.intent.get_name().to_string_box().value);
}
if let Some(rn) = reply_name.clone() {
let to = env.from.clone();
let transport_arc = Arc::clone(&transport_arc);
std::thread::spawn(move || {
// slight delay to avoid lock contention
std::thread::sleep(std::time::Duration::from_millis(5));
let intent = IntentBox::new(rn, serde_json::json!({}));
if let Ok(transport) = transport_arc.read() {
let _ = transport.send(&to, intent, Default::default());
}
});
}
}),
);
}
}
}

View File

@ -1,64 +1,62 @@
/*!
* QRBox - QRコード生成・読み取りBox
*
*
* ## 📝 概要
* QRコードの生成、読み取り、カスタマイズを統一的に管理するBox。
* アプリ間連携、データ共有、認証システムに最適。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 📱 QRコード生成
* - `generate(text)` - テキストからQRコード生成
* - `generateURL(url)` - URL用QRコード生成
* - `generateWiFi(ssid, password, security)` - WiFi設定QR
* - `generateContact(name, phone, email)` - 連絡先QR
*
*
* ### 🎨 カスタマイズ
* - `setSize(width, height)` - QRコードサイズ設定
* - `setColors(fg, bg)` - 前景色・背景色設定
* - `setLogo(image)` - ロゴ埋め込み
* - `setErrorCorrection(level)` - エラー訂正レベル
*
*
* ### 📷 読み取り
* - `scanFromImage(imageData)` - 画像からQR読み取り
* - `scanFromCanvas(canvas)` - Canvasから読み取り
* - `startCamera()` - カメラ読み取り開始
*
*
* ### 📊 情報取得
* - `getDataURL()` - QRコードのData URL取得
* - `getImageData()` - ImageData形式で取得
* - `getInfo()` - QRコード情報取得
*
*
* ## 💡 使用例
* ```nyash
* local qr, canvas
* qr = new QRBox()
* canvas = new WebCanvasBox("qr-canvas", 300, 300)
*
*
* // 基本的なQRコード生成
* qr.generate("https://nyash-lang.org")
* qr.setSize(200, 200)
* qr.setColors("#000000", "#ffffff")
*
*
* // Canvasに描画
* local imageData = qr.getImageData()
* canvas.putImageData(imageData, 50, 50)
*
*
* // WiFi設定QR
* qr.generateWiFi("MyWiFi", "password123", "WPA2")
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{
HtmlCanvasElement, CanvasRenderingContext2d, ImageData
};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData};
/// QRコード管理Box
#[derive(Debug, Clone)]
@ -146,7 +144,11 @@ impl QRBox {
pub fn get_info(&self) -> String {
format!(
"Type: {}, Size: {}x{}, Error Correction: {}, Data Length: {}",
self.qr_type, self.size.0, self.size.1, self.error_correction, self.data.len()
self.qr_type,
self.size.0,
self.size.1,
self.error_correction,
self.data.len()
)
}
@ -170,7 +172,8 @@ impl QRBox {
if let Some(canvas_element) = document.get_element_by_id(canvas_id) {
if let Ok(canvas) = canvas_element.dyn_into::<HtmlCanvasElement>() {
if let Ok(context) = canvas.get_context("2d") {
if let Ok(ctx) = context.unwrap().dyn_into::<CanvasRenderingContext2d>() {
if let Ok(ctx) = context.unwrap().dyn_into::<CanvasRenderingContext2d>()
{
return self.draw_simple_qr(&ctx);
}
}
@ -186,28 +189,29 @@ impl QRBox {
fn draw_simple_qr(&self, ctx: &CanvasRenderingContext2d) -> bool {
let module_size = 8;
let modules = 25; // 25x25のQRコード
// 背景を描画
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(&self.background_color));
ctx.fill_rect(0.0, 0.0, self.size.0 as f64, self.size.1 as f64);
// QRコードパターンを生成簡易版
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(&self.foreground_color));
// データベースの簡単なハッシュを作成
let hash = self.simple_hash(&self.data);
for y in 0..modules {
for x in 0..modules {
// ファインダーパターンの描画
if (x < 7 && y < 7) || (x >= modules - 7 && y < 7) || (x < 7 && y >= modules - 7) {
if (x == 0 || x == 6 || y == 0 || y == 6) ||
(x >= 2 && x <= 4 && y >= 2 && y <= 4) {
if (x == 0 || x == 6 || y == 0 || y == 6)
|| (x >= 2 && x <= 4 && y >= 2 && y <= 4)
{
ctx.fill_rect(
(x * module_size) as f64,
(y * module_size) as f64,
module_size as f64,
module_size as f64
module_size as f64,
);
}
} else {
@ -218,13 +222,13 @@ impl QRBox {
(x * module_size) as f64,
(y * module_size) as f64,
module_size as f64,
module_size as f64
module_size as f64,
);
}
}
}
}
true
}
@ -241,10 +245,16 @@ impl QRBox {
#[cfg(not(target_arch = "wasm32"))]
/// Non-WASM環境用のダミー実装
pub fn draw_to_canvas(&self, canvas_id: &str) -> bool {
println!("QRBox: Drawing QR code to canvas '{}' (simulated)", canvas_id);
println!(
"QRBox: Drawing QR code to canvas '{}' (simulated)",
canvas_id
);
println!(" Data: {}", self.data);
println!(" Size: {}x{}", self.size.0, self.size.1);
println!(" Colors: {} on {}", self.foreground_color, self.background_color);
println!(
" Colors: {} on {}",
self.foreground_color, self.background_color
);
true
}
@ -264,7 +274,8 @@ impl QRBox {
/// バッチ生成機能
pub fn generate_batch(&self, data_list: &[String]) -> Vec<String> {
data_list.iter()
data_list
.iter()
.map(|data| format!("QR for: {}", data))
.collect()
}
@ -279,7 +290,7 @@ impl QRBox {
"H" => 4,
_ => 2,
};
data_len * base_complexity
}
}
@ -288,19 +299,23 @@ impl BoxCore for QRBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "QRBox(type={}, size={}x{})", self.qr_type, self.size.0, self.size.1)
write!(
f,
"QRBox(type={}, size={}x{})",
self.qr_type, self.size.0, self.size.1
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -310,20 +325,23 @@ impl NyashBox for QRBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("QRBox(type={}, size={}x{})", self.qr_type, self.size.0, self.size.1))
StringBox::new(format!(
"QRBox(type={}, size={}x{})",
self.qr_type, self.size.0, self.size.1
))
}
fn type_name(&self) -> &'static str {
"QRBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_qr) = other.as_any().downcast_ref::<QRBox>() {
BoolBox::new(self.base.id == other_qr.base.id)
@ -337,4 +355,4 @@ impl std::fmt::Display for QRBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,46 +1,46 @@
/*! 🎲 RandomBox - 乱数生成Box
*
*
* ## 📝 概要
* 高品質な乱数生成機能を提供するBox。
* ゲーム開発、統計処理、テストデータ生成に最適。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 🔢 基本乱数
* - `random()` - 0.01.0の浮動小数点乱数
* - `randInt(min, max)` - 指定範囲の整数乱数
* - `randBool()` - true/falseのランダム選択
* - `seed(value)` - 乱数種を設定(再現可能な乱数)
*
*
* ### 🎯 選択・配列操作
* - `choice(array)` - 配列からランダム選択
* - `shuffle(array)` - 配列をシャッフル
*
*
* ### 🎨 生成
* - `randString(length)` - ランダム文字列生成
* - `probability(prob)` - 指定確率でtrue
*
*
* ## 💡 使用例
* ```nyash
* local random, result, dice, array
* random = new RandomBox()
*
*
* // 基本的な乱数
* result = random.random() // 0.01.0
* dice = random.randInt(1, 6) // サイコロ(1-6)
* result = random.randBool() // true or false
*
*
* // 配列関連
* array = ["apple", "banana", "cherry"]
* result = random.choice(array) // ランダム選択
* array = random.shuffle(array) // シャッフル
*
*
* // ゲーム用途
* local password, critical_hit
* password = random.randString(8) // 8文字のランダム文字列
* critical_hit = random.probability(0.1) // 10%でクリティカル
* ```
*
*
* ## 🎮 実用例
* ```nyash
* // RPGダメージ計算
@ -50,7 +50,7 @@
* if (is_critical) {
* damage = damage * 2
* }
*
*
* // テストデータ生成
* local users, user_id, user_name
* users = []
@ -60,17 +60,17 @@
* users.push(user_name + ":" + user_id)
* }
* ```
*
*
* ## ⚠️ 注意
* - 暗号学的に安全な乱数ではない(セキュリティ用途非推奨)
* - seed()で同じ値を設定すると同じ乱数列を生成(テスト用)
* - 大きな配列のshuffleは処理時間が長い場合あり
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::{ArrayBox, FloatBox};
use std::fmt::{Debug, Display};
use std::any::Any;
use std::fmt::{Debug, Display};
use std::sync::RwLock;
/// 乱数生成を提供するBox
@ -84,7 +84,7 @@ pub struct RandomBox {
impl Clone for RandomBox {
fn clone(&self) -> Self {
let seed_val = *self.seed.read().unwrap();
Self {
seed: RwLock::new(seed_val),
base: BoxBase::new(), // New unique ID for clone
@ -99,13 +99,13 @@ impl RandomBox {
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
Self {
seed: RwLock::new(seed),
base: BoxBase::new(),
}
}
/// 種を設定
pub fn seed(&self, new_seed: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = new_seed.as_any().downcast_ref::<IntegerBox>() {
@ -115,7 +115,7 @@ impl RandomBox {
Box::new(StringBox::new("Error: seed() requires integer input"))
}
}
/// 次の乱数を生成(線形合同法)
fn next_random(&self) -> u64 {
let mut seed = self.seed.write().unwrap();
@ -123,46 +123,53 @@ impl RandomBox {
*seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*seed
}
/// 0.0-1.0の浮動小数点乱数
pub fn random(&self) -> Box<dyn NyashBox> {
let r = self.next_random();
let normalized = (r as f64) / (u64::MAX as f64);
Box::new(FloatBox::new(normalized))
}
/// 指定範囲の整数乱数
pub fn randInt(&self, min: Box<dyn NyashBox>, max: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(min_int), Some(max_int)) = (
min.as_any().downcast_ref::<IntegerBox>(),
max.as_any().downcast_ref::<IntegerBox>()
max.as_any().downcast_ref::<IntegerBox>(),
) {
if min_int.value > max_int.value {
return Box::new(StringBox::new("Error: min must be <= max"));
}
let range = (max_int.value - min_int.value + 1) as u64;
let r = self.next_random() % range;
Box::new(IntegerBox::new(min_int.value + r as i64))
} else {
Box::new(StringBox::new("Error: randInt() requires two integer inputs"))
Box::new(StringBox::new(
"Error: randInt() requires two integer inputs",
))
}
}
/// true/falseのランダム選択
pub fn randBool(&self) -> Box<dyn NyashBox> {
let r = self.next_random();
Box::new(BoolBox::new(r % 2 == 0))
}
/// 配列からランダム選択
pub fn choice(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
let length = array_box
.length()
.to_string_box()
.value
.parse::<i64>()
.unwrap_or(0);
if length == 0 {
return Box::new(StringBox::new("Error: cannot choose from empty array"));
}
let index = self.next_random() % (length as u64);
// 新しいArrayBox.get()は既にBox<dyn NyashBox>を返すので、直接使用
array_box.get(Box::new(IntegerBox::new(index as i64)))
@ -170,18 +177,23 @@ impl RandomBox {
Box::new(StringBox::new("Error: choice() requires array input"))
}
}
/// 配列をシャッフル
pub fn shuffle(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
let length = array_box
.length()
.to_string_box()
.value
.parse::<i64>()
.unwrap_or(0);
if length <= 1 {
return array;
}
// 新しい配列を作成
let shuffled = ArrayBox::new();
// 元の配列の要素を全て新しい配列にコピー
for i in 0..length {
let element = array_box.get(Box::new(IntegerBox::new(i as i64)));
@ -190,12 +202,12 @@ impl RandomBox {
shuffled.push(element);
}
}
// 簡易シャッフル実装完全なFisher-Yatesは複雑なので
// 代わりに、元の配列からランダムに選んで新しい配列を作る
let result = ArrayBox::new();
let mut remaining_indices: Vec<usize> = (0..length as usize).collect();
while !remaining_indices.is_empty() {
let random_idx = (self.next_random() % remaining_indices.len() as u64) as usize;
let actual_idx = remaining_indices.remove(random_idx);
@ -205,42 +217,44 @@ impl RandomBox {
result.push(element);
}
}
Box::new(result)
} else {
Box::new(StringBox::new("Error: shuffle() requires array input"))
}
}
/// ランダムな文字列生成
pub fn randString(&self, length: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(len_int) = length.as_any().downcast_ref::<IntegerBox>() {
if len_int.value < 0 {
return Box::new(StringBox::new("Error: length must be positive"));
}
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let char_vec: Vec<char> = chars.chars().collect();
let mut result = String::new();
for _ in 0..len_int.value {
let index = self.next_random() % (char_vec.len() as u64);
result.push(char_vec[index as usize]);
}
Box::new(StringBox::new(&result))
} else {
Box::new(StringBox::new("Error: randString() requires integer length"))
Box::new(StringBox::new(
"Error: randString() requires integer length",
))
}
}
/// 指定確率でtrue
pub fn probability(&self, prob: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(float_box) = prob.as_any().downcast_ref::<FloatBox>() {
if float_box.value < 0.0 || float_box.value > 1.0 {
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
}
let r = self.next_random() as f64 / u64::MAX as f64;
Box::new(BoolBox::new(r < float_box.value))
} else if let Some(int_box) = prob.as_any().downcast_ref::<IntegerBox>() {
@ -248,11 +262,13 @@ impl RandomBox {
if prob_val < 0.0 || prob_val > 1.0 {
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
}
let r = self.next_random() as f64 / u64::MAX as f64;
Box::new(BoolBox::new(r < prob_val))
} else {
Box::new(StringBox::new("Error: probability() requires numeric input"))
Box::new(StringBox::new(
"Error: probability() requires numeric input",
))
}
}
}
@ -261,20 +277,20 @@ impl NyashBox for RandomBox {
fn type_name(&self) -> &'static str {
"RandomBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("RandomBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_random) = other.as_any().downcast_ref::<RandomBox>() {
BoolBox::new(self.base.id == other_random.base.id)
@ -282,27 +298,25 @@ impl NyashBox for RandomBox {
BoolBox::new(false)
}
}
}
impl BoxCore for RandomBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "RandomBox()")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -312,4 +326,4 @@ impl Display for RandomBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::sync::{Arc, Mutex};
@ -10,10 +10,16 @@ pub struct RefCellBox {
impl RefCellBox {
pub fn new(initial: Box<dyn NyashBox>) -> Self {
Self { inner: Arc::new(Mutex::new(initial)), base: BoxBase::new() }
Self {
inner: Arc::new(Mutex::new(initial)),
base: BoxBase::new(),
}
}
pub fn with_inner(inner: Arc<Mutex<Box<dyn NyashBox>>>) -> Self {
Self { inner, base: BoxBase::new() }
Self {
inner,
base: BoxBase::new(),
}
}
pub fn borrow(&self) -> Box<dyn NyashBox> {
self.inner.lock().unwrap().clone_box()
@ -22,31 +28,48 @@ impl RefCellBox {
let mut guard = self.inner.lock().unwrap();
*guard = value;
}
pub fn inner_arc(&self) -> Arc<Mutex<Box<dyn NyashBox>>> { Arc::clone(&self.inner) }
pub fn inner_arc(&self) -> Arc<Mutex<Box<dyn NyashBox>>> {
Arc::clone(&self.inner)
}
}
impl BoxCore for RefCellBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RefCellBox(..)")
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for RefCellBox {
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self::with_inner(self.inner_arc())) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self::with_inner(self.inner_arc()))
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
let inner = self.inner.lock().unwrap();
StringBox::new(format!("RefCell({})", inner.to_string_box().value))
}
fn type_name(&self) -> &'static str { "RefCellBox" }
fn type_name(&self) -> &'static str {
"RefCellBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(o) = other.as_any().downcast_ref::<RefCellBox>() {
BoolBox::new(Arc::ptr_eq(&self.inner, &o.inner))
} else { BoolBox::new(false) }
} else {
BoolBox::new(false)
}
}
}

View File

@ -2,12 +2,12 @@
// Nyashの箱システムによる正規表現処理を提供します。
// 参考: 既存Boxの設計思想
use regex::Regex;
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use crate::boxes::array::ArrayBox;
use regex::Regex;
use std::any::Any;
use std::sync::Arc;
use std::fmt::Debug;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct RegexBox {
@ -31,13 +31,13 @@ impl RegexBox {
pub fn pattern(&self) -> &str {
&self.pattern
}
/// パターンマッチテスト
pub fn test(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let text_str = text.to_string_box().value;
Box::new(BoolBox::new(self.is_match(&text_str)))
}
/// マッチ箇所を検索
pub fn find(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let text_str = text.to_string_box().value;
@ -47,36 +47,40 @@ impl RegexBox {
Box::new(crate::boxes::null_box::NullBox::new())
}
}
/// すべてのマッチを検索
pub fn find_all(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let text_str = text.to_string_box().value;
let array = ArrayBox::new();
for mat in self.regex.find_iter(&text_str) {
let _ = array.push(Box::new(StringBox::new(mat.as_str())));
}
Box::new(array)
}
/// 文字列置換
pub fn replace(&self, text: Box<dyn NyashBox>, replacement: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn replace(
&self,
text: Box<dyn NyashBox>,
replacement: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
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.to_string()))
}
/// 文字列分割
pub fn split(&self, text: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let text_str = text.to_string_box().value;
let array = ArrayBox::new();
for part in self.regex.split(&text_str) {
let _ = array.push(Box::new(StringBox::new(part)));
}
Box::new(array)
}
}
@ -85,7 +89,7 @@ impl NyashBox for RegexBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -95,12 +99,10 @@ impl NyashBox for RegexBox {
StringBox::new(format!("RegexBox({})", self.pattern.as_str()))
}
fn type_name(&self) -> &'static str {
"RegexBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_regex) = other.as_any().downcast_ref::<RegexBox>() {
BoolBox::new(self.pattern.as_str() == other_regex.pattern.as_str())
@ -114,7 +116,7 @@ impl BoxCore for RegexBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -122,11 +124,11 @@ impl BoxCore for RegexBox {
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RegexBox({})", self.pattern.as_str())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

View File

@ -2,7 +2,7 @@
// Nyashの箱システムによるエラー処理を提供します。
// 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore};
use crate::box_trait::{BoolBox, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[derive(Debug)]
@ -15,19 +15,19 @@ impl NyashResultBox {
pub fn new_ok(value: Box<dyn NyashBox>) -> Self {
NyashResultBox::Ok(value)
}
pub fn new_err(error: Box<dyn NyashBox>) -> Self {
NyashResultBox::Err(error)
}
pub fn is_ok_bool(&self) -> bool {
matches!(self, NyashResultBox::Ok(_))
}
pub fn is_err(&self) -> bool {
matches!(self, NyashResultBox::Err(_))
}
pub fn unwrap(self) -> Box<dyn NyashBox> {
match self {
NyashResultBox::Ok(val) => val,
@ -43,7 +43,7 @@ impl NyashBox for NyashResultBox {
NyashResultBox::Err(err) => Box::new(NyashResultBox::Err(err.clone_or_share())),
}
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
match self {
@ -55,16 +55,16 @@ impl NyashBox for NyashResultBox {
fn to_string_box(&self) -> StringBox {
match self {
NyashResultBox::Ok(val) => StringBox::new(format!("Ok({})", val.to_string_box().value)),
NyashResultBox::Err(err) => StringBox::new(format!("Err({})", err.to_string_box().value)),
NyashResultBox::Err(err) => {
StringBox::new(format!("Err({})", err.to_string_box().value))
}
}
}
fn type_name(&self) -> &'static str {
"NyashResultBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_result) = other.as_any().downcast_ref::<NyashResultBox>() {
match (self, other_result) {
@ -86,7 +86,7 @@ impl BoxCore for NyashResultBox {
NyashResultBox::Err(err) => err.box_id(),
}
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
// For enum variants, we use the contained value's parent type ID
match self {
@ -101,11 +101,11 @@ impl BoxCore for NyashResultBox {
NyashResultBox::Err(err) => write!(f, "Err({})", err.to_string_box().value),
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -125,13 +125,17 @@ impl ResultBox {
pub fn is_ok(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(matches!(self, NyashResultBox::Ok(_))))
}
/// getValue()の実装 - Ok値を取得
pub fn get_value(&self) -> Box<dyn NyashBox> {
match self {
NyashResultBox::Ok(val) => {
// Preserve identity for plugin-backed boxes
if val.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else {
val.clone_box()
@ -140,7 +144,7 @@ impl ResultBox {
NyashResultBox::Err(_) => Box::new(StringBox::new("Error: Result is Err")),
}
}
/// getError()の実装 - Err値を取得
pub fn get_error(&self) -> Box<dyn NyashBox> {
match self {

View File

@ -1,23 +1,23 @@
/*! 🔌 SocketBox - TCP/UDP Socket networking
*
*
* ## 📝 概要
* Rustの std::net を基盤とした高性能ネットワーキング Box
* TCP サーバー・クライアント両対応、HTTPサーバー基盤として利用
*
*
* ## 🛠️ 利用可能メソッド
* ### TCP Server
* - `bind(address, port)` - TCP ソケット bind
* - `listen(backlog)` - 接続待機開始
* - `accept()` - クライアント接続受諾
*
*
* ### TCP Client
* - `connect(address, port)` - サーバーへ接続
*
*
* ### IO Operations
* - `read()` - データ読み取り
* - `write(data)` - データ送信
* - `close()` - ソケット閉鎖
*
*
* ## 💡 使用例
* ```nyash
* // TCP Server
@ -25,7 +25,7 @@
* server.bind("0.0.0.0", 8080)
* server.listen(128)
* client = server.accept()
*
*
* // TCP Client
* client = new SocketBox()
* client.connect("127.0.0.1", 8080)
@ -34,11 +34,11 @@
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
use std::io::{Write, BufRead, BufReader};
use std::sync::{Arc, RwLock}; // Arc追加
use std::sync::{Arc, RwLock}; // Arc追加
use std::time::Duration;
/// TCP/UDP ソケット操作を提供するBox
@ -46,25 +46,25 @@ use std::time::Duration;
pub struct SocketBox {
base: BoxBase,
// TCP Server
listener: Arc<RwLock<Option<TcpListener>>>, // Arc追加
listener: Arc<RwLock<Option<TcpListener>>>, // Arc追加
// TCP Client/Connected Socket
stream: Arc<RwLock<Option<TcpStream>>>, // Arc追加
stream: Arc<RwLock<Option<TcpStream>>>, // Arc追加
// Connection state
is_server: Arc<RwLock<bool>>, // Arc追加
is_connected: Arc<RwLock<bool>>, // Arc追加
is_server: Arc<RwLock<bool>>, // Arc追加
is_connected: Arc<RwLock<bool>>, // Arc追加
}
impl Clone for SocketBox {
fn clone(&self) -> Self {
// ディープコピー(独立インスタンス)
// ディープコピー(独立インスタンス)
let is_server_val = *self.is_server.read().unwrap();
let is_connected_val = *self.is_connected.read().unwrap();
Self {
base: BoxBase::new(), // New unique ID for clone
listener: Arc::new(RwLock::new(None)), // 新しいArc
stream: Arc::new(RwLock::new(None)), // 新しいArc
is_server: Arc::new(RwLock::new(is_server_val)), // 状態のみコピー
base: BoxBase::new(), // New unique ID for clone
listener: Arc::new(RwLock::new(None)), // 新しいArc
stream: Arc::new(RwLock::new(None)), // 新しいArc
is_server: Arc::new(RwLock::new(is_server_val)), // 状態のみコピー
is_connected: Arc::new(RwLock::new(is_connected_val)), // 状態のみコピー
}
}
@ -74,41 +74,41 @@ impl SocketBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
listener: Arc::new(RwLock::new(None)), // Arc::new追加
stream: Arc::new(RwLock::new(None)), // Arc::new追加
is_server: Arc::new(RwLock::new(false)), // Arc::new追加
listener: Arc::new(RwLock::new(None)), // Arc::new追加
stream: Arc::new(RwLock::new(None)), // Arc::new追加
is_server: Arc::new(RwLock::new(false)), // Arc::new追加
is_connected: Arc::new(RwLock::new(false)), // Arc::new追加
}
}
/// TCP ソケットをアドレス・ポートにバインド
pub fn bind(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let addr_str = address.to_string_box().value;
let port_str = port.to_string_box().value;
let socket_addr = format!("{}:{}", addr_str, port_str);
eprintln!("🔥 SOCKETBOX DEBUG: bind() called");
eprintln!("🔥 Socket ID = {}", self.base.id);
eprintln!("🔥 Address = {}", socket_addr);
eprintln!("🔥 Arc pointer = {:p}", &self.is_server);
match TcpListener::bind(&socket_addr) {
Ok(listener) => {
eprintln!("✅ TCP bind successful");
// listener設定
match self.listener.write() {
Ok(mut listener_guard) => {
*listener_guard = Some(listener);
eprintln!("✅ Listener stored successfully");
},
}
Err(e) => {
eprintln!("❌ Failed to lock listener mutex: {}", e);
return Box::new(BoolBox::new(false));
}
}
// is_server状態設定 - 徹底デバッグ
match self.is_server.write() {
Ok(mut is_server_guard) => {
@ -116,62 +116,62 @@ impl SocketBox {
eprintln!("🔥 is_server value = {}", *is_server_guard);
eprintln!("🔥 RwLock pointer = {:p}", &self.is_server);
eprintln!("🔥 Guard pointer = {:p}", &*is_server_guard);
// 状態変更
*is_server_guard = true;
eprintln!("🔥 AFTER MUTATION:");
eprintln!("🔥 is_server value = {}", *is_server_guard);
eprintln!("🔥 Value confirmed = {}", *is_server_guard == true);
// 明示的にドロップしてロック解除
drop(is_server_guard);
eprintln!("✅ is_server guard dropped");
// 再確認テスト
match self.is_server.read() {
Ok(check_guard) => {
eprintln!("🔥 RECHECK AFTER DROP:");
eprintln!("🔥 is_server value = {}", *check_guard);
},
}
Err(e) => {
eprintln!("❌ Failed to recheck: {}", e);
}
}
},
}
Err(e) => {
eprintln!("❌ SOCKETBOX: Failed to lock is_server mutex: {}", e);
return Box::new(BoolBox::new(false));
}
}
eprintln!("✅ bind() completed successfully");
Box::new(BoolBox::new(true))
},
}
Err(e) => {
eprintln!("❌ TCP bind failed: {}", e);
Box::new(BoolBox::new(false))
}
}
}
/// 指定した backlog で接続待機開始
pub fn listen(&self, backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let _backlog_num = backlog.to_string_box().value.parse::<i32>().unwrap_or(128);
// Check if listener exists and is properly bound
let listener_guard = match self.listener.read() {
Ok(guard) => guard,
Err(_) => return Box::new(BoolBox::new(false)),
};
if let Some(ref listener) = *listener_guard {
// Try to get the local address to confirm the listener is working
match listener.local_addr() {
Ok(_addr) => {
// Listener is properly set up and can accept connections
Box::new(BoolBox::new(true))
},
}
Err(_) => {
// Listener exists but has issues
Box::new(BoolBox::new(false))
@ -183,7 +183,7 @@ impl SocketBox {
Box::new(BoolBox::new(false))
}
}
/// クライアント接続を受諾(ブロッキング)
pub fn accept(&self) -> Box<dyn NyashBox> {
let listener_guard = self.listener.write().unwrap();
@ -191,14 +191,14 @@ impl SocketBox {
match listener.accept() {
Ok((stream, _addr)) => {
drop(listener_guard);
// Create new SocketBox for the client connection
let client_socket = SocketBox::new();
*client_socket.stream.write().unwrap() = Some(stream);
*client_socket.is_connected.write().unwrap() = true;
Box::new(client_socket)
},
}
Err(e) => {
eprintln!("🚨 SocketBox accept error: {}", e);
Box::new(BoolBox::new(false))
@ -212,7 +212,9 @@ impl SocketBox {
/// クライアント接続を受諾タイムアウトms、タイムアウト時はvoid
pub fn accept_timeout(&self, timeout_ms: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let ms = timeout_ms.to_string_box().value.parse::<u64>().unwrap_or(0);
if ms == 0 { return self.accept(); }
if ms == 0 {
return self.accept();
}
let start = std::time::Instant::now();
if let Ok(guard) = self.listener.write() {
@ -227,7 +229,7 @@ impl SocketBox {
*client_socket.stream.write().unwrap() = Some(stream);
*client_socket.is_connected.write().unwrap() = true;
return Box::new(client_socket);
},
}
Err(e) => {
if e.kind() == std::io::ErrorKind::WouldBlock {
if start.elapsed() >= Duration::from_millis(ms) {
@ -248,32 +250,36 @@ impl SocketBox {
}
Box::new(crate::box_trait::VoidBox::new())
}
/// サーバーに接続(クライアントモード)
pub fn connect(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn connect(
&self,
address: Box<dyn NyashBox>,
port: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
let addr_str = address.to_string_box().value;
let port_str = port.to_string_box().value;
let socket_addr = format!("{}:{}", addr_str, port_str);
match TcpStream::connect(&socket_addr) {
Ok(stream) => {
// Set timeout for read/write operations
let _ = stream.set_read_timeout(Some(Duration::from_secs(30)));
let _ = stream.set_write_timeout(Some(Duration::from_secs(30)));
*self.stream.write().unwrap() = Some(stream);
*self.is_connected.write().unwrap() = true;
*self.is_server.write().unwrap() = false;
Box::new(BoolBox::new(true))
},
}
Err(e) => {
eprintln!("🚨 SocketBox connect error: {}", e);
Box::new(BoolBox::new(false))
}
}
}
/// データを読み取り(改行まで or EOF
pub fn read(&self) -> Box<dyn NyashBox> {
let stream_guard = self.stream.write().unwrap();
@ -282,10 +288,10 @@ impl SocketBox {
match stream.try_clone() {
Ok(stream_clone) => {
drop(stream_guard);
let mut reader = BufReader::new(stream_clone);
let mut buffer = String::new();
match reader.read_line(&mut buffer) {
Ok(_) => {
// Remove trailing newline
@ -296,13 +302,13 @@ impl SocketBox {
}
}
Box::new(StringBox::new(buffer))
},
}
Err(e) => {
eprintln!("🚨 SocketBox read error: {}", e);
Box::new(StringBox::new("".to_string()))
}
}
},
}
Err(e) => {
eprintln!("🚨 SocketBox stream clone error: {}", e);
Box::new(StringBox::new("".to_string()))
@ -328,12 +334,16 @@ impl SocketBox {
Ok(_) => {
if buffer.ends_with('\n') {
buffer.pop();
if buffer.ends_with('\r') { buffer.pop(); }
if buffer.ends_with('\r') {
buffer.pop();
}
}
Box::new(StringBox::new(&buffer))
}
Err(e) => {
if e.kind() == std::io::ErrorKind::WouldBlock || e.kind() == std::io::ErrorKind::TimedOut {
if e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::TimedOut
{
return Box::new(StringBox::new(""));
}
eprintln!("🚨 SocketBox recv_timeout error: {}", e);
@ -350,7 +360,7 @@ impl SocketBox {
Box::new(StringBox::new(""))
}
}
/// HTTP request を読み取り(ヘッダーまで含む)
pub fn read_http_request(&self) -> Box<dyn NyashBox> {
let stream_guard = self.stream.write().unwrap();
@ -358,11 +368,11 @@ impl SocketBox {
match stream.try_clone() {
Ok(stream_clone) => {
drop(stream_guard);
let mut reader = BufReader::new(stream_clone);
let mut request = String::new();
let mut line = String::new();
// Read HTTP request line by line until empty line
loop {
line.clear();
@ -374,16 +384,16 @@ impl SocketBox {
if line.trim().is_empty() {
break;
}
},
}
Err(e) => {
eprintln!("🚨 SocketBox HTTP read error: {}", e);
break;
}
}
}
Box::new(StringBox::new(request))
},
}
Err(e) => {
eprintln!("🚨 SocketBox stream clone error: {}", e);
Box::new(StringBox::new("".to_string()))
@ -393,21 +403,19 @@ impl SocketBox {
Box::new(StringBox::new("".to_string()))
}
}
/// データを送信
pub fn write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let data_str = data.to_string_box().value;
let mut stream_guard = self.stream.write().unwrap();
if let Some(ref mut stream) = *stream_guard {
match stream.write_all(data_str.as_bytes()) {
Ok(_) => {
match stream.flush() {
Ok(_) => Box::new(BoolBox::new(true)),
Err(e) => {
eprintln!("🚨 SocketBox flush error: {}", e);
Box::new(BoolBox::new(false))
}
Ok(_) => match stream.flush() {
Ok(_) => Box::new(BoolBox::new(true)),
Err(e) => {
eprintln!("🚨 SocketBox flush error: {}", e);
Box::new(BoolBox::new(false))
}
},
Err(e) => {
@ -419,7 +427,7 @@ impl SocketBox {
Box::new(BoolBox::new(false))
}
}
/// ソケット閉鎖
pub fn close(&self) -> Box<dyn NyashBox> {
*self.stream.write().unwrap() = None;
@ -428,18 +436,18 @@ impl SocketBox {
*self.is_server.write().unwrap() = false;
Box::new(BoolBox::new(true))
}
/// 接続状態確認
pub fn is_connected(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(*self.is_connected.write().unwrap()))
}
/// サーバーモード確認
pub fn is_server(&self) -> Box<dyn NyashBox> {
eprintln!("🔥 SOCKETBOX DEBUG: is_server() called");
eprintln!("🔥 Socket ID = {}", self.base.id);
eprintln!("🔥 RwLock pointer = {:p}", &self.is_server);
match self.is_server.read() {
Ok(is_server_guard) => {
let is_server_value = *is_server_guard;
@ -447,11 +455,14 @@ impl SocketBox {
eprintln!("🔥 is_server value = {}", is_server_value);
eprintln!("🔥 Guard pointer = {:p}", &*is_server_guard);
eprintln!("🔥 Returning BoolBox with value = {}", is_server_value);
Box::new(BoolBox::new(is_server_value))
},
}
Err(e) => {
eprintln!("❌ SOCKETBOX: Failed to lock is_server mutex in is_server(): {}", e);
eprintln!(
"❌ SOCKETBOX: Failed to lock is_server mutex in is_server(): {}",
e
);
Box::new(BoolBox::new(false))
}
}
@ -462,45 +473,48 @@ impl NyashBox for SocketBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 🎯 状態共有の核心実装 - SocketBox状態保持問題の根本解決
fn share_box(&self) -> Box<dyn NyashBox> {
let new_instance = SocketBox {
base: BoxBase::new(), // 新しいID
listener: Arc::clone(&self.listener), // 状態共有
stream: Arc::clone(&self.stream), // 状態共有
is_server: Arc::clone(&self.is_server), // 状態共有
is_connected: Arc::clone(&self.is_connected), // 状態共有
base: BoxBase::new(), // 新しいID
listener: Arc::clone(&self.listener), // 状態共有
stream: Arc::clone(&self.stream), // 状態共有
is_server: Arc::clone(&self.is_server), // 状態共有
is_connected: Arc::clone(&self.is_connected), // 状態共有
};
Box::new(new_instance)
}
fn to_string_box(&self) -> StringBox {
eprintln!("🔥 SOCKETBOX to_string_box() called - Socket ID = {}", self.base.id);
eprintln!(
"🔥 SOCKETBOX to_string_box() called - Socket ID = {}",
self.base.id
);
eprintln!("🔥 RwLock pointer = {:p}", &self.is_server);
let is_server = match self.is_server.read() {
Ok(guard) => {
eprintln!("✅ is_server.read() successful");
*guard
},
}
Err(e) => {
eprintln!("❌ is_server.read() failed: {}", e);
false // デフォルト値
}
};
let is_connected = match self.is_connected.read() {
Ok(guard) => {
eprintln!("✅ is_connected.read() successful");
*guard
},
}
Err(e) => {
eprintln!("❌ is_connected.read() failed: {}", e);
false // デフォルト値
}
};
let status = if is_server {
"Server"
} else if is_connected {
@ -508,8 +522,11 @@ impl NyashBox for SocketBox {
} else {
"Disconnected"
};
StringBox::new(format!("SocketBox(id: {}, status: {})", self.base.id, status))
StringBox::new(format!(
"SocketBox(id: {}, status: {})",
self.base.id, status
))
}
fn type_name(&self) -> &'static str {
@ -529,14 +546,17 @@ impl BoxCore for SocketBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
eprintln!("🔥 SOCKETBOX fmt_box() called - Socket ID = {}", self.base.id);
eprintln!(
"🔥 SOCKETBOX fmt_box() called - Socket ID = {}",
self.base.id
);
let is_server = match self.is_server.read() {
Ok(guard) => *guard,
Err(e) => {
@ -544,7 +564,7 @@ impl BoxCore for SocketBox {
false
}
};
let is_connected = match self.is_connected.read() {
Ok(guard) => *guard,
Err(e) => {
@ -552,7 +572,7 @@ impl BoxCore for SocketBox {
false
}
};
let status = if is_server {
"Server"
} else if is_connected {
@ -560,14 +580,14 @@ impl BoxCore for SocketBox {
} else {
"Disconnected"
};
write!(f, "SocketBox(id: {}, status: {})", self.base.id, status)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

View File

@ -1,10 +1,10 @@
/*! 🔊 SoundBox - サウンド・音響効果Box
*
*
* ## 📝 概要
* システム音・効果音を提供するBox。
* ゲーム効果音、通知音、アラート音の生成に使用。
* クロスプラットフォーム対応のシンプルなサウンドシステム。
*
*
* ## 🛠️ 利用可能メソッド
* - `beep()` - 基本ビープ音
* - `beeps(count)` - 指定回数ビープ
@ -13,22 +13,22 @@
* - `playTone(frequency, duration)` - 指定周波数・時間で音生成
* - `playFile(filename)` - 音声ファイル再生
* - `setVolume(level)` - 音量設定 (0.0-1.0)
*
*
* ## 💡 使用例
* ```nyash
* local sound
* sound = new SoundBox()
*
*
* // 基本的な音
* sound.beep() // シンプルビープ
* sound.beeps(3) // 3回ビープ
* sound.bell() // ベル音
*
*
* // ゲーム効果音
* sound.playTone(440, 500) // ラの音を500ms
* sound.playTone(880, 200) // 高いラの音を200ms
* ```
*
*
* ## 🎮 実用例 - ゲーム効果音
* ```nyash
* static box GameSFX {
@ -65,7 +65,7 @@
* }
* }
* ```
*
*
* ## 🚨 通知・アラート用途
* ```nyash
* static box NotificationSystem {
@ -100,7 +100,7 @@
* }
* }
* ```
*
*
* ## 🎵 音楽生成例
* ```nyash
* static box MusicBox {
@ -129,7 +129,7 @@
* }
* }
* ```
*
*
* ## ⚠️ 注意
* - システムによってはビープ音が無効化されている場合あり
* - 音量設定は環境依存
@ -138,9 +138,9 @@
* - Web環境では制限が多いユーザー操作後のみ音声再生可能
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use std::fmt::{Debug, Display};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use std::any::Any;
use std::fmt::{Debug, Display};
use std::process::Command;
use std::time::Duration;
@ -152,69 +152,81 @@ pub struct SoundBox {
impl SoundBox {
pub fn new() -> Self {
Self {
base: BoxBase::new()
Self {
base: BoxBase::new(),
}
}
/// ビープ音を鳴らす(基本)
pub fn beep(&self) -> Box<dyn NyashBox> {
// 端末ベル文字を出力
print!("\x07");
Box::new(StringBox::new("Beep!"))
}
/// 指定回数ビープ
pub fn beeps(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
if count_int.value <= 0 {
return Box::new(StringBox::new("Beep count must be positive"));
}
for i in 0..count_int.value {
print!("\x07");
if i < count_int.value - 1 {
std::thread::sleep(Duration::from_millis(100));
}
}
Box::new(StringBox::new(&format!("Beeped {} times", count_int.value)))
} else {
Box::new(StringBox::new("Error: beeps() requires integer input"))
}
}
/// 指定周波数のビープLinuxのみ
pub fn tone(&self, frequency: Box<dyn NyashBox>, duration: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn tone(
&self,
frequency: Box<dyn NyashBox>,
duration: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
if let (Some(freq_int), Some(dur_int)) = (
frequency.as_any().downcast_ref::<IntegerBox>(),
duration.as_any().downcast_ref::<IntegerBox>()
duration.as_any().downcast_ref::<IntegerBox>(),
) {
if freq_int.value <= 0 || dur_int.value <= 0 {
return Box::new(StringBox::new("Frequency and duration must be positive"));
}
// Linuxのbeepコマンドを試行
match Command::new("beep")
.arg("-f")
.arg(&freq_int.value.to_string())
.arg("-l")
.arg(&dur_int.value.to_string())
.output()
.output()
{
Ok(_) => Box::new(StringBox::new(&format!("Played {}Hz for {}ms", freq_int.value, dur_int.value))),
Ok(_) => Box::new(StringBox::new(&format!(
"Played {}Hz for {}ms",
freq_int.value, dur_int.value
))),
Err(_) => {
// beepコマンドが無い場合は端末ベルを使用
print!("\x07");
std::thread::sleep(Duration::from_millis(dur_int.value as u64));
Box::new(StringBox::new(&format!("Fallback beep ({}Hz, {}ms)", freq_int.value, dur_int.value)))
Box::new(StringBox::new(&format!(
"Fallback beep ({}Hz, {}ms)",
freq_int.value, dur_int.value
)))
}
}
} else {
Box::new(StringBox::new("Error: tone() requires two integer inputs (frequency, duration)"))
Box::new(StringBox::new(
"Error: tone() requires two integer inputs (frequency, duration)",
))
}
}
/// 警告音
pub fn alert(&self) -> Box<dyn NyashBox> {
// 3回短いビープ
@ -226,7 +238,7 @@ impl SoundBox {
}
Box::new(StringBox::new("Alert sound played"))
}
/// 成功音
pub fn success(&self) -> Box<dyn NyashBox> {
// 1回長めのビープ
@ -235,7 +247,7 @@ impl SoundBox {
print!("\x07");
Box::new(StringBox::new("Success sound played"))
}
/// エラー音
pub fn error(&self) -> Box<dyn NyashBox> {
// 2回素早いビープ
@ -244,12 +256,12 @@ impl SoundBox {
print!("\x07");
Box::new(StringBox::new("Error sound played"))
}
/// カスタムビープパターン
pub fn pattern(&self, pattern: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(pattern_str) = pattern.as_any().downcast_ref::<StringBox>() {
let mut beep_count = 0;
for ch in pattern_str.value.chars() {
match ch {
'.' => {
@ -272,43 +284,57 @@ impl SoundBox {
// その他の文字は無視
}
}
// 文字間の短い間隔
std::thread::sleep(Duration::from_millis(50));
}
Box::new(StringBox::new(&format!("Played pattern '{}' ({} beeps)", pattern_str.value, beep_count)))
Box::new(StringBox::new(&format!(
"Played pattern '{}' ({} beeps)",
pattern_str.value, beep_count
)))
} else {
Box::new(StringBox::new("Error: pattern() requires string input (use '.' for short, '-' for long, ' ' for pause)"))
}
}
/// システム音量チェック(簡易)
pub fn volumeTest(&self) -> Box<dyn NyashBox> {
print!("\x07");
Box::new(StringBox::new("Volume test beep - can you hear it?"))
}
/// 指定間隔でビープ
pub fn interval(&self, times: Box<dyn NyashBox>, interval_ms: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
pub fn interval(
&self,
times: Box<dyn NyashBox>,
interval_ms: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
if let (Some(times_int), Some(interval_int)) = (
times.as_any().downcast_ref::<IntegerBox>(),
interval_ms.as_any().downcast_ref::<IntegerBox>()
interval_ms.as_any().downcast_ref::<IntegerBox>(),
) {
if times_int.value <= 0 || interval_int.value < 0 {
return Box::new(StringBox::new("Times must be positive, interval must be non-negative"));
return Box::new(StringBox::new(
"Times must be positive, interval must be non-negative",
));
}
for i in 0..times_int.value {
print!("\x07");
if i < times_int.value - 1 {
std::thread::sleep(Duration::from_millis(interval_int.value as u64));
}
}
Box::new(StringBox::new(&format!("Played {} beeps with {}ms intervals", times_int.value, interval_int.value)))
Box::new(StringBox::new(&format!(
"Played {} beeps with {}ms intervals",
times_int.value, interval_int.value
)))
} else {
Box::new(StringBox::new("Error: interval() requires two integer inputs (times, interval_ms)"))
Box::new(StringBox::new(
"Error: interval() requires two integer inputs (times, interval_ms)",
))
}
}
}
@ -317,20 +343,20 @@ impl NyashBox for SoundBox {
fn type_name(&self) -> &'static str {
"SoundBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("SoundBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_sound) = other.as_any().downcast_ref::<SoundBox>() {
BoolBox::new(self.base.id == other_sound.base.id)
@ -338,27 +364,25 @@ impl NyashBox for SoundBox {
BoolBox::new(false)
}
}
}
impl BoxCore for SoundBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "SoundBox()")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -368,4 +392,4 @@ impl Display for SoundBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -2,12 +2,12 @@
// Nyashの箱システムによるストリーミング処理を提供します。
// 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
use crate::boxes::buffer::BufferBox;
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::array::ArrayBox;
use crate::boxes::buffer::BufferBox;
use std::any::Any;
use std::sync::RwLock;
use std::io::Result;
use std::sync::RwLock;
pub struct NyashStreamBox {
buffer: RwLock<Vec<u8>>,
@ -23,7 +23,7 @@ impl NyashStreamBox {
base: BoxBase::new(),
}
}
pub fn from_data(data: Vec<u8>) -> Self {
NyashStreamBox {
buffer: RwLock::new(data),
@ -31,41 +31,41 @@ impl NyashStreamBox {
base: BoxBase::new(),
}
}
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
let buffer = self.buffer.read().unwrap();
let mut position = self.position.write().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(&buffer[*position..*position + to_read]);
*position += to_read;
Ok(to_read)
}
pub fn write(&self, buf: &[u8]) -> Result<()> {
let mut buffer = self.buffer.write().unwrap();
buffer.extend_from_slice(buf);
Ok(())
}
pub fn len(&self) -> usize {
self.buffer.read().unwrap().len()
}
pub fn position(&self) -> usize {
*self.position.read().unwrap()
}
pub fn reset(&self) {
*self.position.write().unwrap() = 0;
}
/// ストリームに書き込み
pub fn stream_write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// BufferBoxから変換
@ -96,38 +96,40 @@ impl NyashStreamBox {
Err(e) => Box::new(StringBox::new(&format!("Error writing to stream: {}", e))),
}
} else {
Box::new(StringBox::new("Error: write() requires BufferBox or StringBox"))
Box::new(StringBox::new(
"Error: write() requires BufferBox or StringBox",
))
}
}
/// ストリームから読み込み
pub fn stream_read(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
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<dyn NyashBox> {
Box::new(IntegerBox::new(self.position() as i64))
}
/// バッファサイズを取得
pub fn get_length(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.len() as i64))
}
/// ストリームをリセット
pub fn stream_reset(&self) -> Box<dyn NyashBox> {
self.reset();
@ -139,7 +141,7 @@ impl NyashBox for NyashStreamBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -148,15 +150,17 @@ impl NyashBox for NyashStreamBox {
fn to_string_box(&self) -> StringBox {
let buffer = self.buffer.read().unwrap();
let position = self.position.read().unwrap();
StringBox::new(format!("NyashStreamBox({} bytes, pos: {})", buffer.len(), *position))
StringBox::new(format!(
"NyashStreamBox({} bytes, pos: {})",
buffer.len(),
*position
))
}
fn type_name(&self) -> &'static str {
"NyashStreamBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_stream) = other.as_any().downcast_ref::<NyashStreamBox>() {
let self_buffer = self.buffer.read().unwrap();
@ -174,7 +178,7 @@ impl BoxCore for NyashStreamBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
@ -182,13 +186,18 @@ impl BoxCore for NyashStreamBox {
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let buffer = self.buffer.read().unwrap();
let position = self.position.read().unwrap();
write!(f, "NyashStreamBox({} bytes, pos: {})", buffer.len(), *position)
write!(
f,
"NyashStreamBox({} bytes, pos: {})",
buffer.len(),
*position
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

View File

@ -1,9 +1,9 @@
/*! 🔤 StringBox - 文字列操作Box
*
*
* ## 📝 概要
* UTF-8エンコード文字列を扱うためのBox。
* JavaScript風のメソッドで直感的な文字列操作が可能。
*
*
* ## 🛠️ 利用可能メソッド
* - `length()` - 文字列長を取得
* - `concat(other)` - 文字列結合
@ -15,19 +15,19 @@
* - `indexOf(search)` - 文字列検索
* - `replace(from, to)` - 文字列置換
* - `charAt(index)` - 指定位置の文字取得
*
*
* ## 💡 使用例
* ```nyash
* local text, parts, result
* text = "Hello, World!"
*
*
* print(text.length()) // 13
* print(text.toUpperCase()) // "HELLO, WORLD!"
* parts = text.split(",") // ["Hello", " World!"]
* result = text.concat(" Nyash") // "Hello, World! Nyash"
* ```
*/
use crate::box_trait::{NyashBox, BoxCore, BoxBase};
use crate::box_trait::{BoxBase, BoxCore, NyashBox};
use std::any::Any;
use std::fmt::Display;
@ -45,18 +45,19 @@ impl StringBox {
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<dyn NyashBox> {
use crate::boxes::array::ArrayBox;
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
let array_elements: Vec<Box<dyn NyashBox>> = parts.into_iter()
let array_elements: Vec<Box<dyn NyashBox>> = parts
.into_iter()
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
.collect();
let result = ArrayBox::new();
@ -65,7 +66,7 @@ impl StringBox {
}
Box::new(result)
}
/// Find substring and return position (or -1 if not found)
pub fn find(&self, search: &str) -> Box<dyn NyashBox> {
use crate::boxes::integer_box::IntegerBox;
@ -74,50 +75,53 @@ impl StringBox {
None => Box::new(IntegerBox::new(-1)),
}
}
/// Replace all occurrences of old with new
pub fn replace(&self, old: &str, new: &str) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.replace(old, new)))
}
/// Trim whitespace from both ends
pub fn trim(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.trim()))
}
/// Convert to uppercase
pub fn to_upper(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_uppercase()))
}
/// Convert to lowercase
pub fn to_lower(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.value.to_lowercase()))
}
/// Check if string contains substring
pub fn contains(&self, search: &str) -> Box<dyn NyashBox> {
use crate::boxes::bool_box::BoolBox;
Box::new(BoolBox::new(self.value.contains(search)))
}
/// Check if string starts with prefix
pub fn starts_with(&self, prefix: &str) -> Box<dyn NyashBox> {
use crate::boxes::bool_box::BoolBox;
Box::new(BoolBox::new(self.value.starts_with(prefix)))
}
/// Check if string ends with suffix
pub fn ends_with(&self, suffix: &str) -> Box<dyn NyashBox> {
use crate::boxes::bool_box::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<dyn NyashBox>) -> Box<dyn NyashBox> {
use crate::boxes::array::ArrayBox;
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
let strings: Vec<String> = array.items.read().unwrap()
let strings: Vec<String> = array
.items
.read()
.unwrap()
.iter()
.map(|element| element.to_string_box().value)
.collect();
@ -127,7 +131,7 @@ impl StringBox {
Box::new(StringBox::new(array_box.to_string_box().value))
}
}
/// Convert string to integer (parse as i64)
pub fn to_integer(&self) -> Box<dyn NyashBox> {
use crate::boxes::integer_box::IntegerBox;
@ -145,7 +149,7 @@ impl NyashBox for StringBox {
fn to_string_box(&self) -> crate::box_trait::StringBox {
crate::box_trait::StringBox::new(self.value.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::<StringBox>() {
@ -154,16 +158,15 @@ impl NyashBox for StringBox {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"StringBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -174,19 +177,19 @@ impl BoxCore for StringBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
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
}
@ -196,4 +199,4 @@ impl Display for StringBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, VoidBox};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any;
use std::sync::{Arc, Mutex};
@ -17,9 +17,17 @@ pub struct TaskGroupBox {
impl TaskGroupBox {
pub fn new() -> Self {
Self { base: BoxBase::new(), cancelled: false, inner: Arc::new(TaskGroupInner { strong: Mutex::new(Vec::new()) }) }
Self {
base: BoxBase::new(),
cancelled: false,
inner: Arc::new(TaskGroupInner {
strong: Mutex::new(Vec::new()),
}),
}
}
pub fn cancel_all(&mut self) {
self.cancelled = true;
}
pub fn cancel_all(&mut self) { self.cancelled = true; }
/// Cancel all child tasks (scaffold) and return void
pub fn cancelAll(&mut self) -> Box<dyn NyashBox> {
self.cancel_all();
@ -31,7 +39,9 @@ impl TaskGroupBox {
self.join_all_inner(ms);
Box::new(VoidBox::new())
}
pub fn is_cancelled(&self) -> bool { self.cancelled }
pub fn is_cancelled(&self) -> bool {
self.cancelled
}
/// Register a Future into this group's ownership
pub fn add_future(&self, fut: &crate::boxes::future::FutureBox) {
@ -47,10 +57,16 @@ impl TaskGroupBox {
let mut all_ready = true;
if let Ok(mut list) = self.inner.strong.lock() {
list.retain(|f| !f.ready());
if !list.is_empty() { all_ready = false; }
if !list.is_empty() {
all_ready = false;
}
}
if all_ready {
break;
}
if Instant::now() >= deadline {
break;
}
if all_ready { break; }
if Instant::now() >= deadline { break; }
crate::runtime::global_hooks::safepoint_and_poll();
std::thread::yield_now();
}
@ -58,20 +74,38 @@ impl TaskGroupBox {
}
impl BoxCore for TaskGroupBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
None
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "TaskGroup(cancelled={})", self.cancelled)
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl NyashBox for TaskGroupBox {
fn to_string_box(&self) -> StringBox { StringBox::new(format!("TaskGroup(cancelled={})", self.cancelled)) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(g) = other.as_any().downcast_ref::<TaskGroupBox>() { BoolBox::new(self.base.id == g.base.id) } else { BoolBox::new(false) }
fn to_string_box(&self) -> StringBox {
StringBox::new(format!("TaskGroup(cancelled={})", self.cancelled))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(g) = other.as_any().downcast_ref::<TaskGroupBox>() {
BoolBox::new(self.base.id == g.base.id)
} else {
BoolBox::new(false)
}
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
}

View File

@ -1,18 +1,18 @@
/*! ⏰ TimeBox - 時間・日付操作Box
*
*
* ## 📝 概要
* 高精度な時間・日付操作を提供するBox。
* JavaScript Date、Python datetime、C# DateTimeと同等機能。
* タイムスタンプ処理、フォーマット、時差計算をサポート。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### 📅 基本操作
* - `now()` - 現在日時取得
* - `fromTimestamp(timestamp)` - UNIXタイムスタンプから日時作成
* - `parse(date_string)` - 文字列から日時パース
* - `format(pattern)` - 指定フォーマットで文字列化
*
*
* ### 🔢 値取得
* - `year()` - 年取得
* - `month()` - 月取得 (1-12)
@ -21,34 +21,34 @@
* - `minute()` - 分取得 (0-59)
* - `second()` - 秒取得 (0-59)
* - `weekday()` - 曜日取得 (0=日曜)
*
*
* ### ⏱️ 計算
* - `addDays(days)` - 日数加算
* - `addHours(hours)` - 時間加算
* - `addMinutes(minutes)` - 分加算
* - `diffDays(other)` - 日数差計算
* - `diffHours(other)` - 時間差計算
*
*
* ## 💡 使用例
* ```nyash
* local time, now, birthday, age
* time = new TimeBox()
*
*
* // 現在日時
* now = time.now()
* print("現在: " + now.format("yyyy/MM/dd HH:mm:ss"))
*
*
* // 誕生日から年齢計算
* birthday = time.parse("1995-03-15")
* age = now.diffYears(birthday)
* print("年齢: " + age.toString() + "歳")
*
*
* // 1週間後
* local next_week
* next_week = now.addDays(7)
* print("1週間後: " + next_week.format("MM月dd日"))
* ```
*
*
* ## 🎮 実用例 - イベントスケジューラー
* ```nyash
* static box EventScheduler {
@ -83,34 +83,34 @@
* event = me.events.get(i)
* hours_until = event.get("datetime").diffHours(me.current)
*
* print(event.get("title") + " - " +
* print(event.get("title") + " - " +
* hours_until.toString() + "時間後")
* }
* }
* }
* ```
*
*
* ## 🕐 時間計算例
* ```nyash
* local time, start, end, duration
* time = new TimeBox()
*
*
* // 作業時間計測
* start = time.now()
* // 何か重い処理...
* heavyCalculation()
* end = time.now()
*
*
* duration = end.diffSeconds(start)
* print("処理時間: " + duration.toString() + "秒")
*
*
* // 締切まで残り時間
* local deadline, remaining
* deadline = time.parse("2025-12-31 23:59:59")
* remaining = deadline.diffDays(time.now())
* print("締切まで" + remaining.toString() + "日")
* ```
*
*
* ## ⚠️ 注意
* - ローカルタイムゾーンに基づく処理
* - パース可能な日時フォーマットは限定的
@ -118,11 +118,11 @@
* - 夏時間切り替え時は計算に注意
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use std::fmt::{Debug, Display};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use chrono::{DateTime, Datelike, Local, TimeZone, Timelike};
use std::any::Any;
use std::time::{SystemTime, Duration};
use chrono::{DateTime, Local, TimeZone, Datelike, Timelike};
use std::fmt::{Debug, Display};
use std::time::{Duration, SystemTime};
/// 時間操作を提供するBox
#[derive(Debug, Clone)]
@ -132,23 +132,27 @@ pub struct TimeBox {
impl TimeBox {
pub fn new() -> Self {
Self { base: BoxBase::new() }
Self {
base: BoxBase::new(),
}
}
/// 現在時刻を取得
pub fn now(&self) -> Box<dyn NyashBox> {
Box::new(DateTimeBox::now())
}
/// UNIXタイムスタンプから日時を作成
pub fn fromTimestamp(&self, timestamp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = timestamp.as_any().downcast_ref::<IntegerBox>() {
Box::new(DateTimeBox::from_timestamp(int_box.value))
} else {
Box::new(StringBox::new("Error: fromTimestamp() requires integer input"))
Box::new(StringBox::new(
"Error: fromTimestamp() requires integer input",
))
}
}
/// 日時文字列をパース
pub fn parse(&self, date_str: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(string_box) = date_str.as_any().downcast_ref::<StringBox>() {
@ -160,7 +164,7 @@ impl TimeBox {
Box::new(StringBox::new("Error: parse() requires string input"))
}
}
/// ミリ秒スリープ
pub fn sleep(&self, millis: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = millis.as_any().downcast_ref::<IntegerBox>() {
@ -168,13 +172,15 @@ impl TimeBox {
std::thread::sleep(Duration::from_millis(int_box.value as u64));
Box::new(StringBox::new("ok"))
} else {
Box::new(StringBox::new("Error: sleep() requires positive milliseconds"))
Box::new(StringBox::new(
"Error: sleep() requires positive milliseconds",
))
}
} else {
Box::new(StringBox::new("Error: sleep() requires integer input"))
}
}
/// 現在時刻をフォーマット
pub fn format(&self, format_str: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(str_box) = format_str.as_any().downcast_ref::<StringBox>() {
@ -182,7 +188,9 @@ impl TimeBox {
let formatted = now.format(&str_box.value).to_string();
Box::new(StringBox::new(formatted))
} else {
Box::new(StringBox::new("Error: format() requires string format pattern"))
Box::new(StringBox::new(
"Error: format() requires string format pattern",
))
}
}
}
@ -191,20 +199,20 @@ impl NyashBox for TimeBox {
fn type_name(&self) -> &'static str {
"TimeBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("TimeBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_time) = other.as_any().downcast_ref::<TimeBox>() {
BoolBox::new(self.base.id == other_time.base.id)
@ -212,26 +220,25 @@ impl NyashBox for TimeBox {
BoolBox::new(false)
}
}
}
impl BoxCore for TimeBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TimeBox()")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -253,23 +260,26 @@ pub struct DateTimeBox {
impl DateTimeBox {
/// 現在時刻で作成
pub fn now() -> Self {
Self {
Self {
datetime: Local::now(),
base: BoxBase::new(),
}
}
/// UNIXタイムスタンプから作成
pub fn from_timestamp(timestamp: i64) -> Self {
let datetime = Local.timestamp_opt(timestamp, 0).unwrap();
Self { datetime, base: BoxBase::new() }
Self {
datetime,
base: BoxBase::new(),
}
}
/// 文字列からパース
pub fn parse(date_str: &str) -> Result<Self, String> {
// ISO 8601形式でパース
match DateTime::parse_from_rfc3339(date_str) {
Ok(dt) => Ok(Self {
Ok(dt) => Ok(Self {
datetime: dt.with_timezone(&Local),
base: BoxBase::new(),
}),
@ -278,54 +288,57 @@ impl DateTimeBox {
match chrono::NaiveDateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S") {
Ok(naive_dt) => {
let datetime = Local.from_local_datetime(&naive_dt).unwrap();
Ok(Self { datetime, base: BoxBase::new() })
Ok(Self {
datetime,
base: BoxBase::new(),
})
}
Err(e) => Err(format!("Failed to parse date: {}", e)),
}
}
}
}
/// 年を取得
pub fn year(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.year() as i64))
}
/// 月を取得
pub fn month(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.month() as i64))
}
/// 日を取得
pub fn day(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.day() as i64))
}
/// 時を取得
pub fn hour(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.hour() as i64))
}
/// 分を取得
pub fn minute(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.minute() as i64))
}
/// 秒を取得
pub fn second(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.second() as i64))
}
/// UNIXタイムスタンプを取得
pub fn timestamp(&self) -> Box<dyn NyashBox> {
Box::new(IntegerBox::new(self.datetime.timestamp()))
}
/// ISO 8601形式でフォーマット
pub fn toISOString(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(&self.datetime.to_rfc3339()))
}
/// カスタムフォーマット
pub fn format(&self, fmt: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(string_box) = fmt.as_any().downcast_ref::<StringBox>() {
@ -335,7 +348,7 @@ impl DateTimeBox {
Box::new(StringBox::new("Error: format() requires string input"))
}
}
/// 日付を加算
pub fn addDays(&self, days: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = days.as_any().downcast_ref::<IntegerBox>() {
@ -348,7 +361,7 @@ impl DateTimeBox {
Box::new(StringBox::new("Error: addDays() requires integer input"))
}
}
/// 時間を加算
pub fn addHours(&self, hours: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = hours.as_any().downcast_ref::<IntegerBox>() {
@ -367,20 +380,20 @@ impl NyashBox for DateTimeBox {
fn type_name(&self) -> &'static str {
"DateTimeBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new(&self.datetime.format("%Y-%m-%d %H:%M:%S").to_string())
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_dt) = other.as_any().downcast_ref::<DateTimeBox>() {
BoolBox::new(self.datetime == other_dt.datetime)
@ -388,26 +401,25 @@ impl NyashBox for DateTimeBox {
BoolBox::new(false)
}
}
}
impl BoxCore for DateTimeBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.datetime.format("%Y-%m-%d %H:%M:%S"))
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -433,7 +445,7 @@ impl TimerBox {
base: BoxBase::new(),
}
}
/// 経過時間をミリ秒で取得
pub fn elapsed(&self) -> Box<dyn NyashBox> {
match self.start_time.elapsed() {
@ -444,7 +456,7 @@ impl TimerBox {
Err(_) => Box::new(IntegerBox::new(0)),
}
}
/// タイマーをリセット
pub fn reset(&mut self) -> Box<dyn NyashBox> {
self.start_time = SystemTime::now();
@ -456,20 +468,20 @@ impl NyashBox for TimerBox {
fn type_name(&self) -> &'static str {
"TimerBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("TimerBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_timer) = other.as_any().downcast_ref::<TimerBox>() {
BoolBox::new(self.base.id == other_timer.base.id)
@ -477,26 +489,25 @@ impl NyashBox for TimerBox {
BoolBox::new(false)
}
}
}
impl BoxCore for TimerBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TimerBox()")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -506,4 +517,4 @@ impl Display for TimerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,41 +1,41 @@
/*!
* TimerBox - JavaScript風タイマー機能Box
*
*
* ## 📝 概要
* setTimeout/setInterval/requestAnimationFrameをNyashから利用可能にするBox。
* アニメーション、遅延実行、定期実行を統一的に管理。
*
*
* ## 🛠️ 利用可能メソッド
*
*
* ### ⏱️ 基本タイマー
* - `setTimeout(callback, delay)` - 指定時間後に1回実行
* - `setInterval(callback, interval)` - 指定間隔で繰り返し実行
* - `clearTimeout(id)` - タイマーをキャンセル
* - `clearInterval(id)` - インターバルをキャンセル
*
*
* ### 🎮 アニメーション
* - `requestAnimationFrame(callback)` - 次フレームで実行
* - `cancelAnimationFrame(id)` - アニメーションをキャンセル
*
*
* ### 📊 時間測定
* - `now()` - 現在時刻(ミリ秒)
* - `performance()` - 高精度時刻測定
*
*
* ## 💡 使用例
* ```nyash
* local timer, id
* timer = new TimerBox()
*
*
* // 1秒後に実行
* id = timer.setTimeout(function() {
* print("Hello after 1 second!")
* }, 1000)
*
*
* // 500msごとに実行
* id = timer.setInterval(function() {
* print("Tick every 500ms")
* }, 500)
*
*
* // アニメーションループ
* timer.requestAnimationFrame(function() {
* // 描画処理
@ -45,7 +45,7 @@
* ```
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
@ -81,7 +81,7 @@ impl TimerBox {
js_sys::Date::now()
}
}
#[cfg(not(target_arch = "wasm32"))]
{
use std::time::{SystemTime, UNIX_EPOCH};
@ -101,7 +101,8 @@ impl TimerBox {
/// setTimeout相当の遅延実行
pub fn set_timeout(&self, callback: &js_sys::Function, delay: i32) -> i32 {
if let Some(window) = window() {
window.set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay)
window
.set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay)
.unwrap_or(-1)
} else {
-1
@ -112,7 +113,8 @@ impl TimerBox {
/// setInterval相当の定期実行
pub fn set_interval(&self, callback: &js_sys::Function, interval: i32) -> i32 {
if let Some(window) = window() {
window.set_interval_with_callback_and_timeout_and_arguments_0(callback, interval)
window
.set_interval_with_callback_and_timeout_and_arguments_0(callback, interval)
.unwrap_or(-1)
} else {
-1
@ -192,19 +194,19 @@ impl BoxCore for TimerBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "TimerBox(id={})", self.base.id)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -214,7 +216,7 @@ impl NyashBox for TimerBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -227,7 +229,7 @@ impl NyashBox for TimerBox {
fn type_name(&self) -> &'static str {
"TimerBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_timer) = other.as_any().downcast_ref::<TimerBox>() {
BoolBox::new(self.base.id == other_timer.base.id)
@ -241,4 +243,4 @@ impl std::fmt::Display for TimerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
/// Cancellation token as a Box for structured concurrency
@ -9,30 +9,69 @@ pub struct TokenBox {
}
impl TokenBox {
pub fn new() -> Self { Self { base: BoxBase::new(), token: crate::runtime::scheduler::CancellationToken::new() } }
pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self { Self { base: BoxBase::new(), token } }
pub fn cancel(&self) { self.token.cancel(); }
pub fn is_cancelled(&self) -> bool { self.token.is_cancelled() }
pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken { self.token.clone() }
pub fn new() -> Self {
Self {
base: BoxBase::new(),
token: crate::runtime::scheduler::CancellationToken::new(),
}
}
pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self {
Self {
base: BoxBase::new(),
token,
}
}
pub fn cancel(&self) {
self.token.cancel();
}
pub fn is_cancelled(&self) -> bool {
self.token.is_cancelled()
}
pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken {
self.token.clone()
}
}
impl BoxCore for TokenBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "CancellationToken(cancelled={})", self.token.is_cancelled())
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
None
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"CancellationToken(cancelled={})",
self.token.is_cancelled()
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
impl NyashBox for TokenBox {
fn to_string_box(&self) -> StringBox { StringBox::new(format!("CancellationToken(cancelled={})", self.token.is_cancelled())) }
fn to_string_box(&self) -> StringBox {
StringBox::new(format!(
"CancellationToken(cancelled={})",
self.token.is_cancelled()
))
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(o) = other.as_any().downcast_ref::<TokenBox>() {
BoolBox::new(self.is_cancelled() == o.is_cancelled())
} else { BoolBox::new(false) }
} else {
BoolBox::new(false)
}
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
fn share_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
}

View File

@ -1,6 +1,6 @@
/*!
* Web Boxes Module - ブラウザ専用Box群
*
*
* WebAssembly環境専用のBox群を管理
* HTML5 APIs、DOM操作、Canvas描画等をNyashから利用可能にする
*/
@ -21,4 +21,4 @@ pub use web_display_box::WebDisplayBox;
pub use web_console_box::WebConsoleBox;
#[cfg(target_arch = "wasm32")]
pub use web_canvas_box::WebCanvasBox;
pub use web_canvas_box::WebCanvasBox;

View File

@ -1,21 +1,18 @@
/*!
* WebCanvasBox - ブラウザCanvas完全制御Box
*
*
* WebAssembly環境でHTML5 Canvasの完全制御
* ピクセルの世界を制圧する革命的Box
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{
HtmlCanvasElement,
CanvasRenderingContext2d,
};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
// 🎨 Browser Canvas complete control Box
#[cfg(target_arch = "wasm32")]
@ -30,22 +27,22 @@ pub struct WebCanvasBox {
#[cfg(target_arch = "wasm32")]
impl WebCanvasBox {
pub fn new(canvas_id: String, width: u32, height: u32) -> Self {
let instance = Self {
let instance = Self {
base: BoxBase::new(),
canvas_id: canvas_id.clone(),
width,
height,
};
// キャンバス要素を初期化
if let Some(canvas) = instance.get_canvas_element() {
canvas.set_width(width);
canvas.set_height(height);
}
instance
}
/// Canvas要素を取得
fn get_canvas_element(&self) -> Option<HtmlCanvasElement> {
let window = web_sys::window()?;
@ -53,7 +50,7 @@ impl WebCanvasBox {
let element = document.get_element_by_id(&self.canvas_id)?;
element.dyn_into::<HtmlCanvasElement>().ok()
}
/// 2Dレンダリングコンテキストを取得
fn get_2d_context(&self) -> Option<CanvasRenderingContext2d> {
let canvas = self.get_canvas_element()?;
@ -62,35 +59,35 @@ impl WebCanvasBox {
.ok()?
.and_then(|ctx| ctx.dyn_into::<CanvasRenderingContext2d>().ok())
}
/// キャンバスをクリア
pub fn clear(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.clear_rect(0.0, 0.0, self.width as f64, self.height as f64);
}
}
/// 塗りつぶし色を設定
pub fn set_fill_style(&self, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
}
}
/// 線の色を設定
pub fn set_stroke_style(&self, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
}
}
/// 線の太さを設定
pub fn set_line_width(&self, width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_line_width(width);
}
}
/// 塗りつぶし矩形を描画
pub fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str) {
if let Some(ctx) = self.get_2d_context() {
@ -98,37 +95,47 @@ impl WebCanvasBox {
ctx.fill_rect(x, y, width, height);
}
}
/// 枠線矩形を描画
pub fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str, line_width: f64) {
pub fn stroke_rect(
&self,
x: f64,
y: f64,
width: f64,
height: f64,
color: &str,
line_width: f64,
) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.stroke_rect(x, y, width, height);
}
}
/// 塗りつぶし円を描画
pub fn fill_circle(&self, x: f64, y: f64, radius: f64, color: &str) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color));
ctx.begin_path();
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default();
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI)
.unwrap_or_default();
ctx.fill();
}
}
/// 枠線円を描画
pub fn stroke_circle(&self, x: f64, y: f64, radius: f64, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
ctx.set_line_width(line_width);
ctx.begin_path();
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default();
ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI)
.unwrap_or_default();
ctx.stroke();
}
}
/// 直線を描画
pub fn draw_line(&self, x1: f64, y1: f64, x2: f64, y2: f64, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
@ -140,7 +147,7 @@ impl WebCanvasBox {
ctx.stroke();
}
}
/// テキストを描画(塗りつぶし)
pub fn fill_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str) {
if let Some(ctx) = self.get_2d_context() {
@ -149,9 +156,17 @@ impl WebCanvasBox {
ctx.fill_text(text, x, y).unwrap_or_default();
}
}
/// テキストを描画(枠線)
pub fn stroke_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str, line_width: f64) {
pub fn stroke_text(
&self,
text: &str,
x: f64,
y: f64,
font: &str,
color: &str,
line_width: f64,
) {
if let Some(ctx) = self.get_2d_context() {
ctx.set_font(font);
ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color));
@ -159,35 +174,35 @@ impl WebCanvasBox {
ctx.stroke_text(text, x, y).unwrap_or_default();
}
}
/// パス描画開始
pub fn begin_path(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.begin_path();
}
}
/// パスを指定位置に移動
pub fn move_to(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.move_to(x, y);
}
}
/// パスに直線を追加
pub fn line_to(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.line_to(x, y);
}
}
/// パスを閉じる
pub fn close_path(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.close_path();
}
}
/// パスを塗りつぶし
pub fn fill(&self, color: &str) {
if let Some(ctx) = self.get_2d_context() {
@ -195,7 +210,7 @@ impl WebCanvasBox {
ctx.fill();
}
}
/// パスを枠線描画
pub fn stroke(&self, color: &str, line_width: f64) {
if let Some(ctx) = self.get_2d_context() {
@ -204,56 +219,56 @@ impl WebCanvasBox {
ctx.stroke();
}
}
/// 現在の描画状態を保存
pub fn save(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.save();
}
}
/// 描画状態を復元
pub fn restore(&self) {
if let Some(ctx) = self.get_2d_context() {
ctx.restore();
}
}
/// 座標系を回転
pub fn rotate(&self, angle: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.rotate(angle).unwrap_or_default();
}
}
/// 座標系をスケール
pub fn scale(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.scale(x, y).unwrap_or_default();
}
}
/// 座標系を平行移動
pub fn translate(&self, x: f64, y: f64) {
if let Some(ctx) = self.get_2d_context() {
ctx.translate(x, y).unwrap_or_default();
}
}
/// キャンバスのサイズを取得
pub fn get_width(&self) -> u32 {
self.width
}
pub fn get_height(&self) -> u32 {
self.height
}
/// キャンバスのサイズを変更
pub fn resize(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
if let Some(canvas) = self.get_canvas_element() {
canvas.set_width(width);
canvas.set_height(height);
@ -266,19 +281,23 @@ impl BoxCore for WebCanvasBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "WebCanvasBox({}, {}x{})", self.canvas_id, self.width, self.height)
write!(
f,
"WebCanvasBox({}, {}x{})",
self.canvas_id, self.width, self.height
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -289,7 +308,7 @@ impl NyashBox for WebCanvasBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -297,18 +316,14 @@ impl NyashBox for WebCanvasBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(format!(
"WebCanvasBox({}, {}x{})",
self.canvas_id,
self.width,
self.height
"WebCanvasBox({}, {}x{})",
self.canvas_id, self.width, self.height
))
}
fn type_name(&self) -> &'static str {
"WebCanvasBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_canvas) = other.as_any().downcast_ref::<WebCanvasBox>() {
@ -324,4 +339,4 @@ impl std::fmt::Display for WebCanvasBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,11 +1,11 @@
/*!
* WebConsoleBox - ブラウザHTML要素コンソール出力Box
*
*
* WebAssembly環境でHTML要素へのコンソール風出力
* F12コンソールの代わりに指定要素に出力
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
@ -25,85 +25,91 @@ pub struct WebConsoleBox {
#[cfg(target_arch = "wasm32")]
impl WebConsoleBox {
pub fn new(element_id: String) -> Self {
Self {
Self {
base: BoxBase::new(),
target_element_id: element_id,
}
}
/// 指定した要素IDのHTML要素を取得
fn get_target_element(&self) -> Option<Element> {
let window = web_sys::window()?;
let document = window.document()?;
document.get_element_by_id(&self.target_element_id)
}
/// コンソール出力を追加(改行付き)
fn append_console_line(&self, message: &str, level: &str) {
if let Some(element) = self.get_target_element() {
let timestamp = js_sys::Date::new_0().to_iso_string().as_string().unwrap_or_default();
let time_part = timestamp.split('T').nth(1).unwrap_or("00:00:00").split('.').nth(0).unwrap_or("00:00:00");
let timestamp = js_sys::Date::new_0()
.to_iso_string()
.as_string()
.unwrap_or_default();
let time_part = timestamp
.split('T')
.nth(1)
.unwrap_or("00:00:00")
.split('.')
.nth(0)
.unwrap_or("00:00:00");
let (level_prefix, color) = match level {
"log" => ("📝", "white"),
"warn" => ("⚠️", "yellow"),
"error" => ("", "red"),
"error" => ("", "red"),
"info" => ("", "cyan"),
"debug" => ("🔍", "gray"),
_ => ("📝", "white"),
};
let formatted_line = format!(
"<span style='color: {}'>[{}] {} {}</span><br>",
color,
time_part,
level_prefix,
message
"<span style='color: {}'>[{}] {} {}</span><br>",
color, time_part, level_prefix, message
);
let current_content = element.inner_html();
let new_content = format!("{}{}", current_content, formatted_line);
element.set_inner_html(&new_content);
// 自動スクロール
if let Some(html_element) = element.dyn_ref::<HtmlElement>() {
html_element.set_scroll_top(html_element.scroll_height());
}
}
}
/// ログメッセージを出力
pub fn log(&self, message: &str) {
self.append_console_line(message, "log");
}
/// 警告メッセージを出力
pub fn warn(&self, message: &str) {
self.append_console_line(message, "warn");
}
/// エラーメッセージを出力
pub fn error(&self, message: &str) {
self.append_console_line(message, "error");
}
/// 情報メッセージを出力
pub fn info(&self, message: &str) {
self.append_console_line(message, "info");
}
/// デバッグメッセージを出力
pub fn debug(&self, message: &str) {
self.append_console_line(message, "debug");
}
/// コンソールをクリア
pub fn clear(&self) {
if let Some(element) = self.get_target_element() {
element.set_inner_html("");
}
}
/// 区切り線を追加
pub fn separator(&self) {
if let Some(element) = self.get_target_element() {
@ -113,7 +119,7 @@ impl WebConsoleBox {
element.set_inner_html(&new_content);
}
}
/// グループ開始(見出し付き)
pub fn group(&self, title: &str) {
if let Some(element) = self.get_target_element() {
@ -126,7 +132,7 @@ impl WebConsoleBox {
element.set_inner_html(&new_content);
}
}
/// グループ終了
pub fn group_end(&self) {
if let Some(element) = self.get_target_element() {
@ -143,19 +149,19 @@ impl BoxCore for WebConsoleBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "WebConsoleBox({})", self.target_element_id)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -166,7 +172,7 @@ impl NyashBox for WebConsoleBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -176,11 +182,9 @@ impl NyashBox for WebConsoleBox {
StringBox::new(format!("WebConsoleBox({})", self.target_element_id))
}
fn type_name(&self) -> &'static str {
"WebConsoleBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_console) = other.as_any().downcast_ref::<WebConsoleBox>() {
@ -196,4 +200,4 @@ impl std::fmt::Display for WebConsoleBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}

View File

@ -1,11 +1,11 @@
/*!
* WebDisplayBox - ブラウザHTML要素表示制御Box
*
*
* WebAssembly環境でHTML要素への直接出力・スタイル制御
* プレイグラウンドの出力パネル等を完全制御
*/
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
#[cfg(target_arch = "wasm32")]
@ -25,19 +25,19 @@ pub struct WebDisplayBox {
#[cfg(target_arch = "wasm32")]
impl WebDisplayBox {
pub fn new(element_id: String) -> Self {
Self {
Self {
base: BoxBase::new(),
target_element_id: element_id,
}
}
/// 指定した要素IDのHTML要素を取得
fn get_target_element(&self) -> Option<Element> {
let window = web_sys::window()?;
let document = window.document()?;
document.get_element_by_id(&self.target_element_id)
}
/// テキストを追加出力
pub fn print(&self, message: &str) {
if let Some(element) = self.get_target_element() {
@ -50,7 +50,7 @@ impl WebDisplayBox {
element.set_inner_html(&new_content);
}
}
/// テキストを改行付きで追加出力
pub fn println(&self, message: &str) {
if let Some(element) = self.get_target_element() {
@ -63,14 +63,14 @@ impl WebDisplayBox {
element.set_inner_html(&new_content);
}
}
/// HTMLコンテンツを完全置換
pub fn set_html(&self, html_content: &str) {
if let Some(element) = self.get_target_element() {
element.set_inner_html(html_content);
}
}
/// HTMLコンテンツを追加
pub fn append_html(&self, html_content: &str) {
if let Some(element) = self.get_target_element() {
@ -79,7 +79,7 @@ impl WebDisplayBox {
element.set_inner_html(&new_content);
}
}
/// CSSスタイルを設定
pub fn set_css(&self, property: &str, value: &str) {
if let Some(element) = self.get_target_element() {
@ -89,38 +89,38 @@ impl WebDisplayBox {
}
}
}
/// CSSクラスを追加
pub fn add_class(&self, class_name: &str) {
if let Some(element) = self.get_target_element() {
let _ = element.class_list().add_1(class_name);
}
}
/// CSSクラスを削除
pub fn remove_class(&self, class_name: &str) {
if let Some(element) = self.get_target_element() {
let _ = element.class_list().remove_1(class_name);
}
}
/// 内容をクリア
pub fn clear(&self) {
if let Some(element) = self.get_target_element() {
element.set_inner_html("");
}
}
/// 要素を表示
pub fn show(&self) {
self.set_css("display", "block");
}
/// 要素を非表示
pub fn hide(&self) {
self.set_css("display", "none");
}
/// スクロールを最下部に移動
pub fn scroll_to_bottom(&self) {
if let Some(element) = self.get_target_element() {
@ -136,19 +136,19 @@ impl BoxCore for WebDisplayBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "WebDisplayBox({})", self.target_element_id)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@ -159,7 +159,7 @@ impl NyashBox for WebDisplayBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
/// 仮実装: clone_boxと同じ後で修正
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
@ -169,11 +169,9 @@ impl NyashBox for WebDisplayBox {
StringBox::new(format!("WebDisplayBox({})", self.target_element_id))
}
fn type_name(&self) -> &'static str {
"WebDisplayBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_display) = other.as_any().downcast_ref::<WebDisplayBox>() {
@ -189,4 +187,4 @@ impl std::fmt::Display for WebDisplayBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
}