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