stage3: unify to cleanup; MIR return-defer; docs+smokes updated; LLVM(harness): finalize_phis ownership, ret.py simplified, uses-predeclare; cleanup return override green; method-postfix cleanup return WIP (PHI head)

This commit is contained in:
Selfhosting Dev
2025-09-19 02:07:38 +09:00
parent 951a050592
commit 5e818eeb7e
205 changed files with 9671 additions and 1849 deletions

View File

@ -1,152 +0,0 @@
/*!
* Async Methods Module
*
* Extracted from interpreter/box_methods.rs
* Contains asynchronous Box type method implementations:
*
* - execute_future_method (FutureBox)
* - execute_channel_method (ChannelBox)
*
* These methods handle asynchronous operations, futures, and
* communication channels in the Nyash interpreter.
*/
use super::*;
use crate::box_trait::StringBox;
use crate::channel_box::{ChannelBox, MessageBox};
impl NyashInterpreter {
/// FutureBoxのメソッド呼び出しを実行
///
/// 非同期計算の結果を管理するFutureBoxの基本操作を提供します。
///
/// サポートメソッド:
/// - get() -> 計算結果を取得 (ブロッキング)
/// - ready() -> 計算完了状態をチェック
/// - equals(other) -> 他のFutureBoxと比較
pub(super) fn execute_future_method(
&mut self,
future_box: &FutureBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"get" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(future_box.get())
}
"ready" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ready() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(BoolBox::new(future_box.ready())))
}
"equals" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("equals() expects 1 argument, got {}", arguments.len()),
});
}
let other = self.execute_expression(&arguments[0])?;
Ok(Box::new(future_box.equals(other.as_ref())))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for FutureBox", method),
}),
}
}
/// ChannelBoxのメソッド呼び出しを実行
///
/// 非同期通信チャンネルを管理するChannelBoxの操作を提供します。
/// プロセス間通信やイベント駆動プログラミングに使用されます。
///
/// サポートメソッド:
/// - sendMessage(content) -> メッセージを送信
/// - announce(content) -> ブロードキャスト送信
/// - toString() -> チャンネル情報を文字列化
/// - sender() -> 送信者情報を取得
/// - receiver() -> 受信者情報を取得
pub(super) fn execute_channel_method(
&mut self,
channel_box: &ChannelBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"sendMessage" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"sendMessage() expects 1 argument, got {}",
arg_values.len()
),
});
}
// 簡易実装:メッセージを作成して返す
let content = arg_values[0].to_string_box().value;
let msg = MessageBox::new(&channel_box.sender_name, &content);
Ok(Box::new(msg))
}
"announce" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("announce() expects 1 argument, got {}", arg_values.len()),
});
}
let content = arg_values[0].to_string_box().value;
Ok(Box::new(StringBox::new(&format!(
"Broadcast from {}: {}",
channel_box.sender_name, content
))))
}
"toString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(channel_box.to_string_box()))
}
"sender" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("sender() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(channel_box.sender())
}
"receiver" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"receiver() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(channel_box.receiver())
}
_ => {
// その他のメソッドはChannelBoxに委譲
Ok(channel_box.invoke(method, arg_values))
}
}
}
}

View File

@ -1,40 +0,0 @@
/*!
* Async Operations Module
*
* Extracted from expressions.rs lines 1020-1031 (~11 lines)
* Handles await expression processing for asynchronous operations
* Core philosophy: "Everything is Box" with async support
*/
use super::*;
use crate::boxes::result::NyashResultBox;
use crate::box_trait::StringBox;
impl NyashInterpreter {
/// await式を実行 - 非同期操作の結果を待機
pub(super) fn execute_await(&mut self, expression: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value = self.execute_expression(expression)?;
// FutureBoxなら協調待機して Result.Ok/Err を返す
if let Some(future) = value.as_any().downcast_ref::<FutureBox>() {
let max_ms: u64 = crate::config::env::await_max_ms();
let start = std::time::Instant::now();
let mut spins = 0usize;
while !future.ready() {
crate::runtime::global_hooks::safepoint_and_poll();
std::thread::yield_now();
spins += 1;
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); }
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
let err = Box::new(StringBox::new("Timeout"));
return Ok(Box::new(NyashResultBox::new_err(err)));
}
}
let v = future.get();
Ok(Box::new(NyashResultBox::new_ok(v)))
} else {
// FutureBoxでなければ Ok(value) で返す
Ok(Box::new(NyashResultBox::new_ok(value)))
}
}
}

View File

@ -1,298 +0,0 @@
/*!
* Box Method Handlers Module
*
* Extracted from interpreter.rs lines 1389-2515 (1,126 lines)
* Contains Box type-specific method implementations:
*
* MOVED TO methods/basic_methods.rs:
* - execute_string_method (StringBox)
* - execute_integer_method (IntegerBox)
* - execute_bool_method (BoolBox) - NEW
* - execute_float_method (FloatBox) - NEW
*
* MOVED TO methods/collection_methods.rs:
* - execute_array_method (ArrayBox)
* - execute_map_method (MapBox)
*
* MOVED TO methods/io_methods.rs:
* - execute_file_method (FileBox)
* - execute_result_method (ResultBox)
*
* MOVED TO methods/math_methods.rs:
* - execute_math_method (MathBox)
* - execute_random_method (RandomBox)
*
* MOVED TO system_methods.rs:
* - execute_time_method (TimeBox)
* - execute_datetime_method (DateTimeBox)
* - execute_timer_method (TimerBox)
* - execute_debug_method (DebugBox)
*
* MOVED TO async_methods.rs:
* - execute_future_method (FutureBox)
* - execute_channel_method (ChannelBox)
*
* MOVED TO web_methods.rs:
* - execute_web_display_method (WebDisplayBox)
* - execute_web_console_method (WebConsoleBox)
* - execute_web_canvas_method (WebCanvasBox)
*
* MOVED TO special_methods.rs:
* - execute_sound_method (SoundBox)
* - execute_method_box_method (MethodBox)
*
* REMAINING IN THIS MODULE:
* - execute_console_method
* - execute_null_method
*/
use super::*;
use crate::boxes::NullBox;
impl NyashInterpreter {
// StringBox methods moved to methods/basic_methods.rs
// IntegerBox methods moved to methods/basic_methods.rs
// ArrayBox methods moved to methods/collection_methods.rs
// FileBox methods moved to methods/io_methods.rs
// ResultBox methods moved to methods/io_methods.rs
// FutureBox methods moved to async_methods.rs
// ChannelBox methods moved to async_methods.rs
// MathBox methods moved to methods/math_methods.rs
/// NullBoxのメソッド呼び出しを実行
pub(super) fn execute_null_method(
&mut self,
_null_box: &NullBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"is_null" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("is_null() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(Box::new(BoolBox::new(true)))
}
"is_not_null" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"is_not_null() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(BoolBox::new(false)))
}
"toString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(StringBox::new("null".to_string())))
}
"equals" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("equals() expects 1 argument, got {}", arg_values.len()),
});
}
let other = &arg_values[0];
// NullBoxは他のNullBoxとのみ等しい
let is_equal = other.as_any().downcast_ref::<NullBox>().is_some();
Ok(Box::new(BoolBox::new(is_equal)))
}
"get_or_default" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"get_or_default() expects 1 argument, got {}",
arg_values.len()
),
});
}
// nullの場合はデフォルト値を返す
Ok(arg_values[0].clone_box())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown NullBox method: {}", method),
}),
}
}
// TimeBox methods moved to system_methods.rs
// DateTimeBox methods moved to system_methods.rs
// TimerBox methods moved to system_methods.rs
// MapBox methods moved to methods/collection_methods.rs
// RandomBox methods moved to methods/math_methods.rs
// SoundBox methods moved to special_methods.rs
// DebugBox methods moved to system_methods.rs
/// EguiBoxのメソッド呼び出しを実行非WASM環境のみ
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
pub(super) fn execute_egui_method(
&mut self,
_egui_box: &crate::boxes::EguiBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"setTitle" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("setTitle expects 1 argument, got {}", arg_values.len()),
});
}
// EguiBoxは不変参照なので、新しいインスタンスを返す必要がある
// 実際のGUIアプリではstateを共有するが、今はシンプルに
Ok(Box::new(VoidBox::new()))
}
"setSize" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("setSize expects 2 arguments, got {}", arg_values.len()),
});
}
Ok(Box::new(VoidBox::new()))
}
"run" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("run expects 0 arguments, got {}", arg_values.len()),
});
}
// run()は実際のGUIアプリケーションを起動するため、
// ここでは実行できない(メインスレッドブロッキング)
Err(RuntimeError::InvalidOperation {
message: "EguiBox.run() must be called from main thread".to_string(),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for EguiBox", method),
}),
}
}
/// ConsoleBoxのメソッド呼び出しを実行
pub(super) fn execute_console_method(
&mut self,
console_box: &crate::boxes::console_box::ConsoleBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"log" => {
if arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: "console.log() requires at least 1 argument".to_string(),
});
}
// 引数をすべて文字列に変換
let messages: Vec<String> = arg_values
.iter()
.map(|arg| arg.to_string_box().value)
.collect();
let combined_message = messages.join(" ");
console_box.log(&combined_message);
Ok(Box::new(VoidBox::new()))
}
"warn" => {
if arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: "console.warn() requires at least 1 argument".to_string(),
});
}
let messages: Vec<String> = arg_values
.iter()
.map(|arg| arg.to_string_box().value)
.collect();
let combined_message = messages.join(" ");
console_box.warn(&combined_message);
Ok(Box::new(VoidBox::new()))
}
"error" => {
if arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: "console.error() requires at least 1 argument".to_string(),
});
}
let messages: Vec<String> = arg_values
.iter()
.map(|arg| arg.to_string_box().value)
.collect();
let combined_message = messages.join(" ");
console_box.error(&combined_message);
Ok(Box::new(VoidBox::new()))
}
"clear" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"console.clear() expects 0 arguments, got {}",
arg_values.len()
),
});
}
console_box.clear();
Ok(Box::new(VoidBox::new()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown ConsoleBox method: {}", method),
}),
}
}
// MethodBox methods moved to special_methods.rs
// Web methods moved to web_methods.rs
}

View File

@ -1,82 +0,0 @@
//! Call helpers: centralizes call paths (PluginHost, functions)
use super::{NyashInterpreter, RuntimeError};
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, VoidBox};
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
use crate::runtime::plugin_loader_v2::PluginBoxV2;
impl NyashInterpreter {
/// Invoke a method on a PluginBoxV2 via PluginHost facade.
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
pub(crate) fn call_plugin_method(
&mut self,
plugin_box: &PluginBoxV2,
method: &str,
arg_nodes: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
if plugin_box.is_finalized() {
return Err(RuntimeError::RuntimeFailure {
message: format!("Use after fini: {}", plugin_box.box_type),
});
}
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
for arg in arg_nodes {
arg_values.push(self.execute_expression(arg)?);
}
let host_guard = crate::runtime::get_global_plugin_host();
let host = host_guard
.read()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Plugin host lock poisoned".into(),
})?;
match host.invoke_instance_method(
&plugin_box.box_type,
method,
plugin_box.instance_id(),
&arg_values,
) {
Ok(Some(result_box)) => Ok(result_box),
Ok(None) => Ok(Box::new(VoidBox::new())),
Err(e) => Err(RuntimeError::RuntimeFailure {
message: format!("Plugin method {} failed: {:?}", method, e),
}),
}
}
/// Create a plugin box by type with arguments evaluated from AST.
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
pub(crate) fn create_plugin_box(
&mut self,
box_type: &str,
arg_nodes: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
for arg in arg_nodes {
arg_values.push(self.execute_expression(arg)?);
}
let host_guard = crate::runtime::get_global_plugin_host();
let host = host_guard
.read()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Plugin host lock poisoned".into(),
})?;
host.create_box(box_type, &arg_values)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to construct plugin '{}': {:?}", box_type, e),
})
}
/// Check if a given box type is provided by plugins (per current config).
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
pub(crate) fn is_plugin_box_type(&self, box_type: &str) -> bool {
let host_guard = crate::runtime::get_global_plugin_host();
if let Ok(host) = host_guard.read() {
if let Some(cfg) = host.config_ref() {
return cfg.find_library_for_box(box_type).is_some();
}
}
false
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,382 +0,0 @@
/*!
* Delegation Processing Module
*
* Extracted from expressions.rs lines 1086-1457 (~371 lines)
* Handles 'from' calls, delegation validation, and builtin box method calls
* Core philosophy: "Everything is Box" with explicit delegation
*/
use super::*;
use std::sync::{Arc, Mutex};
use crate::interpreter::SharedNyashBox;
impl NyashInterpreter {
/// from呼び出しを実行 - 完全明示デリゲーション
pub(super) fn execute_from_call(&mut self, parent: &str, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. 現在のコンテキストで'me'変数を取得(現在のインスタンス)
let current_instance_val = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'from' can only be used inside methods".to_string(),
})?;
let current_instance = (*current_instance_val).as_any().downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "'from' requires current instance to be InstanceBox".to_string(),
})?;
// 2. 現在のクラスのデリゲーション関係を検証
let current_class = &current_instance.class_name;
let box_declarations = self.shared.box_declarations.read().unwrap();
let current_box_decl = box_declarations.get(current_class)
.ok_or(RuntimeError::UndefinedClass {
name: current_class.clone()
})?;
// extendsまたはimplementsでparentが指定されているか確認 (Multi-delegation) 🚀
let is_valid_delegation = current_box_decl.extends.contains(&parent.to_string()) ||
current_box_decl.implements.contains(&parent.to_string());
if !is_valid_delegation {
return Err(RuntimeError::InvalidOperation {
message: format!("Class '{}' does not delegate to '{}'. Use 'box {} from {}' to establish delegation.",
current_class, parent, current_class, parent),
});
}
// 🔥 Phase 8.8: pack透明化システム - ビルトインBox判定
use crate::box_trait::{is_builtin_box, BUILTIN_BOXES};
let mut is_builtin = is_builtin_box(parent);
// GUI機能が有効な場合はEguiBoxも追加判定
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
{
if parent == "EguiBox" {
is_builtin = true;
}
}
// 🔥 Phase 8.9: Transparency system removed - all delegation must be explicit
// ビルトインBoxの場合、専用メソッドで処理
if is_builtin {
drop(box_declarations);
// Pass the Arc reference directly for builtin boxes
let me_ref = self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'from' can only be used inside methods".to_string(),
})?;
return self.execute_builtin_box_method(parent, method, (*me_ref).clone_box(), arguments);
}
// 3. 親クラスのBox宣言を取得ユーザー定義Boxの場合
let parent_box_decl = box_declarations.get(parent)
.ok_or(RuntimeError::UndefinedClass {
name: parent.to_string()
})?
.clone();
drop(box_declarations); // ロック早期解放
// 4. constructorまたはinitまたはpackまたはbirthの場合の特別処理
if method == "constructor" || method == "init" || method == "pack" || method == "birth" || method == parent {
return self.execute_from_parent_constructor(parent, &parent_box_decl, current_instance_val.clone_box(), arguments);
}
// 5. 通常の親メソッド実行
self.execute_parent_method(parent, method, &parent_box_decl, current_instance_val.clone_box(), arguments)
}
/// 親クラスのメソッドを実行
fn execute_parent_method(
&mut self,
parent: &str,
method: &str,
parent_box_decl: &super::BoxDeclaration,
current_instance_val: Box<dyn NyashBox>,
arguments: &[ASTNode]
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 親クラスのメソッドを取得
let parent_method = parent_box_decl.methods.get(method)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Method '{}' not found in parent class '{}'", method, parent),
})?
.clone();
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// 親メソッドを実行
if let ASTNode::FunctionDeclaration { params, body, .. } = parent_method {
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("Parent method {}.{} expects {} arguments, got {}",
parent, method, params.len(), arg_values.len()),
});
}
// 🌍 local変数スタックを保存・クリア親メソッド実行開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// 'me'を現在のインスタンスに設定(重要:現在のインスタンスを維持)
self.declare_local_variable("me", current_instance_val.clone_or_share());
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// 親メソッドの本体を実行
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Parent method '{}' is not a valid function declaration", method),
})
}
}
/// 🔥 fromCall専用親コンストラクタ実行処理 - from Parent.constructor(arguments)
fn execute_from_parent_constructor(&mut self, parent: &str, parent_box_decl: &super::BoxDeclaration,
current_instance: Box<dyn NyashBox>, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. 親クラスのコンストラクタを取得(引数の数でキーを作成)
// "birth/引数数"、"pack/引数数"、"init/引数数"、"Box名/引数数" の順で試す
let birth_key = format!("birth/{}", arguments.len());
let pack_key = format!("pack/{}", arguments.len());
let init_key = format!("init/{}", arguments.len());
let box_name_key = format!("{}/{}", parent, arguments.len());
let parent_constructor = parent_box_decl.constructors.get(&birth_key)
.or_else(|| parent_box_decl.constructors.get(&pack_key))
.or_else(|| parent_box_decl.constructors.get(&init_key))
.or_else(|| parent_box_decl.constructors.get(&box_name_key))
.ok_or(RuntimeError::InvalidOperation {
message: format!("No constructor found for parent class '{}' with {} arguments", parent, arguments.len()),
})?
.clone();
// 2. 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// 3. 親コンストラクタを実行
if let ASTNode::FunctionDeclaration { params, body, .. } = parent_constructor {
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("Parent constructor {} expects {} arguments, got {}",
parent, params.len(), arg_values.len()),
});
}
// 🌍 local変数スタックを保存・クリア親コンストラクタ実行開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// 'me'を現在のインスタンスに設定
self.declare_local_variable("me", current_instance.clone_or_share());
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// 親コンストラクタの本体を実行
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
// 親コンストラクタは通常現在のインスタンスを返す
Ok(current_instance)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Parent constructor is not a valid function declaration"),
})
}
}
/// 🔥 ビルトインBoxのメソッド呼び出し
fn execute_builtin_box_method(&mut self, parent: &str, method: &str, mut current_instance: Box<dyn NyashBox>, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 🌟 Phase 8.9: birth method support for builtin boxes
if method == "birth" {
return self.execute_builtin_birth_method(parent, current_instance, arguments);
}
// ビルトインBoxのインスタンスを作成または取得
match parent {
"StringBox" => {
if let Some(sb) = current_instance.as_any().downcast_ref::<StringBox>() {
self.execute_string_method(sb, method, arguments)
} else {
let string_box = StringBox::new("");
self.execute_string_method(&string_box, method, arguments)
}
}
"IntegerBox" => {
if let Some(ib) = current_instance.as_any().downcast_ref::<IntegerBox>() {
self.execute_integer_method(ib, method, arguments)
} else {
let integer_box = IntegerBox::new(0);
self.execute_integer_method(&integer_box, method, arguments)
}
}
"ArrayBox" => {
if let Some(ab) = current_instance.as_any().downcast_ref::<ArrayBox>() {
self.execute_array_method(ab, method, arguments)
} else {
let array_box = ArrayBox::new();
self.execute_array_method(&array_box, method, arguments)
}
}
"MapBox" => {
if let Some(mb) = current_instance.as_any().downcast_ref::<MapBox>() {
self.execute_map_method(mb, method, arguments)
} else {
let map_box = MapBox::new();
self.execute_map_method(&map_box, method, arguments)
}
}
"MathBox" => {
if let Some(math) = current_instance.as_any().downcast_ref::<MathBox>() {
self.execute_math_method(math, method, arguments)
} else {
let math_box = MathBox::new();
self.execute_math_method(&math_box, method, arguments)
}
}
// 他のビルトインBoxは必要に応じて追加
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Builtin box '{}' method '{}' not implemented", parent, method),
})
}
}
}
/// 🌟 Phase 8.9: Execute birth method for builtin boxes
/// Provides constructor functionality for builtin boxes through explicit birth() calls
fn execute_builtin_birth_method(&mut self, builtin_name: &str, current_instance: Box<dyn NyashBox>, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// ビルトインBoxの種類に応じて適切なインスタンスを作成して返す
match builtin_name {
"StringBox" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("StringBox.birth() expects 1 argument, got {}", arg_values.len()),
});
}
// StringBoxの内容を正しく取得
let content = if let Some(string_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
// 引数が既にStringBoxの場合、その値を直接取得
string_box.value.clone()
} else {
// それ以外の場合は、to_string_box()で変換
arg_values[0].to_string_box().value
};
let string_box = StringBox::new(content);
// 現在のインスタンスがInstanceBoxの場合、StringBoxを特別なフィールドに保存
if let Some(instance) = current_instance.as_any().downcast_ref::<InstanceBox>() {
// 特別な内部フィールド "__builtin_content" にStringBoxを保存
let string_box_arc: Arc<dyn NyashBox> = Arc::new(string_box);
instance.set_field_dynamic("__builtin_content".to_string(),
crate::value::NyashValue::Box(string_box_arc.clone()));
}
Ok(Box::new(VoidBox::new())) // Return void to indicate successful initialization
}
"IntegerBox" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("IntegerBox.birth() expects 1 argument, got {}", arg_values.len()),
});
}
let value = if let Ok(int_val) = arg_values[0].to_string_box().value.parse::<i64>() {
int_val
} else {
return Err(RuntimeError::TypeError {
message: format!("Cannot convert '{}' to integer", arg_values[0].to_string_box().value),
});
};
let integer_box = IntegerBox::new(value);
// 現在のインスタンスがInstanceBoxの場合、IntegerBoxを特別なフィールドに保存
if let Some(instance) = current_instance.as_any().downcast_ref::<InstanceBox>() {
let integer_box_arc: Arc<dyn NyashBox> = Arc::new(integer_box);
instance.set_field_dynamic("__builtin_content".to_string(),
crate::value::NyashValue::Box(integer_box_arc.clone()));
}
Ok(Box::new(VoidBox::new()))
}
"MathBox" => {
// MathBoxは引数なしのコンストラクタ
if arg_values.len() != 0 {
return Err(RuntimeError::InvalidOperation {
message: format!("MathBox.birth() expects 0 arguments, got {}", arg_values.len()),
});
}
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("MathBox", &[]) { return Ok(Box::new(VoidBox::new())); }
}
let _math_box = MathBox::new();
Ok(Box::new(VoidBox::new()))
}
// 他のビルトインBoxは必要に応じて追加
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("birth() method not implemented for builtin box '{}'", builtin_name),
})
}
}
}
}

View File

@ -1,122 +0,0 @@
use crate::ast::Span;
use crate::parser::ParseError;
use thiserror::Error;
/// 実行時エラー
#[derive(Error, Debug)]
pub enum RuntimeError {
#[error("Undefined variable '{name}'")]
UndefinedVariable { name: String },
#[error("Undefined function '{name}'")]
UndefinedFunction { name: String },
#[error("Undefined class '{name}'")]
UndefinedClass { name: String },
#[error("Type error: {message}")]
TypeError { message: String },
#[error("Invalid operation: {message}")]
InvalidOperation { message: String },
#[error("Break outside of loop")]
BreakOutsideLoop,
#[error("Return outside of function")]
ReturnOutsideFunction,
#[error("Uncaught exception")]
UncaughtException,
#[error("Parse error: {0}")]
ParseError(#[from] ParseError),
#[error("Environment error: {0}")]
EnvironmentError(String),
// === 🔥 Enhanced Errors with Span Information ===
#[error("Undefined variable '{name}' at {span}")]
UndefinedVariableAt { name: String, span: Span },
#[error("Type error: {message} at {span}")]
TypeErrorAt { message: String, span: Span },
#[error("Invalid operation: {message} at {span}")]
InvalidOperationAt { message: String, span: Span },
#[error("Break outside of loop at {span}")]
BreakOutsideLoopAt { span: Span },
#[error("Return outside of function at {span}")]
ReturnOutsideFunctionAt { span: Span },
#[error("Runtime failure: {message}")]
RuntimeFailure { message: String },
}
impl RuntimeError {
/// エラーの詳細な文脈付きメッセージを生成
pub fn detailed_message(&self, source: Option<&str>) -> String {
match self {
// Enhanced errors with span information
RuntimeError::UndefinedVariableAt { name, span } => {
let mut msg = format!("⚠️ Undefined variable '{}'", name);
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::TypeErrorAt { message, span } => {
let mut msg = format!("⚠️ Type error: {}", message);
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::InvalidOperationAt { message, span } => {
let mut msg = format!("⚠️ Invalid operation: {}", message);
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::BreakOutsideLoopAt { span } => {
let mut msg = "⚠️ Break statement outside of loop".to_string();
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
RuntimeError::ReturnOutsideFunctionAt { span } => {
let mut msg = "⚠️ Return statement outside of function".to_string();
if let Some(src) = source {
msg.push('\n');
msg.push_str(&span.error_context(src));
} else {
msg.push_str(&format!(" at {}", span));
}
msg
}
// Fallback for old error variants without span
_ => format!("⚠️ {}", self),
}
}
}

View File

@ -1,140 +0,0 @@
//! Evaluation entry points: execute program and nodes
use super::{ControlFlow, NyashInterpreter, RuntimeError};
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, VoidBox};
impl NyashInterpreter {
/// ASTを実行
pub fn execute(&mut self, ast: ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
super::core::debug_log("=== NYASH EXECUTION START ===");
let result = self.execute_node(&ast);
if let Err(ref e) = result {
eprintln!("❌ Interpreter error: {}", e);
}
super::core::debug_log("=== NYASH EXECUTION END ===");
result
}
/// ノードを実行
fn execute_node(&mut self, node: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
match node {
ASTNode::Program { statements, .. } => {
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
let last = statements.len().saturating_sub(1);
for (i, statement) in statements.iter().enumerate() {
let prev = self.discard_context;
self.discard_context = i != last; // 最終文以外は値が破棄される
result = self.execute_statement(statement)?;
self.discard_context = prev;
// 制御フローチェック
match &self.control_flow {
ControlFlow::Break => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Continue => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Return(_) => {
return Err(RuntimeError::ReturnOutsideFunction);
}
ControlFlow::Throw(_) => {
return Err(RuntimeError::UncaughtException);
}
ControlFlow::None => {}
}
}
// 🎯 Static Box Main パターン - main()メソッドの自動実行
let has_main_method = {
if let Ok(definitions) = self.shared.static_box_definitions.read() {
if let Some(main_definition) = definitions.get("Main") {
main_definition.methods.contains_key("main")
} else {
false
}
} else {
false
}
};
if has_main_method {
// Main static boxを初期化
self.ensure_static_box_initialized("Main")?;
// Main.main(args?) を呼び出し引数が1つならデフォルトで args を注入)
let mut default_args: Vec<ASTNode> = Vec::new();
if let Ok(defs) = self.shared.static_box_definitions.read() {
if let Some(main_def) = defs.get("Main") {
if let Some(m) = main_def.methods.get("main") {
if let ASTNode::FunctionDeclaration { params, .. } = m {
if params.len() == 1 {
// Try to read script args from env (JSON array); fallback to empty ArrayBox
if let Ok(json) = std::env::var("NYASH_SCRIPT_ARGS_JSON") {
if let Ok(vals) =
serde_json::from_str::<Vec<String>>(&json)
{
let mut str_nodes: Vec<ASTNode> =
Vec::with_capacity(vals.len());
for s in vals {
str_nodes.push(ASTNode::Literal {
value: crate::ast::LiteralValue::String(s),
span: crate::ast::Span::unknown(),
});
}
default_args.push(ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "ArrayBox".to_string(),
span: crate::ast::Span::unknown(),
}),
method: "of".to_string(),
arguments: str_nodes,
span: crate::ast::Span::unknown(),
});
} else {
default_args.push(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
});
}
} else {
default_args.push(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
});
}
}
}
}
}
}
let main_call_ast = ASTNode::MethodCall {
object: Box::new(ASTNode::FieldAccess {
object: Box::new(ASTNode::Variable {
name: "statics".to_string(),
span: crate::ast::Span::unknown(),
}),
field: "Main".to_string(),
span: crate::ast::Span::unknown(),
}),
method: "main".to_string(),
arguments: default_args,
span: crate::ast::Span::unknown(),
};
// main()の戻り値を最終結果として使用
result = self.execute_statement(&main_call_ast)?;
}
Ok(result)
}
_ => self.execute_statement(node),
}
}
}

View File

@ -1,220 +0,0 @@
/*!
* Field access operations
*/
// Removed super::* import - specific imports below
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, SharedNyashBox};
use crate::boxes::FutureBox;
use crate::instance_v2::InstanceBox;
use crate::interpreter::{NyashInterpreter, RuntimeError};
use std::sync::Arc;
// Conditional debug macro - only outputs if NYASH_DEBUG=1 environment variable is set
macro_rules! debug_trace {
($($arg:tt)*) => {
if std::env::var("NYASH_DEBUG").unwrap_or_default() == "1" {
eprintln!($($arg)*);
}
};
}
impl NyashInterpreter {
/// フィールドアクセスを実行 - Field access processing with weak reference support
pub(super) fn execute_field_access(
&mut self,
object: &ASTNode,
field: &str,
) -> Result<SharedNyashBox, RuntimeError> {
// 🔥 Static Boxアクセスチェック
if let ASTNode::Variable { name, .. } = object {
// Static boxの可能性をチェック
if self.is_static_box(name) {
let static_result = self.execute_static_field_access(name, field)?;
return Ok(Arc::from(static_result));
}
}
// 外からのフィールドアクセスかme/this以外を判定
let is_internal_access = match object {
ASTNode::This { .. } | ASTNode::Me { .. } => true,
ASTNode::Variable { name, .. } if name == "me" => true,
_ => false,
};
// オブジェクトを評価(通常のフィールドアクセス)
let obj_value = self.execute_expression(object);
let obj_value = obj_value?;
// InstanceBoxにキャスト
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
// 可視性チェック(互換性: public/privateのどちらかが定義されていれば強制
if !is_internal_access {
let box_decls = self.shared.box_declarations.read().unwrap();
if let Some(box_decl) = box_decls.get(&instance.class_name) {
let has_visibility =
!box_decl.public_fields.is_empty() || !box_decl.private_fields.is_empty();
if has_visibility {
if !box_decl.public_fields.contains(&field.to_string()) {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Field '{}' is private in {}",
field, instance.class_name
),
});
}
}
}
}
// 🔥 finiは何回呼ばれてもエラーにしないユーザー要求
// is_finalized()チェックを削除
// フィールドの値を取得
let field_value = instance
.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in {}", field, instance.class_name),
})?;
// 🔗 Weak Reference Check: Use unified accessor for weak fields
let box_decls = self.shared.box_declarations.read().unwrap();
if let Some(box_decl) = box_decls.get(&instance.class_name) {
if box_decl.weak_fields.contains(&field.to_string()) {
// 🎯 PHASE 2: Use unified accessor for auto-nil weak reference handling
if let Some(weak_value) = instance.get_weak_field(field, self) {
// Pass self
match &weak_value {
crate::value::NyashValue::Null => {
debug_trace!(
"🔗 DEBUG: Weak field '{}' is null (reference dropped)",
field
);
// Return null box for compatibility
return Ok(Arc::new(crate::boxes::null_box::NullBox::new()));
}
_ => {
debug_trace!(
"🔗 DEBUG: Weak field '{}' still has valid reference",
field
);
// Convert back to Box<dyn NyashBox> for now
if let Ok(box_value) = weak_value.to_box() {
if let Ok(inner_box) = box_value.try_lock() {
return Ok(Arc::from(inner_box.clone_or_share()));
}
}
}
}
}
// If weak field access failed, fall through to normal access
}
}
// Return the shared Arc reference directly
Ok(field_value)
} else {
Err(RuntimeError::TypeError {
message: format!(
"Cannot access field '{}' on non-instance type. Type: {}",
field,
obj_value.type_name()
),
})
}
}
/// 🔥 Static Box名前空間のフィールドアクセス
fn execute_static_field_access(
&mut self,
static_box_name: &str,
field: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. Static Boxの初期化を確実に実行
self.ensure_static_box_initialized(static_box_name)?;
// 2. GlobalBox.statics.{static_box_name} からインスタンスを取得
let global_box =
self.shared
.global_box
.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string(),
})?;
let statics_box = global_box
.get_field("statics")
.ok_or(RuntimeError::RuntimeFailure {
message: "statics namespace not found in GlobalBox".to_string(),
})?;
let statics_instance =
statics_box
.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "statics field is not an InstanceBox".to_string(),
})?;
let static_box_instance =
statics_instance
.get_field(static_box_name)
.ok_or(RuntimeError::RuntimeFailure {
message: format!(
"Static box '{}' instance not found in statics namespace",
static_box_name
),
})?;
let instance = static_box_instance
.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: format!("Static box '{}' is not an InstanceBox", static_box_name),
})?;
// 3. フィールドアクセス
let shared_field = instance
.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!(
"Field '{}' not found in static box '{}'",
field, static_box_name
),
})?;
// Convert Arc to Box for compatibility
Ok((*shared_field).clone_or_share())
}
/// await式を実行 - Execute await expression (Result.Ok/Err統一)
pub(super) fn execute_await(
&mut self,
expression: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value = self.execute_expression(expression)?;
if let Some(future) = value.as_any().downcast_ref::<FutureBox>() {
let max_ms: u64 = crate::config::env::await_max_ms();
let start = std::time::Instant::now();
let mut spins = 0usize;
while !future.ready() {
crate::runtime::global_hooks::safepoint_and_poll();
std::thread::yield_now();
spins += 1;
if spins % 1024 == 0 {
std::thread::sleep(std::time::Duration::from_millis(1));
}
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
let err = Box::new(crate::box_trait::StringBox::new("Timeout"));
return Ok(Box::new(crate::boxes::result::NyashResultBox::new_err(err)));
}
}
let v = future.get();
Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(v)))
} else {
Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(
value,
)))
}
}
}

View File

