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

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

View File

@ -1,12 +1,12 @@
/*!
* 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.
*/
@ -17,15 +17,19 @@ 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> {
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() {
@ -54,35 +58,42 @@ impl NyashInterpreter {
}
_ => 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> {
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()),
message: format!(
"sendMessage() expects 1 argument, got {}",
arg_values.len()
),
});
}
// 簡易実装:メッセージを作成して返す
@ -97,12 +108,18 @@ impl NyashInterpreter {
});
}
let content = arg_values[0].to_string_box().value;
Ok(Box::new(StringBox::new(&format!("Broadcast from {}: {}", channel_box.sender_name, content))))
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()),
message: format!(
"toString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(channel_box.to_string_box()))
@ -118,7 +135,10 @@ impl NyashInterpreter {
"receiver" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("receiver() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"receiver() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(channel_box.receiver())
@ -129,4 +149,4 @@ impl NyashInterpreter {
}
}
}
}
}

View File

@ -1,46 +1,46 @@
/*!
* 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
@ -55,9 +55,9 @@ impl NyashInterpreter {
// 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
@ -67,14 +67,18 @@ impl NyashInterpreter {
// 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> {
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" => {
@ -88,7 +92,10 @@ impl NyashInterpreter {
"is_not_null" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("is_not_null() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"is_not_null() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(BoolBox::new(false)))
@ -96,7 +103,10 @@ impl NyashInterpreter {
"toString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"toString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(Box::new(StringBox::new("null".to_string())))
@ -115,17 +125,18 @@ impl NyashInterpreter {
"get_or_default" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("get_or_default() expects 1 argument, got {}", arg_values.len()),
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),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown NullBox method: {}", method),
}),
}
}
@ -145,14 +156,18 @@ impl NyashInterpreter {
/// 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> {
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" => {
@ -185,23 +200,25 @@ impl NyashInterpreter {
message: "EguiBox.run() must be called from main thread".to_string(),
})
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for EguiBox", method),
})
}
_ => 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> {
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" => {
@ -210,15 +227,16 @@ impl NyashInterpreter {
message: "console.log() requires at least 1 argument".to_string(),
});
}
// 引数をすべて文字列に変換
let messages: Vec<String> = arg_values.iter()
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" => {
@ -227,14 +245,15 @@ impl NyashInterpreter {
message: "console.warn() requires at least 1 argument".to_string(),
});
}
let messages: Vec<String> = arg_values.iter()
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" => {
@ -243,35 +262,37 @@ impl NyashInterpreter {
message: "console.error() requires at least 1 argument".to_string(),
});
}
let messages: Vec<String> = arg_values.iter()
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()),
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),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown ConsoleBox method: {}", method),
}),
}
}
// MethodBox methods moved to special_methods.rs
// Web methods moved to web_methods.rs
}
}

View File

@ -1,8 +1,8 @@
//! Call helpers: centralizes call paths (PluginHost, functions)
use super::{NyashInterpreter, RuntimeError};
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, VoidBox};
use super::{NyashInterpreter, RuntimeError};
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
use crate::runtime::plugin_loader_v2::PluginBoxV2;
@ -17,18 +17,31 @@ impl NyashInterpreter {
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) });
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) {
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) }),
Err(e) => Err(RuntimeError::RuntimeFailure {
message: format!("Plugin method {} failed: {:?}", method, e),
}),
}
}
@ -44,9 +57,15 @@ impl NyashInterpreter {
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() })?;
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) })
.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).

View File

@ -1,29 +1,29 @@
/*!
* Nyash Interpreter - Rust Implementation
*
*
* Python版nyashc_v4.pyのインタープリターをRustで完全再実装
* Everything is Box哲学に基づくAST実行エンジン
*/
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, SharedNyashBox};
use crate::instance_v2::InstanceBox;
use super::BuiltinStdlib;
use crate::runtime::{NyashRuntime, NyashRuntimeBuilder};
use crate::box_factory::BoxFactory;
use std::sync::{Arc, Mutex};
use std::collections::{HashMap, HashSet};
use super::{ControlFlow, ConstructorContext, StaticBoxDefinition, StaticBoxState};
use super::{ConstructorContext, ControlFlow, StaticBoxDefinition, StaticBoxState};
use super::{RuntimeError, SharedState};
use crate::ast::ASTNode;
use crate::box_factory::BoxFactory;
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, SharedNyashBox, StringBox, VoidBox};
use crate::instance_v2::InstanceBox;
use crate::runtime::{NyashRuntime, NyashRuntimeBuilder};
use std::collections::{HashMap, HashSet};
use std::fs::OpenOptions;
use std::io::Write;
use std::sync::{Arc, Mutex};
// ファイルロガーexpressions.rsと同じ
pub(crate) fn debug_log(msg: &str) {
if let Ok(mut file) = OpenOptions::new()
.create(true)
.append(true)
.open("/mnt/c/git/nyash/development/debug_hang_issue/debug_trace.log")
.open("/mnt/c/git/nyash/development/debug_hang_issue/debug_trace.log")
{
let _ = writeln!(file, "{}", msg);
let _ = file.flush();
@ -44,31 +44,30 @@ macro_rules! idebug {
};
}
/// Nyashインタープリター - AST実行エンジン
pub struct NyashInterpreter {
/// 共有状態(スレッド間で共有)
pub(super) shared: SharedState,
/// 📦 local変数スタック関数呼び出し時の一時変数
pub(super) local_vars: HashMap<String, SharedNyashBox>,
/// 📤 outbox変数スタックstatic関数内の所有権移転変数
pub(super) outbox_vars: HashMap<String, SharedNyashBox>,
/// 制御フロー状態
pub(super) control_flow: ControlFlow,
/// 現在実行中のコンストラクタ情報
pub(super) current_constructor_context: Option<ConstructorContext>,
/// 🔄 評価スタック - 循環参照検出用
#[allow(dead_code)]
pub(super) evaluation_stack: Vec<usize>,
/// 🔗 Invalidated object IDs for weak reference system
pub invalidated_ids: Arc<Mutex<HashSet<u64>>>,
/// 📚 組み込み標準ライブラリ
pub(super) stdlib: Option<BuiltinStdlib>,
@ -125,7 +124,9 @@ impl NyashInterpreter {
if std::path::Path::new("nyash.toml").exists() {
let needs_init = {
let host = crate::runtime::get_global_plugin_host();
host.read().map(|h| h.config_ref().is_none()).unwrap_or(true)
host.read()
.map(|h| h.config_ref().is_none())
.unwrap_or(true)
};
if needs_init {
let _ = crate::runtime::init_global_plugin_host("nyash.toml");
@ -137,11 +138,19 @@ impl NyashInterpreter {
std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1");
}
// Merge override list with FileBox/TOMLBox only (safe defaults for interpreter flows)
let mut override_types: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
} else { vec![] };
let mut override_types: Vec<String> =
if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
list.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
} else {
vec![]
};
for t in ["FileBox", "TOMLBox"] {
if !override_types.iter().any(|x| x == t) { override_types.push(t.to_string()); }
if !override_types.iter().any(|x| x == t) {
override_types.push(t.to_string());
}
}
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
}
@ -151,8 +160,13 @@ impl NyashInterpreter {
}
/// 互換API: 旧BuiltinGroupsを受け取るコンストラクタ無視して new() 相当)
pub fn new_with_groups<T>(_groups: T) -> Self where T: core::fmt::Debug { Self::new() }
pub fn new_with_groups<T>(_groups: T) -> Self
where
T: core::fmt::Debug,
{
Self::new()
}
/// 共有状態から新しいインタープリターを作成(非同期実行用)
pub fn with_shared(shared: SharedState) -> Self {
// 共有状態に紐づいたランタイムを先に構築
@ -188,8 +202,12 @@ impl NyashInterpreter {
}
/// 互換API: 共有状態旧BuiltinGroups無視
pub fn with_shared_and_groups<T>(shared: SharedState, _groups: T) -> Self where T: core::fmt::Debug { Self::with_shared(shared) }
pub fn with_shared_and_groups<T>(shared: SharedState, _groups: T) -> Self
where
T: core::fmt::Debug,
{
Self::with_shared(shared)
}
/// Register an additional BoxFactory into this interpreter's runtime registry.
/// This allows tests or embedders to inject custom factories without globals.
@ -198,14 +216,16 @@ impl NyashInterpreter {
reg.register(factory);
}
}
// ========== 🌍 GlobalBox変数解決システム ==========
/// 革命的変数解決: local変数 → GlobalBoxフィールド → エラー
pub(super) fn resolve_variable(&self, name: &str) -> Result<SharedNyashBox, RuntimeError> {
let log_msg = format!("resolve_variable: name='{}', local_vars={:?}",
name, self.local_vars.keys().collect::<Vec<_>>());
let log_msg = format!(
"resolve_variable: name='{}', local_vars={:?}",
name,
self.local_vars.keys().collect::<Vec<_>>()
);
debug_log(&log_msg);
// 1. outbox変数を最初にチェックstatic関数内で優先
if let Some(outbox_value) = self.outbox_vars.get(name) {
@ -213,80 +233,87 @@ impl NyashInterpreter {
let shared_value = Arc::clone(outbox_value);
return Ok(shared_value);
}
// 2. local変数をチェック
if let Some(local_value) = self.local_vars.get(name) {
// 🔧 修正clone_box() → Arc::clone() で参照共有
let shared_value = Arc::clone(local_value);
return Ok(shared_value);
}
// 3. GlobalBoxのフィールドをチェック
let global_box = self.shared.global_box.lock().unwrap();
if let Some(field_value) = global_box.get_field(name) {
return Ok(field_value);
}
// 4. statics名前空間内のstatic boxをチェック
if let Some(statics_namespace) = global_box.get_field("statics") {
// MapBoxとして試す
if let Some(map_box) = statics_namespace.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
if let Some(map_box) = statics_namespace
.as_any()
.downcast_ref::<crate::boxes::map_box::MapBox>()
{
let key_box: Box<dyn NyashBox> = Box::new(StringBox::new(name));
let static_box_result = map_box.get(key_box);
// NullBoxでないかチェックMapBoxは見つからない場合NullBoxを返す
if static_box_result.type_name() != "NullBox" {
return Ok(Arc::from(static_box_result));
}
} else if let Some(instance) = statics_namespace.as_any().downcast_ref::<InstanceBox>() {
} else if let Some(instance) = statics_namespace.as_any().downcast_ref::<InstanceBox>()
{
if let Some(static_box) = instance.get_field(name) {
return Ok(static_box);
}
}
}
drop(global_box); // lockを解放してからstdlibチェック
// 5. nyashstd標準ライブラリ名前空間をチェック
// 5. nyashstd標準ライブラリ名前空間をチェック
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) {
// BuiltinStaticBoxをInstanceBoxとしてラップ
let static_instance = InstanceBox::new(
format!("{}_builtin", name),
vec![], // フィールドなし
vec![], // フィールドなし
HashMap::new(), // メソッドは動的に解決される
);
return Ok(Arc::new(static_instance));
}
}
}
// 6. エラー:見つからない
idebug!("🔍 DEBUG: '{}' not found anywhere!", name);
Err(RuntimeError::UndefinedVariable {
name: name.to_string(),
})
}
/// 🔥 厳密変数設定: 明示的宣言のみ許可 - Everything is Box哲学
pub(crate) fn set_variable(&mut self, name: &str, value: Box<dyn NyashBox>) -> Result<(), RuntimeError> {
pub(crate) fn set_variable(
&mut self,
name: &str,
value: Box<dyn NyashBox>,
) -> Result<(), RuntimeError> {
let shared_value = Arc::from(value); // Convert Box to Arc
// 1. outbox変数が存在する場合は更新
if self.outbox_vars.contains_key(name) {
self.outbox_vars.insert(name.to_string(), shared_value);
return Ok(());
}
// 2. local変数が存在する場合は更新
if self.local_vars.contains_key(name) {
self.local_vars.insert(name.to_string(), shared_value);
return Ok(());
}
// 3. GlobalBoxのフィールドが既に存在する場合は更新
{
let global_box = self.shared.global_box.lock().unwrap();
@ -297,7 +324,7 @@ impl NyashInterpreter {
return Ok(());
}
}
// 4. グローバル変数として新規作成(従来の緩い挙動に合わせる)
{
let mut global_box = self.shared.global_box.lock().unwrap();
@ -305,7 +332,7 @@ impl NyashInterpreter {
}
Ok(())
}
/// local変数を宣言関数内でのみ有効
pub(crate) fn declare_local_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
// Pass-by-share for plugin handle types; by-value (clone) semantics can be applied at call sites
@ -313,40 +340,54 @@ impl NyashInterpreter {
let mut store_value = value;
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if store_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if store_value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
store_value = store_value.share_box();
}
}
self.local_vars.insert(name.to_string(), Arc::from(store_value));
self.local_vars
.insert(name.to_string(), Arc::from(store_value));
}
/// outbox変数を宣言static関数内で所有権移転
pub(crate) fn declare_outbox_variable(&mut self, name: &str, value: Box<dyn NyashBox>) {
#[allow(unused_mut)]
let mut store_value = value;
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
{
if store_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if store_value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
store_value = store_value.share_box();
}
}
self.outbox_vars.insert(name.to_string(), Arc::from(store_value));
self.outbox_vars
.insert(name.to_string(), Arc::from(store_value));
}
/// local変数スタックを保存・復元関数呼び出し時
pub(super) fn save_local_vars(&self) -> HashMap<String, Box<dyn NyashBox>> {
self.local_vars.iter()
self.local_vars
.iter()
.map(|(k, v)| {
let b: &dyn NyashBox = &**v;
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if b.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return (k.clone(), b.share_box());
}
(k.clone(), b.clone_box())
})
.collect()
}
pub(super) fn restore_local_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
// 🎯 スコープ離脱時現在のローカル変数に対してfiniを呼ぶ
// ただし「me」は特別扱いインスタンス自身なのでfiniしない
@ -355,72 +396,85 @@ impl NyashInterpreter {
if name == "me" {
continue;
}
// ユーザー定義BoxInstanceBoxの場合
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
let _ = instance.fini();
idebug!("🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", name);
idebug!(
"🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)",
name
);
}
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない明示呼び出しのみ
// ビルトインBoxは元々finiメソッドを持たないので呼ばない
// StringBox、IntegerBox等はリソース管理不要
}
// その後、保存されていた変数で復元
self.local_vars = saved.into_iter()
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
self.local_vars = saved
.into_iter()
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
.collect();
}
/// outbox変数スタックを保存・復元static関数呼び出し時
pub(super) fn save_outbox_vars(&self) -> HashMap<String, Box<dyn NyashBox>> {
self.outbox_vars.iter()
self.outbox_vars
.iter()
.map(|(k, v)| {
let b: &dyn NyashBox = &**v;
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
if b.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return (k.clone(), b.share_box());
}
(k.clone(), b.clone_box())
})
.collect()
}
pub(super) fn restore_outbox_vars(&mut self, saved: HashMap<String, Box<dyn NyashBox>>) {
// 🎯 スコープ離脱時現在のoutbox変数に対してもfiniを呼ぶ
for (name, value) in &self.outbox_vars {
// ユーザー定義BoxInstanceBoxの場合
if let Some(instance) = (**value).as_any().downcast_ref::<InstanceBox>() {
let _ = instance.fini();
idebug!("🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", name);
idebug!(
"🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)",
name
);
}
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない
// ビルトインBoxは元々finiメソッドを持たないので呼ばない要修正
}
// その後、保存されていた変数で復元
self.outbox_vars = saved.into_iter()
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
self.outbox_vars = saved
.into_iter()
.map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc
.collect();
}
/// トップレベル関数をGlobalBoxのメソッドとして登録 - 🔥 暗黙オーバーライド禁止対応
pub(super) fn register_global_function(&mut self, name: String, func_ast: ASTNode) -> Result<(), RuntimeError> {
pub(super) fn register_global_function(
&mut self,
name: String,
func_ast: ASTNode,
) -> Result<(), RuntimeError> {
let mut global_box = self.shared.global_box.lock().unwrap();
global_box.add_method(name, func_ast)
global_box
.add_method(name, func_ast)
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(())
}
/// 値が真と評価されるかチェック
pub(super) fn is_truthy(&self, value: &Box<dyn NyashBox>) -> bool {
#[allow(unused_imports)]
use std::any::Any;
if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() {
bool_box.value
} else if let Some(int_box) = value.as_any().downcast_ref::<IntegerBox>() {
@ -433,11 +487,11 @@ impl NyashInterpreter {
true // 他のBoxは真とみなす
}
}
/// 🌍 革命的変数取得テスト用GlobalBoxのフィールドから取得
pub fn get_variable(&self, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let shared_var = self.resolve_variable(name)?;
Ok((*shared_var).clone_box()) // Convert Arc back to Box for external interface
Ok((*shared_var).clone_box()) // Convert Arc back to Box for external interface
}
}
@ -448,9 +502,13 @@ pub(crate) fn run_function_box(
) -> Result<Box<dyn crate::box_trait::NyashBox>, RuntimeError> {
use crate::box_trait::{NyashBox, VoidBox};
if args.len() != fun.params.len() {
return Err(RuntimeError::InvalidOperation { message: format!(
"Function expects {} args, got {}", fun.params.len(), args.len()
)});
return Err(RuntimeError::InvalidOperation {
message: format!(
"Function expects {} args, got {}",
fun.params.len(),
args.len()
),
});
}
let mut interp = NyashInterpreter::new();
@ -482,7 +540,10 @@ pub(crate) fn run_function_box(
break;
}
}
Err(e) => { crate::runtime::global_hooks::pop_task_scope(); return Err(e); }
Err(e) => {
crate::runtime::global_hooks::pop_task_scope();
return Err(e);
}
}
}
crate::runtime::global_hooks::pop_task_scope();
@ -495,36 +556,36 @@ pub(crate) fn run_function_box(
mod tests {
use super::*;
use crate::parser::NyashParser;
#[test]
fn test_simple_execution() {
let code = r#"
x = 42
print(x)
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
let result = interpreter.execute(ast);
assert!(result.is_ok());
}
#[test]
fn test_arithmetic() {
let code = r#"
result = 10 + 32
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
interpreter.execute(ast).unwrap();
// 🌍 革命的変数取得GlobalBoxから
let result = interpreter.get_variable("result").unwrap();
assert_eq!(result.to_string_box().value, "42");
}
#[test]
fn test_if_statement() {
let code = r#"
@ -535,16 +596,16 @@ mod tests {
y = "failure"
}
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
interpreter.execute(ast).unwrap();
// 🌍 革命的変数取得GlobalBoxから
let result = interpreter.get_variable("y").unwrap();
assert_eq!(result.to_string_box().value, "success");
}
#[test]
fn test_box_instance_creation() {
let code = r#"
@ -564,15 +625,15 @@ mod tests {
obj.value = "test123"
result = obj.getValue()
"#;
let ast = NyashParser::parse_from_string(code).unwrap();
let mut interpreter = NyashInterpreter::new();
interpreter.execute(ast).unwrap();
// 🌍 革命的変数取得:インスタンス作成確認
let obj = interpreter.get_variable("obj").unwrap();
assert!(obj.as_any().downcast_ref::<InstanceBox>().is_some());
// 🌍 革命的変数取得:メソッド呼び出し結果確認
let result = interpreter.get_variable("result").unwrap();
assert_eq!(result.to_string_box().value, "test123");
@ -582,93 +643,107 @@ mod tests {
// ===== 🔥 Static Box管理システム =====
impl NyashInterpreter {
/// Static Box定義を登録
pub fn register_static_box(&mut self, definition: StaticBoxDefinition) -> Result<(), RuntimeError> {
let mut definitions = self.shared.static_box_definitions.write()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire write lock for static box definitions".to_string()
})?;
pub fn register_static_box(
&mut self,
definition: StaticBoxDefinition,
) -> Result<(), RuntimeError> {
let mut definitions = self.shared.static_box_definitions.write().map_err(|_| {
RuntimeError::RuntimeFailure {
message: "Failed to acquire write lock for static box definitions".to_string(),
}
})?;
definitions.insert(definition.name.clone(), definition);
Ok(())
}
/// Static Box宣言を登録AST処理から呼ばれる
pub fn register_static_box_declaration(
&mut self,
&mut self,
name: String,
fields: Vec<String>,
methods: HashMap<String, ASTNode>,
init_fields: Vec<String>,
weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
static_init: Option<Vec<ASTNode>>,
extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
implements: Vec<String>,
type_parameters: Vec<String>
type_parameters: Vec<String>,
) -> Result<(), RuntimeError> {
// 🌍 Static Box定義時にstatics名前空間を確実に作成
self.ensure_statics_namespace()?;
let definition = StaticBoxDefinition {
name: name.clone(),
fields,
methods,
init_fields,
weak_fields, // 🔗 Add weak_fields to static box definition
weak_fields, // 🔗 Add weak_fields to static box definition
static_init,
extends,
implements,
type_parameters,
initialization_state: StaticBoxState::NotInitialized,
};
idebug!("🔥 Static Box '{}' definition registered in statics namespace", name);
idebug!(
"🔥 Static Box '{}' definition registered in statics namespace",
name
);
self.register_static_box(definition)
}
/// Static Boxの初期化を実行遅延初期化
pub fn ensure_static_box_initialized(&mut self, name: &str) -> Result<(), RuntimeError> {
// 1. 定義を取得
let definition = {
let definitions = self.shared.static_box_definitions.read()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire read lock for static box definitions".to_string()
})?;
let definitions = self.shared.static_box_definitions.read().map_err(|_| {
RuntimeError::RuntimeFailure {
message: "Failed to acquire read lock for static box definitions".to_string(),
}
})?;
match definitions.get(name) {
Some(def) => def.clone(),
None => return Err(RuntimeError::UndefinedClass { name: name.to_string() }),
None => {
return Err(RuntimeError::UndefinedClass {
name: name.to_string(),
})
}
}
};
// 2. 初期化状態をチェック
if definition.initialization_state == StaticBoxState::Initialized {
return Ok(()); // 既に初期化済み
}
if definition.initialization_state == StaticBoxState::Initializing {
return Err(RuntimeError::RuntimeFailure {
message: format!("Circular dependency detected during initialization of static box '{}'", name)
message: format!(
"Circular dependency detected during initialization of static box '{}'",
name
),
});
}
// 3. 初期化開始をマーク
self.set_static_box_state(name, StaticBoxState::Initializing)?;
// 4. 「statics」名前空間をGlobalBoxに作成未存在の場合
self.ensure_statics_namespace()?;
// 5. シングルトンインスタンスを作成(メソッドも含む)
let singleton = InstanceBox::new(
format!("{}_singleton", name),
definition.init_fields.clone(),
definition.methods.clone(), // ★ メソッドを正しく設定
);
// 6. GlobalBox.staticsに登録
self.set_static_instance(name, singleton)?;
// 7. static初期化ブロックを実行me変数をバインドして
if let Some(ref init_statements) = definition.static_init {
// statics名前空間からシングルトンインスタンスを取得
@ -678,99 +753,116 @@ impl NyashInterpreter {
let statics_instance = statics_box.as_any().downcast_ref::<InstanceBox>().unwrap();
statics_instance.get_field(name).unwrap()
};
// 🌍 this変数をバインドしてstatic初期化実行me構文のため
self.declare_local_variable("me", (*static_instance).clone_or_share());
for stmt in init_statements {
self.execute_statement(stmt)?;
}
// 🌍 this変数をクリーンアップ
self.local_vars.remove("me");
}
// 8. 初期化完了をマーク
self.set_static_box_state(name, StaticBoxState::Initialized)?;
Ok(())
}
/// Static Box初期化状態を設定
fn set_static_box_state(&mut self, name: &str, state: StaticBoxState) -> Result<(), RuntimeError> {
let mut definitions = self.shared.static_box_definitions.write()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire write lock for static box definitions".to_string()
})?;
fn set_static_box_state(
&mut self,
name: &str,
state: StaticBoxState,
) -> Result<(), RuntimeError> {
let mut definitions = self.shared.static_box_definitions.write().map_err(|_| {
RuntimeError::RuntimeFailure {
message: "Failed to acquire write lock for static box definitions".to_string(),
}
})?;
if let Some(definition) = definitions.get_mut(name) {
definition.initialization_state = state;
}
Ok(())
}
/// 「statics」名前空間をGlobalBoxに作成
fn ensure_statics_namespace(&mut self) -> Result<(), RuntimeError> {
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string()
})?;
let global_box =
self.shared
.global_box
.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string(),
})?;
// 既に存在する場合はスキップ
if global_box.get_field("statics").is_some() {
idebug!("🌍 statics namespace already exists - skipping creation");
return Ok(());
}
// 「statics」用のInstanceBoxを作成
let statics_box = InstanceBox::new(
"statics".to_string(),
vec![],
HashMap::new(),
);
let statics_box = InstanceBox::new("statics".to_string(), vec![], HashMap::new());
// GlobalBoxのfieldsに直接挿入
{
let fields = global_box.get_fields();
let mut fields_locked = fields.lock().unwrap();
fields_locked.insert("statics".to_string(), Arc::new(statics_box));
}
idebug!("🌍 statics namespace created in GlobalBox successfully");
Ok(())
}
/// Static Boxシングルトンインスタンスを設定
fn set_static_instance(&mut self, name: &str, instance: InstanceBox) -> Result<(), RuntimeError> {
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string()
})?;
fn set_static_instance(
&mut self,
name: &str,
instance: InstanceBox,
) -> Result<(), RuntimeError> {
let global_box =
self.shared
.global_box
.lock()
.map_err(|_| RuntimeError::RuntimeFailure {
message: "Failed to acquire global box lock".to_string(),
})?;
// statics名前空間を取得
let statics_box = global_box.get_field("statics")
let statics_box = global_box
.get_field("statics")
.ok_or(RuntimeError::TypeError {
message: "statics namespace not found in GlobalBox".to_string()
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 statics_instance =
statics_box
.as_any()
.downcast_ref::<InstanceBox>()
.ok_or(RuntimeError::TypeError {
message: "statics field is not an InstanceBox".to_string(),
})?;
// statics InstanceBoxのfieldsに直接挿入動的フィールド追加
{
let fields = statics_instance.get_fields();
let mut fields_locked = fields.lock().unwrap();
fields_locked.insert(name.to_string(), Arc::new(instance));
}
idebug!("🔥 Static box '{}' instance registered in statics namespace", name);
idebug!(
"🔥 Static box '{}' instance registered in statics namespace",
name
);
Ok(())
}
/// 🔥 Static Boxかどうかをチェック
pub(super) fn is_static_box(&self, name: &str) -> bool {
if let Ok(definitions) = self.shared.static_box_definitions.read() {
@ -779,11 +871,11 @@ impl NyashInterpreter {
false
}
}
/// 🔗 Trigger weak reference invalidation (expert-validated implementation)
pub(super) fn trigger_weak_reference_invalidation(&mut self, target_info: &str) {
idebug!("🔗 DEBUG: Registering invalidation for: {}", target_info);
// Extract actual object ID from target_info string
// Format: "<ClassName instance #ID>" -> extract ID
if let Some(hash_pos) = target_info.find('#') {
@ -791,7 +883,7 @@ impl NyashInterpreter {
// Find the end of the ID (before '>')
let id_end = id_str.find('>').unwrap_or(id_str.len());
let clean_id_str = &id_str[..id_end];
if let Ok(id) = clean_id_str.parse::<u64>() {
self.invalidated_ids.lock().unwrap().insert(id);
idebug!("🔗 DEBUG: Object with ID {} marked as invalidated", id);
@ -810,28 +902,49 @@ impl NyashInterpreter {
}
// ==== MethodBox Invoker Bridge ==========================================
fn register_methodbox_invoker() {
use crate::method_box::{MethodBox, MethodInvoker, FunctionDefinition, set_method_invoker};
use crate::box_trait::{VoidBox};
use crate::box_trait::VoidBox;
use crate::method_box::{set_method_invoker, FunctionDefinition, MethodBox, MethodInvoker};
use std::sync::Arc;
struct SimpleMethodInvoker;
impl MethodInvoker for SimpleMethodInvoker {
fn invoke(&self, method: &MethodBox, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String> {
fn invoke(
&self,
method: &MethodBox,
args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, String> {
// 1) 取得: メソッド定義
let def: FunctionDefinition = if let Some(def) = &method.method_def {
def.clone()
} else {
let inst_guard = method.get_instance();
let inst_locked = inst_guard.lock().map_err(|_| "MethodBox instance lock poisoned".to_string())?;
let inst_locked = inst_guard
.lock()
.map_err(|_| "MethodBox instance lock poisoned".to_string())?;
if let Some(inst) = inst_locked.as_any().downcast_ref::<InstanceBox>() {
if let Some(ast) = inst.get_method(&method.method_name) {
if let ASTNode::FunctionDeclaration { name, params, body, is_static, .. } = ast {
FunctionDefinition { name: name.clone(), params: params.clone(), body: body.clone(), is_static: *is_static }
if let ASTNode::FunctionDeclaration {
name,
params,
body,
is_static,
..
} = ast
{
FunctionDefinition {
name: name.clone(),
params: params.clone(),
body: body.clone(),
is_static: *is_static,
}
} else {
return Err("Method AST is not a function declaration".to_string());
}
} else {
return Err(format!("Method '{}' not found on instance", method.method_name));
return Err(format!(
"Method '{}' not found on instance",
method.method_name
));
}
} else {
return Err("MethodBox instance is not an InstanceBox".to_string());
@ -843,14 +956,20 @@ fn register_methodbox_invoker() {
// me = instance
let me_box = {
let inst_guard = method.get_instance();
let inst_locked = inst_guard.lock().map_err(|_| "MethodBox instance lock poisoned".to_string())?;
let inst_locked = inst_guard
.lock()
.map_err(|_| "MethodBox instance lock poisoned".to_string())?;
inst_locked.clone_or_share()
};
interp.declare_local_variable("me", me_box);
// 引数をローカルへ
if def.params.len() != args.len() {
return Err(format!("Argument mismatch: expected {}, got {}", def.params.len(), args.len()));
return Err(format!(
"Argument mismatch: expected {}, got {}",
def.params.len(),
args.len()
));
}
for (p, v) in def.params.iter().zip(args.into_iter()) {
interp.declare_local_variable(p, v);

View File

@ -36,7 +36,6 @@ pub enum RuntimeError {
EnvironmentError(String),
// === 🔥 Enhanced Errors with Span Information ===
#[error("Undefined variable '{name}' at {span}")]
UndefinedVariableAt { name: String, span: Span },

View File

@ -1,8 +1,8 @@
//! Evaluation entry points: execute program and nodes
use super::{ControlFlow, NyashInterpreter, RuntimeError};
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, VoidBox};
use super::{NyashInterpreter, RuntimeError, ControlFlow};
impl NyashInterpreter {
/// ASTを実行
@ -73,22 +73,41 @@ impl NyashInterpreter {
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());
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() });
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() }),
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() });
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() });
default_args.push(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
});
}
}
}
@ -97,7 +116,10 @@ impl NyashInterpreter {
}
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() }),
object: Box::new(ASTNode::Variable {
name: "statics".to_string(),
span: crate::ast::Span::unknown(),
}),
field: "Main".to_string(),
span: crate::ast::Span::unknown(),
}),

View File

@ -21,9 +21,11 @@ macro_rules! debug_trace {
impl NyashInterpreter {
/// フィールドアクセスを実行 - Field access processing with weak reference support
pub(super) fn execute_field_access(&mut self, object: &ASTNode, field: &str)
-> Result<SharedNyashBox, RuntimeError> {
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の可能性をチェック
@ -32,8 +34,7 @@ impl NyashInterpreter {
return Ok(Arc::from(static_result));
}
}
// 外からのフィールドアクセスかme/this以外を判定
let is_internal_access = match object {
ASTNode::This { .. } | ASTNode::Me { .. } => true,
@ -41,22 +42,26 @@ impl NyashInterpreter {
_ => 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();
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),
message: format!(
"Field '{}' is private in {}",
field, instance.class_name
),
});
}
}
@ -64,28 +69,35 @@ impl NyashInterpreter {
}
// 🔥 finiは何回呼ばれてもエラーにしないユーザー要求
// is_finalized()チェックを削除
// フィールドの値を取得
let field_value = instance.get_field(field)
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
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);
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);
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() {
@ -98,63 +110,88 @@ impl NyashInterpreter {
// 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()),
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> {
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")
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()
message: "statics namespace not found in GlobalBox".to_string(),
})?;
let statics_instance = statics_box.as_any()
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: "statics field is not an InstanceBox".to_string()
message: format!("Static box '{}' is not an InstanceBox", static_box_name),
})?;
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)
let shared_field = instance
.get_field(field)
.ok_or(RuntimeError::InvalidOperation {
message: format!("Field '{}' not found in static box '{}'", field, static_box_name),
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> {
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();
@ -164,7 +201,9 @@ impl NyashInterpreter {
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 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)));
@ -173,7 +212,9 @@ impl NyashInterpreter {
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)))
Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(
value,
)))
}
}
}

