🚀 feat: P2PBox/IntentBox実装 - NyaMesh通信基盤の第一歩
## 🎯 概要 NyaMeshプロジェクトの基盤となるP2PBox/IntentBoxを実装。 シンプルなsend/onインターフェースで通信ノード間のメッセージングを実現。 ## ✨ 新機能 - **IntentBox**: 通信世界を定義するコンテナ - Transportトレイトで通信方式を抽象化 - LocalTransport実装(プロセス内通信) - 将来のWebSocket/SharedMemory拡張に対応 - **P2PBox**: 通信ノードの実装 - send(intent, data, target) - 特定ノードへ送信 - broadcast(intent, data) - 全ノードへ配信 - on(intent, callback) - リスナー登録 - off(intent) - リスナー解除 - 同一intentに複数リスナー登録可能 ## 🔧 技術詳細 - Arc<Mutex>パターンで完全なスレッドセーフティ - Arc<P2PBoxInner>構造でBox型システムとの整合性確保 - インタープリター完全統合(new/メソッド呼び出し) ## 🧪 テスト - test_p2p_basic.nyash - 基本機能検証 - test_p2p_message_types.nyash - 各種データ型対応 - test_p2p_edge_cases.nyash - エラー処理 - test_p2p_callback_demo.nyash - 実用例 ## 📝 TODO (将来拡張) - WebSocket/SharedMemoryトランスポート - コールバック実行(MethodBox統合待ち) - ノード登録管理システム 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
|
||||
use super::*;
|
||||
use crate::ast::UnaryOperator;
|
||||
use crate::boxes::{buffer::BufferBox, JSONBox, HttpClientBox, StreamBox, RegexBox};
|
||||
use crate::boxes::{buffer::BufferBox, JSONBox, HttpClientBox, StreamBox, RegexBox, IntentBox, P2PBox};
|
||||
use crate::operator_traits::OperatorResolver;
|
||||
// TODO: Fix NullBox import issue later
|
||||
// use crate::NullBox;
|
||||
@ -427,6 +427,16 @@ impl NyashInterpreter {
|
||||
return self.execute_console_method(console_box, method, arguments);
|
||||
}
|
||||
|
||||
// IntentBox method calls
|
||||
if let Some(intent_box) = obj_value.as_any().downcast_ref::<IntentBox>() {
|
||||
return self.execute_intent_box_method(intent_box, method, arguments);
|
||||
}
|
||||
|
||||
// P2PBox method calls
|
||||
if let Some(p2p_box) = obj_value.as_any().downcast_ref::<P2PBox>() {
|
||||
return self.execute_p2p_box_method(p2p_box, method, arguments);
|
||||
}
|
||||
|
||||
// EguiBox method calls (非WASM環境のみ)
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Some(egui_box) = obj_value.as_any().downcast_ref::<crate::boxes::EguiBox>() {
|
||||
|
||||
@ -21,6 +21,7 @@ pub mod collection_methods; // ArrayBox, MapBox
|
||||
pub mod io_methods; // FileBox, ResultBox
|
||||
pub mod data_methods; // BufferBox, JSONBox, RegexBox
|
||||
pub mod network_methods; // HttpClientBox, StreamBox
|
||||
pub mod p2p_methods; // IntentBox, P2PBox
|
||||
|
||||
// Re-export methods for easy access
|
||||
pub use basic_methods::*;
|
||||
|
||||
137
src/interpreter/methods/p2p_methods.rs
Normal file
137
src/interpreter/methods/p2p_methods.rs
Normal file
@ -0,0 +1,137 @@
|
||||
/*! 📡 P2P通信メソッド実装
|
||||
* IntentBoxとP2PBoxのNyashインタープリター統合
|
||||
*/
|
||||
|
||||
use crate::interpreter::core::NyashInterpreter;
|
||||
use crate::interpreter::core::RuntimeError;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::box_trait::{NyashBox, StringBox};
|
||||
use crate::boxes::{IntentBox, P2PBox};
|
||||
|
||||
impl NyashInterpreter {
|
||||
/// IntentBoxのメソッド実行
|
||||
pub(in crate::interpreter) fn execute_intent_box_method(
|
||||
&mut self,
|
||||
intent_box: &IntentBox,
|
||||
method: &str,
|
||||
_arguments: &[ASTNode],
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
match method {
|
||||
// 基本情報取得
|
||||
"getType" | "type" => {
|
||||
Ok(Box::new(StringBox::new("IntentBox")))
|
||||
}
|
||||
|
||||
// メッセージ処理(テスト用)
|
||||
"processMessages" => {
|
||||
let messages = intent_box.process_messages();
|
||||
Ok(Box::new(StringBox::new(format!("Processed {} messages", messages.len()))))
|
||||
}
|
||||
|
||||
_ => Err(RuntimeError::UndefinedVariable {
|
||||
name: format!("IntentBox method '{}' not found", method),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// P2PBoxのメソッド実行
|
||||
pub(in crate::interpreter) fn execute_p2p_box_method(
|
||||
&mut self,
|
||||
p2p_box: &P2PBox,
|
||||
method: &str,
|
||||
arguments: &[ASTNode],
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
match method {
|
||||
// ノードID取得
|
||||
"getNodeId" | "getId" => {
|
||||
Ok(Box::new(StringBox::new(p2p_box.get_node_id())))
|
||||
}
|
||||
|
||||
// メッセージ送信
|
||||
"send" => {
|
||||
if arguments.len() < 3 {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: "send requires 3 arguments: intent, data, target".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let intent = self.execute_expression(&arguments[0])?;
|
||||
let data = self.execute_expression(&arguments[1])?;
|
||||
let target = self.execute_expression(&arguments[2])?;
|
||||
|
||||
if let Some(intent_str) = intent.as_any().downcast_ref::<StringBox>() {
|
||||
if let Some(target_str) = target.as_any().downcast_ref::<StringBox>() {
|
||||
return Ok(p2p_box.send(&intent_str.value, data, &target_str.value));
|
||||
}
|
||||
}
|
||||
|
||||
Err(RuntimeError::TypeError {
|
||||
message: "send requires string arguments for intent and target".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// ブロードキャスト
|
||||
"broadcast" => {
|
||||
if arguments.len() < 2 {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: "broadcast requires 2 arguments: intent, data".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let intent = self.execute_expression(&arguments[0])?;
|
||||
let data = self.execute_expression(&arguments[1])?;
|
||||
|
||||
if let Some(intent_str) = intent.as_any().downcast_ref::<StringBox>() {
|
||||
return Ok(p2p_box.broadcast(&intent_str.value, data));
|
||||
}
|
||||
|
||||
Err(RuntimeError::TypeError {
|
||||
message: "broadcast requires string argument for intent".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// リスナー登録
|
||||
"on" => {
|
||||
if arguments.len() < 2 {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: "on requires 2 arguments: intent, callback".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let intent = self.execute_expression(&arguments[0])?;
|
||||
let callback = self.execute_expression(&arguments[1])?;
|
||||
|
||||
if let Some(intent_str) = intent.as_any().downcast_ref::<StringBox>() {
|
||||
return Ok(p2p_box.on(&intent_str.value, callback));
|
||||
}
|
||||
|
||||
Err(RuntimeError::TypeError {
|
||||
message: "on requires string argument for intent".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// リスナー解除
|
||||
"off" => {
|
||||
if arguments.is_empty() {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: "off requires 1 argument: intent".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let intent = self.execute_expression(&arguments[0])?;
|
||||
|
||||
if let Some(intent_str) = intent.as_any().downcast_ref::<StringBox>() {
|
||||
return Ok(p2p_box.off(&intent_str.value));
|
||||
}
|
||||
|
||||
Err(RuntimeError::TypeError {
|
||||
message: "off requires string argument for intent".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
_ => Err(RuntimeError::UndefinedVariable {
|
||||
name: format!("P2PBox method '{}' not found", method),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,6 +442,58 @@ impl NyashInterpreter {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
"IntentBox" => {
|
||||
// IntentBoxは引数なしで作成(デフォルトローカル通信)
|
||||
if !arguments.is_empty() {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("IntentBox constructor expects 0 arguments, got {}", arguments.len()),
|
||||
});
|
||||
}
|
||||
let intent_box = crate::boxes::IntentBox::new();
|
||||
return Ok(Box::new(intent_box) as Box<dyn NyashBox>);
|
||||
}
|
||||
|
||||
"P2PBox" => {
|
||||
// P2PBoxは引数2個(node_id, intent_box)で作成
|
||||
if arguments.is_empty() {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: "P2PBox requires at least 1 argument (node_id)".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// 引数を評価
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in arguments {
|
||||
arg_values.push(self.execute_expression(arg)?);
|
||||
}
|
||||
|
||||
// 第1引数: ノードID
|
||||
let node_id = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
|
||||
str_box.value.clone()
|
||||
} else {
|
||||
return Err(RuntimeError::TypeError {
|
||||
message: "P2PBox first argument must be a string (node_id)".to_string(),
|
||||
});
|
||||
};
|
||||
|
||||
// 第2引数: IntentBox(省略時はデフォルト)
|
||||
let intent_box = if arg_values.len() > 1 {
|
||||
if let Some(intent) = arg_values[1].as_any().downcast_ref::<crate::boxes::IntentBox>() {
|
||||
std::sync::Arc::new(intent.clone())
|
||||
} else {
|
||||
return Err(RuntimeError::TypeError {
|
||||
message: "P2PBox second argument must be an IntentBox".to_string(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// デフォルトのIntentBoxを作成
|
||||
std::sync::Arc::new(crate::boxes::IntentBox::new())
|
||||
};
|
||||
|
||||
let p2p_box = crate::boxes::P2PBox::new(node_id, intent_box);
|
||||
return Ok(Box::new(p2p_box));
|
||||
}
|
||||
"StreamBox" => {
|
||||
// StreamBoxは引数なしで作成
|
||||
if !arguments.is_empty() {
|
||||
@ -694,7 +746,8 @@ impl NyashInterpreter {
|
||||
"FileBox" | "ResultBox" | "FutureBox" | "ChannelBox" | "MathBox" |
|
||||
"TimeBox" | "DateTimeBox" | "TimerBox" | "RandomBox" | "SoundBox" |
|
||||
"DebugBox" | "MethodBox" | "NullBox" | "ConsoleBox" | "FloatBox" |
|
||||
"BufferBox" | "RegexBox" | "JSONBox" | "StreamBox" | "HTTPClientBox"
|
||||
"BufferBox" | "RegexBox" | "JSONBox" | "StreamBox" | "HTTPClientBox" |
|
||||
"IntentBox" | "P2PBox"
|
||||
);
|
||||
|
||||
// Web専用Box(WASM環境のみ)
|
||||
|
||||
Reference in New Issue
Block a user