@ -1,265 +0,0 @@
/*!
* Builtin box methods and birth methods
*/
use crate::ast::ASTNode;
use crate::box_trait::{IntegerBox, NyashBox, StringBox, VoidBox};
use crate::boxes::{
ArrayBox, ConsoleBox, DebugBox, MapBox, MathBox, RandomBox, SocketBox, SoundBox, TimeBox,
};
use crate::boxes::{HTTPRequestBox, HTTPResponseBox, HTTPServerBox};
use crate::interpreter::{NyashInterpreter, RuntimeError};
use std::sync::{Arc, Mutex};
impl NyashInterpreter {
/// 🔥 ビルトインBoxのメソッド呼び出し
pub(super) fn execute_builtin_box_method(
&mut self,
parent: &str,
method: &str,
_current_instance: Box<dyn NyashBox>,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// Strict plugin-only mode: disallow builtin paths
if std::env::var("NYASH_PLUGIN_ONLY").ok().as_deref() == Some("1") {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Builtin path disabled: {}.{}, use plugin invoke",
parent, method
),
});
}
// 🌟 Phase 8.9: birth method support for builtin boxes
if method == "birth" {
return self.execute_builtin_birth_method(parent, _current_instance, arguments);
}
// ビルトインBoxのインスタンスを作成または取得
// 現在のインスタンスからビルトインBoxのデータを取得し、ビルトインBoxとしてメソッド実行
match parent {
"StringBox" => {
// StringBoxのインスタンスを作成デフォルト値
let string_box = StringBox::new("");
self.execute_string_method(&string_box, method, arguments)
}
"IntegerBox" => {
// IntegerBoxのインスタンスを作成デフォルト値
let integer_box = IntegerBox::new(0);
self.execute_integer_method(&integer_box, method, arguments)
}
"ArrayBox" => {
let array_box = ArrayBox::new();
self.execute_array_method(&array_box, method, arguments)
}
"MapBox" => {
let map_box = MapBox::new();
self.execute_map_method(&map_box, method, arguments)
}
"MathBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("MathBox", &[]) {
// Note: execute_math_method expects builtin MathBox; plugin path should route via VM/BoxCall in new pipeline.
// Here we simply return void; method paths should prefer plugin invoke in VM.
return Ok(Box::new(VoidBox::new()));
}
}
let math_box = MathBox::new();
self.execute_math_method(&math_box, method, arguments)
}
"P2PBox" => {
// P2PBoxの場合、現在のインスタンスからP2PBoxインスタンスを取得する必要がある
// TODO: 現在のインスタンスのフィールドからP2PBoxを取得
return Err(RuntimeError::InvalidOperation {
message: format!(
"P2PBox delegation not yet fully implemented: {}.{}",
parent, method
),
});
}
"FileBox" => {
let file_box = crate::boxes::file::FileBox::new();
self.execute_file_method(&file_box, method, arguments)
}
"ConsoleBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("ConsoleBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let console_box = ConsoleBox::new();
self.execute_console_method(&console_box, method, arguments)
}
"TimeBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("TimeBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let time_box = TimeBox::new();
self.execute_time_method(&time_box, method, arguments)
}
"RandomBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("RandomBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let random_box = RandomBox::new();
self.execute_random_method(&random_box, method, arguments)
}
"DebugBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("DebugBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let debug_box = DebugBox::new();
self.execute_debug_method(&debug_box, method, arguments)
}
"SoundBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("SoundBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let sound_box = SoundBox::new();
self.execute_sound_method(&sound_box, method, arguments)
}
"SocketBox" => {
let socket_box = SocketBox::new();
self.execute_socket_method(&socket_box, method, arguments)
}
"HTTPServerBox" => {
let http_server_box = HTTPServerBox::new();
self.execute_http_server_method(&http_server_box, method, arguments)
}
"HTTPRequestBox" => {
let http_request_box = HTTPRequestBox::new();
self.execute_http_request_method(&http_request_box, method, arguments)
}
"HTTPResponseBox" => {
let http_response_box = HTTPResponseBox::new();
self.execute_http_response_method(&http_response_box, method, arguments)
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown built-in Box type for delegation: {}", parent),
}),
}
}
/// 🌟 Phase 8.9: Execute birth method for builtin boxes
/// Provides constructor functionality for builtin boxes through explicit birth() calls
pub(super) fn execute_builtin_birth_method(
&mut self,
builtin_name: &str,
current_instance: Box<dyn NyashBox>,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// ビルトインBoxの種類に応じて適切なインスタンスを作成して返す
match builtin_name {
"StringBox" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"StringBox.birth() expects 1 argument, got {}",
arg_values.len()
),
});
}
let content = arg_values[0].to_string_box().value;
let string_box = StringBox::new(content.clone());
// 現在のインスタンスがInstanceBoxの場合、StringBoxを特別なフィールドに保存
if let Some(instance) = current_instance
.as_any()
.downcast_ref::<crate::instance_v2::InstanceBox>()
{
// 特別な内部フィールド "__builtin_content" にStringBoxを保存
let string_box_arc: Arc<Mutex<dyn NyashBox>> = Arc::new(Mutex::new(string_box));
instance.set_field_dynamic(
"__builtin_content".to_string(),
crate::value::NyashValue::Box(string_box_arc),
);
}
Ok(Box::new(VoidBox::new())) // Return void to indicate successful initialization
}
"IntegerBox" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"IntegerBox.birth() expects 1 argument, got {}",
arg_values.len()
),
});
}
let value = if let Ok(int_val) = arg_values[0].to_string_box().value.parse::<i64>()
{
int_val
} else {
return Err(RuntimeError::TypeError {
message: format!(
"Cannot convert '{}' to integer",
arg_values[0].to_string_box().value
),
});
};
let _integer_box = IntegerBox::new(value);
Ok(Box::new(VoidBox::new()))
}
"MathBox" => {
// MathBoxは引数なしのコンストラクタ
if arg_values.len() != 0 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"MathBox.birth() expects 0 arguments, got {}",
arg_values.len()
),
});
}
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("MathBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let _math_box = MathBox::new();
Ok(Box::new(VoidBox::new()))
}
"ArrayBox" => {
// ArrayBoxも引数なしのコンストラクタ
if arg_values.len() != 0 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"ArrayBox.birth() expects 0 arguments, got {}",
arg_values.len()
),
});
}
let _array_box = ArrayBox::new();
eprintln!("🌟 DEBUG: ArrayBox.birth() created");
Ok(Box::new(VoidBox::new()))
}
_ => {
// 他のビルトインBoxは今後追加
Err(RuntimeError::InvalidOperation {
message: format!(
"birth() method not yet implemented for builtin box '{}'",
builtin_name
),
})
}
}
}
}

View File

@ -1,880 +0,0 @@
/*!
* Method calls and from delegation calls
*/
use super::*;
use crate::ast::ASTNode;
use crate::box_trait::{IntegerBox, NyashBox, StringBox, VoidBox};
use crate::boxes::MapBox;
use crate::boxes::{DateTimeBox, HTTPRequestBox, HTTPResponseBox, HTTPServerBox};
use crate::boxes::{DebugBox, RandomBox, SoundBox};
use crate::boxes::{IntentBox, SocketBox};
use crate::instance_v2::InstanceBox;
use crate::interpreter::{NyashInterpreter, RuntimeError};
// Debug macro gated by NYASH_DEBUG=1
macro_rules! idebug {
($($arg:tt)*) => {
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
};
}
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
use crate::runtime::plugin_loader_v2::PluginBoxV2;
use std::sync::Arc;
impl NyashInterpreter {
/// メソッド呼び出しを実行 - Method call processing
pub(super) fn execute_method_call(
&mut self,
object: &ASTNode,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 🔥 static関数のチェック
if let ASTNode::Variable { name, .. } = object {
// static関数が存在するかチェック
let static_func = {
let static_funcs = self.shared.static_functions.read().unwrap();
if let Some(box_statics) = static_funcs.get(name) {
if let Some(func) = box_statics.get(method) {
Some(func.clone())
} else {
None
}
} else {
None
}
};
if let Some(static_func) = static_func {
// static関数を実行
if let ASTNode::FunctionDeclaration { params, body, .. } = static_func {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Static method {}.{} expects {} arguments, got {}",
name,
method,
params.len(),
arg_values.len()
),
});
}
// 🌍 local変数スタックを保存・クリアstatic関数呼び出し開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// 📤 outbox変数スタックも保存・クリアstatic関数専用
let saved_outbox = self.save_outbox_vars();
self.outbox_vars.clear();
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// static関数の本体を実行TaskGroupスコープ
crate::runtime::global_hooks::push_task_scope();
let mut result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
crate::runtime::global_hooks::pop_task_scope();
self.restore_local_vars(saved_locals);
// outbox変数スタックを復元
self.restore_outbox_vars(saved_outbox);
return Ok(result);
}
}
// 📚 nyashstd標準ライブラリのメソッドチェック
let stdlib_method = if let Some(ref stdlib) = self.stdlib {
if let Some(nyashstd_namespace) = stdlib.namespaces.get("nyashstd") {
if let Some(static_box) = nyashstd_namespace.static_boxes.get(name) {
if let Some(builtin_method) = static_box.methods.get(method) {
Some(*builtin_method) // Copyトレイトで関数ポインターをコピー
} else {
idebug!("🔍 Method '{}' not found in nyashstd.{}", method, name);
None
}
} else {
idebug!("🔍 Static box '{}' not found in nyashstd", name);
None
}
} else {
idebug!("🔍 nyashstd namespace not found in stdlib");
None
}
} else {
idebug!("🔍 stdlib not initialized for method call");
None
};
if let Some(builtin_method) = stdlib_method {
idebug!("🌟 Calling nyashstd method: {}.{}", name, method);
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// 標準ライブラリのメソッドを実行
let result = builtin_method(&arg_values)?;
idebug!("✅ nyashstd method completed: {}.{}", name, method);
return Ok(result);
}
// 🔥 ユーザー定義のStatic Boxメソッドチェック
if self.is_static_box(name) {
idebug!("🔍 Checking user-defined static box: {}", name);
// Static Boxの初期化を確実に実行
self.ensure_static_box_initialized(name)?;
// GlobalBox.statics.{name} からメソッドを取得してクローン
let (method_clone, static_instance_clone) =
{
let global_box = self.shared.global_box.lock().map_err(|_| {
RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string(),
}
})?;
let statics_box = global_box.get_field("statics").ok_or(
RuntimeError::RuntimeFailure {
message: "statics namespace not found in GlobalBox".to_string(),
},
)?;
let statics_instance = statics_box
.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "statics field is not an InstanceBox".to_string(),
})?;
let static_instance = statics_instance.get_field(name).ok_or(
RuntimeError::InvalidOperation {
message: format!(
"Static box '{}' not found in statics namespace",
name
),
},
)?;
let instance = static_instance
.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: format!("Static box '{}' is not an InstanceBox", name),
})?;
// メソッドを探す
if let Some(method_node) = instance.get_method(method) {
(method_node.clone(), static_instance.clone_box())
} else {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Method '{}' not found in static box '{}'",
method, name
),
});
}
}; // lockはここで解放される
idebug!("🌟 Calling static box method: {}.{}", name, method);
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドのパラメータと本体を取得
if let ASTNode::FunctionDeclaration { params, body, .. } = &method_clone {
// local変数スタックを保存
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// meをstatic boxインスタンスに設定
self.declare_local_variable("me", static_instance_clone);
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// メソッドの本体を実行TaskGroupスコープ
crate::runtime::global_hooks::push_task_scope();
let mut result = Box::new(VoidBox::new()) as Box<dyn NyashBox>;
for statement in body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
crate::runtime::global_hooks::pop_task_scope();
self.restore_local_vars(saved_locals);
idebug!("✅ Static box method completed: {}.{}", name, method);
return Ok(result);
}
}
}
// オブジェクトを評価(通常のメソッド呼び出し)
let obj_value = self.execute_expression(object)?;
idebug!(
"🔍 DEBUG: execute_method_call - object type: {}, method: {}",
obj_value.type_name(),
method
);
// 🌟 ユニバーサルメソッド前段ディスパッチ(非侵襲)
// toString()/type()/equals(x)/clone() をトレイトに直結
match method {
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
return Ok(Box::new(obj_value.to_string_box()));
}
"type" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("type() expects 0 arguments, got {}", arguments.len()),
});
}
return Ok(Box::new(StringBox::new(obj_value.type_name())));
}
"equals" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("equals() expects 1 argument, got {}", arguments.len()),
});
}
let rhs = self.execute_expression(&arguments[0])?;
let eq = obj_value.equals(&*rhs);
return Ok(Box::new(eq));
}
"clone" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clone() expects 0 arguments, got {}", arguments.len()),
});
}
return Ok(obj_value.clone_box());
}
_ => {}
}
// Builtin dispatch (centralized)
if let Some(res) = self.dispatch_builtin_method(&obj_value, method, arguments) {
return res;
}
// DateTimeBox method calls
if let Some(datetime_box) = obj_value.as_any().downcast_ref::<DateTimeBox>() {
return self.execute_datetime_method(datetime_box, method, arguments);
}
// TimerBox method calls
if let Some(timer_box) = obj_value
.as_any()
.downcast_ref::<crate::boxes::time_box::TimerBox>()
{
return self.execute_timer_method(timer_box, method, arguments);
}
// MapBox method calls
if let Some(map_box) = obj_value.as_any().downcast_ref::<MapBox>() {
return self.execute_map_method(map_box, method, arguments);
}
// RandomBox method calls
if let Some(random_box) = obj_value.as_any().downcast_ref::<RandomBox>() {
return self.execute_random_method(random_box, method, arguments);
}
// SoundBox method calls
if let Some(sound_box) = obj_value.as_any().downcast_ref::<SoundBox>() {
return self.execute_sound_method(sound_box, method, arguments);
}
// DebugBox method calls
if let Some(debug_box) = obj_value.as_any().downcast_ref::<DebugBox>() {
return self.execute_debug_method(debug_box, method, arguments);
}
// ConsoleBox method calls
if let Some(console_box) = obj_value
.as_any()
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
{
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);
}
// SocketBox method calls
if let Some(socket_box) = obj_value.as_any().downcast_ref::<SocketBox>() {
let result = self.execute_socket_method(socket_box, method, arguments)?;
// 🔧 FIX: Update stored variable for stateful SocketBox methods
// These methods modify the SocketBox internal state, so we need to update
// the stored variable/field to ensure subsequent accesses get the updated state
if matches!(method, "bind" | "connect" | "close") {
idebug!(
"🔧 DEBUG: Stateful method '{}' called, updating stored instance",
method
);
let updated_instance = socket_box.clone();
idebug!(
"🔧 DEBUG: Updated instance created with ID={}",
updated_instance.box_id()
);
match object {
ASTNode::Variable { name, .. } => {
idebug!("🔧 DEBUG: Updating local variable '{}'", name);
// Handle local variables
if let Some(stored_var) = self.local_vars.get_mut(name) {
idebug!(
"🔧 DEBUG: Found local variable '{}', updating from id={} to id={}",
name,
stored_var.box_id(),
updated_instance.box_id()
);
*stored_var = Arc::new(updated_instance);
} else {
idebug!("🔧 DEBUG: Local variable '{}' not found", name);
}
}
ASTNode::FieldAccess {
object: field_obj,
field,
..
} => {
idebug!("🔧 DEBUG: Updating field access '{}'", field);
// Handle StaticBox fields like me.server
match field_obj.as_ref() {
ASTNode::Variable { name, .. } => {
idebug!("🔧 DEBUG: Field object is variable '{}'", name);
if name == "me" {
idebug!("🔧 DEBUG: Updating me.{} (via variable)", field);
if let Ok(me_instance) = self.resolve_variable("me") {
idebug!(
"🔧 DEBUG: Resolved 'me' instance id={}",
me_instance.box_id()
);
if let Some(instance) =
(*me_instance).as_any().downcast_ref::<InstanceBox>()
{
idebug!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id());
let result = instance
.set_field(field, Arc::new(updated_instance));
idebug!("🔧 DEBUG: set_field result: {:?}", result);
} else {
idebug!(
"🔧 DEBUG: me is not an InstanceBox, type: {}",
me_instance.type_name()
);
}
} else {
idebug!("🔧 DEBUG: Failed to resolve 'me'");
}
} else {
idebug!("🔧 DEBUG: Field object is not 'me', it's '{}'", name);
}
}
ASTNode::Me { .. } => {
idebug!("🔧 DEBUG: Field object is Me node, updating me.{}", field);
if let Ok(me_instance) = self.resolve_variable("me") {
idebug!(
"🔧 DEBUG: Resolved 'me' instance id={}",
me_instance.box_id()
);
if let Some(instance) =
(*me_instance).as_any().downcast_ref::<InstanceBox>()
{
idebug!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id());
let result =
instance.set_field(field, Arc::new(updated_instance));
idebug!("🔧 DEBUG: set_field result: {:?}", result);
} else {
idebug!(
"🔧 DEBUG: me is not an InstanceBox, type: {}",
me_instance.type_name()
);
}
} else {
idebug!("🔧 DEBUG: Failed to resolve 'me'");
}
}
_ => {
idebug!(
"🔧 DEBUG: Field object is not a variable or me, type: {:?}",
field_obj
);
}
}
}
_ => {
idebug!("🔧 DEBUG: Object type not handled: {:?}", object);
}
}
}
return Ok(result);
}
// HTTPServerBox method calls
if let Some(http_server_box) = obj_value.as_any().downcast_ref::<HTTPServerBox>() {
return self.execute_http_server_method(http_server_box, method, arguments);
}
// HTTPRequestBox method calls
if let Some(http_request_box) = obj_value.as_any().downcast_ref::<HTTPRequestBox>() {
return self.execute_http_request_method(http_request_box, method, arguments);
}
// HTTPResponseBox method calls
if let Some(http_response_box) = obj_value.as_any().downcast_ref::<HTTPResponseBox>() {
return self.execute_http_response_method(http_response_box, method, arguments);
}
// P2PBox method calls
if let Some(p2p_box) = obj_value.as_any().downcast_ref::<crate::boxes::P2PBox>() {
return self.execute_p2p_box_method(p2p_box, method, arguments);
}
// EguiBox method calls (非WASM環境のみ)
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
if let Some(egui_box) = obj_value.as_any().downcast_ref::<crate::boxes::EguiBox>() {
return self.execute_egui_method(egui_box, method, arguments);
}
// WebDisplayBox method calls (WASM環境のみ)
#[cfg(target_arch = "wasm32")]
if let Some(web_display_box) = obj_value
.as_any()
.downcast_ref::<crate::boxes::WebDisplayBox>()
{
return self.execute_web_display_method(web_display_box, method, arguments);
}
// WebConsoleBox method calls (WASM環境のみ)
#[cfg(target_arch = "wasm32")]
if let Some(web_console_box) = obj_value
.as_any()
.downcast_ref::<crate::boxes::WebConsoleBox>()
{
return self.execute_web_console_method(web_console_box, method, arguments);
}
// WebCanvasBox method calls (WASM環境のみ)
#[cfg(target_arch = "wasm32")]
if let Some(web_canvas_box) = obj_value
.as_any()
.downcast_ref::<crate::boxes::WebCanvasBox>()
{
return self.execute_web_canvas_method(web_canvas_box, method, arguments);
}
// MethodBox method calls
if let Some(method_box) = obj_value
.as_any()
.downcast_ref::<crate::method_box::MethodBox>()
{
return self.execute_method_box_method(method_box, method, arguments);
}
// IntegerBox method calls
if let Some(integer_box) = obj_value.as_any().downcast_ref::<IntegerBox>() {
return self.execute_integer_method(integer_box, method, arguments);
}
// FloatBox method calls (将来的に追加予定)
// RangeBox method calls (将来的に追加予定)
// PluginBoxV2 method calls
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if let Some(plugin_box) = obj_value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
return self.execute_plugin_box_v2_method(plugin_box, method, arguments);
}
// InstanceBox dispatch
if let Some(res) = self.dispatch_instance_method(object, &obj_value, method, arguments) {
return res;
}
idebug!(
"🔍 DEBUG: Reached non-instance type error for type: {}, method: {}",
obj_value.type_name(),
method
);
Err(RuntimeError::TypeError {
message: format!("Cannot call method '{}' on non-instance type", method),
})
}
/// 🔥 FromCall実行処理 - from Parent.method(arguments) or from Parent.constructor(arguments)
pub(super) fn execute_from_call(
&mut self,
parent: &str,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. 現在のコンテキストで'me'変数を取得(現在のインスタンス)
let current_instance_val =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'from' can only be used inside methods".to_string(),
})?;
let current_instance = (*current_instance_val)
.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "'from' requires current instance to be InstanceBox".to_string(),
})?;
// 2. 現在のクラスのデリゲーション関係を検証
let current_class = &current_instance.class_name;
// ここでは短期ロックで必要な情報だけ抜き出してすぐ解放する
let (has_parent_in_ext, has_parent_in_impl) = {
let box_declarations = self.shared.box_declarations.read().unwrap();
let current_box_decl =
box_declarations
.get(current_class)
.ok_or(RuntimeError::UndefinedClass {
name: current_class.clone(),
})?;
(
current_box_decl.extends.contains(&parent.to_string()),
current_box_decl.implements.contains(&parent.to_string()),
)
};
// extendsまたはimplementsでparentが指定されているか確認 (Multi-delegation) 🚀
let is_valid_delegation = has_parent_in_ext || has_parent_in_impl;
if !is_valid_delegation {
return Err(RuntimeError::InvalidOperation {
message: format!("Class '{}' does not delegate to '{}'. Use 'box {} from {}' to establish delegation.",
current_class, parent, current_class, parent),
});
}
// 先にプラグイン親のコンストラクタ/メソッドを優先的に処理v2プラグイン対応
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
// 親がプラグインで提供されているかを確認
if self.is_plugin_box_type(parent) {
// コンストラクタ相当birth もしくは 親名と同名)の場合は、
// プラグインBoxを生成して __plugin_content に格納
if method == "birth" || method == parent {
match self.create_plugin_box(parent, arguments) {
Ok(pbox) => {
use std::sync::Arc;
let _ = current_instance
.set_field_legacy("__plugin_content", Arc::from(pbox));
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
Err(e) => {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Failed to construct plugin parent '{}': {:?}",
parent, e
),
});
}
}
} else {
// 非コンストラクタ: 既存の __plugin_content を通じてメソッド呼び出し
if let Some(plugin_shared) =
current_instance.get_field_legacy("__plugin_content")
{
let plugin_ref = &*plugin_shared;
if let Some(plugin) = plugin_ref
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
return self.execute_plugin_box_v2_method(plugin, method, arguments);
}
}
}
}
}
// 🔥 Phase 8.8: pack透明化システム - ビルトインBox判定
use crate::box_trait::is_builtin_box;
// GUI機能が有効な場合はEguiBoxも追加判定mut不要の形に
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
let is_builtin = is_builtin_box(parent) || parent == "EguiBox";
#[cfg(not(all(feature = "gui", not(target_arch = "wasm32"))))]
let is_builtin = is_builtin_box(parent);
// 🔥 Phase 8.9: Transparency system removed - all delegation must be explicit
// Removed: if is_builtin && method == parent { ... execute_builtin_constructor_call ... }
if is_builtin {
// ビルトインBoxの場合、直接ビルトインメソッドを実行
return self.execute_builtin_box_method(
parent,
method,
current_instance_val.clone_box(),
arguments,
);
}
// プラグイン親__plugin_content
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
let plugin_ref = &*plugin_shared;
if let Some(plugin) = plugin_ref
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
return self.execute_plugin_box_v2_method(plugin, method, arguments);
}
}
}
// 3. 親クラスのBox宣言を取得ユーザー定義Boxの場合
let parent_box_decl = {
let box_declarations = self.shared.box_declarations.read().unwrap();
box_declarations
.get(parent)
.ok_or(RuntimeError::UndefinedClass {
name: parent.to_string(),
})?
.clone()
};
// 4. constructorまたはinitまたはpackまたはbirthの場合の特別処理
if method == "constructor"
|| method == "init"
|| method == "pack"
|| method == "birth"
|| method == parent
{
return self.execute_from_parent_constructor(
parent,
&parent_box_decl,
current_instance_val.clone_box(),
arguments,
);
}
// 5. 親クラスのメソッドを取得
let parent_method = parent_box_decl
.methods
.get(method)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Method '{}' not found in parent class '{}'", method, parent),
})?
.clone();
// 6. 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// 7. 親メソッドを実行
if let ASTNode::FunctionDeclaration { params, body, .. } = parent_method {
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Parent method {}.{} expects {} arguments, got {}",
parent,
method,
params.len(),
arg_values.len()
),
});
}
// 🌍 local変数スタックを保存・クリア親メソッド実行開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// 'me'を現在のインスタンスに設定(重要:現在のインスタンスを維持)
self.declare_local_variable("me", current_instance_val.clone_or_share());
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// 親メソッドの本体を実行TaskGroupスコープ
crate::runtime::global_hooks::push_task_scope();
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// 🔍 DEBUG: FromCall実行結果をログ出力
idebug!(
"🔍 DEBUG: FromCall {}.{} result: {}",
parent,
method,
result.to_string_box().value
);
// local変数スタックを復元
crate::runtime::global_hooks::pop_task_scope();
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!(
"Parent method '{}' is not a valid function declaration",
method
),
})
}
}
/// 🔥 fromCall専用親コンストラクタ実行処理 - from Parent.constructor(arguments)
fn execute_from_parent_constructor(
&mut self,
parent: &str,
parent_box_decl: &super::BoxDeclaration,
current_instance: Box<dyn NyashBox>,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 1. 親クラスのコンストラクタを取得(引数の数でキーを作成)
// "birth/引数数"、"pack/引数数"、"init/引数数"、"Box名/引数数" の順で試す
let birth_key = format!("birth/{}", arguments.len());
let pack_key = format!("pack/{}", arguments.len());
let init_key = format!("init/{}", arguments.len());
let box_name_key = format!("{}/{}", parent, arguments.len());
let parent_constructor = parent_box_decl
.constructors
.get(&birth_key)
.or_else(|| parent_box_decl.constructors.get(&pack_key))
.or_else(|| parent_box_decl.constructors.get(&init_key))
.or_else(|| parent_box_decl.constructors.get(&box_name_key))
.ok_or(RuntimeError::InvalidOperation {
message: format!(
"No constructor found for parent class '{}' with {} arguments",
parent,
arguments.len()
),
})?
.clone();
// 2. 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// 3. 親コンストラクタを実行
if let ASTNode::FunctionDeclaration { params, body, .. } = parent_constructor {
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Parent constructor {} expects {} arguments, got {}",
parent,
params.len(),
arg_values.len()
),
});
}
// 🌍 local変数スタックを保存・クリア親コンストラクタ実行開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// 'me'を現在のインスタンスに設定
self.declare_local_variable("me", current_instance.clone_or_share());
// 引数をlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// 親コンストラクタの本体を実行
let mut _result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
_result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
_result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
// 親コンストラクタは通常現在のインスタンスを返す
Ok(current_instance)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Parent constructor is not a valid function declaration"),
})
}
}
/// Execute method call on PluginBoxV2
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
fn execute_plugin_box_v2_method(
&mut self,
plugin_box: &PluginBoxV2,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
self.call_plugin_method(plugin_box, method, arguments)
}
}

View File