View File

@ -3,29 +3,41 @@
*/
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, StringBox, IntegerBox, VoidBox};
use crate::boxes::{ArrayBox, MapBox, MathBox, ConsoleBox, TimeBox, RandomBox, DebugBox, SoundBox, SocketBox};
use crate::boxes::{HTTPServerBox, HTTPRequestBox, HTTPResponseBox};
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> {
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) });
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のインスタンスを作成デフォルト値
@ -60,7 +72,10 @@ impl NyashInterpreter {
// P2PBoxの場合、現在のインスタンスからP2PBoxインスタンスを取得する必要がある
// TODO: 現在のインスタンスのフィールドからP2PBoxを取得
return Err(RuntimeError::InvalidOperation {
message: format!("P2PBox delegation not yet fully implemented: {}.{}", parent, method),
message: format!(
"P2PBox delegation not yet fully implemented: {}.{}",
parent, method
),
});
}
"FileBox" => {
@ -87,21 +102,27 @@ impl NyashInterpreter {
}
"RandomBox" => {
if let Ok(reg) = self.runtime.box_registry.lock() {
if let Ok(_b) = reg.create_box("RandomBox", &[]) { return Ok(Box::new(VoidBox::new())); }
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())); }
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())); }
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)
@ -122,62 +143,78 @@ impl NyashInterpreter {
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),
})
}
_ => 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> {
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()),
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>() {
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));
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()),
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>() {
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),
message: format!(
"Cannot convert '{}' to integer",
arg_values[0].to_string_box().value
),
});
};
let _integer_box = IntegerBox::new(value);
Ok(Box::new(VoidBox::new()))
}
@ -185,11 +222,16 @@ impl NyashInterpreter {
// MathBoxは引数なしのコンストラクタ
if arg_values.len() != 0 {
return Err(RuntimeError::InvalidOperation {
message: format!("MathBox.birth() expects 0 arguments, got {}", arg_values.len()),
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())); }
if let Ok(_b) = reg.create_box("MathBox", &[]) {
return Ok(Box::new(VoidBox::new()));
}
}
let _math_box = MathBox::new();
Ok(Box::new(VoidBox::new()))
@ -198,10 +240,13 @@ impl NyashInterpreter {
// ArrayBoxも引数なしのコンストラクタ
if arg_values.len() != 0 {
return Err(RuntimeError::InvalidOperation {
message: format!("ArrayBox.birth() expects 0 arguments, got {}", arg_values.len()),
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()))
@ -209,7 +254,10 @@ impl NyashInterpreter {
_ => {
// 他のビルトインBoxは今後追加
Err(RuntimeError::InvalidOperation {
message: format!("birth() method not yet implemented for builtin box '{}'", builtin_name),
message: format!(
"birth() method not yet implemented for builtin box '{}'",
builtin_name
),
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
/*!
* 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 operators;
mod calls;
mod access;
mod builtins;
mod calls;
mod operators;
use super::*;
use std::sync::Arc;
@ -21,69 +21,178 @@ use std::sync::Arc;
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> {
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)); }
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()); }
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()); }
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, .. } => {
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); } }
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::Loop {
condition, body, ..
} => {
collect(condition, used, locals);
for st in body {
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, .. } => {
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); }
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::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 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); }
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>() {
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
@ -93,13 +202,18 @@ impl NyashInterpreter {
}
} else {
// non-local (global/static): by-value capture
if let Ok(v) = self.resolve_variable(&name) { env.captures.insert(name, v.clone_or_share()); }
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> {
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, .. } => {
@ -111,87 +225,106 @@ impl NyashInterpreter {
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::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::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
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, .. } => {
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::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(),
})?;
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(),
})?;
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)
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 {
@ -199,19 +332,21 @@ impl NyashInterpreter {
})
}
}
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)
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 {
@ -219,30 +354,48 @@ impl NyashInterpreter {
})
}
}
ASTNode::FunctionCall { name, arguments, .. } => {
self.execute_function_call(name, arguments)
}
ASTNode::Call { callee, arguments, .. } => {
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>() {
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)?); }
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()) });
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()); }
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()));
self.declare_local_variable(
"me",
Box::new(crate::boxes::null_box::NullBox::new()),
);
}
}
for (p, v) in fun.params.iter().zip(arg_values.iter()) {
@ -265,30 +418,52 @@ impl NyashInterpreter {
} 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)?); }
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()) });
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()); }
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; } }
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() })
Err(RuntimeError::InvalidOperation {
message: "Callee is not callable".to_string(),
})
}
}
ASTNode::Arrow { sender, receiver, .. } => {
self.execute_arrow(sender, receiver)
}
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>() {
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());
@ -301,7 +476,12 @@ impl NyashInterpreter {
// Not a Result: pass-through
Ok(v)
}
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => {
ASTNode::PeekExpr {
scrutinee,
arms,
else_expr,
..
} => {
let val = self.execute_expression(scrutinee)?;
let sval = val.to_string_box().value;
for (pat, expr) in arms {
@ -309,7 +489,13 @@ impl NyashInterpreter {
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::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(),
};
@ -322,27 +508,31 @@ impl NyashInterpreter {
ASTNode::Lambda { params, body, .. } => {
// 値としての関数ボックスを生成ClosureEnv: me/by-val captures
let env = self.build_closure_env(&params, body)?;
Ok(Box::new(crate::boxes::function_box::FunctionBox::with_env(params.clone(), body.clone(), env)))
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)
}
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> {
@ -353,16 +543,16 @@ impl NyashInterpreter {
}
ASTNode::Me { .. } => {
// 'me'参照の特別なID
Some(usize::MAX)
Some(usize::MAX)
}
ASTNode::This { .. } => {
// 'this'参照の特別なID
// 'this'参照の特別なID
Some(usize::MAX - 1)
}
_ => None, // 他のードタイプはID追跡しない
}
}
/// 🔄 文字列のシンプルなハッシュ関数
#[allow(dead_code)]
fn hash_string(&self, s: &str) -> usize {
@ -372,13 +562,13 @@ impl NyashInterpreter {
}
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>() {
@ -395,6 +585,4 @@ impl NyashInterpreter {
// None // Simplified for now
// }
// }
}

View File

@ -4,18 +4,21 @@
// Removed super::* import - specific imports below
use crate::ast::{ASTNode, BinaryOperator, UnaryOperator};
use crate::box_trait::{NyashBox, BoolBox, CompareBox};
use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一
use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在
use crate::interpreter::{NyashInterpreter, RuntimeError};
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());
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 {
@ -28,156 +31,226 @@ fn unwrap_instance(boxed: &dyn NyashBox) -> &dyn NyashBox {
}
fn best_effort_to_string(val: &dyn NyashBox) -> String {
crate::runtime::semantics::coerce_to_string(val)
.unwrap_or_else(|| val.to_string_box().value)
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>> {
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>()
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))));
return Some(Box::new(StringBox::new(format!(
"{}{}",
left_str.value, right_str.value
))));
}
// BoolBox + BoolBox -> IntegerBox
// BoolBox + BoolBox -> IntegerBox
if let (Some(left_bool), Some(right_bool)) = (
left.as_any().downcast_ref::<BoolBox>(),
right.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))));
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>> {
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>()
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>> {
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());
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>()
right.as_any().downcast_ref::<IntegerBox>(),
) {
eprintln!("✅ IntegerBox downcast success: {} * {}", left_int.value, right_int.value);
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>()
right.as_any().downcast_ref::<BoxesIntegerBox>(),
) {
eprintln!("✅ boxes::IntegerBox downcast success: {} * {}", left_int.value, right_int.value);
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>()
right.as_any().downcast_ref::<IntegerBox>(),
) {
return Some(Box::new(StringBox::new(str_box.value.repeat(count_int.value as usize))));
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> {
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>()
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()))
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> {
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>()
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()))
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> {
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) {
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);
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())) {
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)));
}
}
@ -185,17 +258,53 @@ impl NyashInterpreter {
}
}
}
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) };
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" };
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);
@ -206,14 +315,21 @@ impl NyashInterpreter {
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()) {
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()) {
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)));
@ -222,22 +338,25 @@ impl NyashInterpreter {
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())
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 {
@ -247,7 +366,7 @@ impl NyashInterpreter {
Ok(Box::new(BoolBox::new(right_bool)))
}
}
BinaryOperator::Or => {
let left_bool = self.is_truthy(&left_val);
if left_bool {
@ -257,72 +376,137 @@ impl NyashInterpreter {
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 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);
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())
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 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);
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())
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 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);
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
})
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
})
Err(error_msg) => Err(RuntimeError::InvalidOperation { message: error_msg }),
}
}
BinaryOperator::Shl => {
// Integer-only left shift
if let (Some(li), Some(ri)) = (
@ -332,8 +516,12 @@ impl NyashInterpreter {
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())
Err(RuntimeError::TypeError {
message: format!(
"Shift-left '<<' requires integers (got {} and {})",
left_val.type_name(),
right_val.type_name()
),
})
}
BinaryOperator::Shr => {
@ -344,59 +532,90 @@ impl NyashInterpreter {
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())
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()) })
) {
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()) })
) {
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()) })
) {
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> {
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 => {
// 数値の符号反転

View File

@ -1,6 +1,6 @@
/*!
* 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
@ -10,18 +10,26 @@ use super::*;
impl NyashInterpreter {
/// 関数呼び出しを実行 - 🌍 革命的実装GlobalBoxのメソッド呼び出し
pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
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>() {
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() });
return Err(RuntimeError::InvalidOperation {
message: "Type name must be a string".to_string(),
});
};
if name == "isType" {
@ -41,14 +49,17 @@ impl NyashInterpreter {
}
}
}
// 🌍 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() })?
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 {
// 引数を評価
@ -56,24 +67,28 @@ impl NyashInterpreter {
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()),
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());
@ -104,22 +119,28 @@ impl NyashInterpreter {
})
}
}
/// 関数宣言を登録 - 🌍 革命的実装GlobalBoxのメソッドとして登録
pub(super) fn register_function_declaration(&mut self, name: String, params: Vec<String>, body: Vec<ASTNode>) {
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, // 🔥 通常の関数はオーバーライドでない
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);
});
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
@ -136,7 +157,10 @@ impl NyashInterpreter {
}
/// 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> {
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
@ -151,7 +175,8 @@ impl NyashInterpreter {
"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>() {
} 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)

View File

@ -1,6 +1,6 @@
/*!
* 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
@ -28,7 +28,9 @@ impl NyashInterpreter {
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('/'); }
if !base.ends_with('/') && !base.ends_with('\\') {
base.push('/');
}
let joined = format!("{}{}", base, rest);
return joined;
}
@ -64,47 +66,61 @@ impl NyashInterpreter {
// 検出: 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(" -> "));
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) {
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 {
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 {
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());
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> {
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対応
@ -128,31 +144,45 @@ impl NyashInterpreter {
}
// ファイル読み込みstatic box名検出用
let content = std::fs::read_to_string(&canonical_path)
.map_err(|e| RuntimeError::InvalidOperation {
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 {
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 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) });
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) });
return Err(RuntimeError::InvalidOperation {
message: format!(
"include target '{}' defines multiple static boxes; exactly one is required",
filename
),
});
}
let box_name = static_names.remove(0);
@ -162,7 +192,11 @@ impl NyashInterpreter {
set.contains(&canonical_path)
};
if !already {
self.shared.included_files.lock().unwrap().insert(canonical_path.clone());
self.shared
.included_files
.lock()
.unwrap()
.insert(canonical_path.clone());
let exec_res = self.execute(ast);
// スタックを外す
self.shared.include_stack.lock().unwrap().pop();
@ -176,23 +210,42 @@ impl NyashInterpreter {
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) })?;
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> {
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(),
@ -206,26 +259,31 @@ impl NyashInterpreter {
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>;
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> {
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();
@ -242,28 +300,34 @@ impl NyashInterpreter {
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); }
Ok(result) => {
future_for_sched.set_result(result);
}
Err(e) => {
// エラーをErrorBoxとして設定
let error_box = Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e)));
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); }
Ok(result) => {
future_for_thread.set_result(result);
}
Err(e) => {
let error_box = Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", 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を変数に保存

View File

@ -1,21 +1,21 @@
/*!
* 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.
*/
@ -24,14 +24,18 @@ 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> {
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 {
// 基本数学演算
@ -75,7 +79,7 @@ impl NyashInterpreter {
}
Ok(math_box.sqrt(arg_values[0].clone_box()))
}
// 数学定数
"getPi" => {
if !arg_values.is_empty() {
@ -93,7 +97,7 @@ impl NyashInterpreter {
}
Ok(math_box.getE())
}
// 三角関数
"sin" => {
if arg_values.len() != 1 {
@ -119,7 +123,7 @@ impl NyashInterpreter {
}
Ok(math_box.tan(arg_values[0].clone_box()))
}
// 対数・指数関数
"log" => {
if arg_values.len() != 1 {
@ -145,7 +149,7 @@ impl NyashInterpreter {
}
Ok(math_box.exp(arg_values[0].clone_box()))
}
// 丸め関数
"floor" => {
if arg_values.len() != 1 {
@ -171,25 +175,27 @@ impl NyashInterpreter {
}
Ok(math_box.round(arg_values[0].clone_box()))
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown MathBox method: {}", method),
})
}
_ => 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> {
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 {
// 乱数シード設定
@ -201,7 +207,7 @@ impl NyashInterpreter {
}
Ok(random_box.seed(arg_values[0].clone_box()))
}
// 基本乱数生成
"random" => {
if !arg_values.is_empty() {
@ -222,12 +228,15 @@ impl NyashInterpreter {
"randBool" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("randBool() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"randBool() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(random_box.randBool())
}
// 配列・コレクション操作
"choice" => {
if arg_values.len() != 1 {
@ -245,12 +254,15 @@ impl NyashInterpreter {
}
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()),
message: format!(
"randString() expects 1 argument, got {}",
arg_values.len()
),
});
}
Ok(random_box.randString(arg_values[0].clone_box()))
@ -258,17 +270,18 @@ impl NyashInterpreter {
"probability" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("probability() expects 1 argument, got {}", arg_values.len()),
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),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown RandomBox method: {}", method),
}),
}
}
}
}

View File

@ -1,22 +1,26 @@
/*!
* Basic Box Methods Module
*
*
* Extracted from box_methods.rs
* Contains method implementations for:
* - StringBox (execute_string_method)
* - IntegerBox (execute_integer_method)
* - IntegerBox (execute_integer_method)
* - BoolBox (execute_bool_method)
* - FloatBox (execute_float_method)
*/
use super::super::*;
use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox};
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> {
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 {
@ -93,7 +97,7 @@ impl NyashInterpreter {
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>()
new_value.as_any().downcast_ref::<StringBox>(),
) {
Ok(string_box.replace(&old_str.value, &new_str.value))
} else {
@ -129,7 +133,10 @@ impl NyashInterpreter {
"toInteger" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toInteger() expects 0 arguments, got {}", arguments.len()),
message: format!(
"toInteger() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(string_box.to_integer())
@ -137,12 +144,15 @@ impl NyashInterpreter {
"substring" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("substring() expects 2 arguments, got {}", arguments.len()),
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
@ -151,7 +161,7 @@ impl NyashInterpreter {
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 {
@ -159,20 +169,22 @@ impl NyashInterpreter {
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),
})
}
_ => 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> {
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() {
@ -198,7 +210,9 @@ impl NyashInterpreter {
}
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))))
Ok(Box::new(IntegerBox::new(
integer_box.value.max(other_int.value),
)))
} else {
Err(RuntimeError::TypeError {
message: "max() requires integer argument".to_string(),
@ -213,7 +227,9 @@ impl NyashInterpreter {
}
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))))
Ok(Box::new(IntegerBox::new(
integer_box.value.min(other_int.value),
)))
} else {
Err(RuntimeError::TypeError {
message: "min() requires integer argument".to_string(),
@ -249,17 +265,19 @@ impl NyashInterpreter {
})
}
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for IntegerBox", method),
})
}
_ => 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> {
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() {
@ -316,17 +334,19 @@ impl NyashInterpreter {
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),
})
}
_ => 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> {
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() {
@ -371,7 +391,10 @@ impl NyashInterpreter {
"toInteger" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toInteger() expects 0 arguments, got {}", arguments.len()),
message: format!(
"toInteger() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(Box::new(IntegerBox::new(float_box.value as i64)))
@ -384,9 +407,13 @@ impl NyashInterpreter {
}
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))))
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))))
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(),
@ -401,9 +428,13 @@ impl NyashInterpreter {
}
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))))
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))))
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(),
@ -418,9 +449,15 @@ impl NyashInterpreter {
}
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))))
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(),
@ -512,7 +549,10 @@ impl NyashInterpreter {
"isInfinite" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isInfinite() expects 0 arguments, got {}", arguments.len()),
message: format!(
"isInfinite() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(Box::new(BoolBox::new(float_box.value.is_infinite())))
@ -534,11 +574,9 @@ impl NyashInterpreter {
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),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for FloatBox", method),
}),
}
}
}
}

