chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt
This commit is contained in:
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
// ユーザー定義Box(InstanceBox)の場合
|
||||
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 {
|
||||
// ユーザー定義Box(InstanceBox)の場合
|
||||
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);
|
||||
|
||||
@ -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 },
|
||||
|
||||
|
||||
@ -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(),
|
||||
}),
|
||||
|
||||
@ -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,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
@ -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(¶ms, 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
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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 => {
|
||||
// 数値の符号反転
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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を変数に保存
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
|
||||
@ -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),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
mod ops;
|
||||
mod methods;
|
||||
mod fields;
|
||||
|
||||
mod methods;
|
||||
mod ops;
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,4 +4,3 @@
|
||||
pub fn debug_on() -> bool {
|
||||
std::env::var("NYASH_DEBUG").unwrap_or_default() == "1"
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user