@ -1,588 +0,0 @@
/*!
* Expression Processing Module
*
* Extracted from core.rs lines 408-787 (~380 lines)
* Handles expression evaluation, binary operations, method calls, and field access
* Core philosophy: "Everything is Box" with clean expression evaluation
*/
// Module declarations
mod access;
mod builtins;
mod calls;
mod operators;
use super::*;
use std::sync::Arc;
// Direct implementation approach to avoid import issues
// TODO: Fix NullBox import issue later
// use crate::NullBox;
impl NyashInterpreter {
/// Build closure environment by capturing 'me' and free variables by value (P1)
fn build_closure_env(
&mut self,
params: &Vec<String>,
body: &Vec<ASTNode>,
) -> Result<crate::boxes::function_box::ClosureEnv, RuntimeError> {
use std::collections::HashSet;
let mut env = crate::boxes::function_box::ClosureEnv::new();
// Capture 'me' if bound
if let Ok(mev) = self.resolve_variable("me") {
env.me_value = Some(Arc::downgrade(&mev));
}
// Collect free variables
let mut used: HashSet<String> = HashSet::new();
let mut locals: HashSet<String> = HashSet::new();
// params are considered local
for p in params {
locals.insert(p.clone());
}
// BFS walk statements
fn collect(node: &ASTNode, used: &mut HashSet<String>, locals: &mut HashSet<String>) {
match node {
ASTNode::Variable { name, .. } => {
if !locals.contains(name) && name != "me" && name != "this" {
used.insert(name.clone());
}
}
ASTNode::Local { variables, .. } => {
for v in variables {
locals.insert(v.clone());
}
}
ASTNode::Assignment { target, value, .. } => {
collect(target, used, locals);
collect(value, used, locals);
}
ASTNode::BinaryOp { left, right, .. } => {
collect(left, used, locals);
collect(right, used, locals);
}
ASTNode::UnaryOp { operand, .. } => {
collect(operand, used, locals);
}
ASTNode::MethodCall {
object, arguments, ..
} => {
collect(object, used, locals);
for a in arguments {
collect(a, used, locals);
}
}
ASTNode::FunctionCall { arguments, .. } => {
for a in arguments {
collect(a, used, locals);
}
}
ASTNode::Call {
callee, arguments, ..
} => {
collect(callee, used, locals);
for a in arguments {
collect(a, used, locals);
}
}
ASTNode::FieldAccess { object, .. } => {
collect(object, used, locals);
}
ASTNode::New { arguments, .. } => {
for a in arguments {
collect(a, used, locals);
}
}
ASTNode::If {
condition,
then_body,
else_body,
..
} => {
collect(condition, used, locals);
for st in then_body {
collect(st, used, locals);
}
if let Some(eb) = else_body {
for st in eb {
collect(st, used, locals);
}
}
}
ASTNode::Loop {
condition, body, ..
} => {
collect(condition, used, locals);
for st in body {
collect(st, used, locals);
}
}
ASTNode::TryCatch {
try_body,
catch_clauses,
finally_body,
..
} => {
for st in try_body {
collect(st, used, locals);
}
for c in catch_clauses {
for st in &c.body {
collect(st, used, locals);
}
}
if let Some(fb) = finally_body {
for st in fb {
collect(st, used, locals);
}
}
}
ASTNode::Throw { expression, .. } => {
collect(expression, used, locals);
}
ASTNode::Print { expression, .. } => {
collect(expression, used, locals);
}
ASTNode::Return { value, .. } => {
if let Some(v) = value {
collect(v, used, locals);
}
}
ASTNode::AwaitExpression { expression, .. } => {
collect(expression, used, locals);
}
ASTNode::PeekExpr {
scrutinee,
arms,
else_expr,
..
} => {
collect(scrutinee, used, locals);
for (_, e) in arms {
collect(e, used, locals);
}
collect(else_expr, used, locals);
}
ASTNode::Program { statements, .. } => {
for st in statements {
collect(st, used, locals);
}
}
ASTNode::FunctionDeclaration { params, body, .. } => {
let mut inner = locals.clone();
for p in params {
inner.insert(p.clone());
}
for st in body {
collect(st, used, &mut inner);
}
}
_ => {}
}
}
for st in body {
collect(st, &mut used, &mut locals);
}
// Materialize captures: local by-ref via RefCellBox, others by-value
for name in used.into_iter() {
if let Some(local_arc) = self.local_vars.get(&name) {
let lb: &dyn NyashBox = &**local_arc;
// If already RefCellBox, reuse inner; else wrap and replace local binding
if let Some(rc) = lb
.as_any()
.downcast_ref::<crate::boxes::ref_cell_box::RefCellBox>()
{
env.captures.insert(name.clone(), rc.share_box());
} else {
// wrap existing into RefCell and replace local binding
let wrapped = crate::boxes::ref_cell_box::RefCellBox::new(lb.clone_box());
self.local_vars.insert(name.clone(), wrapped.clone_arc());
env.captures.insert(name, wrapped.share_box());
}
} else {
// non-local (global/static): by-value capture
if let Ok(v) = self.resolve_variable(&name) {
env.captures.insert(name, v.clone_or_share());
}
}
}
Ok(env)
}
/// 式を実行 - Expression evaluation engine
pub(super) fn execute_expression(
&mut self,
expression: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match expression {
// P1: allow block (Program) as expression; value = last statement's value
ASTNode::Program { statements, .. } => {
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
let last = statements.len().saturating_sub(1);
for (i, st) in statements.iter().enumerate() {
let prev = self.discard_context;
self.discard_context = i != last;
result = self.execute_statement(st)?;
self.discard_context = prev;
match &self.control_flow {
ControlFlow::Break => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Continue => {
return Err(RuntimeError::BreakOutsideLoop);
}
ControlFlow::Return(_) => {
return Err(RuntimeError::ReturnOutsideFunction);
}
ControlFlow::Throw(_) => {
return Err(RuntimeError::UncaughtException);
}
ControlFlow::None => {}
}
}
Ok(result)
}
ASTNode::Literal { value, .. } => Ok(value.to_nyash_box()),
ASTNode::Variable { name, .. } => {
// 🌍 革命的変数解決local変数 → GlobalBoxフィールド → エラー
let shared_var =
self.resolve_variable(name)
.map_err(|_| RuntimeError::UndefinedVariableAt {
name: name.clone(),
span: expression.span(),
})?;
Ok((*shared_var).share_box()) // 🎯 State-sharing instead of cloning
}
ASTNode::BinaryOp {
operator,
left,
right,
..
} => self.execute_binary_op(operator, left, right),
ASTNode::UnaryOp {
operator, operand, ..
} => self.execute_unary_op(operator, operand),
ASTNode::AwaitExpression { expression, .. } => self.execute_await(expression),
ASTNode::MethodCall {
object,
method,
arguments,
..
} => {
let result = self.execute_method_call(object, method, arguments);
result
}
ASTNode::FieldAccess { object, field, .. } => {
let shared_result = self.execute_field_access(object, field)?;
Ok((*shared_result).clone_or_share())
}
ASTNode::New {
class,
arguments,
type_arguments,
..
} => self.execute_new(class, arguments, type_arguments),
ASTNode::This { .. } => {
// 🌍 革命的this解決local変数から取得
let shared_this =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is only available inside methods".to_string(),
})?;
Ok((*shared_this).clone_or_share())
}
ASTNode::Me { .. } => {
// 🌍 革命的me解決local変数から取得thisと同じ
let shared_me =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'me' is only available inside methods".to_string(),
})?;
Ok((*shared_me).clone_or_share())
}
ASTNode::ThisField { field, .. } => {
// 🌍 革命的this.fieldアクセスlocal変数から取得
let this_value =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = (*this_value).as_any().downcast_ref::<InstanceBox>() {
let shared_field = instance.get_field(field).ok_or_else(|| {
RuntimeError::InvalidOperation {
message: format!("Field '{}' not found on this", field),
}
})?;
Ok((*shared_field).clone_or_share())
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
ASTNode::MeField { field, .. } => {
// 🌍 革命的me.fieldアクセスlocal変数から取得
let me_value =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = (*me_value).as_any().downcast_ref::<InstanceBox>() {
let shared_field = instance.get_field(field).ok_or_else(|| {
RuntimeError::InvalidOperation {
message: format!("Field '{}' not found on me", field),
}
})?;
Ok((*shared_field).clone_or_share())
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
ASTNode::FunctionCall {
name, arguments, ..
} => self.execute_function_call(name, arguments),
ASTNode::Call {
callee, arguments, ..
} => {
// callee を評価して FunctionBox なら本体を実行
let callee_val = self.execute_expression(callee)?;
if let Some(fun) = callee_val
.as_any()
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
{
// 引数評価
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
for a in arguments {
arg_values.push(self.execute_expression(a)?);
}
if arg_values.len() != fun.params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Function expects {} args, got {}",
fun.params.len(),
arg_values.len()
),
});
}
// スコープ保存
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// キャプチャ注入by-value
for (k, v) in fun.env.captures.iter() {
self.declare_local_variable(k, v.clone_or_share());
}
if let Some(me_w) = &fun.env.me_value {
if let Some(me_arc) = me_w.upgrade() {
self.declare_local_variable("me", (*me_arc).clone_or_share());
} else {
self.declare_local_variable(
"me",
Box::new(crate::boxes::null_box::NullBox::new()),
);
}
}
for (p, v) in fun.params.iter().zip(arg_values.iter()) {
self.declare_local_variable(p, v.clone_or_share());
}
// 実行
crate::runtime::global_hooks::push_task_scope();
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for st in &fun.body {
result = self.execute_statement(st)?;
if let super::ControlFlow::Return(rv) = &self.control_flow {
result = rv.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
crate::runtime::global_hooks::pop_task_scope();
self.restore_local_vars(saved_locals);
Ok(result)
} else if let ASTNode::Lambda { params, body, .. } = callee.as_ref() {
// 直書きLambdaは従来通り実行後方互換
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
for a in arguments {
arg_values.push(self.execute_expression(a)?);
}
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Lambda expects {} args, got {}",
params.len(),
arg_values.len()
),
});
}
let saved_locals = self.save_local_vars();
self.local_vars.clear();
for (p, v) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(p, v.clone_or_share());
}
crate::runtime::global_hooks::push_task_scope();
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for st in body {
result = self.execute_statement(st)?;
if let super::ControlFlow::Return(rv) = &self.control_flow {
result = rv.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
crate::runtime::global_hooks::pop_task_scope();
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: "Callee is not callable".to_string(),
})
}
}
ASTNode::Arrow {
sender, receiver, ..
} => self.execute_arrow(sender, receiver),
ASTNode::QMarkPropagate { expression, .. } => {
let v = self.execute_expression(expression)?;
if let Some(res) = v
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
// ok -> unwrap, err -> early return (propagate)
if matches!(res, crate::boxes::result::NyashResultBox::Ok(_)) {
return Ok(res.get_value());
} else {
// Early return the Result itself
self.control_flow = super::ControlFlow::Return(v.clone_box());
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
}
// Not a Result: pass-through
Ok(v)
}
ASTNode::PeekExpr {
scrutinee,
arms,
else_expr,
..
} => {
let val = self.execute_expression(scrutinee)?;
let sval = val.to_string_box().value;
for (pat, expr) in arms {
let pv = match pat {
crate::ast::LiteralValue::String(s) => s.clone(),
crate::ast::LiteralValue::Integer(i) => i.to_string(),
crate::ast::LiteralValue::Float(f) => f.to_string(),
crate::ast::LiteralValue::Bool(b) => {
if *b {
"true".to_string()
} else {
"false".to_string()
}
}
crate::ast::LiteralValue::Null => "null".to_string(),
crate::ast::LiteralValue::Void => "void".to_string(),
};
if pv == sval {
return self.execute_expression(expr);
}
}
self.execute_expression(else_expr)
}
ASTNode::Lambda { params, body, .. } => {
// 値としての関数ボックスを生成ClosureEnv: me/by-val captures
let env = self.build_closure_env(&params, body)?;
Ok(Box::new(crate::boxes::function_box::FunctionBox::with_env(
params.clone(),
body.clone(),
env,
)))
}
ASTNode::Include { filename, .. } => {
// include式: 最初のstatic boxを返す
self.execute_include_expr(filename)
}
ASTNode::FromCall {
parent,
method,
arguments,
..
} => self.execute_from_call(parent, method, arguments),
_ => Err(RuntimeError::InvalidOperation {
message: format!("Cannot execute {:?} as expression", expression.node_type()),
}),
}
}
/// 🔄 循環参照検出: オブジェクトの一意IDを取得
#[allow(dead_code)]
fn get_object_id(&self, node: &ASTNode) -> Option<usize> {
match node {
ASTNode::Variable { name, .. } => {
// 変数名のハッシュをIDとして使用
Some(self.hash_string(name))
}
ASTNode::Me { .. } => {
// 'me'参照の特別なID
Some(usize::MAX)
}
ASTNode::This { .. } => {
// 'this'参照の特別なID
Some(usize::MAX - 1)
}
_ => None, // 他のードタイプはID追跡しない
}
}
/// 🔄 文字列のシンプルなハッシュ関数
#[allow(dead_code)]
fn hash_string(&self, s: &str) -> usize {
let mut hash = 0usize;
for byte in s.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(byte as usize);
}
hash
}
// fn box_to_nyash_value(&self, box_val: &Box<dyn NyashBox>) -> Option<nyash_rust::value::NyashValue> {
// // Try to convert the box back to NyashValue for weak reference operations
// // This is a simplified conversion - in reality we might need more sophisticated logic
// use nyash_rust::value::NyashValue;
// use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox};
//
// if let Some(string_box) = box_val.as_any().downcast_ref::<StringBox>() {
// Some(NyashValue::String(string_box.value.clone()))
// } else if let Some(int_box) = box_val.as_any().downcast_ref::<IntegerBox>() {
// Some(NyashValue::Integer(int_box.value))
// } else if let Some(bool_box) = box_val.as_any().downcast_ref::<BoolBox>() {
// Some(NyashValue::Bool(bool_box.value))
// } else if box_val.as_any().downcast_ref::<VoidBox>().is_some() {
// Some(NyashValue::Void)
// } else if box_val.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
// Some(NyashValue::Null)
// } else {
// // For complex types, create a Box variant
// // Note: This is where we'd store the weak reference
// None // Simplified for now
// }
// }
}

View File

@ -1,644 +0,0 @@
/*!
* Binary and unary operator evaluation
*/
// Removed super::* import - specific imports below
use crate::ast::{ASTNode, BinaryOperator, UnaryOperator};
use crate::box_trait::{BoolBox, CompareBox, NyashBox};
use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一
use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在
use crate::instance_v2::InstanceBox;
use crate::interpreter::{NyashInterpreter, RuntimeError};
// Local helper functions to bypass import issues
/// InstanceBoxでラップされている場合、内部のBoxを取得する
/// シンプルなヘルパー関数で型地獄を回避
fn unwrap_instance(boxed: &dyn NyashBox) -> &dyn NyashBox {
eprintln!(
"🔍 DEBUG unwrap_instance: input type = {}",
boxed.type_name()
);
if let Some(instance) = boxed.as_any().downcast_ref::<InstanceBox>() {
eprintln!(" ✅ Is InstanceBox");
if let Some(ref inner) = instance.inner_content {
eprintln!(" 📦 Inner content type = {}", inner.type_name());
return inner.as_ref();
}
}
eprintln!(" ❌ Not InstanceBox, returning as is");
boxed
}
fn best_effort_to_string(val: &dyn NyashBox) -> String {
crate::runtime::semantics::coerce_to_string(val).unwrap_or_else(|| val.to_string_box().value)
}
fn best_effort_to_i64(val: &dyn NyashBox) -> Option<i64> {
crate::runtime::semantics::coerce_to_i64(val)
}
pub(super) fn try_add_operation(
left: &dyn NyashBox,
right: &dyn NyashBox,
) -> Option<Box<dyn NyashBox>> {
// 🎯 InstanceBoxのunwrap処理
let left = unwrap_instance(left);
let right = unwrap_instance(right);
// IntegerBox + IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>(),
) {
return Some(Box::new(IntegerBox::new(left_int.value + right_int.value)));
}
// StringBox + anything -> concatenation
if let Some(left_str) = left.as_any().downcast_ref::<StringBox>() {
let right_str = right.to_string_box();
return Some(Box::new(StringBox::new(format!(
"{}{}",
left_str.value, right_str.value
))));
}
// BoolBox + BoolBox -> IntegerBox
if let (Some(left_bool), Some(right_bool)) = (
left.as_any().downcast_ref::<BoolBox>(),
right.as_any().downcast_ref::<BoolBox>(),
) {
return Some(Box::new(IntegerBox::new(
(left_bool.value as i64) + (right_bool.value as i64),
)));
}
None
}
pub(super) fn try_sub_operation(
left: &dyn NyashBox,
right: &dyn NyashBox,
) -> Option<Box<dyn NyashBox>> {
// 🎯 InstanceBoxのunwrap処理
let left = unwrap_instance(left);
let right = unwrap_instance(right);
// IntegerBox - IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>(),
) {
return Some(Box::new(IntegerBox::new(left_int.value - right_int.value)));
}
None
}
pub(super) fn try_mul_operation(
left: &dyn NyashBox,
right: &dyn NyashBox,
) -> Option<Box<dyn NyashBox>> {
// 🎯 InstanceBoxのunwrap処理
let left = unwrap_instance(left);
let right = unwrap_instance(right);
// デバッグ出力
eprintln!(
"🔍 DEBUG try_mul: left type = {}, right type = {}",
left.type_name(),
right.type_name()
);
// IntegerBox * IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>(),
) {
eprintln!(
"✅ IntegerBox downcast success: {} * {}",
left_int.value, right_int.value
);
return Some(Box::new(IntegerBox::new(left_int.value * right_int.value)));
}
// box_trait::IntegerBoxも試す
eprintln!("❌ box_trait::IntegerBox downcast failed, trying boxes::integer_box::IntegerBox");
// boxes::integer_box::IntegerBoxを試す
use crate::boxes::integer_box::IntegerBox as BoxesIntegerBox;
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<BoxesIntegerBox>(),
right.as_any().downcast_ref::<BoxesIntegerBox>(),
) {
eprintln!(
"✅ boxes::IntegerBox downcast success: {} * {}",
left_int.value, right_int.value
);
return Some(Box::new(IntegerBox::new(left_int.value * right_int.value)));
}
// StringBox * IntegerBox -> repetition
if let (Some(str_box), Some(count_int)) = (
left.as_any().downcast_ref::<StringBox>(),
right.as_any().downcast_ref::<IntegerBox>(),
) {
return Some(Box::new(StringBox::new(
str_box.value.repeat(count_int.value as usize),
)));
}
None
}
pub(super) fn try_div_operation(
left: &dyn NyashBox,
right: &dyn NyashBox,
) -> Result<Box<dyn NyashBox>, String> {
// 🎯 InstanceBoxのunwrap処理
let left = unwrap_instance(left);
let right = unwrap_instance(right);
// IntegerBox / IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>(),
) {
if right_int.value == 0 {
return Err("Division by zero".to_string());
}
return Ok(Box::new(IntegerBox::new(left_int.value / right_int.value)));
}
Err(format!(
"Division not supported between {} and {}",
left.type_name(),
right.type_name()
))
}
pub(super) fn try_mod_operation(
left: &dyn NyashBox,
right: &dyn NyashBox,
) -> Result<Box<dyn NyashBox>, String> {
// IntegerBox % IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>(),
) {
if right_int.value == 0 {
return Err("Modulo by zero".to_string());
}
return Ok(Box::new(IntegerBox::new(left_int.value % right_int.value)));
}
Err(format!(
"Modulo not supported between {} and {}",
left.type_name(),
right.type_name()
))
}
impl NyashInterpreter {
/// 二項演算を実行 - Binary operation processing
pub(super) fn execute_binary_op(
&mut self,
op: &BinaryOperator,
left: &ASTNode,
right: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let left_val = self.execute_expression(left)?;
let right_val = self.execute_expression(right)?;
// Binary operation execution
match op {
BinaryOperator::Add => {
// Optional: enforce grammar rule for add (behind env)
if std::env::var("NYASH_GRAMMAR_ENFORCE_ADD").ok().as_deref() == Some("1") {
let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
if let Some((res, _act)) =
crate::grammar::engine::get().decide_add_result(lty, rty)
{
match res {
"String" => {
let ls =
crate::runtime::semantics::coerce_to_string(left_val.as_ref())
.unwrap_or_else(|| left_val.to_string_box().value);
let rs =
crate::runtime::semantics::coerce_to_string(right_val.as_ref())
.unwrap_or_else(|| right_val.to_string_box().value);
return Ok(Box::new(StringBox::new(format!("{}{}", ls, rs))));
}
"Integer" => {
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
return Ok(Box::new(IntegerBox::new(li + ri)));
}
}
_ => {}
}
}
}
let (strat, lty, rty, expect) =
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let strat = crate::grammar::engine::get().add_coercion_strategy();
let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref())
.is_some()
{
"Integer"
} else {
"Other"
};
let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref())
.is_some()
{
"Integer"
} else {
"Other"
};
let rule = crate::grammar::engine::get().decide_add_result(lty, rty);
(
Some(strat.to_string()),
Some(lty.to_string()),
Some(rty.to_string()),
rule.map(|(res, act)| (res.to_string(), act.to_string())),
)
} else {
(None, None, None, None)
};
// 1) Intrinsic fast-paths (Integer+Integer, String+*, Bool+Bool)
if let Some(result) = try_add_operation(left_val.as_ref(), right_val.as_ref()) {
if let (Some(s), Some(l), Some(r)) =
(strat.as_ref(), lty.as_ref(), rty.as_ref())
{
let actual = if result.as_any().downcast_ref::<StringBox>().is_some() {
"String"
} else if result.as_any().downcast_ref::<IntegerBox>().is_some() {
"Integer"
} else {
"Other"
};
eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual={} match={}", s, l, r, expect, actual, expect.as_ref().map(|(res,_)| res.as_str())==Some(actual));
}
return Ok(result);
}
// 2) Concatenation if either side is string-like (semantics)
let ls_opt = crate::runtime::semantics::coerce_to_string(left_val.as_ref());
let rs_opt = crate::runtime::semantics::coerce_to_string(right_val.as_ref());
if ls_opt.is_some() || rs_opt.is_some() {
let ls = ls_opt.unwrap_or_else(|| left_val.to_string_box().value);
let rs = rs_opt.unwrap_or_else(|| right_val.to_string_box().value);
if let (Some(s), Some(l), Some(r)) =
(strat.as_ref(), lty.as_ref(), rty.as_ref())
{
eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual=String match={}", s, l, r, expect, expect.as_ref().map(|(res,_)| res=="String").unwrap_or(false));
}
return Ok(Box::new(StringBox::new(format!("{}{}", ls, rs))));
}
// 3) Numeric fallback via coerce_to_i64
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
if let (Some(s), Some(l), Some(r)) =
(strat.as_ref(), lty.as_ref(), rty.as_ref())
{
eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual=Integer match={}", s, l, r, expect, expect.as_ref().map(|(res,_)| res=="Integer").unwrap_or(false));
}
return Ok(Box::new(IntegerBox::new(li + ri)));
}
// 4) Final error
if let (Some(s), Some(l), Some(r)) = (strat.as_ref(), lty.as_ref(), rty.as_ref()) {
eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual=Error", s, l, r, expect);
}
Err(RuntimeError::InvalidOperation {
message: format!(
"Addition not supported between {} and {}",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::Equal => {
let result = left_val.equals(right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::NotEqual => {
let result = left_val.equals(right_val.as_ref());
Ok(Box::new(BoolBox::new(!result.value)))
}
BinaryOperator::And => {
let left_bool = self.is_truthy(&left_val);
if !left_bool {
Ok(Box::new(BoolBox::new(false)))
} else {
let right_bool = self.is_truthy(&right_val);
Ok(Box::new(BoolBox::new(right_bool)))
}
}
BinaryOperator::Or => {
let left_bool = self.is_truthy(&left_val);
if left_bool {
Ok(Box::new(BoolBox::new(true)))
} else {
let right_bool = self.is_truthy(&right_val);
Ok(Box::new(BoolBox::new(right_bool)))
}
}
BinaryOperator::Subtract => {
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let strat = crate::grammar::engine::get().sub_coercion_strategy();
let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rule = crate::grammar::engine::get().decide_sub_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][Interp] sub strat={} lty={} rty={} expect={:?}",
strat, lty, rty, rule
);
}
// Use helper function instead of trait methods
if let Some(result) = try_sub_operation(left_val.as_ref(), right_val.as_ref()) {
return Ok(result);
}
Err(RuntimeError::InvalidOperation {
message: format!(
"Subtraction not supported between {} and {}",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::Multiply => {
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let strat = crate::grammar::engine::get().mul_coercion_strategy();
let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rule = crate::grammar::engine::get().decide_mul_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][Interp] mul strat={} lty={} rty={} expect={:?}",
strat, lty, rty, rule
);
}
// Use helper function instead of trait methods
if let Some(result) = try_mul_operation(left_val.as_ref(), right_val.as_ref()) {
return Ok(result);
}
Err(RuntimeError::InvalidOperation {
message: format!(
"Multiplication not supported between {} and {}",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::Divide => {
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let strat = crate::grammar::engine::get().div_coercion_strategy();
let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref())
.is_some()
{
"String"
} else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some()
{
"Integer"
} else {
"Other"
};
let rule = crate::grammar::engine::get().decide_div_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][Interp] div strat={} lty={} rty={} expect={:?}",
strat, lty, rty, rule
);
}
// Use helper function instead of trait methods
match try_div_operation(left_val.as_ref(), right_val.as_ref()) {
Ok(result) => Ok(result),
Err(error_msg) => Err(RuntimeError::InvalidOperation { message: error_msg }),
}
}
BinaryOperator::Modulo => {
// Use helper function for modulo operation
match try_mod_operation(left_val.as_ref(), right_val.as_ref()) {
Ok(result) => Ok(result),
Err(error_msg) => Err(RuntimeError::InvalidOperation { message: error_msg }),
}
}
BinaryOperator::Shl => {
// Integer-only left shift
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
let sh = (ri as u32) & 63;
return Ok(Box::new(IntegerBox::new(li.wrapping_shl(sh))));
}
Err(RuntimeError::TypeError {
message: format!(
"Shift-left '<<' requires integers (got {} and {})",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::Shr => {
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
let sh = (ri as u32) & 63;
return Ok(Box::new(IntegerBox::new(((li as u64) >> sh) as i64)));
}
Err(RuntimeError::TypeError {
message: format!(
"Shift-right '>>' requires integers (got {} and {})",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::BitAnd => {
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
return Ok(Box::new(IntegerBox::new(li & ri)));
}
Err(RuntimeError::TypeError {
message: format!(
"Bitwise '&' requires integers (got {} and {})",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::BitOr => {
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
return Ok(Box::new(IntegerBox::new(li | ri)));
}
Err(RuntimeError::TypeError {
message: format!(
"Bitwise '|' requires integers (got {} and {})",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::BitXor => {
if let (Some(li), Some(ri)) = (
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
crate::runtime::semantics::coerce_to_i64(right_val.as_ref()),
) {
return Ok(Box::new(IntegerBox::new(li ^ ri)));
}
Err(RuntimeError::TypeError {
message: format!(
"Bitwise '^' requires integers (got {} and {})",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::Less => {
let result = CompareBox::less(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::Greater => {
let result = CompareBox::greater(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::LessEqual => {
let result = CompareBox::less_equal(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
BinaryOperator::GreaterEqual => {
let result = CompareBox::greater_equal(left_val.as_ref(), right_val.as_ref());
Ok(Box::new(result))
}
}
}
/// 単項演算を実行 - Unary operation processing
pub(super) fn execute_unary_op(
&mut self,
operator: &UnaryOperator,
operand: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let operand_val = self.execute_expression(operand)?;
match operator {
UnaryOperator::Minus => {
// 数値の符号反転
if let Some(int_box) = operand_val.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(IntegerBox::new(-int_box.value)))
} else if let Some(float_box) = operand_val.as_any().downcast_ref::<FloatBox>() {
Ok(Box::new(FloatBox::new(-float_box.value)))
} else {
Err(RuntimeError::TypeError {
message: "Unary minus can only be applied to Integer or Float".to_string(),
})
}
}
UnaryOperator::Not => {
// 論理否定
if let Some(bool_box) = operand_val.as_any().downcast_ref::<BoolBox>() {
Ok(Box::new(BoolBox::new(!bool_box.value)))
} else {
// どんな値でもtruthyness判定してnot演算を適用
let is_truthy = self.is_truthy(&operand_val);
Ok(Box::new(BoolBox::new(!is_truthy)))
}
}
}
}
}

View File

@ -1,128 +0,0 @@
/*!
* Field Access Processing Module
*
* Extracted from expressions.rs lines 901-1019 (~118 lines)
* Handles field access for static boxes and instance boxes
* Core philosophy: "Everything is Box" with unified field access
*/
use super::*;
use crate::box_trait::SharedNyashBox;
use std::sync::Arc;
impl NyashInterpreter {
/// フィールドアクセスを実行 - static box と instance box の統一処理
pub(super) fn execute_field_access(&mut self, object: &ASTNode, field: &str)
-> Result<SharedNyashBox, RuntimeError> {
// 🔥 Static Boxアクセスチェック
if let ASTNode::Variable { name, .. } = object {
// Static boxの可能性をチェック
if self.is_static_box(name) {
let static_result = self.execute_static_field_access(name, field)?;
return Ok(Arc::from(static_result));
}
}
// オブジェクトを評価(通常のフィールドアクセス)
let obj_value = self.execute_expression(object)?;
// InstanceBoxにキャスト
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
return self.execute_instance_field_access(instance, field);
}
Err(RuntimeError::InvalidOperation {
message: format!("Cannot access field '{}' on type '{}'", field, obj_value.type_name()),
})
}
/// Static Boxフィールドアクセス実行
pub(super) fn execute_static_field_access(&mut self, box_name: &str, field: &str)
-> Result<Box<dyn NyashBox>, RuntimeError> {
let static_boxes = self.shared.static_boxes.read().unwrap();
if let Some(static_box) = static_boxes.get(box_name) {
let field_value = static_box.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in static box '{}'", field, box_name),
})?;
Ok((*field_value).clone_or_share())
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Static box '{}' not found", box_name),
})
}
}
/// Instance Boxフィールドアクセス実行
fn execute_instance_field_access(&mut self, instance: &InstanceBox, field: &str)
-> Result<SharedNyashBox, RuntimeError> {
// 🔥 finiは何回呼ばれてもエラーにしないユーザー要求
// is_finalized()チェックを削除
// フィールドの値を取得
let field_value = instance.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in {}", field, instance.class_name),
})?;
eprintln!("✅ FIELD ACCESS: Returning shared reference id={}", field_value.box_id());
// 🔗 Weak Reference Check: Use unified accessor for weak fields
let is_weak_field = {
let box_decls = self.shared.box_declarations.read().unwrap();
if let Some(box_decl) = box_decls.get(&instance.class_name) {
box_decl.weak_fields.contains(&field.to_string())
} else {
false
}
};
if is_weak_field {
return self.handle_weak_field_access(instance, field);
}
// 通常のフィールドアクセス
Ok(field_value)
}
/// Weak参照フィールドアクセス処理
fn handle_weak_field_access(&mut self, instance: &InstanceBox, field: &str)
-> Result<SharedNyashBox, RuntimeError> {
eprintln!("🔗 DEBUG: Accessing weak field '{}' in class '{}'", field, instance.class_name);
// 🎯 PHASE 2: Use unified accessor for auto-nil weak reference handling
if let Some(weak_value) = instance.get_weak_field(field, self) { // Pass self
match &weak_value {
crate::value::NyashValue::Null => {
eprintln!("🔗 DEBUG: Weak field '{}' is null (reference dropped)", field);
// Return null box for compatibility
Ok(Arc::new(crate::boxes::null_box::NullBox::new()))
}
_ => {
eprintln!("🔗 DEBUG: Weak field '{}' has live reference", field);
let converted_box = weak_value.to_nyash_box();
Ok(Arc::new(converted_box))
}
}
} else {
eprintln!("🔗 DEBUG: Weak field '{}' not found, falling back to normal access", field);
// Fallback to normal field access if weak accessor fails
let field_value = instance.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in {}", field, instance.class_name),
})?;
Ok(field_value)
}
}
/// Static Boxかどうかを判定
pub(super) fn is_static_box(&self, name: &str) -> bool {
let static_boxes = self.shared.static_boxes.read().unwrap();
static_boxes.contains_key(name)
}
}

View File

@ -1,188 +0,0 @@
/*!
* Function Processing Module
*
* Extracted from core.rs - function call and definition handling
* Handles function declarations, calls, and function-related operations
* Core philosophy: "Everything is Box" with structured function processing
*/
use super::*;
impl NyashInterpreter {
/// 関数呼び出しを実行 - 🌍 革命的実装GlobalBoxのメソッド呼び出し
pub(super) fn execute_function_call(
&mut self,
name: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// Fallback: built-in type ops as global functions: isType(value, "Type"), asType(value, "Type")
if (name == "isType" || name == "asType") && arguments.len() == 2 {
// Evaluate args
let val = self.execute_expression(&arguments[0])?;
let ty_box = self.execute_expression(&arguments[1])?;
// Get type name string
let type_name = if let Some(s) = ty_box
.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
s.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "Type name must be a string".to_string(),
});
};
if name == "isType" {
let matched = Self::matches_type_name(&val, &type_name);
return Ok(Box::new(crate::box_trait::BoolBox::new(matched)));
} else {
// asType: minimal safe cast (int<->float), otherwise identity
return Self::cast_to_type(val, &type_name);
}
}
// コンストラクタ内での親コンストラクタ呼び出しチェック
if let Some(context) = self.current_constructor_context.clone() {
if let Some(parent_class) = context.parent_class {
if name == parent_class {
// 親コンストラクタ呼び出し
return self.execute_parent_constructor(&parent_class, arguments);
}
}
}
// 🌍 GlobalBoxのメソッドとして実行
let global_box = self.shared.global_box.lock().unwrap();
let method_ast = global_box
.get_method(name)
.ok_or(RuntimeError::UndefinedFunction {
name: name.to_string(),
})?
.clone();
drop(global_box);
// メソッド呼び出しとして実行GlobalBoxインスタンス上で
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// パラメータ数チェック
if arg_values.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Function {} expects {} arguments, got {}",
name,
params.len(),
arg_values.len()
),
});
}
// 🌍 local変数スタックを保存・クリア関数呼び出し開始
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// パラメータをlocal変数として設定
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
// 関数本体を実行TaskGroupスコープをプッシュ
crate::runtime::global_hooks::push_task_scope();
let mut result: Box<dyn NyashBox> = Box::new(VoidBox::new());
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(return_val) = &self.control_flow {
if std::env::var("NYASH_INT_RET_TRACE").ok().as_deref() == Some("1") {
let ty = return_val.type_name();
let sv = return_val.to_string_box().value;
eprintln!("[INT-RET] epilogue capture: type={} value={}", ty, sv);
}
result = return_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// 🌍 local変数スタックを復元関数呼び出し終了
crate::runtime::global_hooks::pop_task_scope();
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Function '{}' is not a valid function declaration", name),
})
}
}
/// 関数宣言を登録 - 🌍 革命的実装GlobalBoxのメソッドとして登録
pub(super) fn register_function_declaration(
&mut self,
name: String,
params: Vec<String>,
body: Vec<ASTNode>,
) {
// 🌍 GlobalBoxのメソッドとして登録
let func_ast = ASTNode::FunctionDeclaration {
name: name.clone(),
params,
body,
is_static: false, // 通常の関数は静的でない
is_override: false, // 🔥 通常の関数はオーバーライドでない
span: crate::ast::Span::unknown(), // デフォルトspan
};
self.register_global_function(name, func_ast)
.unwrap_or_else(|err| {
eprintln!("Warning: Failed to register global function: {}", err);
});
}
/// Helper: match a NyashBox value against a simple type name
fn matches_type_name(val: &Box<dyn NyashBox>, type_name: &str) -> bool {
let tn = val.type_name();
match type_name {
"Integer" | "Int" | "I64" => tn == "IntegerBox",
"Float" | "F64" => tn == "FloatBox",
"Bool" | "Boolean" => tn == "BoolBox",
"String" => tn == "StringBox",
"Void" | "Unit" => tn == "VoidBox",
other => tn == other || tn == format!("{}Box", other),
}
}
/// Helper: cast box to a target type name (minimal support)
fn cast_to_type(
val: Box<dyn NyashBox>,
type_name: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match type_name {
"Integer" | "Int" | "I64" => {
// Float -> Integer (truncate), Integer -> Integer, else error
if let Some(i) = val.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
Ok(Box::new(crate::box_trait::IntegerBox::new(i.value)))
} else if let Some(f) = val.as_any().downcast_ref::<crate::boxes::FloatBox>() {
Ok(Box::new(crate::box_trait::IntegerBox::new(f.value as i64)))
} else {
Ok(val) // identity fallback for now
}
}
"Float" | "F64" => {
if let Some(f) = val.as_any().downcast_ref::<crate::boxes::FloatBox>() {
Ok(Box::new(crate::boxes::FloatBox::new(f.value)))
} else if let Some(i) = val.as_any().downcast_ref::<crate::box_trait::IntegerBox>()
{
Ok(Box::new(crate::boxes::FloatBox::new(i.value as f64)))
} else {
Ok(val)
}
}
_ => Ok(val),
}
}
}

View File

@ -1,339 +0,0 @@
/*!
* I/O Processing Module
*
* Extracted from core.rs - file operations and communication
* Handles include system, arrow operators, and I/O-related operations
* Core philosophy: "Everything is Box" with secure I/O processing
*/
use super::*;
use crate::parser::NyashParser;
impl NyashInterpreter {
/// Resolve include path using nyash.toml [include.roots]
fn resolve_include_path(&self, filename: &str, caller_dir: Option<&str>) -> String {
// If explicit relative path, resolve relative to caller when provided
if filename.starts_with("./") || filename.starts_with("../") {
return filename.to_string();
}
// Try nyash.toml roots: key/path where key is first segment before '/'
let parts: Vec<&str> = filename.splitn(2, '/').collect();
if parts.len() == 2 {
let root = parts[0];
let rest = parts[1];
let cfg_path = "nyash.toml";
if let Ok(toml_str) = std::fs::read_to_string(cfg_path) {
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
if let Some(include) = toml_val.get("include") {
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
if let Some(root_path_val) = roots.get(root).and_then(|v| v.as_str()) {
let mut base = root_path_val.to_string();
if !base.ends_with('/') && !base.ends_with('\\') {
base.push('/');
}
let joined = format!("{}{}", base, rest);
return joined;
}
}
}
}
}
}
// Fallback: if caller_dir provided, join relative
if let Some(dir) = caller_dir {
if !filename.starts_with('/') && !filename.contains(":\\") && !filename.contains(":/") {
return format!("{}/{}", dir.trim_end_matches('/'), filename);
}
}
// Default to ./filename
format!("./{}", filename)
}
/// include文を実行ファイル読み込み・パース・実行 - File inclusion system
pub(super) fn execute_include(&mut self, filename: &str) -> Result<(), RuntimeError> {
// パス解決nyash.toml include.roots + 相対)
let mut canonical_path = self.resolve_include_path(filename, None);
// 拡張子補完・index対応
if std::path::Path::new(&canonical_path).is_dir() {
let idx = format!("{}/index.nyash", canonical_path.trim_end_matches('/'));
canonical_path = idx;
} else if std::path::Path::new(&canonical_path).extension().is_none() {
canonical_path.push_str(".nyash");
}
// 循環検出(ロード中スタック)
{
let mut stack = self.shared.include_stack.lock().unwrap();
if let Some(pos) = stack.iter().position(|p| p == &canonical_path) {
// 検出: A -> ... -> B -> A
let mut chain: Vec<String> = stack[pos..].to_vec();
chain.push(canonical_path.clone());
let msg = format!("include cycle detected: {}", chain.join(" -> "));
return Err(RuntimeError::InvalidOperation { message: msg });
}
stack.push(canonical_path.clone());
}
// 重複読み込みチェック
if self
.shared
.included_files
.lock()
.unwrap()
.contains(&canonical_path)
{
// スタックから外して早期終了
self.shared.include_stack.lock().unwrap().pop();
return Ok(()); // 既に読み込み済み
}
// ファイル読み込み
let content = std::fs::read_to_string(&canonical_path).map_err(|e| {
RuntimeError::InvalidOperation {
message: format!("Failed to read file '{}': {}", filename, e),
}
})?;
// パース
let ast = NyashParser::parse_from_string(&content).map_err(|e| {
RuntimeError::InvalidOperation {
message: format!("Parse error in '{}': {:?}", filename, e),
}
})?;
// 重複防止リストに追加
self.shared
.included_files
.lock()
.unwrap()
.insert(canonical_path.clone());
// 現在の環境で実行
let exec_res = self.execute(ast);
// スタックを外す
self.shared.include_stack.lock().unwrap().pop();
// 実行結果を伝播
exec_res?;
Ok(())
}
/// include式を実行ファイルを評価し、最初のstatic boxを返す
pub(super) fn execute_include_expr(
&mut self,
filename: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// パス解決nyash.toml include.roots + 相対)
let mut canonical_path = self.resolve_include_path(filename, None);
// 拡張子補完・index対応
if std::path::Path::new(&canonical_path).is_dir() {
let idx = format!("{}/index.nyash", canonical_path.trim_end_matches('/'));
canonical_path = idx;
} else if std::path::Path::new(&canonical_path).extension().is_none() {
canonical_path.push_str(".nyash");
}
// 循環検出(ロード中スタック)
{
let mut stack = self.shared.include_stack.lock().unwrap();
if let Some(pos) = stack.iter().position(|p| p == &canonical_path) {
let mut chain: Vec<String> = stack[pos..].to_vec();
chain.push(canonical_path.clone());
let msg = format!("include cycle detected: {}", chain.join(" -> "));
return Err(RuntimeError::InvalidOperation { message: msg });
}
stack.push(canonical_path.clone());
}
// ファイル読み込みstatic box名検出用
let content = std::fs::read_to_string(&canonical_path).map_err(|e| {
RuntimeError::InvalidOperation {
message: format!("Failed to read file '{}': {}", filename, e),
}
})?;
// パースして最初のstatic box名を特定
let ast = NyashParser::parse_from_string(&content).map_err(|e| {
RuntimeError::InvalidOperation {
message: format!("Parse error in '{}': {:?}", filename, e),
}
})?;
let mut static_names: Vec<String> = Vec::new();
if let crate::ast::ASTNode::Program { statements, .. } = &ast {
for st in statements {
if let crate::ast::ASTNode::BoxDeclaration {
name, is_static, ..
} = st
{
if *is_static {
static_names.push(name.clone());
}
}
}
}
if static_names.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("include target '{}' does not define a static box", filename),
});
}
if static_names.len() > 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"include target '{}' defines multiple static boxes; exactly one is required",
filename
),
});
}
let box_name = static_names.remove(0);
// まだ未読なら評価(重複読み込みはスキップ)
let already = {
let set = self.shared.included_files.lock().unwrap();
set.contains(&canonical_path)
};
if !already {
self.shared
.included_files
.lock()
.unwrap()
.insert(canonical_path.clone());
let exec_res = self.execute(ast);
// スタックを外す
self.shared.include_stack.lock().unwrap().pop();
exec_res?;
} else {
// スタックを外す(既に読み込み済みのため)
self.shared.include_stack.lock().unwrap().pop();
}
// static boxを初期化・取得して返す
self.ensure_static_box_initialized(&box_name)?;
// statics名前空間からインスタンスを取り出す
let global_box =
self.shared
.global_box
.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string(),
})?;
let statics = global_box
.get_field("statics")
.ok_or(RuntimeError::TypeError {
message: "statics namespace not found in GlobalBox".to_string(),
})?;
let statics_inst = statics
.as_any()
.downcast_ref::<crate::instance_v2::InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "statics field is not an InstanceBox".to_string(),
})?;
let value = statics_inst
.get_field(&box_name)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Static box '{}' not found after include", box_name),
})?;
Ok((*value).clone_or_share())
}
/// Arrow演算子を実行: sender >> receiver - Channel communication
pub(super) fn execute_arrow(
&mut self,
sender: &ASTNode,
receiver: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 送信者を評価
let sender_value = self.execute_expression(sender)?;
// 受信者を評価
let receiver_str = match receiver {
ASTNode::Variable { name, .. } => name.clone(),
ASTNode::Literal { value, .. } => {
// "*" のようなリテラルの場合
value.to_string()
}
_ => {
// その他の式の場合は評価して文字列化
let receiver_value = self.execute_expression(receiver)?;
receiver_value.to_string_box().value
}
};
// 送信者の名前を取得
let sender_name = sender_value.to_string_box().value;
// ChannelBoxを作成して返す
let channel_box =
Box::new(ChannelBox::new(&sender_name, &receiver_str)) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
Ok(channel_box)
}
/// nowait文を実行 - 非同期実行(真の非同期実装) - Async execution
pub(super) fn execute_nowait(
&mut self,
variable: &str,
expression: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
use crate::boxes::FutureBox;
// FutureBoxを作成
let future_box = FutureBox::new();
// 個別のクローンを用意(スケジュール経路とフォールバック経路で別々に使う)
let future_for_sched = future_box.clone();
let future_for_thread = future_box.clone();
// 式をクローンしてスケジューラ(なければフォールバック)で実行
// それぞれの経路で独立に所有させるためクローンを分けておく
let expr_for_sched = expression.clone();
let expr_for_thread = expression.clone();
let shared_for_sched = self.shared.clone();
let shared_for_thread = self.shared.clone();
// Phase-2: try scheduler first (bound to current TaskGroup token); fallback to thread
let token = crate::runtime::global_hooks::current_group_token();
let scheduled = crate::runtime::global_hooks::spawn_task_with_token(
"nowait",
token,
Box::new(move || {
// 新しいインタープリタインスタンスを作成SharedStateを使用
let mut async_interpreter = NyashInterpreter::with_shared(shared_for_sched);
// 式を評価
match async_interpreter.execute_expression(&expr_for_sched) {
Ok(result) => {
future_for_sched.set_result(result);
}
Err(e) => {
// エラーをErrorBoxとして設定
let error_box =
Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e)));
future_for_sched.set_result(error_box);
}
}
}),
);
if !scheduled {
std::thread::spawn(move || {
let mut async_interpreter = NyashInterpreter::with_shared(shared_for_thread);
match async_interpreter.execute_expression(&expr_for_thread) {
Ok(result) => {
future_for_thread.set_result(result);
}
Err(e) => {
let error_box =
Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e)));
future_for_thread.set_result(error_box);
}
}
});
}
// FutureBoxを現在のTaskGroupに登録暗黙グループ best-effort
crate::runtime::global_hooks::register_future_to_current_group(&future_box);
// FutureBoxを変数に保存
let future_box_instance = Box::new(future_box) as Box<dyn NyashBox>;
self.set_variable(variable, future_box_instance)?;
Ok(Box::new(VoidBox::new()))
}
}