View File

@ -1,6 +1,6 @@
/*!
* Collection Methods Module
*
*
* Extracted from box_methods.rs
* Contains method implementations for collection types:
* - ArrayBox (execute_array_method)
@ -8,13 +8,17 @@
*/
use super::super::*;
use crate::box_trait::{IntegerBox, NyashBox, BoolBox};
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> {
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
@ -154,25 +158,29 @@ impl NyashInterpreter {
"slice" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("slice() expects 2 arguments (start, end), got {}", arguments.len()),
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),
})
}
_ => 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> {
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" => {
@ -260,7 +268,10 @@ impl NyashInterpreter {
"containsKey" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("containsKey() expects 1 argument, got {}", arguments.len()),
message: format!(
"containsKey() expects 1 argument, got {}",
arguments.len()
),
});
}
let key_value = self.execute_expression(&arguments[0])?;
@ -269,7 +280,10 @@ impl NyashInterpreter {
"containsValue" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("containsValue() expects 1 argument, got {}", arguments.len()),
message: format!(
"containsValue() expects 1 argument, got {}",
arguments.len()
),
});
}
let _value = self.execute_expression(&arguments[0])?;
@ -303,11 +317,9 @@ impl NyashInterpreter {
}
Ok(Box::new(map_box.to_string_box()))
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown MapBox method: {}", method),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown MapBox method: {}", method),
}),
}
}
}

