- NewBoxのJIT扱いを安全化(src/jit/lower/core.rs) - NYASH_USE_PLUGIN_BUILTINS=1 && args.is_empty() かつ StringBox/IntegerBox のみJIT許可 - ArrayBox/MapBox等のプラグインBoxまたは引数ありはunsupportedとしてカウント - unsupported>0の関数はJIT対象外となりVM実行にフォールバック(Segfault回避) - DebugBox Phase 1実装(JITトレース機能) - tracePluginCalls(bool)でJITシムトレースON/OFF - getJitEvents()で直近のJITイベント取得 - src/jit/shim_trace.rs追加でトレース基盤実装 - Printのサポート - PrintはJIT非対応に戻しVM経路で確実に出力(出力消失解消) - テストとサンプル追加 - examples/jit_plugin_invoke_param_array.nyash: 最小JITスモークテスト - examples/py_result_*.nyash: Python plugin結果チェーン処理デモ - PyRuntimeBox拡張 - str()メソッドでPyObjectのstring表現を取得可能に - エラーハンドリング改善とResultチェーンサポート 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
409 lines
17 KiB
Rust
409 lines
17 KiB
Rust
/*!
|
||
* System Methods Module
|
||
*
|
||
* Extracted from box_methods.rs
|
||
* Contains system-level Box method implementations:
|
||
* - TimeBox methods (now, fromTimestamp, parse, sleep, format)
|
||
* - DateTimeBox methods (year, month, day, hour, minute, second, timestamp, toISOString, format, addDays, addHours)
|
||
* - TimerBox methods (elapsed, reset)
|
||
* - DebugBox methods (startTracking, stopTracking, trackBox, dumpAll, saveToFile, watch, etc.)
|
||
*/
|
||
|
||
use super::*;
|
||
use crate::box_trait::StringBox;
|
||
use crate::boxes::{TimeBox, DateTimeBox};
|
||
|
||
impl NyashInterpreter {
|
||
/// TimeBoxのメソッド呼び出しを実行
|
||
pub(super) fn execute_time_method(&mut self, time_box: &TimeBox, method: &str, arguments: &[ASTNode])
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// 引数を評価
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.execute_expression(arg)?);
|
||
}
|
||
|
||
// メソッドを実行
|
||
match method {
|
||
"now" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("now() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(time_box.now())
|
||
}
|
||
"fromTimestamp" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("fromTimestamp() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(time_box.fromTimestamp(arg_values[0].clone_box()))
|
||
}
|
||
"parse" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("parse() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(time_box.parse(arg_values[0].clone_box()))
|
||
}
|
||
"sleep" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("sleep() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(time_box.sleep(arg_values[0].clone_box()))
|
||
}
|
||
"format" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("format() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(time_box.format(arg_values[0].clone_box()))
|
||
}
|
||
_ => {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown TimeBox method: {}", method),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
/// DateTimeBoxのメソッド呼び出しを実行
|
||
pub(super) fn execute_datetime_method(&mut self, datetime_box: &DateTimeBox, method: &str, arguments: &[ASTNode])
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// 引数を評価
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.execute_expression(arg)?);
|
||
}
|
||
|
||
// メソッドを実行
|
||
match method {
|
||
"year" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("year() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.year())
|
||
}
|
||
"month" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("month() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.month())
|
||
}
|
||
"day" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("day() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.day())
|
||
}
|
||
"hour" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("hour() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.hour())
|
||
}
|
||
"minute" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("minute() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.minute())
|
||
}
|
||
"second" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("second() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.second())
|
||
}
|
||
"timestamp" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("timestamp() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.timestamp())
|
||
}
|
||
"toISOString" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("toISOString() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.toISOString())
|
||
}
|
||
"format" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("format() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.format(arg_values[0].clone_box()))
|
||
}
|
||
"addDays" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("addDays() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.addDays(arg_values[0].clone_box()))
|
||
}
|
||
"addHours" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("addHours() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(datetime_box.addHours(arg_values[0].clone_box()))
|
||
}
|
||
"toString" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("toString() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(Box::new(datetime_box.to_string_box()))
|
||
}
|
||
_ => {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown DateTimeBox method: {}", method),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
/// TimerBoxのメソッド呼び出しを実行
|
||
pub(super) fn execute_timer_method(&mut self, timer_box: &TimerBox, method: &str, arguments: &[ASTNode])
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// 引数を評価
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.execute_expression(arg)?);
|
||
}
|
||
|
||
// メソッドを実行
|
||
match method {
|
||
"elapsed" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("elapsed() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
Ok(timer_box.elapsed())
|
||
}
|
||
"reset" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("reset() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
// NOTE: resetはmutableメソッドなので、ここでは新しいTimerBoxを作成
|
||
let timer_box = Box::new(TimerBox::new()) as Box<dyn NyashBox>;
|
||
// 🌍 革命的実装:Environment tracking廃止
|
||
Ok(timer_box)
|
||
}
|
||
_ => {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown TimerBox method: {}", method),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
/// DebugBoxのメソッド呼び出しを実行
|
||
pub(super) fn execute_debug_method(&mut self, debug_box: &DebugBox, method: &str, arguments: &[ASTNode])
|
||
-> Result<Box<dyn NyashBox>, RuntimeError> {
|
||
// 引数を評価
|
||
let mut arg_values = Vec::new();
|
||
for arg in arguments {
|
||
arg_values.push(self.execute_expression(arg)?);
|
||
}
|
||
|
||
// メソッドを実行
|
||
match method {
|
||
"startTracking" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("startTracking() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.start_tracking()
|
||
}
|
||
"stopTracking" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("stopTracking() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.stop_tracking()
|
||
}
|
||
"trackBox" => {
|
||
if arg_values.len() != 2 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("trackBox() expects 2 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
// 第2引数をStringBoxとして取得
|
||
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>() {
|
||
str_box.value.clone()
|
||
} else {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: "trackBox() second argument must be a string".to_string(),
|
||
});
|
||
};
|
||
debug_box.track_box(arg_values[0].as_ref(), &name)
|
||
}
|
||
"dumpAll" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("dumpAll() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.dump_all()
|
||
}
|
||
"saveToFile" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("saveToFile() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
let filename = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
|
||
str_box.value.clone()
|
||
} else {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: "saveToFile() argument must be a string".to_string(),
|
||
});
|
||
};
|
||
debug_box.save_to_file(&filename)
|
||
}
|
||
"watch" => {
|
||
if arg_values.len() != 2 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("watch() expects 2 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>() {
|
||
str_box.value.clone()
|
||
} else {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: "watch() second argument must be a string".to_string(),
|
||
});
|
||
};
|
||
debug_box.watch(arg_values[0].as_ref(), &name)
|
||
}
|
||
"memoryReport" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("memoryReport() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.memory_report()
|
||
}
|
||
"setBreakpoint" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("setBreakpoint() expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
let func_name = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
|
||
str_box.value.clone()
|
||
} else {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: "setBreakpoint() argument must be a string".to_string(),
|
||
});
|
||
};
|
||
debug_box.set_breakpoint(&func_name)
|
||
}
|
||
"traceCall" => {
|
||
if arg_values.len() < 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("traceCall() expects at least 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
let func_name = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
|
||
str_box.value.clone()
|
||
} else {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: "traceCall() first argument must be a string".to_string(),
|
||
});
|
||
};
|
||
// 残りの引数を文字列として収集
|
||
let args: Vec<String> = arg_values[1..].iter()
|
||
.map(|v| v.to_string_box().value)
|
||
.collect();
|
||
debug_box.trace_call(&func_name, args)
|
||
}
|
||
"showCallStack" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("showCallStack() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.show_call_stack()
|
||
}
|
||
"tracePluginCalls" => {
|
||
if arg_values.len() != 1 {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("tracePluginCalls(on:bool) expects 1 argument, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
let on = if let Some(b) = arg_values[0].as_any().downcast_ref::<crate::box_trait::BoolBox>() { b.value } else { false };
|
||
debug_box.trace_plugin_calls(on)
|
||
}
|
||
"getJitEvents" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("getJitEvents() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.get_jit_events()
|
||
}
|
||
"clear" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("clear() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.clear()
|
||
}
|
||
"isTracking" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("isTracking() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.is_tracking()
|
||
}
|
||
"getTrackedCount" => {
|
||
if !arg_values.is_empty() {
|
||
return Err(RuntimeError::InvalidOperation {
|
||
message: format!("getTrackedCount() expects 0 arguments, got {}", arg_values.len()),
|
||
});
|
||
}
|
||
debug_box.get_tracked_count()
|
||
}
|
||
_ => {
|
||
Err(RuntimeError::InvalidOperation {
|
||
message: format!("Unknown DebugBox method: {}", method),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|