View File

@ -1,287 +0,0 @@
/*!
* Math and Random Box Method Handlers Module
*
* Extracted from box_methods.rs lines 148-632
* Contains mathematical computation and random number generation method implementations:
*
* MathBox methods:
* - abs, max, min, pow, sqrt - Basic mathematical operations
* - sin, cos, tan - Trigonometric functions
* - log, log10, exp - Logarithmic and exponential functions
* - floor, ceil, round - Rounding operations
* - getPi, getE - Mathematical constants
*
* RandomBox methods:
* - seed, random, randInt, randBool - Basic random generation
* - choice, shuffle, randString - Advanced random operations
* - probability - Probability-based operations
*
* All methods include comprehensive argument validation and error handling.
*/
use super::*;
impl NyashInterpreter {
/// MathBoxのメソッド呼び出しを実行
/// 包括的な数学計算機能を提供
pub(super) fn execute_math_method(
&mut self,
math_box: &MathBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
// 基本数学演算
"abs" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("abs() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.abs(arg_values[0].clone_box()))
}
"max" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("max() expects 2 arguments, got {}", arg_values.len()),
});
}
Ok(math_box.max(arg_values[0].clone_box(), arg_values[1].clone_box()))
}
"min" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("min() expects 2 arguments, got {}", arg_values.len()),
});
}
Ok(math_box.min(arg_values[0].clone_box(), arg_values[1].clone_box()))
}
"pow" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("pow() expects 2 arguments, got {}", arg_values.len()),
});
}
Ok(math_box.pow(arg_values[0].clone_box(), arg_values[1].clone_box()))
}
"sqrt" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("sqrt() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.sqrt(arg_values[0].clone_box()))
}
// 数学定数
"getPi" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getPi() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(math_box.getPi())
}
"getE" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getE() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(math_box.getE())
}
// 三角関数
"sin" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("sin() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.sin(arg_values[0].clone_box()))
}
"cos" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("cos() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.cos(arg_values[0].clone_box()))
}
"tan" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("tan() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.tan(arg_values[0].clone_box()))
}
// 対数・指数関数
"log" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("log() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.log(arg_values[0].clone_box()))
}
"log10" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("log10() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.log10(arg_values[0].clone_box()))
}
"exp" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("exp() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.exp(arg_values[0].clone_box()))
}
// 丸め関数
"floor" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("floor() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.floor(arg_values[0].clone_box()))
}
"ceil" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("ceil() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.ceil(arg_values[0].clone_box()))
}
"round" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("round() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(math_box.round(arg_values[0].clone_box()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown MathBox method: {}", method),
}),
}
}
/// RandomBoxのメソッド呼び出しを実行
/// 乱数生成と確率的操作を提供
pub(super) fn execute_random_method(
&mut self,
random_box: &RandomBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
// 乱数シード設定
"seed" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("seed() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(random_box.seed(arg_values[0].clone_box()))
}
// 基本乱数生成
"random" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("random() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(random_box.random())
}
"randInt" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("randInt() expects 2 arguments, got {}", arg_values.len()),
});
}
Ok(random_box.randInt(arg_values[0].clone_box(), arg_values[1].clone_box()))
}
"randBool" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"randBool() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(random_box.randBool())
}
// 配列・コレクション操作
"choice" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("choice() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(random_box.choice(arg_values[0].clone_box()))
}
"shuffle" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("shuffle() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(random_box.shuffle(arg_values[0].clone_box()))
}
// 文字列・確率操作
"randString" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"randString() expects 1 argument, got {}",
arg_values.len()
),
});
}
Ok(random_box.randString(arg_values[0].clone_box()))
}
"probability" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"probability() expects 1 argument, got {}",
arg_values.len()
),
});
}
Ok(random_box.probability(arg_values[0].clone_box()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown RandomBox method: {}", method),
}),
}
}
}

View File

@ -1,582 +0,0 @@
/*!
* Basic Box Methods Module
*
* Extracted from box_methods.rs
* Contains method implementations for:
* - StringBox (execute_string_method)
* - IntegerBox (execute_integer_method)
* - BoolBox (execute_bool_method)
* - FloatBox (execute_float_method)
*/
use super::super::*;
use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox};
use crate::boxes::FloatBox;
impl NyashInterpreter {
/// StringBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_string_method(
&mut self,
string_box: &StringBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"split" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("split() expects 1 argument, got {}", arguments.len()),
});
}
let delimiter_value = self.execute_expression(&arguments[0])?;
if let Some(delimiter_str) = delimiter_value.as_any().downcast_ref::<StringBox>() {
Ok(string_box.split(&delimiter_str.value))
} else {
Err(RuntimeError::TypeError {
message: "split() requires string delimiter".to_string(),
})
}
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
// StringBoxは自分自身を返す
Ok(Box::new(string_box.clone()))
}
"length" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("length() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(string_box.length())
}
"get" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 1 argument, got {}", arguments.len()),
});
}
let index_value = self.execute_expression(&arguments[0])?;
if let Some(index_int) = index_value.as_any().downcast_ref::<IntegerBox>() {
match string_box.get(index_int.value as usize) {
Some(char_box) => Ok(char_box),
None => Ok(Box::new(VoidBox::new())),
}
} else {
Err(RuntimeError::TypeError {
message: "get() requires integer index".to_string(),
})
}
}
"find" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("find() expects 1 argument, got {}", arguments.len()),
});
}
let search_value = self.execute_expression(&arguments[0])?;
if let Some(search_str) = search_value.as_any().downcast_ref::<StringBox>() {
Ok(string_box.find(&search_str.value))
} else {
Err(RuntimeError::TypeError {
message: "find() requires string argument".to_string(),
})
}
}
"replace" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("replace() expects 2 arguments, got {}", arguments.len()),
});
}
let old_value = self.execute_expression(&arguments[0])?;
let new_value = self.execute_expression(&arguments[1])?;
if let (Some(old_str), Some(new_str)) = (
old_value.as_any().downcast_ref::<StringBox>(),
new_value.as_any().downcast_ref::<StringBox>(),
) {
Ok(string_box.replace(&old_str.value, &new_str.value))
} else {
Err(RuntimeError::TypeError {
message: "replace() requires string arguments".to_string(),
})
}
}
"trim" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("trim() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(string_box.trim())
}
"toUpper" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toUpper() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(string_box.to_upper())
}
"toLower" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toLower() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(string_box.to_lower())
}
"toInteger" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toInteger() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(string_box.to_integer())
}
"substring" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"substring() expects 2 arguments, got {}",
arguments.len()
),
});
}
let start = self.execute_expression(&arguments[0])?;
let end = self.execute_expression(&arguments[1])?;
// Convert arguments to integers
let start_int = if let Some(int_box) = start.as_any().downcast_ref::<IntegerBox>() {
int_box.value as usize
} else {
return Err(RuntimeError::TypeError {
message: "substring() expects integer arguments".to_string(),
});
};
let end_int = if let Some(int_box) = end.as_any().downcast_ref::<IntegerBox>() {
int_box.value as usize
} else {
return Err(RuntimeError::TypeError {
message: "substring() expects integer arguments".to_string(),
});
};
Ok(string_box.substring(start_int, end_int))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for StringBox", method),
}),
}
}
/// IntegerBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_integer_method(
&mut self,
integer_box: &IntegerBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(StringBox::new(integer_box.value.to_string())))
}
"abs" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("abs() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(IntegerBox::new(integer_box.value.abs())))
}
"max" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("max() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
if let Some(other_int) = other_value.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(IntegerBox::new(
integer_box.value.max(other_int.value),
)))
} else {
Err(RuntimeError::TypeError {
message: "max() requires integer argument".to_string(),
})
}
}
"min" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("min() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
if let Some(other_int) = other_value.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(IntegerBox::new(
integer_box.value.min(other_int.value),
)))
} else {
Err(RuntimeError::TypeError {
message: "min() requires integer argument".to_string(),
})
}
}
"toFloat" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toFloat() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(FloatBox::new(integer_box.value as f64)))
}
"pow" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("pow() expects 1 argument, got {}", arguments.len()),
});
}
let exponent_value = self.execute_expression(&arguments[0])?;
if let Some(exponent_int) = exponent_value.as_any().downcast_ref::<IntegerBox>() {
if exponent_int.value >= 0 {
let result = (integer_box.value as f64).powf(exponent_int.value as f64);
Ok(Box::new(FloatBox::new(result)))
} else {
let result = (integer_box.value as f64).powf(exponent_int.value as f64);
Ok(Box::new(FloatBox::new(result)))
}
} else {
Err(RuntimeError::TypeError {
message: "pow() requires integer exponent".to_string(),
})
}
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for IntegerBox", method),
}),
}
}
/// BoolBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_bool_method(
&mut self,
bool_box: &BoolBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(bool_box.to_string_box()))
}
"not" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("not() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(BoolBox::new(!bool_box.value)))
}
"and" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("and() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
if let Some(other_bool) = other_value.as_any().downcast_ref::<BoolBox>() {
Ok(Box::new(BoolBox::new(bool_box.value && other_bool.value)))
} else {
// Support truthiness evaluation for non-boolean types
let is_truthy = self.is_truthy(&other_value);
Ok(Box::new(BoolBox::new(bool_box.value && is_truthy)))
}
}
"or" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("or() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
if let Some(other_bool) = other_value.as_any().downcast_ref::<BoolBox>() {
Ok(Box::new(BoolBox::new(bool_box.value || other_bool.value)))
} else {
// Support truthiness evaluation for non-boolean types
let is_truthy = self.is_truthy(&other_value);
Ok(Box::new(BoolBox::new(bool_box.value || is_truthy)))
}
}
"equals" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("equals() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
Ok(Box::new(bool_box.equals(&*other_value)))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for BoolBox", method),
}),
}
}
/// FloatBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_float_method(
&mut self,
float_box: &FloatBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(StringBox::new(float_box.value.to_string())))
}
"abs" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("abs() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(FloatBox::new(float_box.value.abs())))
}
"floor" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("floor() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(IntegerBox::new(float_box.value.floor() as i64)))
}
"ceil" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ceil() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(IntegerBox::new(float_box.value.ceil() as i64)))
}
"round" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("round() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(IntegerBox::new(float_box.value.round() as i64)))
}
"toInteger" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toInteger() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(Box::new(IntegerBox::new(float_box.value as i64)))
}
"max" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("max() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
if let Some(other_float) = other_value.as_any().downcast_ref::<FloatBox>() {
Ok(Box::new(FloatBox::new(
float_box.value.max(other_float.value),
)))
} else if let Some(other_int) = other_value.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(FloatBox::new(
float_box.value.max(other_int.value as f64),
)))
} else {
Err(RuntimeError::TypeError {
message: "max() requires numeric argument".to_string(),
})
}
}
"min" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("min() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
if let Some(other_float) = other_value.as_any().downcast_ref::<FloatBox>() {
Ok(Box::new(FloatBox::new(
float_box.value.min(other_float.value),
)))
} else if let Some(other_int) = other_value.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(FloatBox::new(
float_box.value.min(other_int.value as f64),
)))
} else {
Err(RuntimeError::TypeError {
message: "min() requires numeric argument".to_string(),
})
}
}
"pow" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("pow() expects 1 argument, got {}", arguments.len()),
});
}
let exponent_value = self.execute_expression(&arguments[0])?;
if let Some(exponent_float) = exponent_value.as_any().downcast_ref::<FloatBox>() {
Ok(Box::new(FloatBox::new(
float_box.value.powf(exponent_float.value),
)))
} else if let Some(exponent_int) =
exponent_value.as_any().downcast_ref::<IntegerBox>()
{
Ok(Box::new(FloatBox::new(
float_box.value.powf(exponent_int.value as f64),
)))
} else {
Err(RuntimeError::TypeError {
message: "pow() requires numeric exponent".to_string(),
})
}
}
"sqrt" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("sqrt() expects 0 arguments, got {}", arguments.len()),
});
}
if float_box.value < 0.0 {
Err(RuntimeError::InvalidOperation {
message: "Cannot take square root of negative number".to_string(),
})
} else {
Ok(Box::new(FloatBox::new(float_box.value.sqrt())))
}
}
"sin" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("sin() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(FloatBox::new(float_box.value.sin())))
}
"cos" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("cos() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(FloatBox::new(float_box.value.cos())))
}
"tan" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("tan() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(FloatBox::new(float_box.value.tan())))
}
"log" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("log() expects 0 arguments, got {}", arguments.len()),
});
}
if float_box.value <= 0.0 {
Err(RuntimeError::InvalidOperation {
message: "Cannot take logarithm of non-positive number".to_string(),
})
} else {
Ok(Box::new(FloatBox::new(float_box.value.ln())))
}
}
"log10" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("log10() expects 0 arguments, got {}", arguments.len()),
});
}
if float_box.value <= 0.0 {
Err(RuntimeError::InvalidOperation {
message: "Cannot take logarithm of non-positive number".to_string(),
})
} else {
Ok(Box::new(FloatBox::new(float_box.value.log10())))
}
}
"exp" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("exp() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(FloatBox::new(float_box.value.exp())))
}
"isNaN" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isNaN() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(BoolBox::new(float_box.value.is_nan())))
}
"isInfinite" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"isInfinite() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(Box::new(BoolBox::new(float_box.value.is_infinite())))
}
"isFinite" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isFinite() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(BoolBox::new(float_box.value.is_finite())))
}
"equals" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("equals() expects 1 argument, got {}", arguments.len()),
});
}
let other_value = self.execute_expression(&arguments[0])?;
Ok(Box::new(float_box.equals(&*other_value)))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for FloatBox", method),
}),
}
}
}

View File

@ -1,325 +0,0 @@
/*!
* Collection Methods Module
*
* Extracted from box_methods.rs
* Contains method implementations for collection types:
* - ArrayBox (execute_array_method)
* - MapBox (execute_map_method)
*/
use super::super::*;
use crate::box_trait::{BoolBox, IntegerBox, NyashBox};
use crate::boxes::{ArrayBox, MapBox};
impl NyashInterpreter {
/// ArrayBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_array_method(
&mut self,
array_box: &ArrayBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"of" => {
// Build a new ArrayBox from provided arguments
let mut elems: Vec<Box<dyn NyashBox>> = Vec::with_capacity(arguments.len());
for arg in arguments {
let v = self.execute_expression(arg)?;
elems.push(v);
}
let arr = ArrayBox::new_with_elements(elems);
return Ok(Box::new(arr));
}
"push" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("push() expects 1 argument, got {}", arguments.len()),
});
}
let element = self.execute_expression(&arguments[0])?;
Ok(array_box.push(element))
}
"pop" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("pop() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(array_box.pop())
}
"length" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("length() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(array_box.length())
}
"get" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 1 argument, got {}", arguments.len()),
});
}
let index_value = self.execute_expression(&arguments[0])?;
Ok(array_box.get(index_value))
}
"set" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("set() expects 2 arguments, got {}", arguments.len()),
});
}
let index_value = self.execute_expression(&arguments[0])?;
let element_value = self.execute_expression(&arguments[1])?;
Ok(array_box.set(index_value, element_value))
}
"remove" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("remove() expects 1 argument, got {}", arguments.len()),
});
}
let index_value = self.execute_expression(&arguments[0])?;
Ok(array_box.remove(index_value))
}
"indexOf" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("indexOf() expects 1 argument, got {}", arguments.len()),
});
}
let element = self.execute_expression(&arguments[0])?;
Ok(array_box.indexOf(element))
}
"contains" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("contains() expects 1 argument, got {}", arguments.len()),
});
}
let element = self.execute_expression(&arguments[0])?;
Ok(array_box.contains(element))
}
"clear" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(array_box.clear())
}
"join" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("join() expects 1 argument, got {}", arguments.len()),
});
}
let delimiter_value = self.execute_expression(&arguments[0])?;
Ok(array_box.join(delimiter_value))
}
"isEmpty" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isEmpty() expects 0 arguments, got {}", arguments.len()),
});
}
let length = array_box.length();
if let Some(int_box) = length.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(BoolBox::new(int_box.value == 0)))
} else {
Ok(Box::new(BoolBox::new(false)))
}
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(array_box.to_string_box()))
}
"sort" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("sort() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(array_box.sort())
}
"reverse" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("reverse() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(array_box.reverse())
}
"slice" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"slice() expects 2 arguments (start, end), got {}",
arguments.len()
),
});
}
let start_value = self.execute_expression(&arguments[0])?;
let end_value = self.execute_expression(&arguments[1])?;
Ok(array_box.slice(start_value, end_value))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for ArrayBox", method),
}),
}
}
/// MapBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_map_method(
&mut self,
map_box: &MapBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// メソッドを実行(必要時評価方式)
match method {
"set" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("set() expects 2 arguments, got {}", arguments.len()),
});
}
let key_value = self.execute_expression(&arguments[0])?;
let value_value = self.execute_expression(&arguments[1])?;
Ok(map_box.set(key_value, value_value))
}
"get" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 1 argument, got {}", arguments.len()),
});
}
let key_value = self.execute_expression(&arguments[0])?;
Ok(map_box.get(key_value))
}
"has" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("has() expects 1 argument, got {}", arguments.len()),
});
}
let key_value = self.execute_expression(&arguments[0])?;
Ok(map_box.has(key_value))
}
"delete" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("delete() expects 1 argument, got {}", arguments.len()),
});
}
let key_value = self.execute_expression(&arguments[0])?;
Ok(map_box.delete(key_value))
}
"keys" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("keys() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(map_box.keys())
}
"values" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("values() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(map_box.values())
}
"size" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("size() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(map_box.size())
}
"clear" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(map_box.clear())
}
"isEmpty" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isEmpty() expects 0 arguments, got {}", arguments.len()),
});
}
let size = map_box.size();
if let Some(int_box) = size.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(BoolBox::new(int_box.value == 0)))
} else {
Ok(Box::new(BoolBox::new(false)))
}
}
"containsKey" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"containsKey() expects 1 argument, got {}",
arguments.len()
),
});
}
let key_value = self.execute_expression(&arguments[0])?;
Ok(map_box.has(key_value))
}
"containsValue" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"containsValue() expects 1 argument, got {}",
arguments.len()
),
});
}
let _value = self.execute_expression(&arguments[0])?;
// Simple implementation: check if any value equals the given value
Ok(Box::new(BoolBox::new(false))) // TODO: implement proper value search
}
"forEach" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("forEach() expects 1 argument, got {}", arguments.len()),
});
}
let callback_value = self.execute_expression(&arguments[0])?;
Ok(map_box.forEach(callback_value))
}
"toJSON" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toJSON() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(map_box.toJSON())
}
// Note: merge, filter, map methods not implemented in MapBox yet
// These would require more complex callback handling
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(map_box.to_string_box()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown MapBox method: {}", method),
}),
}
}
}

View File

@ -1,255 +0,0 @@
/*!
* Data Processing Box Methods Module
*
* Contains method implementations for data processing Box types:
* - BufferBox (execute_buffer_method) - Binary data operations
* - JSONBox (execute_json_method) - JSON parsing and manipulation
* - RegexBox (execute_regex_method) - Regular expression operations
*/
use super::super::*;
use crate::box_trait::NyashBox;
use crate::boxes::{buffer::BufferBox, JSONBox, RegexBox};
impl NyashInterpreter {
/// BufferBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_buffer_method(
&mut self,
buffer_box: &BufferBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"write" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("write() expects 1 argument, got {}", arguments.len()),
});
}
let data = self.execute_expression(&arguments[0])?;
Ok(buffer_box.write(data))
}
"readAll" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("readAll() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(buffer_box.readAll())
}
"read" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("read() expects 1 argument, got {}", arguments.len()),
});
}
let count = self.execute_expression(&arguments[0])?;
Ok(buffer_box.read(count))
}
"clear" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(buffer_box.clear())
}
"length" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("length() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(buffer_box.length())
}
"append" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("append() expects 1 argument, got {}", arguments.len()),
});
}
let other = self.execute_expression(&arguments[0])?;
Ok(buffer_box.append(other))
}
"slice" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("slice() expects 2 arguments, got {}", arguments.len()),
});
}
let start = self.execute_expression(&arguments[0])?;
let end = self.execute_expression(&arguments[1])?;
Ok(buffer_box.slice(start, end))
}
// ⭐ Phase 10: Zero-copy detection APIs
"is_shared_with" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"is_shared_with() expects 1 argument, got {}",
arguments.len()
),
});
}
let other = self.execute_expression(&arguments[0])?;
Ok(buffer_box.is_shared_with(other))
}
"share_reference" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"share_reference() expects 1 argument, got {}",
arguments.len()
),
});
}
let data = self.execute_expression(&arguments[0])?;
Ok(buffer_box.share_reference(data))
}
"memory_footprint" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"memory_footprint() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(buffer_box.memory_footprint())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for BufferBox", method),
}),
}
}
/// JSONBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_json_method(
&mut self,
json_box: &JSONBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"parse" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("parse() expects 1 argument, got {}", arguments.len()),
});
}
let data = self.execute_expression(&arguments[0])?;
Ok(JSONBox::parse(data))
}
"stringify" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"stringify() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(json_box.stringify())
}
"get" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 1 argument, got {}", arguments.len()),
});
}
let key = self.execute_expression(&arguments[0])?;
Ok(json_box.get(key))
}
"set" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("set() expects 2 arguments, got {}", arguments.len()),
});
}
let key = self.execute_expression(&arguments[0])?;
let value = self.execute_expression(&arguments[1])?;
Ok(json_box.set(key, value))
}
"has" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("has() expects 1 argument, got {}", arguments.len()),
});
}
let key = self.execute_expression(&arguments[0])?;
Ok(json_box.has(key))
}
"keys" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("keys() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(json_box.keys())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for JSONBox", method),
}),
}
}
/// RegexBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_regex_method(
&mut self,
regex_box: &RegexBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"test" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("test() expects 1 argument, got {}", arguments.len()),
});
}
let text = self.execute_expression(&arguments[0])?;
Ok(regex_box.test(text))
}
"find" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("find() expects 1 argument, got {}", arguments.len()),
});
}
let text = self.execute_expression(&arguments[0])?;
Ok(regex_box.find(text))
}
"findAll" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("findAll() expects 1 argument, got {}", arguments.len()),
});
}
let text = self.execute_expression(&arguments[0])?;
Ok(regex_box.find_all(text))
}
"replace" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("replace() expects 2 arguments, got {}", arguments.len()),
});
}
let text = self.execute_expression(&arguments[0])?;
let replacement = self.execute_expression(&arguments[1])?;
Ok(regex_box.replace(text, replacement))
}
"split" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("split() expects 1 argument, got {}", arguments.len()),
});
}
let text = self.execute_expression(&arguments[0])?;
Ok(regex_box.split(text))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for RegexBox", method),
}),
}
}
}

View File

@ -1,326 +0,0 @@
/*! 🌐 HTTP Method Implementations
*
* HTTP関連Boxのメソッド実行を実装
* SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox
*/
use super::super::*;
use crate::boxes::{HTTPRequestBox, HTTPResponseBox, HTTPServerBox, SocketBox};
impl NyashInterpreter {
/// SocketBox methods
pub(in crate::interpreter) fn execute_socket_method(
&mut self,
socket_box: &SocketBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"bind" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("bind() expects 2 arguments, got {}", arguments.len()),
});
}
let address = self.execute_expression(&arguments[0])?;
let port = self.execute_expression(&arguments[1])?;
let result = socket_box.bind(address, port);
Ok(result)
}
"listen" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("listen() expects 1 argument, got {}", arguments.len()),
});
}
let backlog = self.execute_expression(&arguments[0])?;
Ok(socket_box.listen(backlog))
}
"accept" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("accept() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(socket_box.accept())
}
"acceptTimeout" | "accept_timeout" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"acceptTimeout(ms) expects 1 argument, got {}",
arguments.len()
),
});
}
let ms = self.execute_expression(&arguments[0])?;
Ok(socket_box.accept_timeout(ms))
}
"connect" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("connect() expects 2 arguments, got {}", arguments.len()),
});
}
let address = self.execute_expression(&arguments[0])?;
let port = self.execute_expression(&arguments[1])?;
Ok(socket_box.connect(address, port))
}
"read" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("read() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(socket_box.read())
}
"recvTimeout" | "recv_timeout" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"recvTimeout(ms) expects 1 argument, got {}",
arguments.len()
),
});
}
let ms = self.execute_expression(&arguments[0])?;
Ok(socket_box.recv_timeout(ms))
}
"readHttpRequest" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"readHttpRequest() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(socket_box.read_http_request())
}
"write" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("write() expects 1 argument, got {}", arguments.len()),
});
}
let data = self.execute_expression(&arguments[0])?;
Ok(socket_box.write(data))
}
"close" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("close() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(socket_box.close())
}
"isConnected" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"isConnected() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(socket_box.is_connected())
}
"isServer" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isServer() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(socket_box.is_server())
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(socket_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
name: format!("SocketBox method '{}' not found", method),
}),
}
}
/// HTTPServerBox methods
pub(in crate::interpreter) fn execute_http_server_method(
&mut self,
server_box: &HTTPServerBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"bind" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("bind() expects 2 arguments, got {}", arguments.len()),
});
}
let address = self.execute_expression(&arguments[0])?;
let port = self.execute_expression(&arguments[1])?;
Ok(server_box.bind(address, port))
}
"listen" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("listen() expects 1 argument, got {}", arguments.len()),
});
}
let backlog = self.execute_expression(&arguments[0])?;
Ok(server_box.listen(backlog))
}
"start" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("start() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(server_box.start())
}
"stop" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("stop() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(server_box.stop())
}
"get" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 2 arguments, got {}", arguments.len()),
});
}
let path = self.execute_expression(&arguments[0])?;
let handler = self.execute_expression(&arguments[1])?;
Ok(server_box.get(path, handler))
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(server_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
name: format!("HTTPServerBox method '{}' not found", method),
}),
}
}
/// HTTPRequestBox methods
pub(in crate::interpreter) fn execute_http_request_method(
&mut self,
request_box: &HTTPRequestBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"getMethod" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"getMethod() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(request_box.get_method())
}
"getPath" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getPath() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(request_box.get_path())
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(request_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
name: format!("HTTPRequestBox method '{}' not found", method),
}),
}
}
/// HTTPResponseBox methods
pub(in crate::interpreter) fn execute_http_response_method(
&mut self,
response_box: &HTTPResponseBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"setStatus" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"setStatus() expects 2 arguments, got {}",
arguments.len()
),
});
}
let code = self.execute_expression(&arguments[0])?;
let message = self.execute_expression(&arguments[1])?;
Ok(response_box.set_status(code, message))
}
"toHttpString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toHttpString() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(response_box.to_http_string())
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(response_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
name: format!("HTTPResponseBox method '{}' not found", method),
}),
}
}
}

View File

