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:
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
@ -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 = ¤t_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),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 = ¤t_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)
|
||||
}
|
||||
}
|
||||
@ -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(¶ms, 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
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()))
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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() }) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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) } }
|
||||
|
||||
@ -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 {}
|
||||
|
||||
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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(())
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user