View File

@ -1,6 +1,6 @@
/*!
* 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
@ -13,8 +13,12 @@ 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> {
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 {
@ -81,7 +85,10 @@ impl NyashInterpreter {
"is_shared_with" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("is_shared_with() expects 1 argument, got {}", arguments.len()),
message: format!(
"is_shared_with() expects 1 argument, got {}",
arguments.len()
),
});
}
let other = self.execute_expression(&arguments[0])?;
@ -90,7 +97,10 @@ impl NyashInterpreter {
"share_reference" => {
if arguments.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("share_reference() expects 1 argument, got {}", arguments.len()),
message: format!(
"share_reference() expects 1 argument, got {}",
arguments.len()
),
});
}
let data = self.execute_expression(&arguments[0])?;
@ -99,20 +109,27 @@ impl NyashInterpreter {
"memory_footprint" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("memory_footprint() expects 0 arguments, got {}", arguments.len()),
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> {
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 {
@ -126,7 +143,10 @@ impl NyashInterpreter {
"stringify" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("stringify() expects 0 arguments, got {}", arguments.len()),
message: format!(
"stringify() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(json_box.stringify())
@ -169,13 +189,17 @@ impl NyashInterpreter {
}
_ => 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> {
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 {
@ -225,7 +249,7 @@ impl NyashInterpreter {
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for RegexBox", method),
})
}),
}
}
}
}

View File

@ -1,19 +1,19 @@
/*! 🌐 HTTP Method Implementations
*
*
* HTTP関連Boxのメソッド実行を実装
* SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox
*/
use super::super::*;
use crate::boxes::{SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox};
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]
&mut self,
socket_box: &SocketBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"bind" => {
@ -22,7 +22,7 @@ impl NyashInterpreter {
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);
@ -34,7 +34,7 @@ impl NyashInterpreter {
message: format!("listen() expects 1 argument, got {}", arguments.len()),
});
}
let backlog = self.execute_expression(&arguments[0])?;
Ok(socket_box.listen(backlog))
}
@ -44,13 +44,16 @@ impl NyashInterpreter {
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()),
message: format!(
"acceptTimeout(ms) expects 1 argument, got {}",
arguments.len()
),
});
}
let ms = self.execute_expression(&arguments[0])?;
@ -62,7 +65,7 @@ impl NyashInterpreter {
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))
@ -73,13 +76,16 @@ impl NyashInterpreter {
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()),
message: format!(
"recvTimeout(ms) expects 1 argument, got {}",
arguments.len()
),
});
}
let ms = self.execute_expression(&arguments[0])?;
@ -88,10 +94,13 @@ impl NyashInterpreter {
"readHttpRequest" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("readHttpRequest() expects 0 arguments, got {}", arguments.len()),
message: format!(
"readHttpRequest() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(socket_box.read_http_request())
}
"write" => {
@ -100,7 +109,7 @@ impl NyashInterpreter {
message: format!("write() expects 1 argument, got {}", arguments.len()),
});
}
let data = self.execute_expression(&arguments[0])?;
Ok(socket_box.write(data))
}
@ -110,16 +119,19 @@ impl NyashInterpreter {
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()),
message: format!(
"isConnected() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(socket_box.is_connected())
}
"isServer" => {
@ -128,7 +140,7 @@ impl NyashInterpreter {
message: format!("isServer() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(socket_box.is_server())
}
"toString" => {
@ -137,7 +149,7 @@ impl NyashInterpreter {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(socket_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
@ -148,10 +160,10 @@ impl NyashInterpreter {
/// HTTPServerBox methods
pub(in crate::interpreter) fn execute_http_server_method(
&mut self,
server_box: &HTTPServerBox,
method: &str,
arguments: &[ASTNode]
&mut self,
server_box: &HTTPServerBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"bind" => {
@ -160,7 +172,7 @@ impl NyashInterpreter {
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))
@ -171,7 +183,7 @@ impl NyashInterpreter {
message: format!("listen() expects 1 argument, got {}", arguments.len()),
});
}
let backlog = self.execute_expression(&arguments[0])?;
Ok(server_box.listen(backlog))
}
@ -181,7 +193,7 @@ impl NyashInterpreter {
message: format!("start() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(server_box.start())
}
"stop" => {
@ -190,7 +202,7 @@ impl NyashInterpreter {
message: format!("stop() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(server_box.stop())
}
"get" => {
@ -199,7 +211,7 @@ impl NyashInterpreter {
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))
@ -210,7 +222,7 @@ impl NyashInterpreter {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(server_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
@ -221,19 +233,22 @@ impl NyashInterpreter {
/// HTTPRequestBox methods
pub(in crate::interpreter) fn execute_http_request_method(
&mut self,
request_box: &HTTPRequestBox,
method: &str,
arguments: &[ASTNode]
&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()),
message: format!(
"getMethod() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(request_box.get_method())
}
"getPath" => {
@ -242,7 +257,7 @@ impl NyashInterpreter {
message: format!("getPath() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(request_box.get_path())
}
"toString" => {
@ -251,7 +266,7 @@ impl NyashInterpreter {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(request_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {
@ -262,19 +277,22 @@ impl NyashInterpreter {
/// HTTPResponseBox methods
pub(in crate::interpreter) fn execute_http_response_method(
&mut self,
response_box: &HTTPResponseBox,
method: &str,
arguments: &[ASTNode]
&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()),
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))
@ -282,10 +300,13 @@ impl NyashInterpreter {
"toHttpString" => {
if !arguments.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toHttpString() expects 0 arguments, got {}", arguments.len()),
message: format!(
"toHttpString() expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(response_box.to_http_string())
}
"toString" => {
@ -294,7 +315,7 @@ impl NyashInterpreter {
message: format!("toString() expects 0 arguments, got {}", arguments.len()),
});
}
Ok(Box::new(response_box.to_string_box()))
}
_ => Err(RuntimeError::UndefinedVariable {

View File

@ -1,6 +1,6 @@
/*!
* 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
@ -8,17 +8,21 @@
*/
use super::super::*;
use crate::boxes::ResultBox;
use crate::box_trait::{StringBox, NyashBox};
use crate::boxes::FileBox;
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> {
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() {
@ -70,14 +74,18 @@ impl NyashInterpreter {
}
_ => 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> {
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() {
@ -105,38 +113,50 @@ impl NyashInterpreter {
}
_ => 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> {
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()) });
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()) }); }
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) })
_ => 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])
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) => {
@ -147,11 +167,11 @@ impl NyashInterpreter {
}
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) => {
@ -172,25 +192,25 @@ impl NyashInterpreter {
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 {}",
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);
@ -205,15 +225,15 @@ impl NyashInterpreter {
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>,
&self,
encoder: &mut crate::bid::tlv::TlvEncoder,
value: Box<dyn NyashBox>,
mapping: &crate::bid::ArgTypeMapping
) -> Result<(), RuntimeError> {
// determine_bid_tag()を使って適切なタグを決定
@ -221,7 +241,7 @@ impl NyashInterpreter {
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Unsupported type mapping: {} -> {}", mapping.from, mapping.to),
})?;
// タグに応じてエンコード
match tag {
crate::bid::BidTag::String => {
@ -267,7 +287,7 @@ impl NyashInterpreter {
})
}
}
/// デフォルトエンコード(型情報がない場合のフォールバック)
fn encode_value_default(
&self,
@ -297,26 +317,26 @@ impl NyashInterpreter {
})
}
}
/// 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();
@ -359,7 +379,7 @@ impl NyashInterpreter {
/// 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])
pub(in crate::interpreter) fn execute_plugin_file_method(&mut self, plugin_file_box: &PluginFileBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 🎯 新しい汎用システムにリダイレクト
self.execute_plugin_method_generic(plugin_file_box, method, arguments)

View File

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

View File

@ -1,6 +1,6 @@
/*!
* 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
@ -12,8 +12,12 @@ 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> {
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 {
@ -66,13 +70,17 @@ impl NyashInterpreter {
}
_ => 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> {
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 {
@ -118,7 +126,7 @@ impl NyashInterpreter {
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for StreamBox", method),
})
}),
}
}
}
}

View File

@ -3,11 +3,11 @@
* Arc<Mutex>パターン対応版
*/
use crate::interpreter::NyashInterpreter;
use crate::interpreter::RuntimeError;
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版)
@ -16,29 +16,23 @@ impl NyashInterpreter {
intent_box: &IntentBox,
method: &str,
_arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
// メッセージ名取得
"getName" | "name" => {
Ok(intent_box.get_name())
}
"getName" | "name" => Ok(intent_box.get_name()),
// ペイロード取得JSON文字列として
"getPayload" | "payload" => {
Ok(intent_box.get_payload())
}
"getPayload" | "payload" => Ok(intent_box.get_payload()),
// 型情報取得
"getType" | "type" => {
Ok(Box::new(StringBox::new("IntentBox")))
}
"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,
@ -46,8 +40,14 @@ impl NyashInterpreter {
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());
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取得
@ -59,7 +59,9 @@ impl NyashInterpreter {
// ノード到達可能性確認
"isReachable" => {
if arguments.is_empty() {
return Err(RuntimeError::InvalidOperation { message: "isReachable requires node_id argument".to_string() });
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))
@ -68,7 +70,9 @@ impl NyashInterpreter {
// send メソッド実装ResultBox返却
"send" => {
if arguments.len() < 2 {
return Err(RuntimeError::InvalidOperation { message: "send requires (to, intent) arguments".to_string() });
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])?;
@ -78,7 +82,9 @@ impl NyashInterpreter {
// 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() });
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 {
@ -93,7 +99,9 @@ impl NyashInterpreter {
// on メソッド実装ResultBox返却
"on" => {
if arguments.len() < 2 {
return Err(RuntimeError::InvalidOperation { message: "on requires (intentName, handler) arguments".to_string() });
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])?;
@ -109,7 +117,9 @@ impl NyashInterpreter {
// onOnce / off
"onOnce" | "on_once" => {
if arguments.len() < 2 {
return Err(RuntimeError::InvalidOperation { message: "onOnce requires (intentName, handler) arguments".to_string() });
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])?;
@ -117,13 +127,17 @@ impl NyashInterpreter {
}
"off" => {
if arguments.len() < 1 {
return Err(RuntimeError::InvalidOperation { message: "off requires (intentName) argument".to_string() });
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) }),
_ => Err(RuntimeError::UndefinedVariable {
name: format!("P2PBox method '{}' not found", method),
}),
}
}
}

View File

@ -1,15 +1,15 @@
/*!
* 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::{NyashBox, BoolBox};
use crate::boxes::gc_config_box::GcConfigBox;
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 {
@ -24,59 +24,71 @@ impl NyashInterpreter {
"setFlag" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("GcConfigBox.setFlag expects 2 arguments, got {}", arguments.len()),
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()),
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()),
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()),
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,
@ -88,81 +100,99 @@ impl NyashInterpreter {
"setFlag" => {
if arguments.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("DebugConfigBox.setFlag expects 2 arguments, got {}", arguments.len()),
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()),
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()),
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()),
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()),
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()),
message: format!(
"DebugConfigBox.summary expects 0 arguments, got {}",
arguments.len()
),
});
}
Ok(debug_box.summary())
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("DebugConfigBox has no method '{}'", method),
}),

View File

@ -1,14 +1,17 @@
//! Central builtin method dispatcher (thin wrapper)
use crate::ast::ASTNode;
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore};
use crate::boxes::{ArrayBox, FloatBox, BufferBox, ResultBox, FutureBox, JSONBox, HttpClientBox, StreamBox, RegexBox, MathBox};
use crate::boxes::{null_box, time_box, map_box, random_box, sound_box, debug_box, console_box};
use crate::boxes::{gc_config_box::GcConfigBox, debug_config_box::DebugConfigBox};
use crate::boxes::ref_cell_box::RefCellBox as RcCell;
use crate::boxes::file;
use crate::channel_box::ChannelBox;
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.
@ -136,10 +139,10 @@ impl NyashInterpreter {
method: &str,
arguments: &[ASTNode],
) -> Option<Result<Box<dyn NyashBox>, RuntimeError>> {
use crate::box_trait::{StringBox, IntegerBox};
use crate::box_trait::{IntegerBox, StringBox};
use crate::boxes::MathBox;
use crate::instance_v2::InstanceBox;
use crate::finalization;
use crate::instance_v2::InstanceBox;
let instance = match obj_value.as_any().downcast_ref::<InstanceBox>() {
Some(i) => i,
@ -149,11 +152,18 @@ impl NyashInterpreter {
// 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::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 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!(
@ -175,11 +185,17 @@ impl NyashInterpreter {
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>;
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)); }
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;
@ -191,7 +207,9 @@ impl NyashInterpreter {
}
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 })); }
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())));
}
@ -199,7 +217,10 @@ impl NyashInterpreter {
// 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);
eprintln!(
"[dbg] enter instance method {}.{}",
instance.class_name, method
);
// Evaluate args in current context
let mut arg_values = Vec::new();
for a in arguments {
@ -210,7 +231,12 @@ impl NyashInterpreter {
}
if arg_values.len() != params.len() {
return Some(Err(RuntimeError::InvalidOperation {
message: format!("Method {} expects {} arguments, got {}", method, params.len(), arg_values.len()),
message: format!(
"Method {} expects {} arguments, got {}",
method,
params.len(),
arg_values.len()
),
}));
}
let saved = self.save_local_vars();
@ -222,7 +248,9 @@ impl NyashInterpreter {
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; },
Ok(v) => {
result = v;
}
Err(e) => return Some(Err(e)),
}
if let super::ControlFlow::Return(ret) = &self.control_flow {
@ -232,17 +260,25 @@ impl NyashInterpreter {
}
}
self.restore_local_vars(saved);
eprintln!("[dbg] exit instance method {}.{}", instance.class_name, method);
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) }));
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()
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) {
@ -280,13 +316,18 @@ impl NyashInterpreter {
{
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>() {
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) }))
Some(Err(RuntimeError::InvalidOperation {
message: format!("Method '{}' not found in {}", method, instance.class_name),
}))
}
}

View File

@ -1,54 +1,53 @@
/*!
* 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
*/
// Import all necessary dependencies
use crate::ast::{ASTNode, CatchClause};
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, ErrorBox, BoxCore};
use crate::boxes::FutureBox;
use crate::instance_v2::InstanceBox;
use crate::channel_box::ChannelBox;
use crate::boxes::math_box::MathBox;
use crate::boxes::time_box::TimerBox;
use crate::boxes::random_box::RandomBox;
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::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
use crate::boxes::web::{WebCanvasBox, WebConsoleBox, WebDisplayBox};
use crate::exception_box;
use std::collections::HashMap;
// Module declarations
// Module declarations
mod async_methods;
mod box_methods;
mod core;
mod eval;
mod calls;
mod methods_dispatch;
pub mod utils;
pub mod state;
mod core;
pub mod errors;
mod eval;
mod expressions;
mod statements;
mod functions;
mod io;
mod math_methods;
mod methods;
mod methods_dispatch;
pub mod objects;
mod objects_basic_constructors;
mod io;
mod methods;
mod math_methods;
mod system_methods;
mod web_methods;
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 state::SharedState;
pub use errors::RuntimeError;
pub use state::SharedState;
/// 実行制御フロー
#[derive(Debug)]
@ -77,9 +76,9 @@ pub struct StaticBoxDefinition {
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 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>,
/// 初期化状態
@ -89,9 +88,9 @@ pub struct StaticBoxDefinition {
/// 🔥 Static Box初期化状態
#[derive(Debug, Clone, PartialEq)]
pub enum StaticBoxState {
NotInitialized, // 未初期化
Initializing, // 初期化中(循環参照検出用)
Initialized, // 初期化完了
NotInitialized, // 未初期化
Initializing, // 初期化中(循環参照検出用)
Initialized, // 初期化完了
}
/// 関数宣言を保持する構造体
@ -105,5 +104,5 @@ pub struct FunctionDeclaration {
// Re-export core interpreter types
pub use core::*;
// Import and re-export stdlib for interpreter modules
// Import and re-export stdlib for interpreter modules
pub use crate::stdlib::BuiltinStdlib;

View File

@ -15,10 +15,14 @@ impl NyashInterpreter {
is_interface: bool,
extends: Vec<String>,
implements: Vec<String>,
type_parameters: Vec<String>
type_parameters: Vec<String>,
) -> Result<(), RuntimeError> {
if !constructors.is_empty() {
eprintln!("🐛 DEBUG: Registering Box '{}' with constructors: {:?}", name, constructors.keys().collect::<Vec<_>>());
eprintln!(
"🐛 DEBUG: Registering Box '{}' with constructors: {:?}",
name,
constructors.keys().collect::<Vec<_>>()
);
}
if constructors.len() > 1 {
let constructor_names: Vec<String> = constructors.keys().cloned().collect();
@ -31,7 +35,7 @@ impl NyashInterpreter {
name,
constructors.len(),
constructor_names.join(", ")
)
),
});
}
let box_decl = super::BoxDeclaration {
@ -56,8 +60,11 @@ impl NyashInterpreter {
}
/// 🔥 ジェネリクス型引数の検証
pub(super) fn validate_generic_arguments(&self, box_decl: &BoxDeclaration, type_arguments: &[String])
-> Result<(), RuntimeError> {
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!(
@ -81,7 +88,9 @@ impl NyashInterpreter {
}
for type_arg in type_arguments {
if !self.is_valid_type(type_arg) {
return Err(RuntimeError::TypeError { message: format!("Unknown type '{}'", type_arg) });
return Err(RuntimeError::TypeError {
message: format!("Unknown type '{}'", type_arg),
});
}
}
Ok(())
@ -90,14 +99,22 @@ impl NyashInterpreter {
/// 型が有効かどうかをチェック
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; }
if reg.has_type(type_name) {
return true;
}
}
self.shared.box_declarations.read().unwrap().contains_key(type_name)
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> {
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 {
@ -105,20 +122,28 @@ impl NyashInterpreter {
let is_builtin = is_builtin_box(parent_name);
#[cfg(all(feature = "gui", not(target_arch = "wasm32")))]
{
if parent_name == "EguiBox" { is_builtin = true; }
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() })?
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),
message: format!(
"Cannot extend interface '{}'. Use 'implements' instead.",
parent_name
),
});
}
let (parent_fields, parent_methods) = self.resolve_inheritance(&parent_decl)?;
@ -128,7 +153,9 @@ impl NyashInterpreter {
}
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()); }
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());
@ -136,18 +163,25 @@ impl NyashInterpreter {
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() })?
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) });
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),
message: format!(
"Class '{}' must implement method '{}' from interface '{}'",
box_decl.name, required_method, interface_name
),
});
}
}
@ -159,7 +193,7 @@ impl NyashInterpreter {
pub(super) fn specialize_generic_class(
&self,
generic_decl: &BoxDeclaration,
type_arguments: &[String]
type_arguments: &[String],
) -> Result<BoxDeclaration, RuntimeError> {
use std::collections::HashMap;
let specialized_name = format!("{}_{}", generic_decl.name, type_arguments.join("_"));
@ -170,7 +204,8 @@ impl NyashInterpreter {
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);
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) {
@ -186,7 +221,7 @@ impl NyashInterpreter {
pub(super) fn substitute_types_in_fields(
&self,
fields: &[String],
_type_mapping: &HashMap<String, String>
_type_mapping: &HashMap<String, String>,
) -> Vec<String> {
fields.to_vec()
}

View File

@ -8,14 +8,26 @@ impl NyashInterpreter {
instance: &SharedNyashBox,
constructor: &ASTNode,
arguments: &[ASTNode],
box_decl: &BoxDeclaration
box_decl: &BoxDeclaration,
) -> Result<(), RuntimeError> {
if let ASTNode::FunctionDeclaration { name: _, params, body, .. } = constructor {
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)?); }
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()),
message: format!(
"Constructor expects {} arguments, got {}",
params.len(),
arg_values.len()
),
});
}
let saved_locals = self.save_local_vars();
@ -31,35 +43,52 @@ impl NyashInterpreter {
});
let mut result = Ok(());
for statement in body.iter() {
if let Err(e) = self.execute_statement(statement) { result = Err(e); break; }
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() })
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> {
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() })?
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(),
})?;
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()),
message: format!(
"No constructor found for parent class {} with {} arguments",
parent_class,
arguments.len()
),
})
}
}