@ -1,388 +0,0 @@
/*!
* I/O Operations Box Methods Module
*
* Extracted from box_methods.rs
* Contains method implementations for I/O and error handling operations:
* - FileBox (execute_file_method) - File I/O operations
* - ResultBox (execute_result_method) - Error handling and result operations
*/
use super::super::*;
use crate::box_trait::{NyashBox, StringBox};
use crate::boxes::ref_cell_box::RefCellBox;
use crate::boxes::FileBox;
use crate::boxes::ResultBox;
// use crate::bid::plugin_box::PluginFileBox; // legacy - FileBox専用
impl NyashInterpreter {
/// FileBoxのメソッド呼び出しを実行
/// Handles file I/O operations including read, write, exists, delete, and copy
pub(in crate::interpreter) fn execute_file_method(
&mut self,
file_box: &FileBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"read" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("read() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(file_box.read())
}
"write" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("write() expects 1 argument, got {}", arguments.len()),
});
}
let content = self.execute_expression(&arguments[0])?;
Ok(file_box.write(content))
}
"exists" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("exists() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(file_box.exists())
}
"delete" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("delete() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(file_box.delete())
}
"copy" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("copy() expects 1 argument, got {}", arguments.len()),
});
}
let dest_value = self.execute_expression(&arguments[0])?;
if let Some(dest_str) = dest_value.as_any().downcast_ref::<StringBox>() {
Ok(file_box.copy(&dest_str.value))
} else {
Err(RuntimeError::TypeError {
message: "copy() requires string destination path".to_string(),
})
}
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for FileBox", method),
}),
}
}
/// ResultBoxのメソッド呼び出しを実行
/// Handles result/error checking operations for error handling patterns
pub(in crate::interpreter) fn execute_result_method(
&mut self,
result_box: &ResultBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"isOk" | "is_ok" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isOk() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(result_box.is_ok())
}
"getValue" | "get_value" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getValue() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(result_box.get_value())
}
"getError" | "get_error" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getError() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(result_box.get_error())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for ResultBox", method),
}),
}
}
/// RefCellBox のメソッド: get()/set(value)
pub(in crate::interpreter) fn execute_refcell_method(
&mut self,
cell: &RefCellBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"get" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(cell.borrow())
}
"set" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("set() expects 1 argument, got {}", arguments.len()),
});
}
let v = self.execute_expression(&arguments[0])?;
cell.replace(v);
Ok(Box::new(crate::box_trait::VoidBox::new()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for RefCellBox", method),
}),
}
}
/* legacy - PluginFileBox専用
/// 汎用プラグインメソッド呼び出し実行 (BID-FFI system)
/// Handles generic plugin method calls via dynamic method discovery
pub(in crate::interpreter) fn execute_plugin_method_generic(&mut self, plugin_box: &PluginFileBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
eprintln!("🔍 execute_plugin_method_generic: method='{}', args_count={}", method, arguments.len());
// まず利用可能なメソッドを確認
match plugin_box.get_available_methods() {
Ok(methods) => {
eprintln!("🔍 Available plugin methods:");
for (id, name, sig) in &methods {
eprintln!("🔍 - {} [ID: {}, Sig: 0x{:08X}]", name, id, sig);
}
}
Err(e) => eprintln!("⚠️ Failed to get plugin methods: {:?}", e),
}
// 引数をTLVエンコードメソッド名も渡す
let encoded_args = self.encode_arguments_to_tlv(arguments, method)?;
eprintln!("🔍 Encoded args length: {} bytes", encoded_args.len());
// プラグインのメソッドを動的呼び出し
match plugin_box.call_method(method, &encoded_args) {
Ok(response_bytes) => {
eprintln!("🔍 Plugin method '{}' succeeded, response length: {} bytes", method, response_bytes.len());
// レスポンスをデコードしてNyashBoxに変換
self.decode_tlv_to_nyash_box(&response_bytes, method)
}
Err(e) => {
eprintln!("🔍 Plugin method '{}' failed with error: {:?}", method, e);
Err(RuntimeError::InvalidOperation {
message: format!("Plugin method '{}' failed: {:?}", method, e),
})
}
}
}
/// 引数をTLVエンコード型情報に基づく美しい実装
fn encode_arguments_to_tlv(&mut self, arguments: &[ASTNode], method_name: &str) -> Result<Vec<u8>, RuntimeError> {
use crate::bid::tlv::TlvEncoder;
use crate::bid::registry;
let mut encoder = TlvEncoder::new();
// 型情報を取得FileBoxのみ対応、後で拡張
let type_info = registry::global()
.and_then(|reg| reg.get_method_type_info("FileBox", method_name));
// 型情報がある場合は、それに従って変換
if let Some(type_info) = type_info {
eprintln!("✨ Using type info for method '{}'", method_name);
// 引数の数をチェック
if arguments.len() != type_info.args.len() {
return Err(RuntimeError::InvalidOperation {
message: format!("{} expects {} arguments, got {}",
method_name, type_info.args.len(), arguments.len()),
});
}
// 各引数を型情報に従ってエンコード
for (i, (arg, mapping)) in arguments.iter().zip(&type_info.args).enumerate() {
eprintln!(" 🔄 Arg[{}]: {} -> {} conversion", i, mapping.from, mapping.to);
let value = self.execute_expression(arg)?;
self.encode_value_with_mapping(&mut encoder, value, mapping)?;
}
} else {
// 型情報がない場合は、従来のデフォルト動作
eprintln!("⚠️ No type info for method '{}', using default encoding", method_name);
for arg in arguments {
let value = self.execute_expression(arg)?;
self.encode_value_default(&mut encoder, value)?;
}
}
Ok(encoder.finish())
}
/// 型マッピングに基づいて値をエンコード(美しい!)
fn encode_value_with_mapping(
&self,
encoder: &mut crate::bid::tlv::TlvEncoder,
value: Box<dyn NyashBox>,
mapping: &crate::bid::ArgTypeMapping
) -> Result<(), RuntimeError> {
// determine_bid_tag()を使って適切なタグを決定
let tag = mapping.determine_bid_tag()
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Unsupported type mapping: {} -> {}", mapping.from, mapping.to),
})?;
// タグに応じてエンコード
match tag {
crate::bid::BidTag::String => {
let str_val = value.to_string_box().value;
encoder.encode_string(&str_val)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV string encoding failed: {:?}", e),
})
}
crate::bid::BidTag::Bytes => {
let str_val = value.to_string_box().value;
encoder.encode_bytes(str_val.as_bytes())
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV bytes encoding failed: {:?}", e),
})
}
crate::bid::BidTag::I32 => {
if let Some(int_box) = value.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
encoder.encode_i32(int_box.value as i32)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV i32 encoding failed: {:?}", e),
})
} else {
Err(RuntimeError::TypeError {
message: format!("Expected integer for {} -> i32 conversion", mapping.from),
})
}
}
crate::bid::BidTag::Bool => {
if let Some(bool_box) = value.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
encoder.encode_bool(bool_box.value)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV bool encoding failed: {:?}", e),
})
} else {
Err(RuntimeError::TypeError {
message: format!("Expected bool for {} -> bool conversion", mapping.from),
})
}
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unsupported BID tag: {:?}", tag),
})
}
}
/// デフォルトエンコード(型情報がない場合のフォールバック)
fn encode_value_default(
&self,
encoder: &mut crate::bid::tlv::TlvEncoder,
value: Box<dyn NyashBox>
) -> Result<(), RuntimeError> {
if let Some(str_box) = value.as_any().downcast_ref::<StringBox>() {
encoder.encode_bytes(str_box.value.as_bytes())
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV bytes encoding failed: {:?}", e),
})
} else if let Some(int_box) = value.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
encoder.encode_i32(int_box.value as i32)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV integer encoding failed: {:?}", e),
})
} else if let Some(bool_box) = value.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
encoder.encode_bool(bool_box.value)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV bool encoding failed: {:?}", e),
})
} else {
let str_val = value.to_string_box().value;
encoder.encode_bytes(str_val.as_bytes())
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV default bytes encoding failed: {:?}", e),
})
}
}
/// TLVレスポンスをNyashBoxに変換
fn decode_tlv_to_nyash_box(&self, response_bytes: &[u8], method_name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
use crate::bid::tlv::TlvDecoder;
use crate::bid::types::BidTag;
if response_bytes.is_empty() {
return Ok(Box::new(StringBox::new("".to_string())));
}
let mut decoder = TlvDecoder::new(response_bytes)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV decoder creation failed: {:?}", e),
})?;
if let Some((tag, payload)) = decoder.decode_next()
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV decoding failed: {:?}", e),
})? {
match tag {
BidTag::String => {
let text = String::from_utf8_lossy(payload).to_string();
Ok(Box::new(StringBox::new(text)))
}
BidTag::Bytes => {
// ファイル読み取り等のバイトデータは文字列として返す
let text = String::from_utf8_lossy(payload).to_string();
Ok(Box::new(StringBox::new(text)))
}
BidTag::I32 => {
let value = TlvDecoder::decode_i32(payload)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV i32 decoding failed: {:?}", e),
})?;
Ok(Box::new(crate::box_trait::IntegerBox::new(value as i64)))
}
BidTag::Bool => {
let value = TlvDecoder::decode_bool(payload)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("TLV bool decoding failed: {:?}", e),
})?;
Ok(Box::new(crate::box_trait::BoolBox::new(value)))
}
BidTag::Void => {
Ok(Box::new(StringBox::new("OK".to_string())))
}
_ => {
Ok(Box::new(StringBox::new(format!("Unknown TLV tag: {:?}", tag))))
}
}
} else {
Ok(Box::new(StringBox::new("".to_string())))
}
}
*/
/* legacy - PluginFileBox専用
/// PluginFileBoxのメソッド呼び出しを実行 (BID-FFI system) - LEGACY HARDCODED VERSION
/// Handles plugin-backed file I/O operations via FFI interface
/// 🚨 DEPRECATED: This method has hardcoded method names and violates BID-FFI principles
/// Use execute_plugin_method_generic instead for true dynamic method calling
pub(in crate::interpreter) fn execute_plugin_file_method(&mut self, plugin_file_box: &PluginFileBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 🎯 新しい汎用システムにリダイレクト
self.execute_plugin_method_generic(plugin_file_box, method, arguments)
}
*/
}

View File

@ -1,379 +0,0 @@
/*!
* Math Methods Module
*
* MathBox, RandomBox, TimeBox, DateTimeBoxのメソッド実装
* Phase 9.75f-2: 動的ライブラリ化対応
*/
use crate::interpreter::{NyashInterpreter, RuntimeError};
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox};
use crate::boxes::FloatBox;
#[cfg(feature = "dynamic-file")]
use crate::interpreter::plugin_loader::{MathBoxProxy, RandomBoxProxy, TimeBoxProxy, DateTimeBoxProxy};
#[cfg(not(feature = "dynamic-file"))]
use crate::boxes::{MathBox, RandomBox, TimeBox, DateTimeBox};
impl NyashInterpreter {
/// MathBox用メソッドを実行動的ライブラリ対応
#[cfg(feature = "dynamic-file")]
pub fn execute_math_proxy_method(&mut self,
_math_box: &MathBoxProxy,
method: &str,
arguments: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"sqrt" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("sqrt() expects 1 argument, got {}", arguments.len()),
});
}
let arg = self.execute_expression(&arguments[0])?;
let value = self.to_float(&arg)?;
let cache = crate::interpreter::plugin_loader::PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
if let Ok(sqrt_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(f64) -> f64>>(b"nyash_math_sqrt\0") {
let result = sqrt_fn(value);
return Ok(Box::new(FloatBox::new(result)));
}
}
}
Err(RuntimeError::InvalidOperation {
message: "Failed to call sqrt".to_string(),
})
}
"pow" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("pow() expects 2 arguments, got {}", arguments.len()),
});
}
let base_value = self.execute_expression(&arguments[0])?;
let exp_value = self.execute_expression(&arguments[1])?;
let base = self.to_float(&base_value)?;
let exp = self.to_float(&exp_value)?;
let cache = crate::interpreter::plugin_loader::PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
if let Ok(pow_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(f64, f64) -> f64>>(b"nyash_math_pow\0") {
let result = pow_fn(base, exp);
return Ok(Box::new(FloatBox::new(result)));
}
}
}
Err(RuntimeError::InvalidOperation {
message: "Failed to call pow".to_string(),
})
}
"sin" => self.call_unary_math_fn("nyash_math_sin", arguments),
"cos" => self.call_unary_math_fn("nyash_math_cos", arguments),
"tan" => self.call_unary_math_fn("nyash_math_tan", arguments),
"abs" => self.call_unary_math_fn("nyash_math_abs", arguments),
"floor" => self.call_unary_math_fn("nyash_math_floor", arguments),
"ceil" => self.call_unary_math_fn("nyash_math_ceil", arguments),
"round" => self.call_unary_math_fn("nyash_math_round", arguments),
"log" => self.call_unary_math_fn("nyash_math_log", arguments),
"log10" => self.call_unary_math_fn("nyash_math_log10", arguments),
"exp" => self.call_unary_math_fn("nyash_math_exp", arguments),
"min" => self.call_binary_math_fn("nyash_math_min", arguments),
"max" => self.call_binary_math_fn("nyash_math_max", arguments),
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(_math_box.to_string_box()))
}
"type_name" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("type_name() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(StringBox::new(_math_box.type_name())))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown MathBox method: {}", method),
}),
}
}
/// RandomBox用メソッドを実行動的ライブラリ対応
#[cfg(feature = "dynamic-file")]
pub fn execute_random_proxy_method(&mut self,
random_box: &RandomBoxProxy,
method: &str,
arguments: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"next" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("next() expects 0 arguments, got {}", arguments.len()),
});
}
random_box.next()
}
"range" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("range() expects 2 arguments, got {}", arguments.len()),
});
}
let min_value = self.execute_expression(&arguments[0])?;
let max_value = self.execute_expression(&arguments[1])?;
let min = self.to_float(&min_value)?;
let max = self.to_float(&max_value)?;
random_box.range(min, max)
}
"int" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("int() expects 2 arguments, got {}", arguments.len()),
});
}
let min_value = self.execute_expression(&arguments[0])?;
let max_value = self.execute_expression(&arguments[1])?;
let min = self.to_integer(&min_value)?;
let max = self.to_integer(&max_value)?;
random_box.int(min, max)
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(random_box.to_string_box()))
}
"type_name" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("type_name() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(StringBox::new(random_box.type_name())))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown RandomBox method: {}", method),
}),
}
}
/// TimeBox用メソッドを実行動的ライブラリ対応
#[cfg(feature = "dynamic-file")]
pub fn execute_time_proxy_method(&mut self,
_time_box: &TimeBoxProxy,
method: &str,
arguments: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"now" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("now() expects 0 arguments, got {}", arguments.len()),
});
}
crate::interpreter::plugin_loader::PluginLoader::create_datetime_now()
}
"parse" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("parse() expects 1 argument, got {}", arguments.len()),
});
}
let time_str = self.execute_expression(&arguments[0])?.to_string_box().value;
crate::interpreter::plugin_loader::PluginLoader::create_datetime_from_string(&time_str)
}
"toString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(_time_box.to_string_box()))
}
"type_name" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("type_name() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(StringBox::new(_time_box.type_name())))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimeBox method: {}", method),
}),
}
}
/// DateTimeBox用メソッドを実行動的ライブラリ対応
#[cfg(feature = "dynamic-file")]
pub fn execute_datetime_proxy_method(&mut self,
datetime_box: &DateTimeBoxProxy,
method: &str,
arguments: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
// type_name と toString は引数チェックを個別で行う
if !arguments.is_empty() && method != "type_name" && method != "toString" {
return Err(RuntimeError::InvalidOperation {
message: format!("{}() expects 0 arguments, got {}", method, arguments.len()),
});
}
let cache = crate::interpreter::plugin_loader::PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
match method {
"year" => {
if let Ok(year_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> i32>>(b"nyash_datetime_year\0") {
let year = year_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(year as i64)));
}
}
"month" => {
if let Ok(month_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> u32>>(b"nyash_datetime_month\0") {
let month = month_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(month as i64)));
}
}
"day" => {
if let Ok(day_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> u32>>(b"nyash_datetime_day\0") {
let day = day_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(day as i64)));
}
}
"hour" => {
if let Ok(hour_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> u32>>(b"nyash_datetime_hour\0") {
let hour = hour_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(hour as i64)));
}
}
"minute" => {
if let Ok(minute_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> u32>>(b"nyash_datetime_minute\0") {
let minute = minute_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(minute as i64)));
}
}
"second" => {
if let Ok(second_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> u32>>(b"nyash_datetime_second\0") {
let second = second_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(second as i64)));
}
}
"timestamp" => {
if let Ok(timestamp_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(*mut std::ffi::c_void) -> i64>>(b"nyash_datetime_timestamp\0") {
let timestamp = timestamp_fn(datetime_box.handle.ptr);
return Ok(Box::new(IntegerBox::new(timestamp)));
}
}
"toString" => {
return Ok(Box::new(datetime_box.to_string_box()));
}
"type_name" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("type_name() expects 0 arguments, got {}", arguments.len()),
});
}
return Ok(Box::new(StringBox::new(datetime_box.type_name())));
}
_ => {}
}
}
}
Err(RuntimeError::InvalidOperation {
message: format!("Unknown DateTimeBox method: {}", method),
})
}
// ヘルパーメソッド
#[cfg(feature = "dynamic-file")]
fn call_unary_math_fn(&mut self, fn_name: &str, arguments: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("{}() expects 1 argument, got {}", fn_name.strip_prefix("nyash_math_").unwrap_or(fn_name), arguments.len()),
});
}
let arg_value = self.execute_expression(&arguments[0])?;
let value = self.to_float(&arg_value)?;
let cache = crate::interpreter::plugin_loader::PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let fn_name_bytes = format!("{}\0", fn_name);
if let Ok(math_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(f64) -> f64>>(fn_name_bytes.as_bytes()) {
let result = math_fn(value);
return Ok(Box::new(FloatBox::new(result)));
}
}
}
Err(RuntimeError::InvalidOperation {
message: format!("Failed to call {}", fn_name),
})
}
#[cfg(feature = "dynamic-file")]
fn call_binary_math_fn(&mut self, fn_name: &str, arguments: &[ASTNode]) -> Result<Box<dyn NyashBox>, RuntimeError> {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("{}() expects 2 arguments, got {}", fn_name.strip_prefix("nyash_math_").unwrap_or(fn_name), arguments.len()),
});
}
let a_value = self.execute_expression(&arguments[0])?;
let b_value = self.execute_expression(&arguments[1])?;
let a = self.to_float(&a_value)?;
let b = self.to_float(&b_value)?;
let cache = crate::interpreter::plugin_loader::PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let fn_name_bytes = format!("{}\0", fn_name);
if let Ok(math_fn) = plugin.library.get::<libloading::Symbol<unsafe extern "C" fn(f64, f64) -> f64>>(fn_name_bytes.as_bytes()) {
let result = math_fn(a, b);
return Ok(Box::new(FloatBox::new(result)));
}
}
}
Err(RuntimeError::InvalidOperation {
message: format!("Failed to call {}", fn_name),
})
}
// 型変換ヘルパー
fn to_float(&self, value: &Box<dyn NyashBox>) -> Result<f64, RuntimeError> {
if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Ok(float_box.value)
} else if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Ok(int_box.value as f64)
} else {
Err(RuntimeError::TypeError {
message: "Value must be a number".to_string(),
})
}
}
fn to_integer(&self, value: &Box<dyn NyashBox>) -> Result<i64, RuntimeError> {
if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
Ok(int_box.value)
} else if let Some(float_box) = value.as_any().downcast_ref::<FloatBox>() {
Ok(float_box.value as i64)
} else {
Err(RuntimeError::TypeError {
message: "Value must be a number".to_string(),
})
}
}
}

View File

@ -1,28 +0,0 @@
/*!
* Box Methods Module Organization
*
* 旧box_methods.rsを機能別に分割したモジュール群
* 保守性と可読性の向上を目的とした再構成
*
* Current implementation:
* - basic_methods: StringBox, IntegerBox, BoolBox, FloatBox
* - collection_methods: ArrayBox, MapBox
* - io_methods: FileBox, ResultBox ✅ IMPLEMENTED
* Future modules (planned):
* - system_methods: TimeBox, DateTimeBox, TimerBox, DebugBox
* - math_methods: MathBox, RandomBox
* - async_methods: FutureBox, ChannelBox
* - web_methods: WebDisplayBox, WebConsoleBox, WebCanvasBox
* - special_methods: MethodBox, SoundBox
*/
pub mod basic_methods; // StringBox, IntegerBox, BoolBox, FloatBox
pub mod collection_methods; // ArrayBox, MapBox
pub mod data_methods; // BufferBox, JSONBox, RegexBox
pub mod http_methods; // SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox
pub mod io_methods; // FileBox, ResultBox
pub mod network_methods; // HttpClientBox, StreamBox
pub mod p2p_methods; // IntentBox, P2PBox
pub mod system_methods; // GcConfigBox, DebugConfigBox
// Re-export methods for easy access

View File

@ -1,132 +0,0 @@
/*!
* Network and Communication Box Methods Module
*
* Contains method implementations for network-related Box types:
* - HttpClientBox (execute_http_method) - HTTP client operations
* - StreamBox (execute_stream_method) - Stream processing operations
*/
use super::super::*;
use crate::box_trait::NyashBox;
use crate::boxes::{HttpClientBox, StreamBox};
impl NyashInterpreter {
/// HttpClientBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_http_method(
&mut self,
http_box: &HttpClientBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"get" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("get() expects 1 argument, got {}", arguments.len()),
});
}
let url = self.execute_expression(&arguments[0])?;
Ok(http_box.http_get(url))
}
"post" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("post() expects 2 arguments, got {}", arguments.len()),
});
}
let url = self.execute_expression(&arguments[0])?;
let body = self.execute_expression(&arguments[1])?;
Ok(http_box.post(url, body))
}
"put" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("put() expects 2 arguments, got {}", arguments.len()),
});
}
let url = self.execute_expression(&arguments[0])?;
let body = self.execute_expression(&arguments[1])?;
Ok(http_box.put(url, body))
}
"delete" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("delete() expects 1 argument, got {}", arguments.len()),
});
}
let url = self.execute_expression(&arguments[0])?;
Ok(http_box.delete(url))
}
"request" => {
if arguments.len() != 3 {
return Err(RuntimeError::InvalidOperation {
message: format!("request() expects 3 arguments, got {}", arguments.len()),
});
}
let method_arg = self.execute_expression(&arguments[0])?;
let url = self.execute_expression(&arguments[1])?;
let options = self.execute_expression(&arguments[2])?;
Ok(http_box.request(method_arg, url, options))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for HttpClientBox", method),
}),
}
}
/// StreamBoxのメソッド呼び出しを実行
pub(in crate::interpreter) fn execute_stream_method(
&mut self,
stream_box: &StreamBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"write" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("write() expects 1 argument, got {}", arguments.len()),
});
}
let data = self.execute_expression(&arguments[0])?;
Ok(stream_box.stream_write(data))
}
"read" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("read() expects 1 argument, got {}", arguments.len()),
});
}
let count = self.execute_expression(&arguments[0])?;
Ok(stream_box.stream_read(count))
}
"position" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("position() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(stream_box.get_position())
}
"length" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("length() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(stream_box.get_length())
}
"reset" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("reset() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(stream_box.stream_reset())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for StreamBox", method),
}),
}
}
}

View File

@ -1,143 +0,0 @@
/*! 📡 P2P通信メソッド実装 (NEW ARCHITECTURE)
* IntentBoxとP2PBoxのNyashインタープリター統合
* Arc<Mutex>パターン対応版
*/
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, StringBox};
use crate::boxes::{IntentBox, P2PBox};
use crate::interpreter::NyashInterpreter;
use crate::interpreter::RuntimeError;
impl NyashInterpreter {
/// IntentBoxのメソッド実行 (RwLock版)
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 {
// メッセージ名取得
"getName" | "name" => Ok(intent_box.get_name()),
// ペイロード取得JSON文字列として
"getPayload" | "payload" => Ok(intent_box.get_payload()),
// 型情報取得
"getType" | "type" => Ok(Box::new(StringBox::new("IntentBox"))),
_ => Err(RuntimeError::UndefinedVariable {
name: format!("IntentBox method '{}' not found", method),
}),
}
}
// P2PBoxのメソッド実装RwLockベース
pub(in crate::interpreter) fn execute_p2p_box_method(
&mut self,
p2p_box: &P2PBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
if crate::interpreter::utils::debug_on()
|| std::env::var("NYASH_DEBUG_P2P").unwrap_or_default() == "1"
{
eprintln!(
"[Interp:P2P] {}(..) called with {} args",
method,
arguments.len()
);
}
match method {
// ードID取得
"getNodeId" | "getId" => Ok(p2p_box.get_node_id()),
// トランスポート種類取得
"getTransportType" | "transport" => Ok(p2p_box.get_transport_type()),
// ノード到達可能性確認
"isReachable" => {
if arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: "isReachable requires node_id argument".to_string(),
});
}
let node_id_result = self.execute_expression(&arguments[0])?;
Ok(p2p_box.is_reachable(node_id_result))
}
// send メソッド実装ResultBox返却
"send" => {
if arguments.len() < 2 {
return Err(RuntimeError::InvalidOperation {
message: "send requires (to, intent) arguments".to_string(),
});
}
let to_result = self.execute_expression(&arguments[0])?;
let intent_result = self.execute_expression(&arguments[1])?;
Ok(p2p_box.send(to_result, intent_result))
}
// ping: health check using sys.ping/sys.pong
"ping" => {
if arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: "ping requires (to [, timeout_ms]) arguments".to_string(),
});
}
let to_result = self.execute_expression(&arguments[0])?;
if arguments.len() >= 2 {
let tmo_val = self.execute_expression(&arguments[1])?;
let tmo_ms = tmo_val.to_string_box().value.parse::<u64>().unwrap_or(200);
Ok(p2p_box.ping_with_timeout(to_result, tmo_ms))
} else {
Ok(p2p_box.ping(to_result))
}
}
// on メソッド実装ResultBox返却
"on" => {
if arguments.len() < 2 {
return Err(RuntimeError::InvalidOperation {
message: "on requires (intentName, handler) arguments".to_string(),
});
}
let name_val = self.execute_expression(&arguments[0])?;
let handler_val = self.execute_expression(&arguments[1])?;
Ok(p2p_box.on(name_val, handler_val))
}
// 最後の受信情報(ループバック検証用)
"getLastFrom" => Ok(p2p_box.get_last_from()),
"getLastIntentName" => Ok(p2p_box.get_last_intent_name()),
"debug_nodes" | "debugNodes" => Ok(p2p_box.debug_nodes()),
"debug_bus_id" | "debugBusId" => Ok(p2p_box.debug_bus_id()),
// onOnce / off
"onOnce" | "on_once" => {
if arguments.len() < 2 {
return Err(RuntimeError::InvalidOperation {
message: "onOnce requires (intentName, handler) arguments".to_string(),
});
}
let name_val = self.execute_expression(&arguments[0])?;
let handler_val = self.execute_expression(&arguments[1])?;
Ok(p2p_box.on_once(name_val, handler_val))
}
"off" => {
if arguments.len() < 1 {
return Err(RuntimeError::InvalidOperation {
message: "off requires (intentName) argument".to_string(),
});
}
let name_val = self.execute_expression(&arguments[0])?;
Ok(p2p_box.off(name_val))
}
_ => Err(RuntimeError::UndefinedVariable {
name: format!("P2PBox method '{}' not found", method),
}),
}
}
}

View File

@ -1,201 +0,0 @@
/*!
* System Methods Module
*
* Contains system-level Box type method implementations:
* - GcConfigBox: Garbage collector configuration
* - DebugConfigBox: Debug and observability configuration
*/
use crate::ast::ASTNode;
use crate::box_trait::{BoolBox, NyashBox};
use crate::boxes::debug_config_box::DebugConfigBox;
use crate::boxes::gc_config_box::GcConfigBox;
use crate::interpreter::{NyashInterpreter, RuntimeError};
impl NyashInterpreter {
/// Execute GcConfigBox methods
pub(crate) fn execute_gc_config_method(
&mut self,
gc_box: &GcConfigBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"setFlag" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"GcConfigBox.setFlag expects 2 arguments, got {}",
arguments.len()
),
});
}
let name = self.execute_expression(&arguments[0])?;
let on = self.execute_expression(&arguments[1])?;
let name_str = name.to_string_box().value;
let on_bool = if let Some(b) = on.as_any().downcast_ref::<BoolBox>() {
b.value
} else {
on.to_string_box().value.to_lowercase() == "true"
};
let mut gc_clone = gc_box.clone();
gc_clone.set_flag(&name_str, on_bool);
Ok(Box::new(gc_clone))
}
"getFlag" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"GcConfigBox.getFlag expects 1 argument, got {}",
arguments.len()
),
});
}
let name = self.execute_expression(&arguments[0])?;
let name_str = name.to_string_box().value;
Ok(gc_box.get_flag(&name_str))
}
"apply" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"GcConfigBox.apply expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(gc_box.apply())
}
"summary" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"GcConfigBox.summary expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(gc_box.summary())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("GcConfigBox has no method '{}'", method),
}),
}
}
/// Execute DebugConfigBox methods
pub(crate) fn execute_debug_config_method(
&mut self,
debug_box: &DebugConfigBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"setFlag" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"DebugConfigBox.setFlag expects 2 arguments, got {}",
arguments.len()
),
});
}
let name = self.execute_expression(&arguments[0])?;
let on = self.execute_expression(&arguments[1])?;
let name_str = name.to_string_box().value;
let on_bool = if let Some(b) = on.as_any().downcast_ref::<BoolBox>() {
b.value
} else {
on.to_string_box().value.to_lowercase() == "true"
};
let mut debug_clone = debug_box.clone();
debug_clone.set_flag(&name_str, on_bool);
Ok(Box::new(debug_clone))
}
"setPath" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"DebugConfigBox.setPath expects 2 arguments, got {}",
arguments.len()
),
});
}
let name = self.execute_expression(&arguments[0])?;
let path = self.execute_expression(&arguments[1])?;
let name_str = name.to_string_box().value;
let path_str = path.to_string_box().value;
let mut debug_clone = debug_box.clone();
debug_clone.set_path(&name_str, &path_str);
Ok(Box::new(debug_clone))
}
"getFlag" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"DebugConfigBox.getFlag expects 1 argument, got {}",
arguments.len()
),
});
}
let name = self.execute_expression(&arguments[0])?;
let name_str = name.to_string_box().value;
Ok(debug_box.get_flag(&name_str))
}
"getPath" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"DebugConfigBox.getPath expects 1 argument, got {}",
arguments.len()
),
});
}
let name = self.execute_expression(&arguments[0])?;
let name_str = name.to_string_box().value;
Ok(debug_box.get_path(&name_str))
}
"apply" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"DebugConfigBox.apply expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(debug_box.apply())
}
"summary" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"DebugConfigBox.summary expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(debug_box.summary())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("DebugConfigBox has no method '{}'", method),
}),
}
}
}

View File

@ -1,333 +0,0 @@
//! Central builtin method dispatcher (thin wrapper)
use super::{NyashInterpreter, RuntimeError};
use crate::ast::ASTNode;
use crate::box_trait::{BoolBox, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::file;
use crate::boxes::ref_cell_box::RefCellBox as RcCell;
use crate::boxes::{console_box, debug_box, map_box, null_box, random_box, sound_box, time_box};
use crate::boxes::{debug_config_box::DebugConfigBox, gc_config_box::GcConfigBox};
use crate::boxes::{
ArrayBox, BufferBox, FloatBox, FutureBox, HttpClientBox, JSONBox, MathBox, RegexBox, ResultBox,
StreamBox,
};
use crate::channel_box::ChannelBox;
impl NyashInterpreter {
/// Try dispatching a builtin method based on dynamic type.
/// Returns Some(Result) if handled, or None to let caller continue other paths.
pub(crate) fn dispatch_builtin_method(
&mut self,
obj: &Box<dyn NyashBox>,
method: &str,
arguments: &[ASTNode],
) -> Option<Result<Box<dyn NyashBox>, RuntimeError>> {
// StringBox
if let Some(b) = obj.as_any().downcast_ref::<StringBox>() {
return Some(self.execute_string_method(b, method, arguments));
}
// IntegerBox
if let Some(b) = obj.as_any().downcast_ref::<IntegerBox>() {
return Some(self.execute_integer_method(b, method, arguments));
}
// FloatBox
if let Some(b) = obj.as_any().downcast_ref::<FloatBox>() {
return Some(self.execute_float_method(b, method, arguments));
}
// BoolBox
if let Some(b) = obj.as_any().downcast_ref::<BoolBox>() {
return Some(self.execute_bool_method(b, method, arguments));
}
// ArrayBox
if let Some(b) = obj.as_any().downcast_ref::<ArrayBox>() {
return Some(self.execute_array_method(b, method, arguments));
}
// BufferBox
if let Some(b) = obj.as_any().downcast_ref::<BufferBox>() {
return Some(self.execute_buffer_method(b, method, arguments));
}
// FileBox
if let Some(b) = obj.as_any().downcast_ref::<file::FileBox>() {
return Some(self.execute_file_method(b, method, arguments));
}
// ResultBox
if let Some(b) = obj.as_any().downcast_ref::<ResultBox>() {
return Some(self.execute_result_method(b, method, arguments));
}
// FutureBox
if let Some(b) = obj.as_any().downcast_ref::<FutureBox>() {
return Some(self.execute_future_method(b, method, arguments));
}
// ChannelBox
if let Some(b) = obj.as_any().downcast_ref::<ChannelBox>() {
return Some(self.execute_channel_method(b, method, arguments));
}
// JSONBox
if let Some(b) = obj.as_any().downcast_ref::<JSONBox>() {
return Some(self.execute_json_method(b, method, arguments));
}
// HttpClientBox
if let Some(b) = obj.as_any().downcast_ref::<HttpClientBox>() {
return Some(self.execute_http_method(b, method, arguments));
}
// StreamBox
if let Some(b) = obj.as_any().downcast_ref::<StreamBox>() {
return Some(self.execute_stream_method(b, method, arguments));
}
// RegexBox
if let Some(b) = obj.as_any().downcast_ref::<RegexBox>() {
return Some(self.execute_regex_method(b, method, arguments));
}
// MathBox
if let Some(b) = obj.as_any().downcast_ref::<MathBox>() {
return Some(self.execute_math_method(b, method, arguments));
}
// NullBox
if let Some(b) = obj.as_any().downcast_ref::<null_box::NullBox>() {
return Some(self.execute_null_method(b, method, arguments));
}
// TimeBox
if let Some(b) = obj.as_any().downcast_ref::<time_box::TimeBox>() {
return Some(self.execute_time_method(b, method, arguments));
}
// TimerBox
if let Some(b) = obj.as_any().downcast_ref::<time_box::TimerBox>() {
return Some(self.execute_timer_method(b, method, arguments));
}
// MapBox
if let Some(b) = obj.as_any().downcast_ref::<map_box::MapBox>() {
return Some(self.execute_map_method(b, method, arguments));
}
// RandomBox
if let Some(b) = obj.as_any().downcast_ref::<random_box::RandomBox>() {
return Some(self.execute_random_method(b, method, arguments));
}
// SoundBox
if let Some(b) = obj.as_any().downcast_ref::<sound_box::SoundBox>() {
return Some(self.execute_sound_method(b, method, arguments));
}
// DebugBox
if let Some(b) = obj.as_any().downcast_ref::<debug_box::DebugBox>() {
return Some(self.execute_debug_method(b, method, arguments));
}
// ConsoleBox
if let Some(b) = obj.as_any().downcast_ref::<console_box::ConsoleBox>() {
return Some(self.execute_console_method(b, method, arguments));
}
// GcConfigBox
if let Some(b) = obj.as_any().downcast_ref::<GcConfigBox>() {
return Some(self.execute_gc_config_method(b, method, arguments));
}
// DebugConfigBox
if let Some(b) = obj.as_any().downcast_ref::<DebugConfigBox>() {
return Some(self.execute_debug_config_method(b, method, arguments));
}
// RefCellBox (by-ref proxy)
if let Some(b) = obj.as_any().downcast_ref::<RcCell>() {
return Some(self.execute_refcell_method(b, method, arguments));
}
None
}
/// Dispatch user-defined instance methods (InstanceBox path).
/// Returns Some(Result) if handled, or None if obj is not an InstanceBox.
pub(crate) fn dispatch_instance_method(
&mut self,
object_ast: &ASTNode,
obj_value: &Box<dyn NyashBox>,
method: &str,
arguments: &[ASTNode],
) -> Option<Result<Box<dyn NyashBox>, RuntimeError>> {
use crate::box_trait::{IntegerBox, StringBox};
use crate::boxes::MathBox;
use crate::finalization;
use crate::instance_v2::InstanceBox;
let instance = match obj_value.as_any().downcast_ref::<InstanceBox>() {
Some(i) => i,
None => return None,
};
// fini() special handling (idempotent, weak prohibition)
if method == "fini" {
// weak-fini prohibition check: me.<weak_field>.fini()
if let ASTNode::FieldAccess {
object: field_object,
field,
..
} = object_ast
{
if let ASTNode::Variable { name, .. } = field_object.as_ref() {
if name == "me" {
if let Ok(current_me) = self.resolve_variable("me") {
if let Some(current_instance) =
(*current_me).as_any().downcast_ref::<InstanceBox>()
{
if current_instance.is_weak_field(field) {
return Some(Err(RuntimeError::InvalidOperation {
message: format!(
"Cannot finalize weak field '{}' (non-owning reference)",
field
),
}));
}
}
}
}
}
}
if instance.is_finalized() {
return Some(Ok(Box::new(crate::box_trait::VoidBox::new())));
}
if let Some(fini_method) = instance.get_method("fini") {
if let ASTNode::FunctionDeclaration { body, .. } = fini_method.clone() {
let saved = self.save_local_vars();
self.local_vars.clear();
self.declare_local_variable("me", obj_value.clone_or_share());
let mut _result =
Box::new(crate::box_trait::VoidBox::new()) as Box<dyn NyashBox>;
for statement in &body {
match self.execute_statement(statement) {
Ok(v) => {
_result = v;
}
Err(e) => {
self.restore_local_vars(saved);
return Some(Err(e));
}
}
if let super::ControlFlow::Return(_) = &self.control_flow {
self.control_flow = super::ControlFlow::None;
break;
}
}
self.restore_local_vars(saved);
}
}
let target_info = obj_value.to_string_box().value;
self.trigger_weak_reference_invalidation(&target_info);
if let Err(e) = instance.fini() {
return Some(Err(RuntimeError::InvalidOperation { message: e }));
}
finalization::mark_as_finalized(instance.box_id());
return Some(Ok(Box::new(crate::box_trait::VoidBox::new())));
}
// Local method on instance
if let Some(method_ast) = instance.get_method(method) {
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast.clone() {
eprintln!(
"[dbg] enter instance method {}.{}",
instance.class_name, method
);
// Evaluate args in current context
let mut arg_values = Vec::new();
for a in arguments {
match self.execute_expression(a) {
Ok(v) => arg_values.push(v),
Err(e) => return Some(Err(e)),
}
}
if arg_values.len() != params.len() {
return Some(Err(RuntimeError::InvalidOperation {
message: format!(
"Method {} expects {} arguments, got {}",
method,
params.len(),
arg_values.len()
),
}));
}
let saved = self.save_local_vars();
self.local_vars.clear();
self.declare_local_variable("me", obj_value.clone_or_share());
for (p, v) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(p, v.clone_or_share());
}
let mut result: Box<dyn NyashBox> = Box::new(crate::box_trait::VoidBox::new());
for stmt in &body {
match self.execute_statement(stmt) {
Ok(v) => {
result = v;
}
Err(e) => return Some(Err(e)),
}
if let super::ControlFlow::Return(ret) = &self.control_flow {
result = ret.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
self.restore_local_vars(saved);
eprintln!(
"[dbg] exit instance method {}.{}",
instance.class_name, method
);
return Some(Ok(result));
} else {
return Some(Err(RuntimeError::InvalidOperation {
message: format!("Method '{}' is not a valid function declaration", method),
}));
}
}
// Builtin parent method promotion (StringBox/IntegerBox/MathBox)
let parent_names = {
let decls = self.shared.box_declarations.read().unwrap();
decls
.get(&instance.class_name)
.map(|d| d.extends.clone())
.unwrap_or_default()
};
for parent_name in &parent_names {
if crate::box_trait::is_builtin_box(parent_name) {
if parent_name == "StringBox" {
if let Some(builtin_value) = instance.get_field_ng("__builtin_content") {
if let crate::value::NyashValue::Box(boxed) = builtin_value {
let g = boxed.lock().unwrap();
if let Some(sb) = g.as_any().downcast_ref::<StringBox>() {
return Some(self.execute_string_method(sb, method, arguments));
}
}
}
let sb = StringBox::new("");
return Some(self.execute_string_method(&sb, method, arguments));
} else if parent_name == "IntegerBox" {
if let Some(builtin_value) = instance.get_field_ng("__builtin_content") {
if let crate::value::NyashValue::Box(boxed) = builtin_value {
let g = boxed.lock().unwrap();
if let Some(ib) = g.as_any().downcast_ref::<IntegerBox>() {
return Some(self.execute_integer_method(ib, method, arguments));
}
}
}
let ib = IntegerBox::new(0);
return Some(self.execute_integer_method(&ib, method, arguments));
} else if parent_name == "MathBox" {
let math_box = MathBox::new();
return Some(self.execute_math_method(&math_box, method, arguments));
}
}
}
// Plugin parent via __plugin_content
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if let Some(plugin_shared) = instance.get_field_legacy("__plugin_content") {
let plugin_ref = &*plugin_shared;
if let Some(plugin) = plugin_ref
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
return Some(self.call_plugin_method(plugin, method, arguments));
}
}
}
// Not handled here
Some(Err(RuntimeError::InvalidOperation {
message: format!("Method '{}' not found in {}", method, instance.class_name),
}))
}
}

View File

@ -1,108 +1,6 @@
/*!
* Nyash Interpreter - Modular Rust Implementation
*
* Refactored from massive 2,633-line interpreter.rs into logical modules
* Everything is Box philosophy with clean separation of concerns
*/
#![cfg(feature = "interpreter-legacy")]
// Shim module to re-export legacy interpreter from archive path
#[path = "../archive/interpreter_legacy/mod.rs"]
mod legacy_interpreter_mod;
pub use legacy_interpreter_mod::*;
// Import all necessary dependencies
use crate::ast::{ASTNode, CatchClause};
use crate::box_trait::{BoolBox, BoxCore, ErrorBox, NyashBox, StringBox, VoidBox};
use crate::boxes::debug_box::DebugBox;
use crate::boxes::math_box::MathBox;
use crate::boxes::random_box::RandomBox;
use crate::boxes::time_box::TimerBox;
use crate::boxes::FutureBox;
use crate::channel_box::ChannelBox;
use crate::instance_v2::InstanceBox;
// WASM-specific Box types (conditionally included)
#[cfg(target_arch = "wasm32")]
use crate::boxes::web::{WebCanvasBox, WebConsoleBox, WebDisplayBox};
use crate::exception_box;
use std::collections::HashMap;
// Module declarations
mod async_methods;
mod box_methods;
mod calls;
mod core;
pub mod errors;
mod eval;
mod expressions;
mod functions;
mod io;
mod math_methods;
mod methods;
mod methods_dispatch;
pub mod objects;
mod objects_basic_constructors;
mod special_methods;
pub mod state;
mod statements;
mod system_methods;
pub mod utils;
mod web_methods;
// Main interpreter implementation - will be moved from interpreter.rs
pub use core::NyashInterpreter;
pub use errors::RuntimeError;
pub use state::SharedState;
/// 実行制御フロー
#[derive(Debug)]
pub enum ControlFlow {
None,
Break,
Continue,
Return(Box<dyn NyashBox>),
Throw(Box<dyn NyashBox>),
}
/// コンストラクタ実行コンテキスト
#[derive(Debug, Clone)]
pub struct ConstructorContext {
pub class_name: String,
pub parent_class: Option<String>,
}
// Re-export core model so existing interpreter modules keep working
pub use crate::core::model::BoxDeclaration;
/// 🔥 Static Box定義を保持する構造体
#[derive(Debug, Clone)]
pub struct StaticBoxDefinition {
pub name: String,
pub fields: Vec<String>,
pub methods: HashMap<String, ASTNode>,
pub init_fields: Vec<String>,
pub weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
pub static_init: Option<Vec<ASTNode>>, // static { } ブロック
pub extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
pub implements: Vec<String>,
pub type_parameters: Vec<String>,
/// 初期化状態
pub initialization_state: StaticBoxState,
}
/// 🔥 Static Box初期化状態
#[derive(Debug, Clone, PartialEq)]
pub enum StaticBoxState {
NotInitialized, // 未初期化
Initializing, // 初期化中(循環参照検出用)
Initialized, // 初期化完了
}
/// 関数宣言を保持する構造体
#[derive(Debug, Clone)]
pub struct FunctionDeclaration {
pub name: String,
pub params: Vec<String>,
pub body: Vec<ASTNode>,
}
// Re-export core interpreter types
pub use core::*;
// Import and re-export stdlib for interpreter modules
pub use crate::stdlib::BuiltinStdlib;

View File

@ -1,228 +0,0 @@
use super::*;
impl NyashInterpreter {
/// Box宣言を登録 - 🔥 コンストラクタオーバーロード禁止対応
pub(crate) fn register_box_declaration(
&mut self,
name: String,
fields: Vec<String>,
public_fields: Vec<String>,
private_fields: Vec<String>,
methods: HashMap<String, ASTNode>,
constructors: HashMap<String, ASTNode>,
init_fields: Vec<String>,
weak_fields: Vec<String>,
is_interface: bool,
extends: Vec<String>,
implements: Vec<String>,
type_parameters: Vec<String>,
) -> Result<(), RuntimeError> {
if !constructors.is_empty() {
eprintln!(
"🐛 DEBUG: Registering Box '{}' with constructors: {:?}",
name,
constructors.keys().collect::<Vec<_>>()
);
}
if constructors.len() > 1 {
let constructor_names: Vec<String> = constructors.keys().cloned().collect();
return Err(RuntimeError::InvalidOperation {
message: format!(
"🚨 CONSTRUCTOR OVERLOAD FORBIDDEN: Box '{}' has {} constructors: [{}].\n\
🌟 Nyash's explicit philosophy: One Box, One Constructor!\n\
💡 Use different Box classes for different initialization patterns.\n\
📖 Example: UserBox, AdminUserBox, GuestUserBox instead of User(type)",
name,
constructors.len(),
constructor_names.join(", ")
),
});
}
let box_decl = super::BoxDeclaration {
name: name.clone(),
fields,
public_fields,
private_fields,
methods,
constructors,
init_fields,
weak_fields,
is_interface,
extends,
implements,
type_parameters,
};
{
let mut box_decls = self.shared.box_declarations.write().unwrap();
box_decls.insert(name, box_decl);
}
Ok(())
}
/// 🔥 ジェネリクス型引数の検証
pub(super) fn validate_generic_arguments(
&self,
box_decl: &BoxDeclaration,
type_arguments: &[String],
) -> Result<(), RuntimeError> {
if box_decl.type_parameters.len() != type_arguments.len() {
return Err(RuntimeError::TypeError {
message: format!(
"Generic class '{}' expects {} type parameters, got {}. Expected: <{}>, Got: <{}>",
box_decl.name,
box_decl.type_parameters.len(),
type_arguments.len(),
box_decl.type_parameters.join(", "),
type_arguments.join(", ")
),
});
}
if box_decl.type_parameters.is_empty() && !type_arguments.is_empty() {
return Err(RuntimeError::TypeError {
message: format!(
"Class '{}' is not generic, but got type arguments <{}>",
box_decl.name,
type_arguments.join(", ")
),
});
}
for type_arg in type_arguments {
if !self.is_valid_type(type_arg) {
return Err(RuntimeError::TypeError {
message: format!("Unknown type '{}'", type_arg),
});
}
}
Ok(())
}
/// 型が有効かどうかをチェック
fn is_valid_type(&self, type_name: &str) -> bool {
if let Ok(reg) = self.runtime.box_registry.lock() {
if reg.has_type(type_name) {
return true;
}
}
self.shared
.box_declarations
.read()
.unwrap()
.contains_key(type_name)
}
/// 継承チェーンを解決してフィールドとメソッドを収集 - Inheritance resolution
pub(crate) fn resolve_inheritance(
&self,
box_decl: &BoxDeclaration,
) -> Result<(Vec<String>, HashMap<String, ASTNode>), RuntimeError> {
let mut all_fields = Vec::new();
let mut all_methods = HashMap::new();
for parent_name in &box_decl.extends {
use crate::box_trait::is_builtin_box;
let is_builtin = is_builtin_box(parent_name);
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
{
if parent_name == "EguiBox" {
is_builtin = true;
}
}
if is_builtin {
// skip builtin inheritance
} else {
let parent_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls
.get(parent_name)
.ok_or(RuntimeError::UndefinedClass {
name: parent_name.to_string(),
})?
.clone()
};
if parent_decl.is_interface {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Cannot extend interface '{}'. Use 'implements' instead.",
parent_name
),
});
}
let (parent_fields, parent_methods) = self.resolve_inheritance(&parent_decl)?;
all_fields.extend(parent_fields);
all_methods.extend(parent_methods);
}
}
all_fields.extend(box_decl.fields.clone());
for init_field in &box_decl.init_fields {
if !all_fields.contains(init_field) {
all_fields.push(init_field.clone());
}
}
for (method_name, method_ast) in &box_decl.methods {
all_methods.insert(method_name.clone(), method_ast.clone());
}
for interface_name in &box_decl.implements {
let interface_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls
.get(interface_name)
.ok_or(RuntimeError::UndefinedClass {
name: interface_name.clone(),
})?
.clone()
};
if !interface_decl.is_interface {
return Err(RuntimeError::InvalidOperation {
message: format!("'{}' is not an interface", interface_name),
});
}
for (required_method, _) in &interface_decl.methods {
if !all_methods.contains_key(required_method) {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Class '{}' must implement method '{}' from interface '{}'",
box_decl.name, required_method, interface_name
),
});
}
}
}
Ok((all_fields, all_methods))
}
/// 🚀 ジェネリクス型を特殊化してBoxDeclarationを生成
pub(super) fn specialize_generic_class(
&self,
generic_decl: &BoxDeclaration,
type_arguments: &[String],
) -> Result<BoxDeclaration, RuntimeError> {
use std::collections::HashMap;
let specialized_name = format!("{}_{}", generic_decl.name, type_arguments.join("_"));
let mut type_mapping = HashMap::new();
for (i, param) in generic_decl.type_parameters.iter().enumerate() {
type_mapping.insert(param.clone(), type_arguments[i].clone());
}
let mut specialized = generic_decl.clone();
specialized.name = specialized_name.clone();
specialized.type_parameters.clear();
specialized.init_fields =
self.substitute_types_in_fields(&specialized.init_fields, &type_mapping);
let mut updated_constructors = HashMap::new();
for (old_key, constructor_node) in &generic_decl.constructors {
if let Some(args_count) = old_key.split('/').nth(1) {
let new_key = format!("{}/{}", specialized_name, args_count);
updated_constructors.insert(new_key, constructor_node.clone());
}
}
specialized.constructors = updated_constructors;
Ok(specialized)
}
/// フィールドの型置換(現状はそのまま)
pub(super) fn substitute_types_in_fields(
&self,
fields: &[String],
_type_mapping: &HashMap<String, String>,
) -> Vec<String> {
fields.to_vec()
}
}

View File

@ -1,95 +0,0 @@
use super::*;
use crate::box_trait::SharedNyashBox;
impl NyashInterpreter {
/// コンストラクタを実行 - Constructor execution
pub(super) fn execute_constructor(
&mut self,
instance: &SharedNyashBox,
constructor: &ASTNode,
arguments: &[ASTNode],
box_decl: &BoxDeclaration,
) -> Result<(), RuntimeError> {
if let ASTNode::FunctionDeclaration {
name: _,
params,
body,
..
} = constructor
{
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
if params.len() != arg_values.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Constructor expects {} arguments, got {}",
params.len(),
arg_values.len()
),
});
}
let saved_locals = self.save_local_vars();
self.local_vars.clear();
for (param, value) in params.iter().zip(arg_values.iter()) {
self.declare_local_variable(param, value.clone_or_share());
}
self.declare_local_variable("me", instance.clone_or_share());
let old_context = self.current_constructor_context.clone();
self.current_constructor_context = Some(ConstructorContext {
class_name: box_decl.name.clone(),
parent_class: box_decl.extends.first().cloned(),
});
let mut result = Ok(());
for statement in body.iter() {
if let Err(e) = self.execute_statement(statement) {
result = Err(e);
break;
}
}
self.restore_local_vars(saved_locals);
self.current_constructor_context = old_context;
result
} else {
Err(RuntimeError::InvalidOperation {
message: "Invalid constructor node".to_string(),
})
}
}
/// 親コンストラクタを実行 - Parent constructor execution
pub(crate) fn execute_parent_constructor(
&mut self,
parent_class: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let parent_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls
.get(parent_class)
.ok_or(RuntimeError::UndefinedClass {
name: parent_class.to_string(),
})?
.clone()
};
let birth_key = format!("birth/{}", arguments.len());
if let Some(parent_constructor) = parent_decl.constructors.get(&birth_key) {
let this_instance =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' not available in parent constructor call".to_string(),
})?;
self.execute_constructor(&this_instance, parent_constructor, arguments, &parent_decl)?;
Ok(Box::new(VoidBox::new()))
} else {
Err(RuntimeError::InvalidOperation {
message: format!(
"No constructor found for parent class {} with {} arguments",
parent_class,
arguments.len()
),
})
}
}
}

View File

@ -1,14 +0,0 @@
/*!
* Interpreter Objects Module (mod)
*
* Split into submodules:
* - ops.rs: instantiation (execute_new) and helpers
* - methods.rs: constructor-related methods
* - fields.rs: declarations, inheritance, generics utilities
*/
use super::*;
mod fields;
mod methods;
mod ops;

View File

@ -1,127 +0,0 @@
use super::*;
use crate::box_trait::SharedNyashBox;
use std::sync::Arc;
impl NyashInterpreter {
/// Evaluate `new` expression arguments to NyashBox values
pub(super) fn new_eval_args(
&mut self,
arguments: &[ASTNode],
) -> Result<Vec<Box<dyn NyashBox>>, RuntimeError> {
arguments
.iter()
.map(|arg| self.execute_expression(arg))
.collect()
}
/// If user-defined and type args provided, validate/specialize and register declaration
pub(super) fn new_specialize_if_needed(
&self,
class: &str,
type_arguments: &[String],
) -> Result<String, RuntimeError> {
let mut target_class = class.to_string();
let user_defined_exists = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls.contains_key(class)
};
if user_defined_exists && !type_arguments.is_empty() {
let generic_decl = {
let box_decls = self.shared.box_declarations.read().unwrap();
box_decls.get(class).cloned()
};
if let Some(generic_decl) = generic_decl {
self.validate_generic_arguments(&generic_decl, type_arguments)?;
let specialized = self.specialize_generic_class(&generic_decl, type_arguments)?;
target_class = specialized.name.clone();
// Insert specialized declaration so registry can create it
let mut box_decls = self.shared.box_declarations.write().unwrap();
box_decls.insert(target_class.clone(), specialized);
}
}
Ok(target_class)
}
/// Create box via registry and optionally run user-defined constructor (birth/arity)
pub(super) fn new_create_via_registry_and_maybe_ctor(
&mut self,
target_class: &str,
args: Vec<Box<dyn NyashBox>>,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// Try unified registry (use interpreter's runtime registry to include user-defined boxes)
let registry = self.runtime.box_registry.clone();
let registry_lock = registry.lock().unwrap();
match registry_lock.create_box(target_class, &args) {
Ok(box_instance) => {
// Check if this is a user-defined box that needs constructor execution
if let Some(_instance_box) = box_instance
.as_any()
.downcast_ref::<crate::instance_v2::InstanceBox>()
{
// Check if we have a box declaration for this class
let (box_decl_opt, constructor_opt) = {
let box_decls = self.shared.box_declarations.read().unwrap();
if let Some(box_decl) = box_decls.get(target_class) {
// Find the birth constructor (unified constructor system)
let birth_key = format!("birth/{}", arguments.len());
let constructor = box_decl.constructors.get(&birth_key).cloned();
(Some(box_decl.clone()), constructor)
} else {
(None, None)
}
};
if let Some(box_decl) = box_decl_opt {
if let Some(constructor) = constructor_opt {
// Execute the constructor
let instance_arc: SharedNyashBox = Arc::from(box_instance);
drop(registry_lock); // Release lock before executing constructor
self.execute_constructor(
&instance_arc,
&constructor,
arguments,
&box_decl,
)?;
return Ok((*instance_arc).clone_box());
} else if arguments.is_empty() {
// No constructor needed for zero arguments
return Ok(box_instance);
} else {
return Err(RuntimeError::InvalidOperation {
message: format!(
"No constructor found for {} with {} arguments",
target_class,
arguments.len()
),
});
}
}
}
// Not a user-defined box or no constructor needed
Ok(box_instance)
}
Err(e) => {
// Fallback: handle basic built-in boxes directly (e.g., FutureBox)
// This keeps interpreter usability when registry has no provider.
drop(registry_lock);
match self.create_basic_box(target_class, arguments) {
Ok(b) => Ok(b),
Err(_) => Err(e),
}
}
}
}
/// new式を実行 - Object creation engine
pub(crate) fn execute_new(
&mut self,
class: &str,
arguments: &[ASTNode],
type_arguments: &[String],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 80/20 path: unified registry + constructor
let args = self.new_eval_args(arguments)?;
let target_class = self.new_specialize_if_needed(class, type_arguments)?;
self.new_create_via_registry_and_maybe_ctor(&target_class, args, arguments)
}
}

View File

@ -1,205 +0,0 @@
//! Basic type constructors for execute_new
//! Handles StringBox, IntegerBox, BoolBox, ArrayBox, etc.
use crate::ast::ASTNode;
use crate::box_trait::*;
use crate::boxes::map_box::MapBox;
use crate::boxes::null_box::NullBox;
use crate::boxes::FloatBox;
use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError};
impl Interpreter {
/// Create basic type boxes (StringBox, IntegerBox, BoolBox, etc.)
pub(super) fn create_basic_box(
&mut self,
class: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match class {
"StringBox" => {
// StringBoxは引数1個文字列値で作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"StringBox constructor expects 1 argument, got {}",
arguments.len()
),
});
}
let value = self.execute_expression(&arguments[0])?;
if let Some(s) = value.as_any().downcast_ref::<StringBox>() {
return Ok(Box::new(StringBox::new(s.value.clone())));
} else if let Some(i) = value.as_any().downcast_ref::<IntegerBox>() {
return Ok(Box::new(StringBox::new(i.value.to_string())));
} else if let Some(b) = value.as_any().downcast_ref::<BoolBox>() {
return Ok(Box::new(StringBox::new(b.value.to_string())));
} else {
return Ok(Box::new(StringBox::new(value.to_string_box().value)));
}
}
"IntegerBox" => {
// IntegerBoxは引数1個整数値で作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"IntegerBox constructor expects 1 argument, got {}",
arguments.len()
),
});
}
let value = self.execute_expression(&arguments[0])?;
if let Some(i) = value.as_any().downcast_ref::<IntegerBox>() {
return Ok(Box::new(IntegerBox::new(i.value)));
} else if let Some(s) = value.as_any().downcast_ref::<StringBox>() {
match s.value.parse::<i64>() {
Ok(n) => return Ok(Box::new(IntegerBox::new(n))),
Err(_) => {
return Err(RuntimeError::TypeError {
message: format!("Cannot convert '{}' to integer", s.value),
})
}
}
} else {
return Err(RuntimeError::TypeError {
message: "IntegerBox constructor requires integer or string argument"
.to_string(),
});
}
}
"BoolBox" => {
// BoolBoxは引数1個ブール値で作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"BoolBox constructor expects 1 argument, got {}",
arguments.len()
),
});
}
let value = self.execute_expression(&arguments[0])?;
if let Some(b) = value.as_any().downcast_ref::<BoolBox>() {
return Ok(Box::new(BoolBox::new(b.value)));
} else if let Some(s) = value.as_any().downcast_ref::<StringBox>() {
let val = match s.value.as_str() {
"true" => true,
"false" => false,
_ => {
return Err(RuntimeError::TypeError {
message: format!("Cannot convert '{}' to boolean", s.value),
})
}
};
return Ok(Box::new(BoolBox::new(val)));
} else {
return Err(RuntimeError::TypeError {
message: "BoolBox constructor requires boolean or string argument"
.to_string(),
});
}
}
"ArrayBox" => {
// ArrayBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"ArrayBox constructor expects 0 arguments, got {}",
arguments.len()
),
});
}
return Ok(Box::new(ArrayBox::new()));
}
"NullBox" => {
// NullBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"NullBox constructor expects 0 arguments, got {}",
arguments.len()
),
});
}
return Ok(Box::new(NullBox::new()));
}
"MapBox" => {
// MapBoxは引数なしで作成
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"MapBox constructor expects 0 arguments, got {}",
arguments.len()
),
});
}
let map_box = Box::new(MapBox::new()) as Box<dyn NyashBox>;
return Ok(map_box);
}
"FloatBox" => {
// FloatBoxは引数1個浮動小数点数値で作成
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"FloatBox constructor expects 1 argument, got {}",
arguments.len()
),
});
}
let value = self.execute_expression(&arguments[0])?;
if let Some(f) = value.as_any().downcast_ref::<FloatBox>() {
return Ok(Box::new(FloatBox::new(f.value)));
} else if let Some(i) = value.as_any().downcast_ref::<IntegerBox>() {
return Ok(Box::new(FloatBox::new(i.value as f64)));
} else if let Some(s) = value.as_any().downcast_ref::<StringBox>() {
match s.value.parse::<f64>() {
Ok(n) => return Ok(Box::new(FloatBox::new(n))),
Err(_) => {
return Err(RuntimeError::TypeError {
message: format!("Cannot convert '{}' to float", s.value),
})
}
}
} else {
return Err(RuntimeError::TypeError {
message: "FloatBox constructor requires float, integer, or string argument"
.to_string(),
});
}
}
"FutureBox" => {
// FutureBox([value]) — optional initial value
if arguments.len() > 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"FutureBox constructor expects 0 or 1 argument, got {}",
arguments.len()
),
});
}
let fut = crate::boxes::future::NyashFutureBox::new();
if arguments.len() == 1 {
let value = self.execute_expression(&arguments[0])?;
fut.set_result(value);
}
return Ok(Box::new(fut));
}
_ => {
// Not a basic type
Err(RuntimeError::TypeError {
message: format!("Not a basic type: {}", class),
})
}
}
}
}

View File

@ -1,87 +0,0 @@
//! Non-basic type constructors for execute_new
//! Handles MathBox, ConsoleBox, GUI boxes, Network boxes, etc.
use crate::ast::ASTNode;
use crate::box_trait::*;
use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError};
use crate::boxes::math_box::MathBox;
use crate::boxes::random_box::RandomBox;
use crate::boxes::sound_box::SoundBox;
use crate::boxes::debug_box::DebugBox;
use crate::box_factory::BoxFactory;
impl Interpreter {
/// Create non-basic type boxes (MathBox, ConsoleBox, GUI/Network boxes, etc.)
pub(super) fn create_non_basic_box(
&mut self,
class: &str,
arguments: &[ASTNode]
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match class {
"MathBox" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation { message: format!("MathBox constructor expects 0 arguments, got {}", arguments.len()) });
}
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(b) = reg.create_box("MathBox", &[]) { return Ok(b); }
}
// fallback to builtin
return Ok(Box::new(MathBox::new()) as Box<dyn NyashBox>);
}
"ConsoleBox" => {
// ConsoleBoxは引数なしで作成可能なら統一レジストリ経由でプラグイン優先
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("ConsoleBox constructor expects 0 arguments, got {}", arguments.len()),
});
}
// Delegate to unified registry so env-based plugin overrides apply consistently
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(b) = reg.create_box("ConsoleBox", &[]) {
return Ok(b);
}
}
// Fallback to builtin mock if registry path failed
return Ok(Box::new(crate::box_trait::ConsoleBox::new()) as Box<dyn NyashBox>);
}
"RandomBox" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation { message: format!("RandomBox constructor expects 0 arguments, got {}", arguments.len()) });
}
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(b) = reg.create_box("RandomBox", &[]) { return Ok(b); }
}
return Ok(Box::new(RandomBox::new()) as Box<dyn NyashBox>);
}
"SoundBox" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation { message: format!("SoundBox constructor expects 0 arguments, got {}", arguments.len()) });
}
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(b) = reg.create_box("SoundBox", &[]) { return Ok(b); }
}
return Ok(Box::new(SoundBox::new()) as Box<dyn NyashBox>);
}
"DebugBox" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation { message: format!("DebugBox constructor expects 0 arguments, got {}", arguments.len()) });
}
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(b) = reg.create_box("DebugBox", &[]) { return Ok(b); }
}
return Ok(Box::new(DebugBox::new()) as Box<dyn NyashBox>);
}
_ => {
// Not a non-basic type handled here
Err(RuntimeError::TypeError {
message: format!("Not a non-basic type handled in this method: {}", class),
})
}
}
}
}

View File