View File

@ -9,7 +9,6 @@
use super::*;
mod ops;
mod methods;
mod fields;
mod methods;
mod ops;

View File

@ -4,12 +4,22 @@ 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()
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> {
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();
@ -45,7 +55,10 @@ impl NyashInterpreter {
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>() {
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();
@ -54,28 +67,39 @@ impl NyashInterpreter {
let birth_key = format!("birth/{}", arguments.len());
let constructor = box_decl.constructors.get(&birth_key).cloned();
(Some(box_decl.clone()), constructor)
} else { (None, None) }
} 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)?;
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()),
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.
@ -84,13 +108,17 @@ impl NyashInterpreter {
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> {
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)?;

View File

@ -3,27 +3,30 @@
use crate::ast::ASTNode;
use crate::box_trait::*;
use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError};
use crate::boxes::FloatBox;
use crate::boxes::null_box::NullBox;
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]
&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()),
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())));
@ -35,40 +38,49 @@ impl Interpreter {
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()),
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),
}),
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(),
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()),
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)));
@ -76,57 +88,72 @@ impl Interpreter {
let val = match s.value.as_str() {
"true" => true,
"false" => false,
_ => return Err(RuntimeError::TypeError {
message: format!("Cannot convert '{}' to boolean", s.value),
}),
_ => {
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(),
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()),
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()),
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()),
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()),
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)));
@ -135,22 +162,28 @@ impl Interpreter {
} 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),
}),
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(),
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()),
message: format!(
"FutureBox constructor expects 0 or 1 argument, got {}",
arguments.len()
),
});
}
let fut = crate::boxes::future::NyashFutureBox::new();
@ -160,7 +193,7 @@ impl Interpreter {
}
return Ok(Box::new(fut));
}
_ => {
// Not a basic type
Err(RuntimeError::TypeError {

View File

@ -1,12 +1,12 @@
/*!
* 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
@ -14,12 +14,12 @@
use super::*;
use crate::boxes::SoundBox;
use crate::method_box::MethodBox;
use crate::instance_v2::InstanceBox;
use crate::method_box::MethodBox;
impl NyashInterpreter {
/// SoundBoxのメソッド呼び出しを実行
///
///
/// SoundBoxはオーディオ機能を提供する重要なBox:
/// - beep(), beeps() - 基本的なビープ音
/// - tone() - カスタム周波数/期間の音
@ -27,14 +27,18 @@ impl NyashInterpreter {
/// - pattern() - 音パターン再生
/// - volumeTest() - 音量テスト
/// - interval() - 間隔付き音再生
pub(super) fn execute_sound_method(&mut self, sound_box: &SoundBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
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" => {
@ -96,7 +100,10 @@ impl NyashInterpreter {
"volumeTest" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("volumeTest() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"volumeTest() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(sound_box.volumeTest())
@ -104,27 +111,32 @@ impl NyashInterpreter {
"interval" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("interval() expects 2 arguments, got {}", arg_values.len()),
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),
})
}
_ => 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> {
pub(super) fn execute_method_box_method(
&mut self,
method_box: &MethodBox,
method: &str,
arguments: &[ASTNode],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match method {
"invoke" => {
// 引数を評価
@ -132,20 +144,18 @@ impl NyashInterpreter {
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),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown MethodBox method: {}", method),
}),
}
}
/// MethodBoxでメソッドを実際に呼び出す
///
///
/// この関数はMethodBoxの中核機能:
/// 1. インスタンスとメソッド名からメソッドを取得
/// 2. 引数数の検証
@ -153,48 +163,56 @@ impl NyashInterpreter {
/// 4. 'me' 変数の設定
/// 5. メソッド実行
/// 6. 戻り値処理
fn invoke_method_box(&mut self, method_box: &MethodBox, args: Vec<Box<dyn NyashBox>>)
-> Result<Box<dyn NyashBox>, RuntimeError> {
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)
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()),
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();
@ -202,14 +220,17 @@ impl NyashInterpreter {
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),
message: format!(
"Method '{}' is not a valid function declaration",
method_box.method_name
),
})
}
} else {

View File

@ -1,7 +1,7 @@
use super::{BoxDeclaration, StaticBoxDefinition};
use crate::instance_v2::InstanceBox;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex, RwLock};
use super::{BoxDeclaration, StaticBoxDefinition};
/// スレッド間で共有される状態
#[derive(Clone)]
@ -30,8 +30,8 @@ impl SharedState {
pub fn new() -> Self {
let global_box = InstanceBox::new(
"Global".to_string(),
vec![], // フィールド名(空から始める)
HashMap::new(), // メソッド(グローバル関数)
vec![], // フィールド名(空から始める)
HashMap::new(), // メソッド(グローバル関数)
);
Self {

View File

@ -1,14 +1,14 @@
/*!
* 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 super::BuiltinStdlib;
use std::sync::Arc;
// Conditional debug macro - unified with utils::debug_on()
@ -27,46 +27,69 @@ macro_rules! idebug {
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; }
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() {
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"
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);
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> {
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::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::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)?
@ -82,7 +105,7 @@ impl NyashInterpreter {
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()))
@ -91,24 +114,46 @@ impl NyashInterpreter {
self.control_flow = super::ControlFlow::Continue;
Ok(Box::new(VoidBox::new()))
}
ASTNode::Nowait { variable, expression, .. } => {
self.execute_nowait(variable, expression)
}
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); }
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, .. } => {
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(
@ -116,39 +161,45 @@ impl NyashInterpreter {
fields.clone(),
methods.clone(),
init_fields.clone(),
weak_fields.clone(), // 🔗 Add weak_fields parameter
weak_fields.clone(), // 🔗 Add weak_fields parameter
static_init.clone(),
extends.clone(),
implements.clone(),
type_parameters.clone()
type_parameters.clone(),
)?;
} else {
// 通常のBox宣言の処理 - 🔥 コンストラクタオーバーロード禁止対応
self.register_box_declaration(
name.clone(),
fields.clone(),
name.clone(),
fields.clone(),
public_fields.clone(),
private_fields.clone(),
methods.clone(),
constructors.clone(),
init_fields.clone(),
weak_fields.clone(), // 🔗 Add weak_fields parameter
weak_fields.clone(), // 🔗 Add weak_fields parameter
*is_interface,
extends.clone(),
implements.clone(),
type_parameters.clone() // 🔥 ジェネリクス型パラメータ追加
type_parameters.clone(), // 🔥 ジェネリクス型パラメータ追加
)?; // 🔥 エラーハンドリング追加
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::FunctionDeclaration { name, params, body, is_static, .. } => {
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(),
@ -158,7 +209,7 @@ impl NyashInterpreter {
is_override: false,
span: crate::ast::Span::unknown(),
};
{
let mut static_funcs = self.shared.static_functions.write().unwrap();
static_funcs
@ -166,11 +217,14 @@ impl NyashInterpreter {
.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);
idebug!(
"⚠️ Static function '{}' needs box prefix (e.g., Math.min)",
name
);
}
} else {
// 通常の関数従来通りGlobalBoxメソッドとして登録
@ -178,23 +232,28 @@ impl NyashInterpreter {
}
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, .. } => {
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) {
@ -208,8 +267,12 @@ impl NyashInterpreter {
}
Ok(Box::new(VoidBox::new()))
}
ASTNode::Outbox { variables, initial_values, .. } => {
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) {
@ -223,24 +286,28 @@ impl NyashInterpreter {
}
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> {
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 {
@ -260,12 +327,16 @@ impl NyashInterpreter {
}
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> {
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)?;
@ -279,11 +350,11 @@ impl NyashInterpreter {
break;
}
}
// ループ本体を実行
for statement in body {
self.execute_statement(statement)?;
match &self.control_flow {
super::ControlFlow::Break => {
self.control_flow = super::ControlFlow::None;
@ -305,44 +376,58 @@ impl NyashInterpreter {
}
}
}
Ok(Box::new(VoidBox::new()))
}
/// 代入処理を実行 - Assignment processing
pub(super) fn execute_assignment(&mut self, target: &ASTNode, value: &ASTNode) -> Result<Box<dyn NyashBox>, RuntimeError> {
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);
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);
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() {
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else {
val.clone_box()
@ -363,7 +448,7 @@ impl NyashInterpreter {
self.set_variable(name, assigned)?;
Ok(val)
}
ASTNode::FieldAccess { object, field, .. } => {
// フィールドへの代入
// 内部me/thisからの代入かどうか
@ -374,50 +459,69 @@ impl NyashInterpreter {
};
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()) {
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),
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);
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())
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() {
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else { val.clone_box() }
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{ val.clone_box() }
{
val.clone_box()
}
};
// セル反映: 既存フィールドが RefCellBox なら中身を置換
if let Some(cur) = instance.get_field(field) {
@ -426,7 +530,8 @@ impl NyashInterpreter {
return Ok(val);
}
}
instance.set_field(field, Arc::from(stored))
instance
.set_field(field, Arc::from(stored))
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
@ -435,30 +540,39 @@ impl NyashInterpreter {
})
}
}
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(),
})?;
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() {
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else { val.clone_box() }
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{ val.clone_box() }
{
val.clone_box()
}
};
// セル反映: 既存フィールドが RefCellBox なら中身を置換
if let Some(cur) = instance.get_field(field) {
@ -467,7 +581,8 @@ impl NyashInterpreter {
return Ok(val);
}
}
instance.set_field(field, Arc::from(stored))
instance
.set_field(field, Arc::from(stored))
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
@ -476,32 +591,42 @@ impl NyashInterpreter {
})
}
}
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(),
})?;
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() {
if val
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
val.share_box()
} else { val.clone_box() }
} else {
val.clone_box()
}
}
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
{ val.clone_box() }
{
val.clone_box()
}
};
instance.set_field(field, Arc::from(stored))
instance
.set_field(field, Arc::from(stored))
.map_err(|e| RuntimeError::InvalidOperation { message: e })?;
Ok(val)
} else {
@ -510,18 +635,22 @@ impl NyashInterpreter {
})
}
}
_ => 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> {
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 {
@ -540,13 +669,14 @@ impl NyashInterpreter {
}
Err(e) => {
// RuntimeErrorを例外として扱う
thrown_exception = Some(Box::new(exception_box::ErrorBox::new(&format!("{:?}", e))));
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 {
@ -556,12 +686,12 @@ impl NyashInterpreter {
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)?;
@ -569,17 +699,17 @@ impl NyashInterpreter {
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 {
@ -589,38 +719,48 @@ impl NyashInterpreter {
}
}
}
// 未処理の例外があれば再スロー
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> {
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))
};
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);
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");
@ -635,14 +775,19 @@ impl NyashInterpreter {
}
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) });
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);
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() {

View File

@ -1,6 +1,6 @@
/*!
* System Methods Module
*
*
* Extracted from box_methods.rs
* Contains system-level Box method implementations:
* - TimeBox methods (now, fromTimestamp, parse, sleep, format)
@ -11,18 +11,22 @@
use super::*;
use crate::box_trait::StringBox;
use crate::boxes::{TimeBox, DateTimeBox};
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> {
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" => {
@ -36,7 +40,10 @@ impl NyashInterpreter {
"fromTimestamp" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("fromTimestamp() expects 1 argument, got {}", arg_values.len()),
message: format!(
"fromTimestamp() expects 1 argument, got {}",
arg_values.len()
),
});
}
Ok(time_box.fromTimestamp(arg_values[0].clone_box()))
@ -65,23 +72,25 @@ impl NyashInterpreter {
}
Ok(time_box.format(arg_values[0].clone_box()))
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimeBox method: {}", method),
})
}
_ => 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> {
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" => {
@ -135,7 +144,10 @@ impl NyashInterpreter {
"timestamp" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("timestamp() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"timestamp() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(datetime_box.timestamp())
@ -143,7 +155,10 @@ impl NyashInterpreter {
"toISOString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toISOString() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"toISOString() expects 0 arguments, got {}",
arg_values.len()
),
});
}
Ok(datetime_box.toISOString())
@ -175,28 +190,33 @@ impl NyashInterpreter {
"toString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arg_values.len()),
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),
})
}
_ => 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> {
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" => {
@ -218,29 +238,34 @@ impl NyashInterpreter {
// 🌍 革命的実装Environment tracking廃止
Ok(timer_box)
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimerBox method: {}", method),
})
}
_ => 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> {
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()),
message: format!(
"startTracking() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.start_tracking()
@ -248,7 +273,10 @@ impl NyashInterpreter {
"stopTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("stopTracking() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"stopTracking() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.stop_tracking()
@ -256,11 +284,15 @@ impl NyashInterpreter {
"trackBox" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("trackBox() expects 2 arguments, got {}", arg_values.len()),
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>() {
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>()
{
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
@ -280,16 +312,20 @@ impl NyashInterpreter {
"saveToFile" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("saveToFile() expects 1 argument, got {}", arg_values.len()),
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(),
});
};
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" => {
@ -298,7 +334,8 @@ impl NyashInterpreter {
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>() {
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>()
{
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
@ -310,7 +347,10 @@ impl NyashInterpreter {
"memoryReport" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("memoryReport() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"memoryReport() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.memory_report()
@ -318,33 +358,42 @@ impl NyashInterpreter {
"setBreakpoint" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("setBreakpoint() expects 1 argument, got {}", arg_values.len()),
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(),
});
};
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()),
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 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()
let args: Vec<String> = arg_values[1..]
.iter()
.map(|v| v.to_string_box().value)
.collect();
debug_box.trace_call(&func_name, args)
@ -352,7 +401,10 @@ impl NyashInterpreter {
"showCallStack" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("showCallStack() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"showCallStack() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.show_call_stack()
@ -360,16 +412,29 @@ impl NyashInterpreter {
"tracePluginCalls" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("tracePluginCalls(on:bool) expects 1 argument, got {}", arg_values.len()),
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 };
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()),
message: format!(
"getJitEvents() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.get_jit_events()
@ -385,7 +450,10 @@ impl NyashInterpreter {
"isTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isTracking() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"isTracking() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.is_tracking()
@ -393,16 +461,17 @@ impl NyashInterpreter {
"getTrackedCount" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getTrackedCount() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"getTrackedCount() expects 0 arguments, got {}",
arg_values.len()
),
});
}
debug_box.get_tracked_count()
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown DebugBox method: {}", method),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown DebugBox method: {}", method),
}),
}
}
}

View File

@ -4,4 +4,3 @@
pub fn debug_on() -> bool {
std::env::var("NYASH_DEBUG").unwrap_or_default() == "1"
}

View File

@ -1,20 +1,20 @@
/*!
* 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::{WebDisplayBox, WebConsoleBox, WebCanvasBox};
use crate::boxes::web::{WebCanvasBox, WebConsoleBox, WebDisplayBox};
#[cfg(target_arch = "wasm32")]
use crate::boxes::FloatBox;
@ -22,14 +22,18 @@ use crate::boxes::FloatBox;
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> {
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" => {
@ -65,7 +69,10 @@ impl NyashInterpreter {
"appendHTML" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("appendHTML() expects 1 argument, got {}", arg_values.len()),
message: format!(
"appendHTML() expects 1 argument, got {}",
arg_values.len()
),
});
}
let html_content = arg_values[0].to_string_box().value;
@ -75,7 +82,10 @@ impl NyashInterpreter {
"setCSS" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("setCSS() expects 2 arguments (property, value), got {}", arg_values.len()),
message: format!(
"setCSS() expects 2 arguments (property, value), got {}",
arg_values.len()
),
});
}
let property = arg_values[0].to_string_box().value;
@ -96,7 +106,10 @@ impl NyashInterpreter {
"removeClass" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("removeClass() expects 1 argument, got {}", arg_values.len()),
message: format!(
"removeClass() expects 1 argument, got {}",
arg_values.len()
),
});
}
let class_name = arg_values[0].to_string_box().value;
@ -133,30 +146,35 @@ impl NyashInterpreter {
"scrollToBottom" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("scrollToBottom() expects 0 arguments, got {}", arg_values.len()),
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),
})
}
_ => 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> {
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" => {
@ -221,7 +239,10 @@ impl NyashInterpreter {
"separator" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("separator() expects 0 arguments, got {}", arg_values.len()),
message: format!(
"separator() expects 0 arguments, got {}",
arg_values.len()
),
});
}
web_console_box.separator();
@ -240,30 +261,35 @@ impl NyashInterpreter {
"groupEnd" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("groupEnd() expects 0 arguments, got {}", arg_values.len()),
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),
})
}
_ => 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> {
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" => {
@ -278,7 +304,10 @@ impl NyashInterpreter {
"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()),
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>() {
@ -364,22 +393,26 @@ impl NyashInterpreter {
});
};
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(),
});
};
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()),
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>() {
@ -416,7 +449,10 @@ impl NyashInterpreter {
"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()),
message: format!(
"fillText() expects 5 arguments (text, x, y, font, color), got {}",
arg_values.len()
),
});
}
let text = arg_values[0].to_string_box().value;
@ -443,11 +479,9 @@ impl NyashInterpreter {
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),
})
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown method '{}' for WebCanvasBox", method),
}),
}
}
}
}