@ -1,405 +0,0 @@
/*!
* Operators Processing Module
*
* Extracted from expressions.rs
* Handles binary operations, unary operations, and operator helper functions
* Core philosophy: "Everything is Box" with type-safe operator overloading
*/
use super::*;
use crate::ast::UnaryOperator;
use crate::box_trait::{BoolBox, SharedNyashBox};
use crate::operator_traits::{DynamicAdd, DynamicSub, DynamicMul, DynamicDiv, OperatorError};
// ========================================================================================
// Helper Functions for Binary Operations
// ========================================================================================
pub(super) fn try_add_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
// IntegerBox + IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
return Some(Box::new(IntegerBox::new(left_int.value + right_int.value)));
}
// StringBox + anything -> concatenation
if let Some(left_str) = left.as_any().downcast_ref::<StringBox>() {
let right_str = right.to_string_box();
return Some(Box::new(StringBox::new(format!("{}{}", left_str.value, right_str.value))));
}
// BoolBox + BoolBox -> IntegerBox
if let (Some(left_bool), Some(right_bool)) = (
left.as_any().downcast_ref::<BoolBox>(),
right.as_any().downcast_ref::<BoolBox>()
) {
return Some(Box::new(IntegerBox::new((left_bool.value as i64) + (right_bool.value as i64))));
}
None
}
pub(super) fn try_sub_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
// IntegerBox - IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
return Some(Box::new(IntegerBox::new(left_int.value - right_int.value)));
}
None
}
pub(super) fn try_mul_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
// IntegerBox * IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
return Some(Box::new(IntegerBox::new(left_int.value * right_int.value)));
}
// StringBox * IntegerBox -> repetition
if let (Some(str_box), Some(count_int)) = (
left.as_any().downcast_ref::<StringBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
return Some(Box::new(StringBox::new(str_box.value.repeat(count_int.value as usize))));
}
None
}
pub(super) fn try_div_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Result<Box<dyn NyashBox>, String> {
// IntegerBox / IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
if right_int.value == 0 {
return Err("Division by zero".to_string());
}
return Ok(Box::new(IntegerBox::new(left_int.value / right_int.value)));
}
Err(format!("Division not supported between {} and {}", left.type_name(), right.type_name()))
}
pub(super) fn try_mod_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Result<Box<dyn NyashBox>, String> {
// IntegerBox % IntegerBox
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
if right_int.value == 0 {
return Err("Modulo by zero".to_string());
}
return Ok(Box::new(IntegerBox::new(left_int.value % right_int.value)));
}
Err(format!("Modulo not supported between {} and {}", left.type_name(), right.type_name()))
}
// ========================================================================================
// NyashInterpreter Implementation - Binary and Unary Operations
// ========================================================================================
impl NyashInterpreter {
/// Try to extract a UTF-8 string from a NyashBox: supports internal StringBox,
/// Result.Ok(String-like), and Plugin StringBox via toUtf8 (when plugins enabled).
fn try_box_to_string(&self, b: &dyn NyashBox) -> Option<String> {
// Internal StringBox
if let Some(sb) = b.as_any().downcast_ref::<StringBox>() { return Some(sb.value.clone()); }
// Result.Ok(inner) → recurse
if let Some(res) = b.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return self.try_box_to_string(inner.as_ref()); }
}
// Plugin StringBox via toUtf8
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) {
if let Some(vb) = val_opt {
if let Some(sb2) = vb.as_any().downcast_ref::<StringBox>() { return Some(sb2.value.clone()); }
}
}
}
}
}
}
None
}
/// 二項演算を実行
pub(super) fn execute_binary_op(&mut self, op: &BinaryOperator, left: &ASTNode, right: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError>
{
// 🎯 State-sharing evaluation for performance
let left_shared = self.execute_expression_shared(left)?;
let right_shared = self.execute_expression_shared(right)?;
let left_val = &**left_shared;
let right_val = &**right_shared;
match op {
BinaryOperator::Add => {
// Prefer string-like concatenation when either side is string-like
if let (Some(ls), Some(rs)) = (self.try_box_to_string(left_val), self.try_box_to_string(right_val)) {
return Ok(Box::new(StringBox::new(format!("{}{}", ls, rs))));
}
if let Some(ls) = self.try_box_to_string(left_val) {
return Ok(Box::new(StringBox::new(format!("{}{}", ls, right_val.to_string_box().value))));
}
if let Some(rs) = self.try_box_to_string(right_val) {
return Ok(Box::new(StringBox::new(format!("{}{}", left_val.to_string_box().value, rs))));
}
// Numeric fallback: if both sides stringify to valid integers, add them
{
let ls = left_val.to_string_box().value;
let rs = right_val.to_string_box().value;
if let (Ok(li), Ok(ri)) = (ls.trim().parse::<i64>(), rs.trim().parse::<i64>()) {
return Ok(Box::new(IntegerBox::new(li + ri)));
}
}
if let Some(result) = try_add_operation(left_val, right_val) {
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Cannot add {} and {}", left_val.type_name(), right_val.type_name()),
})
}
},
BinaryOperator::Subtract => {
if let Some(result) = try_sub_operation(left_val, right_val) {
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Cannot subtract {} from {}", right_val.type_name(), left_val.type_name()),
})
}
},
BinaryOperator::Multiply => {
if let Some(result) = try_mul_operation(left_val, right_val) {
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Cannot multiply {} and {}", left_val.type_name(), right_val.type_name()),
})
}
},
BinaryOperator::Divide => {
match try_div_operation(left_val, right_val) {
Ok(result) => Ok(result),
Err(msg) => Err(RuntimeError::InvalidOperation { message: msg }),
}
},
BinaryOperator::Modulo => {
match try_mod_operation(left_val, right_val) {
Ok(result) => Ok(result),
Err(msg) => Err(RuntimeError::InvalidOperation { message: msg }),
}
},
BinaryOperator::Equal => {
let result = self.compare_values(left_val, right_val)?;
Ok(Box::new(BoolBox::new(result)))
},
BinaryOperator::NotEqual => {
let result = self.compare_values(left_val, right_val)?;
Ok(Box::new(BoolBox::new(!result)))
},
BinaryOperator::LessThan => {
let result = self.less_than_values(left_val, right_val)?;
Ok(Box::new(BoolBox::new(result)))
},
BinaryOperator::LessThanOrEqual => {
let less = self.less_than_values(left_val, right_val)?;
let equal = self.compare_values(left_val, right_val)?;
Ok(Box::new(BoolBox::new(less || equal)))
},
BinaryOperator::GreaterThan => {
let less = self.less_than_values(left_val, right_val)?;
let equal = self.compare_values(left_val, right_val)?;
Ok(Box::new(BoolBox::new(!less && !equal)))
},
BinaryOperator::GreaterThanOrEqual => {
let less = self.less_than_values(left_val, right_val)?;
Ok(Box::new(BoolBox::new(!less)))
},
BinaryOperator::And => {
// Short-circuit evaluation
if !self.is_truthy(left_val) {
Ok(Box::new(BoolBox::new(false)))
} else {
Ok(Box::new(BoolBox::new(self.is_truthy(right_val))))
}
},
BinaryOperator::Or => {
// Short-circuit evaluation
if self.is_truthy(left_val) {
Ok(Box::new(BoolBox::new(true)))
} else {
Ok(Box::new(BoolBox::new(self.is_truthy(right_val))))
}
},
BinaryOperator::Shl => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) {
Ok(Box::new(IntegerBox::new(li.value.wrapping_shl((ri.value as u32) & 63))))
} else {
Err(RuntimeError::InvalidOperation { message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
}
},
BinaryOperator::Shr => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) {
Ok(Box::new(IntegerBox::new(((li.value as u64) >> ((ri.value as u32) & 63)) as i64)))
} else {
Err(RuntimeError::InvalidOperation { message: format!("Shift-right '>>' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
}
},
BinaryOperator::BitAnd => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) { Ok(Box::new(IntegerBox::new(li.value & ri.value))) } else { Err(RuntimeError::InvalidOperation { message: format!("Bitwise '&' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) }
},
BinaryOperator::BitOr => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) { Ok(Box::new(IntegerBox::new(li.value | ri.value))) } else { Err(RuntimeError::InvalidOperation { message: format!("Bitwise '|' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) }
},
BinaryOperator::BitXor => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) { Ok(Box::new(IntegerBox::new(li.value ^ ri.value))) } else { Err(RuntimeError::InvalidOperation { message: format!("Bitwise '^' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) }
},
}
}
/// 単項演算を実行
pub(super) fn execute_unary_op(&mut self, operator: &UnaryOperator, operand: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError>
{
let operand_shared = self.execute_expression_shared(operand)?;
let operand_val = &**operand_shared;
match operator {
UnaryOperator::Not => {
let is_truthy = self.is_truthy(operand_val);
Ok(Box::new(BoolBox::new(!is_truthy)))
},
UnaryOperator::Minus => {
if let Some(int_val) = operand_val.as_any().downcast_ref::<IntegerBox>() {
Ok(Box::new(IntegerBox::new(-int_val.value)))
} else {
Err(RuntimeError::InvalidOperation {
message: format!("Cannot negate {}", operand_val.type_name()),
})
}
},
}
}
// ========================================================================================
// Helper Methods for Comparisons
// ========================================================================================
/// 値の等価性を比較
pub(super) fn compare_values(&self, left: &dyn NyashBox, right: &dyn NyashBox) -> Result<bool, RuntimeError> {
// IntegerBox comparison
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
return Ok(left_int.value == right_int.value);
}
// String-like comparison (internal/Result.Ok/plugin StringBox)
if let (Some(ls), Some(rs)) = (self.try_box_to_string(left), self.try_box_to_string(right)) {
return Ok(ls == rs);
}
// BoolBox comparison
if let (Some(left_bool), Some(right_bool)) = (
left.as_any().downcast_ref::<BoolBox>(),
right.as_any().downcast_ref::<BoolBox>()
) {
return Ok(left_bool.value == right_bool.value);
}
// NullBox comparison
if left.type_name() == "NullBox" && right.type_name() == "NullBox" {
return Ok(true);
}
// Different types are not equal
Ok(false)
}
/// 値の大小関係を比較 (left < right)
pub(super) fn less_than_values(&self, left: &dyn NyashBox, right: &dyn NyashBox) -> Result<bool, RuntimeError> {
// IntegerBox comparison
if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>()
) {
return Ok(left_int.value < right_int.value);
}
// String-like comparison (lexicographic)
if let (Some(ls), Some(rs)) = (self.try_box_to_string(left), self.try_box_to_string(right)) {
return Ok(ls < rs);
}
Err(RuntimeError::InvalidOperation {
message: format!("Cannot compare {} and {}", left.type_name(), right.type_name()),
})
}
/// 値の真偽性を判定
pub(super) fn is_truthy(&self, value: &dyn NyashBox) -> bool {
// BoolBox
if let Some(bool_val) = value.as_any().downcast_ref::<BoolBox>() {
return bool_val.value;
}
// IntegerBox (0 is false, non-zero is true)
if let Some(int_val) = value.as_any().downcast_ref::<IntegerBox>() {
return int_val.value != 0;
}
// StringBox (empty string is false)
if let Some(str_val) = value.as_any().downcast_ref::<StringBox>() {
return !str_val.value.is_empty();
}
// NullBox is always false
if value.type_name() == "NullBox" {
return false;
}
// Everything else is true
true
}
}

View File

@ -1,171 +0,0 @@
//! Loader entrypoints for dynamic plugins
use std::ffi::{CString, c_char, c_void};
#[cfg(feature = "dynamic-file")]
use libloading::{Library, Symbol};
use crate::box_trait::NyashBox;
use crate::interpreter::RuntimeError;
use super::proxies::{FileBoxProxy, MathBoxProxy, RandomBoxProxy, TimeBoxProxy, DateTimeBoxProxy};
use super::types::{PLUGIN_CACHE, LoadedPlugin, PluginInfo};
/// Public plugin loader API
pub struct PluginLoader;
impl PluginLoader {
/// Load File plugin
#[cfg(feature = "dynamic-file")]
pub fn load_file_plugin() -> Result<(), RuntimeError> {
let mut cache = PLUGIN_CACHE.write().unwrap();
if cache.contains_key("file") { return Ok(()); }
let lib_name = if cfg!(target_os = "windows") { "nyash_file.dll" } else if cfg!(target_os = "macos") { "libnyash_file.dylib" } else { "libnyash_file.so" };
let possible_paths = vec![
format!("./target/release/{}", lib_name),
format!("./target/debug/{}", lib_name),
format!("./plugins/{}", lib_name),
format!("./{}", lib_name),
];
let lib_path = possible_paths.iter().find(|p| std::path::Path::new(p.as_str()).exists()).cloned()
.ok_or_else(|| RuntimeError::InvalidOperation { message: format!("Failed to find file plugin library. Searched paths: {:?}", possible_paths) })?;
unsafe {
let library = Library::new(&lib_path).map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to load file plugin: {}", e) })?;
let init_fn: Symbol<unsafe extern "C" fn() -> *const c_void> = library.get(b"nyash_plugin_init\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get plugin init: {}", e) })?;
let plugin_info_ptr = init_fn();
if plugin_info_ptr.is_null() { return Err(RuntimeError::InvalidOperation { message: "Plugin initialization failed".to_string() }); }
let info = PluginInfo { name: "file".to_string(), version: 1, api_version: 1 };
cache.insert("file".to_string(), LoadedPlugin { library, info });
}
Ok(())
}
/// Create FileBox
#[cfg(feature = "dynamic-file")]
pub fn create_file_box(path: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
Self::load_file_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("file") {
let c_path = CString::new(path).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid path string".to_string() })?;
unsafe {
let open_fn: Symbol<unsafe extern "C" fn(*const c_char) -> *mut c_void> = plugin.library.get(b"nyash_file_open\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_open: {}", e) })?;
let handle = open_fn(c_path.as_ptr());
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: format!("Failed to open file: {}", path) }); }
Ok(Box::new(FileBoxProxy::new(handle, path.to_string())))
}
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
}
/// Check FileBox existence
#[cfg(feature = "dynamic-file")]
pub fn file_exists(path: &str) -> Result<bool, RuntimeError> {
Self::load_file_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("file") {
let c_path = CString::new(path).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid path string".to_string() })?;
unsafe {
let exists_fn: Symbol<unsafe extern "C" fn(*const c_char) -> i32> = plugin.library.get(b"nyash_file_exists\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_exists: {}", e) })?;
Ok(exists_fn(c_path.as_ptr()) != 0)
}
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
}
/// Load Math plugin
#[cfg(feature = "dynamic-file")]
pub fn load_math_plugin() -> Result<(), RuntimeError> {
let mut cache = PLUGIN_CACHE.write().unwrap();
if cache.contains_key("math") { return Ok(()); }
let lib_name = if cfg!(target_os = "windows") { "nyash_math.dll" } else if cfg!(target_os = "macos") { "libnyash_math.dylib" } else { "libnyash_math.so" };
let possible_paths = vec![
format!("./target/release/{}", lib_name),
format!("./target/debug/{}", lib_name),
format!("./plugins/{}", lib_name),
format!("./{}", lib_name),
];
let lib_path = possible_paths.iter().find(|p| std::path::Path::new(p.as_str()).exists()).cloned()
.ok_or_else(|| RuntimeError::InvalidOperation { message: format!("Failed to find math plugin library. Searched paths: {:?}", possible_paths) })?;
unsafe {
let library = Library::new(&lib_path).map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to load math plugin: {}", e) })?;
let info = PluginInfo { name: "math".to_string(), version: 1, api_version: 1 };
cache.insert("math".to_string(), LoadedPlugin { library, info });
}
Ok(())
}
/// Create MathBox
#[cfg(feature = "dynamic-file")]
pub fn create_math_box() -> Result<Box<dyn NyashBox>, RuntimeError> {
Self::load_math_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let create_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_math_create\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_math_create: {}", e) })?;
let handle = create_fn();
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create MathBox".to_string() }); }
Ok(Box::new(MathBoxProxy::new(handle)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
/// Create RandomBox
#[cfg(feature = "dynamic-file")]
pub fn create_random_box() -> Result<Box<dyn NyashBox>, RuntimeError> {
Self::load_math_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let create_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_random_create\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_create: {}", e) })?;
let handle = create_fn();
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create RandomBox".to_string() }); }
Ok(Box::new(RandomBoxProxy::new(handle)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
/// Create TimeBox
#[cfg(feature = "dynamic-file")]
pub fn create_time_box() -> Result<Box<dyn NyashBox>, RuntimeError> {
Self::load_math_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let create_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_time_create\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_time_create: {}", e) })?;
let handle = create_fn();
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create TimeBox".to_string() }); }
Ok(Box::new(TimeBoxProxy::new(handle)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
/// Create DateTimeBox (now)
#[cfg(feature = "dynamic-file")]
pub fn create_datetime_now() -> Result<Box<dyn NyashBox>, RuntimeError> {
Self::load_math_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let now_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_time_now\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_time_now: {}", e) })?;
let handle = now_fn();
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create DateTimeBox".to_string() }); }
Ok(Box::new(DateTimeBoxProxy::new(handle)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
/// Create DateTimeBox from string
#[cfg(feature = "dynamic-file")]
pub fn create_datetime_from_string(time_str: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
Self::load_math_plugin()?;
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
let c_str = CString::new(time_str).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid time string".to_string() })?;
unsafe {
let parse_fn: Symbol<unsafe extern "C" fn(*const c_char) -> *mut c_void> = plugin.library.get(b"nyash_time_parse\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_time_parse: {}", e) })?;
let handle = parse_fn(c_str.as_ptr());
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: format!("Failed to parse time string: {}", time_str) }); }
Ok(Box::new(DateTimeBoxProxy::new(handle)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
}

View File

@ -1,23 +0,0 @@
//! Dynamic Plugin Loader for Nyash (split module)
//!
//! Refactored into smaller files to improve readability while preserving
//! the original public API surface used across the interpreter:
//! - types.rs: globals and native handles
//! - proxies.rs: Box proxy implementations
//! - loader.rs: public loader entrypoints
mod types;
mod proxies;
mod loader;
// Re-export to preserve original paths like
// crate::interpreter::plugin_loader::{PluginLoader, FileBoxProxy, ..., PLUGIN_CACHE}
pub use loader::PluginLoader;
pub use proxies::{
FileBoxProxy, MathBoxProxy, RandomBoxProxy, TimeBoxProxy, DateTimeBoxProxy,
};
pub use types::{
PLUGIN_CACHE, LoadedPlugin, PluginInfo, FileBoxHandle, MathBoxHandle,
RandomBoxHandle, TimeBoxHandle, DateTimeBoxHandle,
};

View File

@ -1,274 +0,0 @@
//! Proxies for dynamic plugins (File/Math/Random/Time/DateTime)
use std::ffi::{CStr, CString, c_char, c_void};
use std::sync::Arc;
#[cfg(feature = "dynamic-file")]
use libloading::Symbol;
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase, IntegerBox};
use crate::boxes::FloatBox;
use crate::interpreter::RuntimeError;
use super::types::{PLUGIN_CACHE, FileBoxHandle, MathBoxHandle, RandomBoxHandle, TimeBoxHandle, DateTimeBoxHandle};
use super::PluginLoader;
// ================== FileBoxProxy ==================
#[derive(Debug)]
pub struct FileBoxProxy {
pub(crate) handle: Arc<FileBoxHandle>,
pub(crate) path: String,
pub(crate) base: BoxBase,
}
unsafe impl Send for FileBoxProxy {}
unsafe impl Sync for FileBoxProxy {}
impl FileBoxProxy {
pub fn new(handle: *mut c_void, path: String) -> Self {
FileBoxProxy { handle: Arc::new(FileBoxHandle { ptr: handle }), path, base: BoxBase::new() }
}
pub fn read(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
#[cfg(feature = "dynamic-file")]
{
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("file") {
unsafe {
let read_fn: Symbol<unsafe extern "C" fn(*mut c_void) -> *mut c_char> =
plugin.library.get(b"nyash_file_read\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_read: {}", e) })?;
let result_ptr = read_fn(self.handle.ptr);
if result_ptr.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to read file".to_string() }); }
let content = CStr::from_ptr(result_ptr).to_string_lossy().into_owned();
let free_fn: Symbol<unsafe extern "C" fn(*mut c_char)> =
plugin.library.get(b"nyash_string_free\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_string_free: {}", e) })?;
free_fn(result_ptr);
Ok(Box::new(StringBox::new(content)))
}
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
}
#[cfg(not(feature = "dynamic-file"))]
{ Err(RuntimeError::InvalidOperation { message: "Dynamic file support not enabled".to_string() }) }
}
pub fn write(&self, content: Box<dyn NyashBox>) -> Result<Box<dyn NyashBox>, RuntimeError> {
#[cfg(feature = "dynamic-file")]
{
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("file") {
let content_str = content.to_string_box().value;
let c_content = CString::new(content_str).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid content string".to_string() })?;
unsafe {
let write_fn: Symbol<unsafe extern "C" fn(*mut c_void, *const c_char) -> i32> =
plugin.library.get(b"nyash_file_write\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_write: {}", e) })?;
let result = write_fn(self.handle.ptr, c_content.as_ptr());
if result == 0 { return Err(RuntimeError::InvalidOperation { message: "Failed to write file".to_string() }); }
Ok(Box::new(StringBox::new("ok")))
}
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
}
#[cfg(not(feature = "dynamic-file"))]
{ Err(RuntimeError::InvalidOperation { message: "Dynamic file support not enabled".to_string() }) }
}
pub fn exists(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
Ok(Box::new(BoolBox::new(std::path::Path::new(&self.path).exists())))
}
}
impl BoxCore for FileBoxProxy {
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 std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
impl NyashBox for FileBoxProxy {
fn type_name(&self) -> &'static str { "FileBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_file_box(&self.path) { Ok(b) => b, Err(_) => Box::new(FileBoxProxy::new(self.handle.ptr, self.path.clone())) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FileBox({})", self.path)) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<FileBoxProxy>().is_some().into() }
}
impl std::fmt::Display for FileBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
// ================== MathBoxProxy ==================
#[derive(Debug)]
pub struct MathBoxProxy { pub(crate) handle: Arc<MathBoxHandle>, pub(crate) base: BoxBase }
unsafe impl Send for MathBoxProxy {}
unsafe impl Sync for MathBoxProxy {}
impl MathBoxProxy { pub fn new(handle: *mut c_void) -> Self { MathBoxProxy { handle: Arc::new(MathBoxHandle { ptr: handle }), base: BoxBase::new() } } }
impl BoxCore for MathBoxProxy {
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, "MathBox") }
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
impl NyashBox for MathBoxProxy {
fn type_name(&self) -> &'static str { "MathBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_math_box() { Ok(new_box) => new_box, Err(_) => Box::new(MathBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new("MathBox") }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<MathBoxProxy>().is_some().into() }
}
impl std::fmt::Display for MathBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
// ================== RandomBoxProxy ==================
#[derive(Debug)]
pub struct RandomBoxProxy { pub(crate) handle: Arc<RandomBoxHandle>, pub(crate) base: BoxBase }
unsafe impl Send for RandomBoxProxy {}
unsafe impl Sync for RandomBoxProxy {}
impl RandomBoxProxy { pub fn new(handle: *mut c_void) -> Self { RandomBoxProxy { handle: Arc::new(RandomBoxHandle { ptr: handle }), base: BoxBase::new() } } }
impl RandomBoxProxy {
pub fn next(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
#[cfg(feature = "dynamic-file")]
{
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let next_fn: Symbol<unsafe extern "C" fn(*mut c_void) -> f64> = plugin.library.get(b"nyash_random_next\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_next: {}", e) })?;
let value = next_fn(self.handle.ptr);
Ok(Box::new(FloatBox::new(value)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
#[cfg(not(feature = "dynamic-file"))]
{ Err(RuntimeError::InvalidOperation { message: "Dynamic loading not enabled".to_string() }) }
}
pub fn range(&self, min: f64, max: f64) -> Result<Box<dyn NyashBox>, RuntimeError> {
#[cfg(feature = "dynamic-file")]
{
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let range_fn: Symbol<unsafe extern "C" fn(*mut c_void, f64, f64) -> f64> = plugin.library.get(b"nyash_random_range\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_range: {}", e) })?;
let value = range_fn(self.handle.ptr, min, max);
Ok(Box::new(FloatBox::new(value)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
#[cfg(not(feature = "dynamic-file"))]
{ Err(RuntimeError::InvalidOperation { message: "Dynamic loading not enabled".to_string() }) }
}
pub fn int(&self, min: i64, max: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
#[cfg(feature = "dynamic-file")]
{
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
let int_fn: Symbol<unsafe extern "C" fn(*mut c_void, i64, i64) -> i64> = plugin.library.get(b"nyash_random_int\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_int: {}", e) })?;
let value = int_fn(self.handle.ptr, min, max);
Ok(Box::new(IntegerBox::new(value)))
}
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
}
#[cfg(not(feature = "dynamic-file"))]
{ Err(RuntimeError::InvalidOperation { message: "Dynamic loading not enabled".to_string() }) }
}
}
impl BoxCore for RandomBoxProxy {
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, "RandomBox") }
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
impl NyashBox for RandomBoxProxy {
fn type_name(&self) -> &'static str { "RandomBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_random_box() { Ok(new_box) => new_box, Err(_) => Box::new(RandomBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new("RandomBox") }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<RandomBoxProxy>().is_some().into() }
}
impl std::fmt::Display for RandomBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
// ================== TimeBoxProxy ==================
#[derive(Debug)]
pub struct TimeBoxProxy { pub(crate) handle: Arc<TimeBoxHandle>, pub(crate) base: BoxBase }
unsafe impl Send for TimeBoxProxy {}
unsafe impl Sync for TimeBoxProxy {}
impl TimeBoxProxy { pub fn new(handle: *mut c_void) -> Self { TimeBoxProxy { handle: Arc::new(TimeBoxHandle { ptr: handle }), base: BoxBase::new() } } }
impl BoxCore for TimeBoxProxy {
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, "TimeBox") }
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
impl NyashBox for TimeBoxProxy {
fn type_name(&self) -> &'static str { "TimeBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_time_box() { Ok(new_box) => new_box, Err(_) => Box::new(TimeBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new("TimeBox") }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<TimeBoxProxy>().is_some().into() }
}
impl std::fmt::Display for TimeBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
// ================== DateTimeBoxProxy ==================
#[derive(Debug)]
pub struct DateTimeBoxProxy { pub(crate) handle: Arc<DateTimeBoxHandle>, pub(crate) base: BoxBase }
unsafe impl Send for DateTimeBoxProxy {}
unsafe impl Sync for DateTimeBoxProxy {}
impl DateTimeBoxProxy { pub fn new(handle: *mut c_void) -> Self { DateTimeBoxProxy { handle: Arc::new(DateTimeBoxHandle { ptr: handle }), base: BoxBase::new() } } }
impl BoxCore for DateTimeBoxProxy {
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, "DateTimeBox") }
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
impl NyashBox for DateTimeBoxProxy {
fn type_name(&self) -> &'static str { "DateTimeBox" }
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_datetime_now() { Ok(new_box) => new_box, Err(_) => Box::new(DateTimeBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
fn to_string_box(&self) -> StringBox { StringBox::new("DateTimeBox") }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_datetime) = other.as_any().downcast_ref::<DateTimeBoxProxy>() {
#[cfg(feature = "dynamic-file")]
{
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
if let Ok(timestamp_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void) -> i64>>(b"nyash_datetime_timestamp\0") {
let this_ts = timestamp_fn(self.handle.ptr);
let other_ts = timestamp_fn(other_datetime.handle.ptr);
return BoolBox::new(this_ts == other_ts);
}
}
}
}
BoolBox::new(false)
} else { BoolBox::new(false) }
}
}
impl std::fmt::Display for DateTimeBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }

View File

@ -1,159 +0,0 @@
//! Types and globals for interpreter plugin loader
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::ffi::c_void;
#[cfg(feature = "dynamic-file")]
use libloading::Library;
lazy_static::lazy_static! {
/// Global cache for loaded plugins (keyed by simple name like "file" or "math")
pub(crate) static ref PLUGIN_CACHE: RwLock<HashMap<String, LoadedPlugin>> = RwLock::new(HashMap::new());
}
/// Loaded plugin handle + basic info
#[cfg(feature = "dynamic-file")]
pub(crate) struct LoadedPlugin {
pub(crate) library: Library,
pub(crate) info: PluginInfo,
}
/// Minimal plugin info (simplified)
#[derive(Clone)]
pub(crate) struct PluginInfo {
pub(crate) name: String,
pub(crate) version: u32,
pub(crate) api_version: u32,
}
/// FileBox native handle wrapper
#[derive(Debug)]
pub(crate) struct FileBoxHandle { pub(crate) ptr: *mut c_void }
impl Drop for FileBoxHandle {
fn drop(&mut self) {
#[cfg(feature = "dynamic-file")]
{
if !self.ptr.is_null() {
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("file") {
unsafe {
use libloading::Symbol;
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_file_free\0") {
free_fn(self.ptr);
}
}
}
}
}
}
}
unsafe impl Send for FileBoxHandle {}
unsafe impl Sync for FileBoxHandle {}
/// MathBox native handle wrapper
#[derive(Debug)]
pub(crate) struct MathBoxHandle { pub(crate) ptr: *mut c_void }
impl Drop for MathBoxHandle {
fn drop(&mut self) {
#[cfg(feature = "dynamic-file")]
{
if !self.ptr.is_null() {
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
use libloading::Symbol;
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_math_free\0") {
free_fn(self.ptr);
}
}
}
}
}
}
}
unsafe impl Send for MathBoxHandle {}
unsafe impl Sync for MathBoxHandle {}
/// RandomBox native handle wrapper
#[derive(Debug)]
pub(crate) struct RandomBoxHandle { pub(crate) ptr: *mut c_void }
impl Drop for RandomBoxHandle {
fn drop(&mut self) {
#[cfg(feature = "dynamic-file")]
{
if !self.ptr.is_null() {
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
use libloading::Symbol;
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_random_free\0") {
free_fn(self.ptr);
}
}
}
}
}
}
}
unsafe impl Send for RandomBoxHandle {}
unsafe impl Sync for RandomBoxHandle {}
/// TimeBox native handle wrapper
#[derive(Debug)]
pub(crate) struct TimeBoxHandle { pub(crate) ptr: *mut c_void }
impl Drop for TimeBoxHandle {
fn drop(&mut self) {
#[cfg(feature = "dynamic-file")]
{
if !self.ptr.is_null() {
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
use libloading::Symbol;
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_time_free\0") {
free_fn(self.ptr);
}
}
}
}
}
}
}
unsafe impl Send for TimeBoxHandle {}
unsafe impl Sync for TimeBoxHandle {}
/// DateTimeBox native handle wrapper
#[derive(Debug)]
pub(crate) struct DateTimeBoxHandle { pub(crate) ptr: *mut c_void }
impl Drop for DateTimeBoxHandle {
fn drop(&mut self) {
#[cfg(feature = "dynamic-file")]
{
if !self.ptr.is_null() {
let cache = PLUGIN_CACHE.read().unwrap();
if let Some(plugin) = cache.get("math") {
unsafe {
use libloading::Symbol;
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_datetime_free\0") {
free_fn(self.ptr);
}
}
}
}
}
}
}
unsafe impl Send for DateTimeBoxHandle {}
unsafe impl Sync for DateTimeBoxHandle {}

View File

@ -1,242 +0,0 @@
/*!
* Special Methods Module
*
* Extracted from box_methods.rs
* Contains specialized Box method implementations:
*
* - execute_method_box_method (MethodBox) - イベントハンドラー/関数ポインタ機能
* - execute_sound_method (SoundBox) - オーディオ機能
*
* These are critical special-purpose Box implementations:
* - MethodBox: Essential for event handling and callback functionality
* - SoundBox: Essential for audio feedback and game sound effects
*/
use super::*;
use crate::boxes::SoundBox;
use crate::instance_v2::InstanceBox;
use crate::method_box::MethodBox;
impl NyashInterpreter {
/// SoundBoxのメソッド呼び出しを実行
///
/// SoundBoxはオーディオ機能を提供する重要なBox:
/// - beep(), beeps() - 基本的なビープ音
/// - tone() - カスタム周波数/期間の音
/// - alert(), success(), error() - UI音効果
/// - pattern() - 音パターン再生
/// - volumeTest() - 音量テスト
/// - interval() - 間隔付き音再生
pub(super) fn execute_sound_method(
&mut self,
sound_box: &SoundBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"beep" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("beep() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(sound_box.beep())
}
"beeps" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("beeps() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(sound_box.beeps(arg_values[0].clone_box()))
}
"tone" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("tone() expects 2 arguments, got {}", arg_values.len()),
});
}
Ok(sound_box.tone(arg_values[0].clone_box(), arg_values[1].clone_box()))
}
"alert" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("alert() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(sound_box.alert())
}
"success" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("success() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(sound_box.success())
}
"error" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("error() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(sound_box.error())
}
"pattern" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("pattern() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(sound_box.pattern(arg_values[0].clone_box()))
}
"volumeTest" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"volumeTest() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(sound_box.volumeTest())
}
"interval" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"interval() expects 2 arguments, got {}",
arg_values.len()
),
});
}
Ok(sound_box.interval(arg_values[0].clone_box(), arg_values[1].clone_box()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown SoundBox method: {}", method),
}),
}
}
/// MethodBoxのメソッド呼び出しを実行
///
/// MethodBoxはイベントハンドラー機能の核心:
/// - invoke() - メソッド参照を実際に呼び出し
/// - 関数ポインタ相当の機能を提供
/// - GUI/イベント駆動プログラミングに必須
pub(super) fn execute_method_box_method(
&mut self,
method_box: &MethodBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"invoke" => {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// MethodBoxのinvokeを呼び出す
self.invoke_method_box(method_box, arg_values)
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown MethodBox method: {}", method),
}),
}
}
/// MethodBoxでメソッドを実際に呼び出す
///
/// この関数はMethodBoxの中核機能:
/// 1. インスタンスとメソッド名からメソッドを取得
/// 2. 引数数の検証
/// 3. local変数スタック管理
/// 4. 'me' 変数の設定
/// 5. メソッド実行
/// 6. 戻り値処理
fn invoke_method_box(
&mut self,
method_box: &MethodBox,
args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// インスタンスを取得
let instance_arc = method_box.get_instance();
let instance = instance_arc.lock().unwrap();
// InstanceBoxにダウンキャスト
if let Some(instance_box) = instance.as_any().downcast_ref::<InstanceBox>() {
// メソッドを取得
let method_ast = instance_box
.get_method(&method_box.method_name)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Method '{}' not found", method_box.method_name),
})?
.clone();
// メソッド呼び出しを実行
if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
// パラメータ数チェック
if args.len() != params.len() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"Method {} expects {} arguments, got {}",
method_box.method_name,
params.len(),
args.len()
),
});
}
// local変数スタックを保存
let saved_locals = self.save_local_vars();
self.local_vars.clear();
// meをlocal変数として設定インスタンス自体
self.declare_local_variable("me", instance.clone_or_share());
// パラメータをlocal変数として設定
for (param, arg) in params.iter().zip(args.iter()) {
self.declare_local_variable(param, arg.clone_or_share());
}
// メソッド本体を実行
let mut result = Box::new(crate::box_trait::VoidBox::new()) as Box<dyn NyashBox>;
for statement in &body {
result = self.execute_statement(statement)?;
// return文チェック
if let super::ControlFlow::Return(ret_val) = &self.control_flow {
result = ret_val.clone_box();
self.control_flow = super::ControlFlow::None;
break;
}
}
// local変数スタックを復元
self.restore_local_vars(saved_locals);
Ok(result)
} else {
Err(RuntimeError::InvalidOperation {
message: format!(
"Method '{}' is not a valid function declaration",
method_box.method_name
),
})
}
} else {
Err(RuntimeError::TypeError {
message: "MethodBox instance is not an InstanceBox".to_string(),
})
}
}
}

View File

@ -1,46 +0,0 @@
use super::{BoxDeclaration, StaticBoxDefinition};
use crate::instance_v2::InstanceBox;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex, RwLock};
/// スレッド間で共有される状態
#[derive(Clone)]
pub struct SharedState {
/// 🌍 GlobalBox - すべてのトップレベル関数とグローバル変数を管理
pub global_box: Arc<Mutex<InstanceBox>>,
/// Box宣言のレジストリ読み込みが多いのでRwLock
pub box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>,
/// 🔥 静的関数のレジストリ読み込みが多いのでRwLock
pub static_functions: Arc<RwLock<HashMap<String, HashMap<String, crate::ast::ASTNode>>>>,
/// 🔥 Static Box定義レジストリ遅延初期化用
pub static_box_definitions: Arc<RwLock<HashMap<String, StaticBoxDefinition>>>,
/// 読み込み済みファイル(重複防止)
pub included_files: Arc<Mutex<HashSet<String>>>,
/// includeロード中スタック循環検出用: A -> B -> A を検出)
pub include_stack: Arc<Mutex<Vec<String>>>,
}
impl SharedState {
/// 新しい共有状態を作成
pub fn new() -> Self {
let global_box = InstanceBox::new(
"Global".to_string(),
vec![], // フィールド名(空から始める)
HashMap::new(), // メソッド(グローバル関数)
);
Self {
global_box: Arc::new(Mutex::new(global_box)),
box_declarations: Arc::new(RwLock::new(HashMap::new())),
static_functions: Arc::new(RwLock::new(HashMap::new())),
static_box_definitions: Arc::new(RwLock::new(HashMap::new())),
included_files: Arc::new(Mutex::new(HashSet::new())),
include_stack: Arc::new(Mutex::new(Vec::new())),
}
}
}

View File

@ -1,800 +0,0 @@
/*!
* Statement Processing Module
*
* Extracted from core.rs - statement execution engine
* Handles all statement types: assignments, if/else, loops, control flow
* Core philosophy: "Everything is Box" with structured statement processing
*/
use super::BuiltinStdlib;
use super::*;
use crate::boxes::ref_cell_box::RefCellBox;
use std::sync::Arc;
// Conditional debug macro - unified with utils::debug_on()
macro_rules! debug_trace {
($($arg:tt)*) => {
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
};
}
// Local debug helper
macro_rules! idebug {
($($arg:tt)*) => {
if crate::interpreter::utils::debug_on() { eprintln!($($arg)*); }
};
}
impl NyashInterpreter {
fn warn_if_must_use(&self, value: &Box<dyn NyashBox>) {
if std::env::var("NYASH_LINT_MUSTUSE").unwrap_or_default() != "1" {
return;
}
if !self.discard_context {
return;
}
// 重資源のヒューリスティクス: プラグインBox、またはHTTP/Socket/File系の型名
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
eprintln!("[lint:must_use] Discarded resource value (plugin box). Consider assigning it or calling fini().");
return;
}
}
let ty = value.type_name();
let heavy = matches!(
ty,
"FileBox"
| "SocketBox"
| "SocketServerBox"
| "SocketClientBox"
| "SocketConnBox"
| "HTTPServerBox"
| "HTTPRequestBox"
| "HTTPResponseBox"
| "HttpClientBox"
);
if heavy {
eprintln!(
"[lint:must_use] Discarded {} value. Consider assigning it or calling fini().",
ty
);
}
}
/// 文を実行 - Core statement execution engine
pub(crate) fn execute_statement(
&mut self,
statement: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match statement {
ASTNode::Assignment { target, value, .. } => self.execute_assignment(target, value),
ASTNode::Print { expression, .. } => {
let value = self.execute_expression(expression)?;
println!("{}", value.to_string_box());
Ok(Box::new(VoidBox::new()))
}
ASTNode::If {
condition,
then_body,
else_body,
..
} => self.execute_if(condition, then_body, else_body),
ASTNode::Loop {
condition, body, ..
} => self.execute_loop(condition, body),
ASTNode::Return { value, .. } => {
let return_value = if let Some(val) = value {
self.execute_expression(val)?
} else {
Box::new(VoidBox::new())
};
// Optional diagnostic: trace return value (type + string view)
if std::env::var("NYASH_INT_RET_TRACE").ok().as_deref() == Some("1") {
let ty = return_value.type_name();
let sv = return_value.to_string_box().value;
eprintln!("[INT-RET] return set: type={} value={}", ty, sv);
}
self.control_flow = super::ControlFlow::Return(return_value);
Ok(Box::new(VoidBox::new()))
}
ASTNode::Break { .. } => {
self.control_flow = super::ControlFlow::Break;
Ok(Box::new(VoidBox::new()))
}
ASTNode::Continue { .. } => {
self.control_flow = super::ControlFlow::Continue;
Ok(Box::new(VoidBox::new()))
}
ASTNode::Nowait {
variable,
expression,
..
} => self.execute_nowait(variable, expression),
ASTNode::UsingStatement { namespace_name, .. } => {
self.execute_using_statement(namespace_name)
}
ASTNode::ImportStatement { path, alias, .. } => {
// Stage-0 import: no-op (record/log only)
if std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1") {
if let Some(a) = alias {
eprintln!("[import] {} as {}", path, a);
} else {
eprintln!("[import] {}", path);
}
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::BoxDeclaration {
name,
fields,
public_fields,
private_fields,
methods,
constructors,
init_fields,
weak_fields,
is_interface,
extends,
implements,
type_parameters,
is_static,
static_init,
..
} => {
if *is_static {
// 🔥 Static Box宣言の処理
self.register_static_box_declaration(
name.clone(),
fields.clone(),
methods.clone(),
init_fields.clone(),
weak_fields.clone(), // 🔗 Add weak_fields parameter
static_init.clone(),
extends.clone(),
implements.clone(),
type_parameters.clone(),
)?;
} else {
// 通常のBox宣言の処理 - 🔥 コンストラクタオーバーロード禁止対応
self.register_box_declaration(
name.clone(),
fields.clone(),
public_fields.clone(),
private_fields.clone(),
methods.clone(),
constructors.clone(),
init_fields.clone(),
weak_fields.clone(), // 🔗 Add weak_fields parameter
*is_interface,
extends.clone(),
implements.clone(),
type_parameters.clone(), // 🔥 ジェネリクス型パラメータ追加
)?; // 🔥 エラーハンドリング追加
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::FunctionDeclaration {
name,
params,
body,
is_static,
..
} => {
if *is_static {
// 🔥 静的関数box名.関数名の形式で解析
if let Some(dot_pos) = name.find('.') {
let box_name = name[..dot_pos].to_string();
let func_name = name[dot_pos + 1..].to_string();
// boxのstaticメソッドとして登録
let func_ast = ASTNode::FunctionDeclaration {
name: func_name.clone(),
params: params.clone(),
body: body.clone(),
is_static: true,
is_override: false,
span: crate::ast::Span::unknown(),
};
{
let mut static_funcs = self.shared.static_functions.write().unwrap();
static_funcs
.entry(box_name.clone())
.or_insert_with(HashMap::new)
.insert(func_name.clone(), func_ast);
}
idebug!("🔥 Static function '{}.{}' registered", box_name, func_name);
} else {
// box名なしのstatic関数将来的にはエラーにする
idebug!(
"⚠️ Static function '{}' needs box prefix (e.g., Math.min)",
name
);
}
} else {
// 通常の関数従来通りGlobalBoxメソッドとして登録
self.register_function_declaration(name.clone(), params.clone(), body.clone());
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::GlobalVar { name, value, .. } => {
let val = self.execute_expression(value)?;
// 🌍 革命的グローバル変数GlobalBoxのフィールドとして設定
self.set_variable(name, val.clone_or_share())?;
Ok(Box::new(VoidBox::new()))
}
ASTNode::TryCatch {
try_body,
catch_clauses,
finally_body,
..
} => self.execute_try_catch(try_body, catch_clauses, finally_body),
ASTNode::Throw { expression, .. } => self.execute_throw(expression),
ASTNode::Local {
variables,
initial_values,
..
} => {
// 🌍 革命的local変数宣言local変数スタックに追加初期化対応
for (i, var_name) in variables.iter().enumerate() {
if let Some(Some(init_expr)) = initial_values.get(i) {
// 🚀 初期化付きlocal宣言: local x = value
let init_value = self.execute_expression(init_expr)?;
self.declare_local_variable(var_name, init_value);
} else {
// 従来のlocal宣言: local x
self.declare_local_variable(var_name, Box::new(VoidBox::new()));
}
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::Outbox {
variables,
initial_values,
..
} => {
// 📤 革命的outbox変数宣言static関数内で所有権移転初期化対応
for (i, var_name) in variables.iter().enumerate() {
if let Some(Some(init_expr)) = initial_values.get(i) {
// 🚀 初期化付きoutbox宣言: outbox x = value
let init_value = self.execute_expression(init_expr)?;
self.declare_outbox_variable(var_name, init_value);
} else {
// 従来のoutbox宣言: outbox x
self.declare_outbox_variable(var_name, Box::new(VoidBox::new()));
}
}
Ok(Box::new(VoidBox::new()))
}
// 式文結果は多くの場合破棄されるため、must_use警告を出力
_ => {
let v = self.execute_expression(statement)?;
self.warn_if_must_use(&v);
Ok(v)
}
}
}
/// 条件分岐を実行 - If/else statement processing
pub(super) fn execute_if(
&mut self,
condition: &ASTNode,
then_body: &[ASTNode],
else_body: &Option<Vec<ASTNode>>,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let condition_value = self.execute_expression(condition)?;
// 条件を真偉値として評価
let is_true = self.is_truthy(&condition_value);
if is_true {
eprintln!("[dbg] if-then enter");
for statement in then_body {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
eprintln!("[dbg] if-then exit");
} else if let Some(else_statements) = else_body {
eprintln!("[dbg] if-else enter");
for statement in else_statements {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
eprintln!("[dbg] if-else exit");
}
Ok(Box::new(VoidBox::new()))
}
/// ループを実行 - Loop processing: loop(condition) { body } のみ
pub(super) fn execute_loop(
&mut self,
condition: &Box<ASTNode>,
body: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
loop {
// 常に条件をチェック
let condition_result = self.execute_expression(condition)?;
if let Some(bool_box) = condition_result.as_any().downcast_ref::<BoolBox>() {
if !bool_box.value {
break; // 条件がfalseの場合はループ終了
}
} else {
// 条件が真偉値でない場合は、Interpreter::is_truthy()を使用
if !self.is_truthy(&condition_result) {
break;
}
}
// ループ本体を実行
for statement in body {
self.execute_statement(statement)?;
match &self.control_flow {
super::ControlFlow::Break => {
self.control_flow = super::ControlFlow::None;
return Ok(Box::new(VoidBox::new()));
}
super::ControlFlow::Continue => {
self.control_flow = super::ControlFlow::None;
continue;
}
super::ControlFlow::Return(_) => {
// returnはループを抜けるが、上位に伝播
return Ok(Box::new(VoidBox::new()));
}
super::ControlFlow::Throw(_) => {
// 例外はループを抜けて上位に伝播
return Ok(Box::new(VoidBox::new()));
}
super::ControlFlow::None => {}
}
}
}
Ok(Box::new(VoidBox::new()))
}
/// 代入処理を実行 - Assignment processing
pub(super) fn execute_assignment(
&mut self,
target: &ASTNode,
value: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let val = self.execute_expression(value)?;
match target {
ASTNode::Variable { name, .. } => {
// 🌍 革命的代入local変数 → GlobalBoxフィールド
// 🔗 DEMO: Weak Reference Invalidation Simulation
// If we're setting a variable to 0, simulate "dropping" the previous value
if val.to_string_box().value == "0" {
debug_trace!(
"🔗 DEBUG: Variable '{}' set to 0 - simulating object drop",
name
);
// Get the current value before dropping it
if let Ok(old_value) = self.resolve_variable(name) {
let old_value_str = old_value.to_string_box().value;
debug_trace!("🔗 DEBUG: Old value being dropped: {}", old_value_str);
// For demo purposes, if we're dropping a "parent" variable,
// manually invalidate weak references to Parent instances
if name.contains("parent") && old_value_str.contains("instance #") {
debug_trace!(
"🔗 DEBUG: Triggering weak reference invalidation for: {}",
old_value_str
);
// Call the interpreter method with actual object info
self.trigger_weak_reference_invalidation(&old_value_str);
}
}
}
// Assign-by-share for plugin handle types; clone for others
let assigned = {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{
val.clone_box()
}
};
// セル反映: 既存が RefCellBox なら中身のみ置換
if let Ok(existing) = self.resolve_variable(name) {
if let Some(rc) = existing.as_any().downcast_ref::<RefCellBox>() {
rc.replace(assigned);
return Ok(val);
}
}
self.set_variable(name, assigned)?;
Ok(val)
}
ASTNode::FieldAccess { object, field, .. } => {
// フィールドへの代入
// 内部me/thisからの代入かどうか
let is_internal = match &**object {
ASTNode::This { .. } | ASTNode::Me { .. } => true,
ASTNode::Variable { name, .. } if name == "me" => true,
_ => false,
};
let obj_value = self.execute_expression(object)?;
if let Some(instance) = obj_value.as_any().downcast_ref::<InstanceBox>() {
// 可視性チェック(外部アクセスの場合のみ)
if !is_internal {
let box_decls = self.shared.box_declarations.read().unwrap();
if let Some(box_decl) = box_decls.get(&instance.class_name) {
let has_visibility = !box_decl.public_fields.is_empty()
|| !box_decl.private_fields.is_empty();
if has_visibility
&& !box_decl.public_fields.contains(&field.to_string())
{
return Err(RuntimeError::InvalidOperation {
message: format!(
"Field '{}' is private in {}",
field, instance.class_name
),
});
}
}
}
// 🔥 finiは何回呼ばれてもエラーにしないユーザー要求
// is_finalized()チェックを削除
// 🔗 Weak Reference Assignment Check
let box_decls = self.shared.box_declarations.read().unwrap();
if let Some(box_decl) = box_decls.get(&instance.class_name) {
if box_decl.weak_fields.contains(&field.to_string()) {
debug_trace!(
"🔗 DEBUG: Assigning to weak field '{}' in class '{}'",
field,
instance.class_name
);
// 🎯 PHASE 2: Use the new legacy conversion helper
instance
.set_weak_field_from_legacy(field.to_string(), val.clone_box())
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
return Ok(val);
}
}
// 🚨 フィールド差し替え時の自動finiは削除Nyashの明示的哲学
// プログラマーが必要なら明示的にfini()を呼ぶべき
// Store-by-share for plugin handle types; clone for others
let stored = {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{
val.clone_box()
}
};
// セル反映: 既存フィールドが RefCellBox なら中身を置換
if let Some(cur) = instance.get_field(field) {
if let Some(rc) = cur.as_any().downcast_ref::<RefCellBox>() {
rc.replace(stored);
return Ok(val);
}
}
instance
.set_field(field, Arc::from(stored))
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
Err(RuntimeError::TypeError {
message: format!("Cannot set field '{}' on non-instance type", field),
})
}
}
ASTNode::ThisField { field, .. } => {
// 🌍 革命的this.field代入local変数から取得
let this_value =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = (*this_value).as_any().downcast_ref::<InstanceBox>() {
// 🔥 finiは何回呼ばれてもエラーにしないユーザー要求
// is_finalized()チェックを削除
// 🚨 フィールド差し替え時の自動finiは削除Nyashの明示的哲学
// プログラマーが必要なら明示的にfini()を呼ぶべき
let stored = {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{
val.clone_box()
}
};
// セル反映: 既存フィールドが RefCellBox なら中身を置換
if let Some(cur) = instance.get_field(field) {
if let Some(rc) = cur.as_any().downcast_ref::<RefCellBox>() {
rc.replace(stored);
return Ok(val);
}
}
instance
.set_field(field, Arc::from(stored))
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
ASTNode::MeField { field, .. } => {
// 🌍 革命的me.field代入local変数から取得
let me_value =
self.resolve_variable("me")
.map_err(|_| RuntimeError::InvalidOperation {
message: "'this' is not bound in the current context".to_string(),
})?;
if let Some(instance) = (*me_value).as_any().downcast_ref::<InstanceBox>() {
// 🔥 finiは何回呼ばれてもエラーにしないユーザー要求
// is_finalized()チェックを削除
// 🚨 フィールド差し替え時の自動finiは削除Nyashの明示的哲学
// プログラマーが必要なら明示的にfini()を呼ぶべき
let stored = {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{
val.clone_box()
}
};
instance
.set_field(field, Arc::from(stored))
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
Err(RuntimeError::TypeError {
message: "'this' is not an instance".to_string(),
})
}
}
_ => Err(RuntimeError::InvalidOperation {
message: "Invalid assignment target".to_string(),
}),
}
}
/// try/catch/finally文を実行 - Exception handling
pub(super) fn execute_try_catch(
&mut self,
try_body: &[ASTNode],
catch_clauses: &[super::CatchClause],
finally_body: &Option<Vec<ASTNode>>,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut thrown_exception: Option<Box<dyn NyashBox>> = None;
// Try block execution
let mut try_result = Ok(Box::new(VoidBox::new()));
for statement in try_body {
match self.execute_statement(statement) {
Ok(_) => {
// 制御フローをチェック
if !matches!(self.control_flow, super::ControlFlow::None) {
if let super::ControlFlow::Throw(exception) = &self.control_flow {
thrown_exception = Some(exception.clone_box());
self.control_flow = super::ControlFlow::None;
break;
} else {
break; // Return/Break等は上位に伝播
}
}
}
Err(e) => {
// RuntimeErrorを例外として扱う
thrown_exception =
Some(Box::new(exception_box::ErrorBox::new(&format!("{:?}", e))));
try_result = Err(e);
break;
}
}
}
// Catch clause processing
if let Some(exception) = &thrown_exception {
for catch_clause in catch_clauses {
// 型チェック
if let Some(exception_type) = &catch_clause.exception_type {
if !exception_box::is_exception_type(exception.as_ref(), exception_type) {
continue; // 型が合わない場合は次のcatch句へ
}
}
// 🌍 革命的例外変数束縛local変数として設定
if let Some(var_name) = &catch_clause.variable_name {
self.declare_local_variable(var_name, exception.clone_box());
}
// Catch body execution
for statement in &catch_clause.body {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
// 🌍 革命的例外変数クリーンアップlocal変数から削除
if let Some(var_name) = &catch_clause.variable_name {
self.local_vars.remove(var_name);
}
thrown_exception = None; // 例外が処理された
break;
}
}
// Finally block execution (always executed)
if let Some(ref finally_statements) = finally_body {
for statement in finally_statements {
self.execute_statement(statement)?;
if !matches!(self.control_flow, super::ControlFlow::None) {
break;
}
}
}
// 未処理の例外があれば再スロー
if let Some(exception) = thrown_exception {
self.control_flow = super::ControlFlow::Throw(exception);
}
match try_result {
Ok(result) => Ok(result),
Err(_) => Ok(Box::new(VoidBox::new()) as Box<dyn NyashBox>),
}
}
/// throw文を実行 - Throw exception
pub(super) fn execute_throw(
&mut self,
expression: &ASTNode,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value = self.execute_expression(expression)?;
// 値を例外として扱う
let exception =
if let Some(error_box) = value.as_any().downcast_ref::<exception_box::ErrorBox>() {
Box::new(error_box.clone()) as Box<dyn NyashBox>
} else {
// 文字列や他の値はErrorBoxに変換
Box::new(exception_box::ErrorBox::new(&value.to_string_box().value))
};
self.control_flow = super::ControlFlow::Throw(exception);
Ok(Box::new(VoidBox::new()))
}
/// using文を実行 - Import namespace
pub(super) fn execute_using_statement(
&mut self,
namespace_name: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
idebug!(
"🌟 DEBUG: execute_using_statement called with namespace: {}",
namespace_name
);
// First, handle the builtin stdlib namespace
if namespace_name == "nyashstd" {
idebug!("🌟 DEBUG: About to call ensure_stdlib_initialized");
self.ensure_stdlib_initialized()?;
idebug!("🌟 DEBUG: ensure_stdlib_initialized completed");
return Ok(Box::new(VoidBox::new()));
}
// Otherwise, consult the modules registry (resolved by runner/CLI/header)
if crate::runtime::modules_registry::get(namespace_name).is_some() {
// Resolved via registry; no further action at runtime stage-0
return Ok(Box::new(VoidBox::new()));
}
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
if strict {
return Err(RuntimeError::InvalidOperation {
message: format!("Unresolved namespace '{}' (strict)", namespace_name),
});
}
if crate::interpreter::utils::debug_on() {
eprintln!(
"[using] unresolved '{}' (non-strict, continuing)",
namespace_name
);
}
Ok(Box::new(VoidBox::new()))
}
/// 標準ライブラリの初期化を確保
fn ensure_stdlib_initialized(&mut self) -> Result<(), RuntimeError> {
if self.stdlib.is_none() {
idebug!("🌟 Initializing BuiltinStdlib...");
self.stdlib = Some(BuiltinStdlib::new());
idebug!("✅ BuiltinStdlib initialized successfully");
}
Ok(())
}
}

View File

@ -1,477 +0,0 @@
/*!
* System Methods Module
*
* Extracted from box_methods.rs
* Contains system-level Box method implementations:
* - TimeBox methods (now, fromTimestamp, parse, sleep, format)
* - DateTimeBox methods (year, month, day, hour, minute, second, timestamp, toISOString, format, addDays, addHours)
* - TimerBox methods (elapsed, reset)
* - DebugBox methods (startTracking, stopTracking, trackBox, dumpAll, saveToFile, watch, etc.)
*/
use super::*;
use crate::box_trait::StringBox;
use crate::boxes::{DateTimeBox, TimeBox};
impl NyashInterpreter {
/// TimeBoxのメソッド呼び出しを実行
pub(super) fn execute_time_method(
&mut self,
time_box: &TimeBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"now" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("now() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(time_box.now())
}
"fromTimestamp" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"fromTimestamp() expects 1 argument, got {}",
arg_values.len()
),
});
}
Ok(time_box.fromTimestamp(arg_values[0].clone_box()))
}
"parse" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("parse() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.parse(arg_values[0].clone_box()))
}
"sleep" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("sleep() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.sleep(arg_values[0].clone_box()))
}
"format" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("format() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.format(arg_values[0].clone_box()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimeBox method: {}", method),
}),
}
}
/// DateTimeBoxのメソッド呼び出しを実行
pub(super) fn execute_datetime_method(
&mut self,
datetime_box: &DateTimeBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"year" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("year() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.year())
}
"month" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("month() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.month())
}
"day" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("day() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.day())
}
"hour" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("hour() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.hour())
}
"minute" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("minute() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.minute())
}
"second" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("second() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.second())
}
"timestamp" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"timestamp() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(datetime_box.timestamp())
}
"toISOString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toISOString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(datetime_box.toISOString())
}
"format" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("format() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(datetime_box.format(arg_values[0].clone_box()))
}
"addDays" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("addDays() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(datetime_box.addDays(arg_values[0].clone_box()))
}
"addHours" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("addHours() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(datetime_box.addHours(arg_values[0].clone_box()))
}
"toString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"toString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(datetime_box.to_string_box()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown DateTimeBox method: {}", method),
}),
}
}
/// TimerBoxのメソッド呼び出しを実行
pub(super) fn execute_timer_method(
&mut self,
timer_box: &TimerBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"elapsed" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("elapsed() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(timer_box.elapsed())
}
"reset" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("reset() expects 0 arguments, got {}", arg_values.len()),
});
}
// NOTE: resetはmutableメソッドなので、ここでは新しいTimerBoxを作成
let timer_box = Box::new(TimerBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
Ok(timer_box)
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimerBox method: {}", method),
}),
}
}
/// DebugBoxのメソッド呼び出しを実行
pub(super) fn execute_debug_method(
&mut self,
debug_box: &DebugBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"startTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"startTracking() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.start_tracking()
}
"stopTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"stopTracking() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.stop_tracking()
}
"trackBox" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"trackBox() expects 2 arguments, got {}",
arg_values.len()
),
});
}
// 第2引数をStringBoxとして取得
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>()
{
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "trackBox() second argument must be a string".to_string(),
});
};
debug_box.track_box(arg_values[0].as_ref(), &name)
}
"dumpAll" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("dumpAll() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.dump_all()
}
"saveToFile" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"saveToFile() expects 1 argument, got {}",
arg_values.len()
),
});
}
let filename =
if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "saveToFile() argument must be a string".to_string(),
});
};
debug_box.save_to_file(&filename)
}
"watch" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("watch() expects 2 arguments, got {}", arg_values.len()),
});
}
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>()
{
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "watch() second argument must be a string".to_string(),
});
};
debug_box.watch(arg_values[0].as_ref(), &name)
}
"memoryReport" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"memoryReport() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.memory_report()
}
"setBreakpoint" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"setBreakpoint() expects 1 argument, got {}",
arg_values.len()
),
});
}
let func_name =
if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "setBreakpoint() argument must be a string".to_string(),
});
};
debug_box.set_breakpoint(&func_name)
}
"traceCall" => {
if arg_values.len() < 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"traceCall() expects at least 1 argument, got {}",
arg_values.len()
),
});
}
let func_name =
if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "traceCall() first argument must be a string".to_string(),
});
};
// 残りの引数を文字列として収集
let args: Vec<String> = arg_values[1..]
.iter()
.map(|v| v.to_string_box().value)
.collect();
debug_box.trace_call(&func_name, args)
}
"showCallStack" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"showCallStack() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.show_call_stack()
}
"tracePluginCalls" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"tracePluginCalls(on:bool) expects 1 argument, got {}",
arg_values.len()
),
});
}
let on = if let Some(b) = arg_values[0]
.as_any()
.downcast_ref::<crate::box_trait::BoolBox>()
{
b.value
} else {
false
};
debug_box.trace_plugin_calls(on)
}
"getJitEvents" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"getJitEvents() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.get_jit_events()
}
"clear" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.clear()
}
"isTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"isTracking() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.is_tracking()
}
"getTrackedCount" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"getTrackedCount() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.get_tracked_count()
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown DebugBox method: {}", method),
}),
}
}
}

View File

@ -1,6 +0,0 @@
//! Interpreter utilities: debug toggles
/// Global debug toggle for interpreter layer (NYASH_DEBUG=1)
pub fn debug_on() -> bool {
std::env::var("NYASH_DEBUG").unwrap_or_default() == "1"
}

View File

@ -1,487 +0,0 @@
/*!
* Web Box Methods Module
*
* Extracted from box_methods.rs
* Contains WASM/browser-specific Box type method implementations:
*
* - execute_web_display_method (WebDisplayBox) - HTML DOM manipulation
* - execute_web_console_method (WebConsoleBox) - Browser console logging
* - execute_web_canvas_method (WebCanvasBox) - Canvas drawing operations
*
* All methods are conditionally compiled for WASM target architecture only.
*/
#[cfg(target_arch = "wasm32")]
use super::*;
#[cfg(target_arch = "wasm32")]
use crate::boxes::web::{WebCanvasBox, WebConsoleBox, WebDisplayBox};
#[cfg(target_arch = "wasm32")]
use crate::boxes::FloatBox;
#[cfg(target_arch = "wasm32")]
impl NyashInterpreter {
/// WebDisplayBoxメソッド実行 (WASM環境のみ)
/// HTML DOM操作、CSS スタイル設定、クラス管理などの包括的なWeb表示機能
pub(super) fn execute_web_display_method(
&mut self,
web_display_box: &WebDisplayBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"print" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("print() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_display_box.print(&message);
Ok(Box::new(VoidBox::new()))
}
"println" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("println() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_display_box.println(&message);
Ok(Box::new(VoidBox::new()))
}
"setHTML" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("setHTML() expects 1 argument, got {}", arg_values.len()),
});
}
let html_content = arg_values[0].to_string_box().value;
web_display_box.set_html(&html_content);
Ok(Box::new(VoidBox::new()))
}
"appendHTML" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"appendHTML() expects 1 argument, got {}",
arg_values.len()
),
});
}
let html_content = arg_values[0].to_string_box().value;
web_display_box.append_html(&html_content);
Ok(Box::new(VoidBox::new()))
}
"setCSS" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"setCSS() expects 2 arguments (property, value), got {}",
arg_values.len()
),
});
}
let property = arg_values[0].to_string_box().value;
let value = arg_values[1].to_string_box().value;
web_display_box.set_css(&property, &value);
Ok(Box::new(VoidBox::new()))
}
"addClass" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("addClass() expects 1 argument, got {}", arg_values.len()),
});
}
let class_name = arg_values[0].to_string_box().value;
web_display_box.add_class(&class_name);
Ok(Box::new(VoidBox::new()))
}
"removeClass" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"removeClass() expects 1 argument, got {}",
arg_values.len()
),
});
}
let class_name = arg_values[0].to_string_box().value;
web_display_box.remove_class(&class_name);
Ok(Box::new(VoidBox::new()))
}
"clear" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arg_values.len()),
});
}
web_display_box.clear();
Ok(Box::new(VoidBox::new()))
}
"show" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("show() expects 0 arguments, got {}", arg_values.len()),
});
}
web_display_box.show();
Ok(Box::new(VoidBox::new()))
}
"hide" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("hide() expects 0 arguments, got {}", arg_values.len()),
});
}
web_display_box.hide();
Ok(Box::new(VoidBox::new()))
}
"scrollToBottom" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"scrollToBottom() expects 0 arguments, got {}",
arg_values.len()
),
});
}
web_display_box.scroll_to_bottom();
Ok(Box::new(VoidBox::new()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for WebDisplayBox", method),
}),
}
}
/// WebConsoleBoxメソッド実行 (WASM環境のみ)
/// ブラウザーコンソールへの多彩なログ出力、グループ化、区切り表示機能
pub(super) fn execute_web_console_method(
&mut self,
web_console_box: &WebConsoleBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"log" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("log() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_console_box.log(&message);
Ok(Box::new(VoidBox::new()))
}
"warn" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("warn() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_console_box.warn(&message);
Ok(Box::new(VoidBox::new()))
}
"error" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("error() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_console_box.error(&message);
Ok(Box::new(VoidBox::new()))
}
"info" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("info() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_console_box.info(&message);
Ok(Box::new(VoidBox::new()))
}
"debug" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("debug() expects 1 argument, got {}", arg_values.len()),
});
}
let message = arg_values[0].to_string_box().value;
web_console_box.debug(&message);
Ok(Box::new(VoidBox::new()))
}
"clear" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arg_values.len()),
});
}
web_console_box.clear();
Ok(Box::new(VoidBox::new()))
}
"separator" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"separator() expects 0 arguments, got {}",
arg_values.len()
),
});
}
web_console_box.separator();
Ok(Box::new(VoidBox::new()))
}
"group" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("group() expects 1 argument, got {}", arg_values.len()),
});
}
let title = arg_values[0].to_string_box().value;
web_console_box.group(&title);
Ok(Box::new(VoidBox::new()))
}
"groupEnd" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!(
"groupEnd() expects 0 arguments, got {}",
arg_values.len()
),
});
}
web_console_box.group_end();
Ok(Box::new(VoidBox::new()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for WebConsoleBox", method),
}),
}
}
/// WebCanvasBoxメソッド実行 (WASM環境のみ)
/// HTML5 Canvas描画操作 - 矩形、円、テキスト描画の包括的な2D描画機能
pub(super) fn execute_web_canvas_method(
&mut self,
web_canvas_box: &WebCanvasBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"clear" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arg_values.len()),
});
}
web_canvas_box.clear();
Ok(Box::new(VoidBox::new()))
}
"fillRect" => {
if arg_values.len() != 5 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"fillRect() expects 5 arguments (x, y, width, height, color), got {}",
arg_values.len()
),
});
}
let x = if let Some(n) = arg_values[0].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[0].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillRect() x must be a number".to_string(),
});
};
let y = if let Some(n) = arg_values[1].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[1].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillRect() y must be a number".to_string(),
});
};
let width = if let Some(n) = arg_values[2].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[2].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillRect() width must be a number".to_string(),
});
};
let height = if let Some(n) = arg_values[3].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[3].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillRect() height must be a number".to_string(),
});
};
let color = arg_values[4].to_string_box().value;
web_canvas_box.fill_rect(x, y, width, height, &color);
Ok(Box::new(VoidBox::new()))
}
"strokeRect" => {
if arg_values.len() != 6 {
return Err(RuntimeError::InvalidOperation {
message: format!("strokeRect() expects 6 arguments (x, y, width, height, color, lineWidth), got {}", arg_values.len()),
});
}
let x = if let Some(n) = arg_values[0].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[0].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "strokeRect() x must be a number".to_string(),
});
};
let y = if let Some(n) = arg_values[1].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[1].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "strokeRect() y must be a number".to_string(),
});
};
let width = if let Some(n) = arg_values[2].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[2].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "strokeRect() width must be a number".to_string(),
});
};
let height = if let Some(n) = arg_values[3].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[3].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "strokeRect() height must be a number".to_string(),
});
};
let color = arg_values[4].to_string_box().value;
let line_width =
if let Some(n) = arg_values[5].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[5].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "strokeRect() lineWidth must be a number".to_string(),
});
};
web_canvas_box.stroke_rect(x, y, width, height, &color, line_width);
Ok(Box::new(VoidBox::new()))
}
"fillCircle" => {
if arg_values.len() != 4 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"fillCircle() expects 4 arguments (x, y, radius, color), got {}",
arg_values.len()
),
});
}
let x = if let Some(n) = arg_values[0].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[0].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillCircle() x must be a number".to_string(),
});
};
let y = if let Some(n) = arg_values[1].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[1].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillCircle() y must be a number".to_string(),
});
};
let radius = if let Some(n) = arg_values[2].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[2].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillCircle() radius must be a number".to_string(),
});
};
let color = arg_values[3].to_string_box().value;
web_canvas_box.fill_circle(x, y, radius, &color);
Ok(Box::new(VoidBox::new()))
}
"fillText" => {
if arg_values.len() != 5 {
return Err(RuntimeError::InvalidOperation {
message: format!(
"fillText() expects 5 arguments (text, x, y, font, color), got {}",
arg_values.len()
),
});
}
let text = arg_values[0].to_string_box().value;
let x = if let Some(n) = arg_values[1].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[1].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillText() x must be a number".to_string(),
});
};
let y = if let Some(n) = arg_values[2].as_any().downcast_ref::<IntegerBox>() {
n.value as f64
} else if let Some(n) = arg_values[2].as_any().downcast_ref::<FloatBox>() {
n.value
} else {
return Err(RuntimeError::TypeError {
message: "fillText() y must be a number".to_string(),
});
};
let font = arg_values[3].to_string_box().value;
let color = arg_values[4].to_string_box().value;
web_canvas_box.fill_text(&text, x, y, &font, &color);
Ok(Box::new(VoidBox::new()))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for WebCanvasBox", method),
}),
}
}
}