Phase 12.7文法改革: ドキュメント文法統一 + VMリファクタリング準備
🌟 Phase 12.7文法改革に基づくドキュメント更新 - init {} → field: TypeBox 個別フィールド宣言形式 - init() → birth() コンストラクタ統一 - pack() → 廃止(birth()に統一) - public {}/private {} → 個別フィールド修飾子 - override → 廃止(メソッド定義はシンプルに) 📚 更新したドキュメント - CLAUDE.md: メイン開発ガイド - docs/quick-reference/syntax-cheatsheet.md: 構文早見表 - docs/reference/language/LANGUAGE_REFERENCE_2025.md: 言語リファレンス - docs/development/roadmap/phases/phase-15/README.md: Phase 15計画 🔧 VMリファクタリング準備 - vm_methods.rs: VMメソッド呼び出しの分離 - plugin_loader.rs → plugin_loader/: ディレクトリ構造化 - mir/builder/exprs.rs: 式ビルダー分離 📝 新規ドキュメント追加 - 論文戦略・ロードマップ - Phase 15セルフホスティング準備資料 - Codex Androidセットアップガイド ビルドは正常に通ることを確認済み!🎉 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -17,6 +17,7 @@ pub mod vm_control_flow;
|
||||
mod vm_gc; // A3: GC roots & diagnostics extracted
|
||||
mod vm_exec; // A3: execution loop extracted
|
||||
mod vm_state; // A3: state & basic helpers extracted
|
||||
mod vm_methods; // A3-S1: method dispatch wrappers extracted
|
||||
pub mod abi_util; // Shared ABI/utility helpers
|
||||
pub mod mir_interpreter; // Lightweight MIR interpreter
|
||||
|
||||
|
||||
@ -244,67 +244,7 @@ pub struct VM {
|
||||
impl VM {
|
||||
pub fn runtime_ref(&self) -> &NyashRuntime { &self.runtime }
|
||||
|
||||
/// Print a simple breakdown of root VMValue kinds and top BoxRef types (old-moved placeholder)
|
||||
pub(super) fn gc_print_roots_breakdown_old(&self) {
|
||||
use std::collections::HashMap;
|
||||
let roots = self.scope_tracker.roots_snapshot();
|
||||
let mut kinds: HashMap<&'static str, u64> = HashMap::new();
|
||||
let mut box_types: HashMap<String, u64> = HashMap::new();
|
||||
for v in &roots {
|
||||
match v {
|
||||
VMValue::Integer(_) => *kinds.entry("Integer").or_insert(0) += 1,
|
||||
VMValue::Float(_) => *kinds.entry("Float").or_insert(0) += 1,
|
||||
VMValue::Bool(_) => *kinds.entry("Bool").or_insert(0) += 1,
|
||||
VMValue::String(_) => *kinds.entry("String").or_insert(0) += 1,
|
||||
VMValue::Future(_) => *kinds.entry("Future").or_insert(0) += 1,
|
||||
VMValue::Void => *kinds.entry("Void").or_insert(0) += 1,
|
||||
VMValue::BoxRef(b) => {
|
||||
let tn = b.type_name().to_string();
|
||||
*box_types.entry(tn).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln!("[GC] roots_breakdown: kinds={:?}", kinds);
|
||||
let mut top: Vec<(String, u64)> = box_types.into_iter().collect();
|
||||
top.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
top.truncate(5);
|
||||
eprintln!("[GC] roots_boxref_top5: {:?}", top);
|
||||
}
|
||||
pub(super) fn gc_print_reachability_depth2_old(&self) {
|
||||
use std::collections::HashMap;
|
||||
let roots = self.scope_tracker.roots_snapshot();
|
||||
let mut child_types: HashMap<String, u64> = HashMap::new();
|
||||
let mut child_count = 0u64;
|
||||
for v in &roots {
|
||||
if let VMValue::BoxRef(b) = v {
|
||||
if let Some(arr) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Ok(items) = arr.items.read() {
|
||||
for item in items.iter() {
|
||||
let tn = item.type_name().to_string();
|
||||
*child_types.entry(tn).or_insert(0) += 1;
|
||||
child_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let vals = map.values();
|
||||
if let Some(arr2) = vals.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Ok(items) = arr2.items.read() {
|
||||
for item in items.iter() {
|
||||
let tn = item.type_name().to_string();
|
||||
*child_types.entry(tn).or_insert(0) += 1;
|
||||
child_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut top: Vec<(String, u64)> = child_types.into_iter().collect();
|
||||
top.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
top.truncate(5);
|
||||
eprintln!("[GC] depth2_children: total={} top5={:?}", child_count, top);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: Re-enable when interpreter refactoring is complete
|
||||
@ -330,232 +270,11 @@ impl VM {
|
||||
}
|
||||
*/
|
||||
|
||||
/// Execute a MIR module (old placeholder; moved to vm_exec.rs)
|
||||
pub fn execute_module_old_moved(&mut self, _module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
fn print_cache_stats_summary_old(&self) {
|
||||
let sites_poly = self.boxcall_poly_pic.len();
|
||||
let entries_poly: usize = self.boxcall_poly_pic.values().map(|v| v.len()).sum();
|
||||
let avg_entries = if sites_poly > 0 { (entries_poly as f64) / (sites_poly as f64) } else { 0.0 };
|
||||
let sites_mono = self.boxcall_pic_funcname.len();
|
||||
let hits_total: u64 = self.boxcall_pic_hits.values().map(|v| *v as u64).sum();
|
||||
let vt_entries = self.boxcall_vtable_funcname.len();
|
||||
eprintln!(
|
||||
"[VM] PIC/VT summary: poly_sites={} avg_entries={:.2} mono_sites={} hits_total={} vt_entries={} | hits: vt={} poly={} mono={} generic={}",
|
||||
sites_poly, avg_entries, sites_mono, hits_total, vt_entries,
|
||||
self.boxcall_hits_vtable, self.boxcall_hits_poly_pic, self.boxcall_hits_mono_pic, self.boxcall_hits_generic
|
||||
);
|
||||
// Top sites by hits (up to 5)
|
||||
let mut hits: Vec<(&String, &u32)> = self.boxcall_pic_hits.iter().collect();
|
||||
hits.sort_by(|a, b| b.1.cmp(a.1));
|
||||
for (i, (k, v)) in hits.into_iter().take(5).enumerate() {
|
||||
eprintln!(" #{} {} hits={}", i+1, k, v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a MIR function by name with VMValue arguments
|
||||
pub(super) fn call_function_by_name_old(&mut self, func_name: &str, args: Vec<VMValue>) -> Result<VMValue, VMError> {
|
||||
// Root region: ensure args stay rooted during nested call
|
||||
self.enter_root_region();
|
||||
self.pin_roots(args.iter());
|
||||
let module_ref = self.module.as_ref().ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?;
|
||||
let function_ref = module_ref.get_function(func_name)
|
||||
.ok_or_else(|| VMError::InvalidInstruction(format!("Function '{}' not found", func_name)))?;
|
||||
// Clone function to avoid borrowing conflicts during execution
|
||||
let function = function_ref.clone();
|
||||
|
||||
// Save current frame
|
||||
let saved_values = std::mem::take(&mut self.values);
|
||||
let saved_current_function = self.current_function.clone();
|
||||
let saved_current_block = self.frame.current_block;
|
||||
let saved_previous_block = self.previous_block;
|
||||
let saved_pc = self.frame.pc;
|
||||
let saved_last_result = self.frame.last_result;
|
||||
|
||||
// Bind parameters
|
||||
for (i, param_id) in function.params.iter().enumerate() {
|
||||
if let Some(arg) = args.get(i) {
|
||||
self.set_value(*param_id, arg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Heuristic: map `me` (first param) to class name parsed from function name (e.g., User.method/N)
|
||||
if let Some(first) = function.params.get(0) {
|
||||
if let Some((class_part, _rest)) = func_name.split_once('.') {
|
||||
// Record class for internal field visibility checks
|
||||
self.object_class.insert(*first, class_part.to_string());
|
||||
// Mark internal reference
|
||||
self.object_internal.insert(*first);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the function
|
||||
let result = self.execute_function(&function);
|
||||
|
||||
// Restore frame
|
||||
self.values = saved_values;
|
||||
self.current_function = saved_current_function;
|
||||
self.frame.current_block = saved_current_block;
|
||||
self.previous_block = saved_previous_block;
|
||||
self.frame.pc = saved_pc;
|
||||
self.frame.last_result = saved_last_result;
|
||||
// Leave GC root region
|
||||
self.scope_tracker.leave_root_region();
|
||||
result
|
||||
}
|
||||
|
||||
/// Execute a single function
|
||||
fn execute_function_old(&mut self, function: &MirFunction) -> Result<VMValue, VMError> {
|
||||
self.current_function = Some(function.signature.name.clone());
|
||||
// Phase 10_a: JIT profiling (function entry)
|
||||
if let Some(jm) = &mut self.jit_manager {
|
||||
// Allow threshold to react to env updates (e.g., DebugConfigBox.apply at runtime)
|
||||
if let Ok(s) = std::env::var("NYASH_JIT_THRESHOLD") {
|
||||
if let Ok(t) = s.parse::<u32>() { if t > 0 { jm.set_threshold(t); } }
|
||||
}
|
||||
jm.record_entry(&function.signature.name);
|
||||
// Try compile if hot (no-op for now, returns fake handle)
|
||||
let _ = jm.maybe_compile(&function.signature.name, function);
|
||||
// Record per-function lower stats captured during last JIT lower (if any)
|
||||
// Note: The current engine encapsulates its LowerCore; expose via last_stats on a new instance as needed.
|
||||
if jm.is_compiled(&function.signature.name) && std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
|
||||
if let Some(h) = jm.handle_of(&function.signature.name) {
|
||||
eprintln!("[JIT] dispatch would go to handle={} for {} (stub)", h, function.signature.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize loop executor for this function
|
||||
self.loop_executor.initialize();
|
||||
|
||||
// Enter a new scope for this function
|
||||
self.scope_tracker.push_scope();
|
||||
crate::runtime::global_hooks::push_task_scope();
|
||||
|
||||
// Phase 10_c: try a JIT dispatch when enabled; fallback to VM on trap/miss
|
||||
// Prepare arguments from current frame params before borrowing jit_manager mutably
|
||||
let args_vec: Vec<VMValue> = function
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|pid| self.get_value(*pid).ok())
|
||||
.collect();
|
||||
if std::env::var("NYASH_JIT_EXEC").ok().as_deref() == Some("1") {
|
||||
let jit_only = std::env::var("NYASH_JIT_ONLY").ok().as_deref() == Some("1");
|
||||
// Root regionize args for JIT call
|
||||
self.enter_root_region();
|
||||
self.pin_roots(args_vec.iter());
|
||||
if let Some(compiled) = self.jit_manager.as_ref().map(|jm| jm.is_compiled(&function.signature.name)) {
|
||||
if compiled {
|
||||
crate::runtime::host_api::set_current_vm(self as *mut _);
|
||||
let jit_val = if let Some(jm_mut) = self.jit_manager.as_mut() { jm_mut.execute_compiled(&function.signature.name, &function.signature.return_type, &args_vec) } else { None };
|
||||
crate::runtime::host_api::clear_current_vm();
|
||||
if let Some(val) = jit_val {
|
||||
// Exit scope before returning
|
||||
self.leave_root_region();
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Ok(val);
|
||||
} else if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") ||
|
||||
std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1") {
|
||||
eprintln!("[JIT] fallback: VM path taken for {}", function.signature.name);
|
||||
if jit_only {
|
||||
self.leave_root_region();
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Err(VMError::InvalidInstruction(format!("JIT-only enabled and JIT trap occurred for {}", function.signature.name)));
|
||||
}
|
||||
}
|
||||
} else if jit_only {
|
||||
// Try to compile now and execute; if not possible, error out
|
||||
if let Some(jm_mut) = self.jit_manager.as_mut() { let _ = jm_mut.maybe_compile(&function.signature.name, function); }
|
||||
if self.jit_manager.as_ref().map(|jm| jm.is_compiled(&function.signature.name)).unwrap_or(false) {
|
||||
crate::runtime::host_api::set_current_vm(self as *mut _);
|
||||
let jit_val = if let Some(jm_mut) = self.jit_manager.as_mut() { jm_mut.execute_compiled(&function.signature.name, &function.signature.return_type, &args_vec) } else { None };
|
||||
crate::runtime::host_api::clear_current_vm();
|
||||
if let Some(val) = jit_val {
|
||||
self.leave_root_region();
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Ok(val);
|
||||
} else {
|
||||
self.leave_root_region();
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Err(VMError::InvalidInstruction(format!("JIT-only enabled and JIT execution failed for {}", function.signature.name)));
|
||||
}
|
||||
} else {
|
||||
self.leave_root_region();
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Err(VMError::InvalidInstruction(format!("JIT-only enabled but function not compiled: {}", function.signature.name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Leave root region if not compiled or after fallback
|
||||
self.leave_root_region();
|
||||
} else {
|
||||
if let Some(jm_mut) = &mut self.jit_manager {
|
||||
let argc = function.params.len();
|
||||
let _would = jm_mut.maybe_dispatch(&function.signature.name, argc);
|
||||
}
|
||||
}
|
||||
|
||||
// Start at entry block
|
||||
let mut current_block = function.entry_block;
|
||||
|
||||
loop {
|
||||
let block = function.get_block(current_block)
|
||||
.ok_or_else(|| VMError::InvalidBasicBlock(format!("Block {} not found", current_block)))?;
|
||||
|
||||
self.frame.current_block = Some(current_block);
|
||||
self.frame.pc = 0;
|
||||
|
||||
let mut next_block = None;
|
||||
let mut should_return = None;
|
||||
|
||||
// Execute instructions in this block (including terminator)
|
||||
let all_instructions: Vec<_> = block.all_instructions().collect();
|
||||
for (index, instruction) in all_instructions.iter().enumerate() {
|
||||
self.frame.pc = index;
|
||||
|
||||
match self.execute_instruction(instruction)? {
|
||||
ControlFlow::Continue => continue,
|
||||
ControlFlow::Jump(target) => {
|
||||
next_block = Some(target);
|
||||
break;
|
||||
},
|
||||
ControlFlow::Return(value) => {
|
||||
should_return = Some(value);
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Handle control flow
|
||||
if let Some(return_value) = should_return {
|
||||
// Exit scope before returning
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Ok(return_value);
|
||||
} else if let Some(target) = next_block {
|
||||
// Update previous block before jumping and record transition via control_flow helper
|
||||
control_flow::record_transition(&mut self.previous_block, &mut self.loop_executor, current_block, target).ok();
|
||||
current_block = target;
|
||||
} else {
|
||||
// Block ended without terminator - this shouldn't happen in well-formed MIR
|
||||
// but let's handle it gracefully by returning void
|
||||
// Exit scope before returning
|
||||
self.scope_tracker.pop_scope();
|
||||
crate::runtime::global_hooks::pop_task_scope();
|
||||
return Ok(VMValue::Void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a single instruction (old placeholder; moved to vm_exec.rs)
|
||||
fn execute_instruction_old(&mut self, _instruction: &MirInstruction) -> Result<ControlFlow, VMError> { unreachable!("moved") }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -565,49 +284,11 @@ impl VM {
|
||||
|
||||
|
||||
|
||||
/// Phase 9.78a: Unified method dispatch for all Box types
|
||||
fn call_unified_method(&self, box_value: Box<dyn NyashBox>, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
// For now, we use the simplified method dispatch
|
||||
// In a full implementation, this would check for InstanceBox and dispatch appropriately
|
||||
self.call_box_method_impl(box_value, method, args)
|
||||
}
|
||||
|
||||
/// Call a method on a Box - simplified version of interpreter method dispatch
|
||||
pub(super) fn call_box_method(&self, box_value: Box<dyn NyashBox>, method: &str, mut _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
// For now, implement basic methods for common box types
|
||||
// This is a simplified version - real implementation would need full method dispatch
|
||||
|
||||
// 🌟 Universal methods pre-dispatch (non-invasive)
|
||||
match method {
|
||||
"toString" => {
|
||||
if !_args.is_empty() {
|
||||
return Ok(Box::new(StringBox::new(format!("Error: toString() expects 0 arguments, got {}", _args.len()))));
|
||||
}
|
||||
return Ok(Box::new(StringBox::new(box_value.to_string_box().value)));
|
||||
}
|
||||
"type" => {
|
||||
if !_args.is_empty() {
|
||||
return Ok(Box::new(StringBox::new(format!("Error: type() expects 0 arguments, got {}", _args.len()))));
|
||||
}
|
||||
return Ok(Box::new(StringBox::new(box_value.type_name())));
|
||||
}
|
||||
"equals" => {
|
||||
if _args.len() != 1 {
|
||||
return Ok(Box::new(StringBox::new(format!("Error: equals() expects 1 argument, got {}", _args.len()))));
|
||||
}
|
||||
let rhs = _args.remove(0);
|
||||
let eq = box_value.equals(&*rhs);
|
||||
return Ok(Box::new(eq));
|
||||
}
|
||||
"clone" => {
|
||||
if !_args.is_empty() {
|
||||
return Ok(Box::new(StringBox::new(format!("Error: clone() expects 0 arguments, got {}", _args.len()))));
|
||||
}
|
||||
return Ok(box_value.clone_box());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
// Call a method on a Box - moved to vm_methods.rs (wrapper now in vm_methods)
|
||||
// removed: old inline implementation
|
||||
/*
|
||||
// ResultBox (NyashResultBox - new)
|
||||
if let Some(result_box) = box_value.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||
match method {
|
||||
@ -1004,10 +685,7 @@ impl VM {
|
||||
}
|
||||
return Ok(Box::new(VoidBox::new()));
|
||||
}
|
||||
|
||||
// Default: return void for any unrecognized box type or method
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// RAII guard for GC root regions
|
||||
@ -1024,7 +702,7 @@ impl Default for VM {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock};
|
||||
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock, BinaryOp};
|
||||
use crate::parser::NyashParser;
|
||||
use crate::runtime::NyashRuntime;
|
||||
use crate::core::model::BoxDeclaration as CoreBoxDecl;
|
||||
|
||||
34
src/backend/vm_methods.rs
Normal file
34
src/backend/vm_methods.rs
Normal file
@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* VM Methods Glue
|
||||
*
|
||||
* Extracted wrappers for Box method dispatch to keep vm.rs slim.
|
||||
* These delegate to the real implementation in vm_boxcall.rs, preserving
|
||||
* the existing VM API surface.
|
||||
*/
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
use super::vm::{VM, VMError};
|
||||
|
||||
impl VM {
|
||||
/// Unified method dispatch entry. Currently delegates to `call_box_method_impl`.
|
||||
fn call_unified_method(
|
||||
&self,
|
||||
box_value: Box<dyn NyashBox>,
|
||||
method: &str,
|
||||
args: Vec<Box<dyn NyashBox>>,
|
||||
) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
self.call_box_method_impl(box_value, method, args)
|
||||
}
|
||||
|
||||
/// Public-facing method call used by vm_instructions::boxcall.
|
||||
/// Kept as a thin wrapper to the implementation in vm_boxcall.rs.
|
||||
pub(super) fn call_box_method(
|
||||
&self,
|
||||
box_value: Box<dyn NyashBox>,
|
||||
method: &str,
|
||||
args: Vec<Box<dyn NyashBox>>,
|
||||
) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
self.call_box_method_impl(box_value, method, args)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
171
src/interpreter/plugin_loader/loader.rs
Normal file
171
src/interpreter/plugin_loader/loader.rs
Normal file
@ -0,0 +1,171 @@
|
||||
//! Loader entrypoints for dynamic plugins
|
||||
|
||||
use std::ffi::{CString, c_char, c_void};
|
||||
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::interpreter::RuntimeError;
|
||||
|
||||
use super::proxies::{FileBoxProxy, MathBoxProxy, RandomBoxProxy, TimeBoxProxy, DateTimeBoxProxy};
|
||||
use super::types::{PLUGIN_CACHE, LoadedPlugin, PluginInfo};
|
||||
|
||||
/// Public plugin loader API
|
||||
pub struct PluginLoader;
|
||||
|
||||
impl PluginLoader {
|
||||
/// Load File plugin
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn load_file_plugin() -> Result<(), RuntimeError> {
|
||||
let mut cache = PLUGIN_CACHE.write().unwrap();
|
||||
if cache.contains_key("file") { return Ok(()); }
|
||||
let lib_name = if cfg!(target_os = "windows") { "nyash_file.dll" } else if cfg!(target_os = "macos") { "libnyash_file.dylib" } else { "libnyash_file.so" };
|
||||
let possible_paths = vec![
|
||||
format!("./target/release/{}", lib_name),
|
||||
format!("./target/debug/{}", lib_name),
|
||||
format!("./plugins/{}", lib_name),
|
||||
format!("./{}", lib_name),
|
||||
];
|
||||
let lib_path = possible_paths.iter().find(|p| std::path::Path::new(p.as_str()).exists()).cloned()
|
||||
.ok_or_else(|| RuntimeError::InvalidOperation { message: format!("Failed to find file plugin library. Searched paths: {:?}", possible_paths) })?;
|
||||
unsafe {
|
||||
let library = Library::new(&lib_path).map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to load file plugin: {}", e) })?;
|
||||
let init_fn: Symbol<unsafe extern "C" fn() -> *const c_void> = library.get(b"nyash_plugin_init\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get plugin init: {}", e) })?;
|
||||
let plugin_info_ptr = init_fn();
|
||||
if plugin_info_ptr.is_null() { return Err(RuntimeError::InvalidOperation { message: "Plugin initialization failed".to_string() }); }
|
||||
let info = PluginInfo { name: "file".to_string(), version: 1, api_version: 1 };
|
||||
cache.insert("file".to_string(), LoadedPlugin { library, info });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create FileBox
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn create_file_box(path: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Self::load_file_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("file") {
|
||||
let c_path = CString::new(path).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid path string".to_string() })?;
|
||||
unsafe {
|
||||
let open_fn: Symbol<unsafe extern "C" fn(*const c_char) -> *mut c_void> = plugin.library.get(b"nyash_file_open\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_open: {}", e) })?;
|
||||
let handle = open_fn(c_path.as_ptr());
|
||||
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: format!("Failed to open file: {}", path) }); }
|
||||
Ok(Box::new(FileBoxProxy::new(handle, path.to_string())))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
|
||||
}
|
||||
|
||||
/// Check FileBox existence
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn file_exists(path: &str) -> Result<bool, RuntimeError> {
|
||||
Self::load_file_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("file") {
|
||||
let c_path = CString::new(path).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid path string".to_string() })?;
|
||||
unsafe {
|
||||
let exists_fn: Symbol<unsafe extern "C" fn(*const c_char) -> i32> = plugin.library.get(b"nyash_file_exists\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_exists: {}", e) })?;
|
||||
Ok(exists_fn(c_path.as_ptr()) != 0)
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
|
||||
}
|
||||
|
||||
/// Load Math plugin
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn load_math_plugin() -> Result<(), RuntimeError> {
|
||||
let mut cache = PLUGIN_CACHE.write().unwrap();
|
||||
if cache.contains_key("math") { return Ok(()); }
|
||||
let lib_name = if cfg!(target_os = "windows") { "nyash_math.dll" } else if cfg!(target_os = "macos") { "libnyash_math.dylib" } else { "libnyash_math.so" };
|
||||
let possible_paths = vec![
|
||||
format!("./target/release/{}", lib_name),
|
||||
format!("./target/debug/{}", lib_name),
|
||||
format!("./plugins/{}", lib_name),
|
||||
format!("./{}", lib_name),
|
||||
];
|
||||
let lib_path = possible_paths.iter().find(|p| std::path::Path::new(p.as_str()).exists()).cloned()
|
||||
.ok_or_else(|| RuntimeError::InvalidOperation { message: format!("Failed to find math plugin library. Searched paths: {:?}", possible_paths) })?;
|
||||
unsafe {
|
||||
let library = Library::new(&lib_path).map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to load math plugin: {}", e) })?;
|
||||
let info = PluginInfo { name: "math".to_string(), version: 1, api_version: 1 };
|
||||
cache.insert("math".to_string(), LoadedPlugin { library, info });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create MathBox
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn create_math_box() -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Self::load_math_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let create_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_math_create\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_math_create: {}", e) })?;
|
||||
let handle = create_fn();
|
||||
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create MathBox".to_string() }); }
|
||||
Ok(Box::new(MathBoxProxy::new(handle)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
|
||||
/// Create RandomBox
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn create_random_box() -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Self::load_math_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let create_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_random_create\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_create: {}", e) })?;
|
||||
let handle = create_fn();
|
||||
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create RandomBox".to_string() }); }
|
||||
Ok(Box::new(RandomBoxProxy::new(handle)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
|
||||
/// Create TimeBox
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn create_time_box() -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Self::load_math_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let create_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_time_create\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_time_create: {}", e) })?;
|
||||
let handle = create_fn();
|
||||
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create TimeBox".to_string() }); }
|
||||
Ok(Box::new(TimeBoxProxy::new(handle)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
|
||||
/// Create DateTimeBox (now)
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn create_datetime_now() -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Self::load_math_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let now_fn: Symbol<unsafe extern "C" fn() -> *mut c_void> = plugin.library.get(b"nyash_time_now\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_time_now: {}", e) })?;
|
||||
let handle = now_fn();
|
||||
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to create DateTimeBox".to_string() }); }
|
||||
Ok(Box::new(DateTimeBoxProxy::new(handle)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
|
||||
/// Create DateTimeBox from string
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub fn create_datetime_from_string(time_str: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Self::load_math_plugin()?;
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
let c_str = CString::new(time_str).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid time string".to_string() })?;
|
||||
unsafe {
|
||||
let parse_fn: Symbol<unsafe extern "C" fn(*const c_char) -> *mut c_void> = plugin.library.get(b"nyash_time_parse\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_time_parse: {}", e) })?;
|
||||
let handle = parse_fn(c_str.as_ptr());
|
||||
if handle.is_null() { return Err(RuntimeError::InvalidOperation { message: format!("Failed to parse time string: {}", time_str) }); }
|
||||
Ok(Box::new(DateTimeBoxProxy::new(handle)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
}
|
||||
|
||||
23
src/interpreter/plugin_loader/mod.rs
Normal file
23
src/interpreter/plugin_loader/mod.rs
Normal file
@ -0,0 +1,23 @@
|
||||
//! Dynamic Plugin Loader for Nyash (split module)
|
||||
//!
|
||||
//! Refactored into smaller files to improve readability while preserving
|
||||
//! the original public API surface used across the interpreter:
|
||||
//! - types.rs: globals and native handles
|
||||
//! - proxies.rs: Box proxy implementations
|
||||
//! - loader.rs: public loader entrypoints
|
||||
|
||||
mod types;
|
||||
mod proxies;
|
||||
mod loader;
|
||||
|
||||
// Re-export to preserve original paths like
|
||||
// crate::interpreter::plugin_loader::{PluginLoader, FileBoxProxy, ..., PLUGIN_CACHE}
|
||||
pub use loader::PluginLoader;
|
||||
pub use proxies::{
|
||||
FileBoxProxy, MathBoxProxy, RandomBoxProxy, TimeBoxProxy, DateTimeBoxProxy,
|
||||
};
|
||||
pub use types::{
|
||||
PLUGIN_CACHE, LoadedPlugin, PluginInfo, FileBoxHandle, MathBoxHandle,
|
||||
RandomBoxHandle, TimeBoxHandle, DateTimeBoxHandle,
|
||||
};
|
||||
|
||||
274
src/interpreter/plugin_loader/proxies.rs
Normal file
274
src/interpreter/plugin_loader/proxies.rs
Normal file
@ -0,0 +1,274 @@
|
||||
//! Proxies for dynamic plugins (File/Math/Random/Time/DateTime)
|
||||
|
||||
use std::ffi::{CStr, CString, c_char, c_void};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
use libloading::Symbol;
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase, IntegerBox};
|
||||
use crate::boxes::FloatBox;
|
||||
use crate::interpreter::RuntimeError;
|
||||
|
||||
use super::types::{PLUGIN_CACHE, FileBoxHandle, MathBoxHandle, RandomBoxHandle, TimeBoxHandle, DateTimeBoxHandle};
|
||||
use super::PluginLoader;
|
||||
|
||||
// ================== FileBoxProxy ==================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileBoxProxy {
|
||||
pub(crate) handle: Arc<FileBoxHandle>,
|
||||
pub(crate) path: String,
|
||||
pub(crate) base: BoxBase,
|
||||
}
|
||||
|
||||
unsafe impl Send for FileBoxProxy {}
|
||||
unsafe impl Sync for FileBoxProxy {}
|
||||
|
||||
impl FileBoxProxy {
|
||||
pub fn new(handle: *mut c_void, path: String) -> Self {
|
||||
FileBoxProxy { handle: Arc::new(FileBoxHandle { ptr: handle }), path, base: BoxBase::new() }
|
||||
}
|
||||
|
||||
pub fn read(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("file") {
|
||||
unsafe {
|
||||
let read_fn: Symbol<unsafe extern "C" fn(*mut c_void) -> *mut c_char> =
|
||||
plugin.library.get(b"nyash_file_read\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_read: {}", e) })?;
|
||||
let result_ptr = read_fn(self.handle.ptr);
|
||||
if result_ptr.is_null() { return Err(RuntimeError::InvalidOperation { message: "Failed to read file".to_string() }); }
|
||||
let content = CStr::from_ptr(result_ptr).to_string_lossy().into_owned();
|
||||
let free_fn: Symbol<unsafe extern "C" fn(*mut c_char)> =
|
||||
plugin.library.get(b"nyash_string_free\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_string_free: {}", e) })?;
|
||||
free_fn(result_ptr);
|
||||
Ok(Box::new(StringBox::new(content)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
|
||||
}
|
||||
#[cfg(not(feature = "dynamic-file"))]
|
||||
{ Err(RuntimeError::InvalidOperation { message: "Dynamic file support not enabled".to_string() }) }
|
||||
}
|
||||
|
||||
pub fn write(&self, content: Box<dyn NyashBox>) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("file") {
|
||||
let content_str = content.to_string_box().value;
|
||||
let c_content = CString::new(content_str).map_err(|_| RuntimeError::InvalidOperation { message: "Invalid content string".to_string() })?;
|
||||
unsafe {
|
||||
let write_fn: Symbol<unsafe extern "C" fn(*mut c_void, *const c_char) -> i32> =
|
||||
plugin.library.get(b"nyash_file_write\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_file_write: {}", e) })?;
|
||||
let result = write_fn(self.handle.ptr, c_content.as_ptr());
|
||||
if result == 0 { return Err(RuntimeError::InvalidOperation { message: "Failed to write file".to_string() }); }
|
||||
Ok(Box::new(StringBox::new("ok")))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "File plugin not loaded".to_string() }) }
|
||||
}
|
||||
#[cfg(not(feature = "dynamic-file"))]
|
||||
{ Err(RuntimeError::InvalidOperation { message: "Dynamic file support not enabled".to_string() }) }
|
||||
}
|
||||
|
||||
pub fn exists(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
Ok(Box::new(BoolBox::new(std::path::Path::new(&self.path).exists())))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for FileBoxProxy {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "FileBox({})", self.path) }
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for FileBoxProxy {
|
||||
fn type_name(&self) -> &'static str { "FileBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_file_box(&self.path) { Ok(b) => b, Err(_) => Box::new(FileBoxProxy::new(self.handle.ptr, self.path.clone())) } }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FileBox({})", self.path)) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<FileBoxProxy>().is_some().into() }
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FileBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
|
||||
|
||||
// ================== MathBoxProxy ==================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MathBoxProxy { pub(crate) handle: Arc<MathBoxHandle>, pub(crate) base: BoxBase }
|
||||
|
||||
unsafe impl Send for MathBoxProxy {}
|
||||
unsafe impl Sync for MathBoxProxy {}
|
||||
|
||||
impl MathBoxProxy { pub fn new(handle: *mut c_void) -> Self { MathBoxProxy { handle: Arc::new(MathBoxHandle { ptr: handle }), base: BoxBase::new() } } }
|
||||
|
||||
impl BoxCore for MathBoxProxy {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "MathBox") }
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for MathBoxProxy {
|
||||
fn type_name(&self) -> &'static str { "MathBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_math_box() { Ok(new_box) => new_box, Err(_) => Box::new(MathBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("MathBox") }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<MathBoxProxy>().is_some().into() }
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MathBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
|
||||
|
||||
// ================== RandomBoxProxy ==================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RandomBoxProxy { pub(crate) handle: Arc<RandomBoxHandle>, pub(crate) base: BoxBase }
|
||||
|
||||
unsafe impl Send for RandomBoxProxy {}
|
||||
unsafe impl Sync for RandomBoxProxy {}
|
||||
|
||||
impl RandomBoxProxy { pub fn new(handle: *mut c_void) -> Self { RandomBoxProxy { handle: Arc::new(RandomBoxHandle { ptr: handle }), base: BoxBase::new() } } }
|
||||
|
||||
impl RandomBoxProxy {
|
||||
pub fn next(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let next_fn: Symbol<unsafe extern "C" fn(*mut c_void) -> f64> = plugin.library.get(b"nyash_random_next\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_next: {}", e) })?;
|
||||
let value = next_fn(self.handle.ptr);
|
||||
Ok(Box::new(FloatBox::new(value)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
#[cfg(not(feature = "dynamic-file"))]
|
||||
{ Err(RuntimeError::InvalidOperation { message: "Dynamic loading not enabled".to_string() }) }
|
||||
}
|
||||
pub fn range(&self, min: f64, max: f64) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let range_fn: Symbol<unsafe extern "C" fn(*mut c_void, f64, f64) -> f64> = plugin.library.get(b"nyash_random_range\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_range: {}", e) })?;
|
||||
let value = range_fn(self.handle.ptr, min, max);
|
||||
Ok(Box::new(FloatBox::new(value)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
#[cfg(not(feature = "dynamic-file"))]
|
||||
{ Err(RuntimeError::InvalidOperation { message: "Dynamic loading not enabled".to_string() }) }
|
||||
}
|
||||
pub fn int(&self, min: i64, max: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
let int_fn: Symbol<unsafe extern "C" fn(*mut c_void, i64, i64) -> i64> = plugin.library.get(b"nyash_random_int\0").map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to get nyash_random_int: {}", e) })?;
|
||||
let value = int_fn(self.handle.ptr, min, max);
|
||||
Ok(Box::new(IntegerBox::new(value)))
|
||||
}
|
||||
} else { Err(RuntimeError::InvalidOperation { message: "Math plugin not loaded".to_string() }) }
|
||||
}
|
||||
#[cfg(not(feature = "dynamic-file"))]
|
||||
{ Err(RuntimeError::InvalidOperation { message: "Dynamic loading not enabled".to_string() }) }
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for RandomBoxProxy {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "RandomBox") }
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for RandomBoxProxy {
|
||||
fn type_name(&self) -> &'static str { "RandomBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_random_box() { Ok(new_box) => new_box, Err(_) => Box::new(RandomBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("RandomBox") }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<RandomBoxProxy>().is_some().into() }
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RandomBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
|
||||
|
||||
// ================== TimeBoxProxy ==================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TimeBoxProxy { pub(crate) handle: Arc<TimeBoxHandle>, pub(crate) base: BoxBase }
|
||||
|
||||
unsafe impl Send for TimeBoxProxy {}
|
||||
unsafe impl Sync for TimeBoxProxy {}
|
||||
|
||||
impl TimeBoxProxy { pub fn new(handle: *mut c_void) -> Self { TimeBoxProxy { handle: Arc::new(TimeBoxHandle { ptr: handle }), base: BoxBase::new() } } }
|
||||
|
||||
impl BoxCore for TimeBoxProxy {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "TimeBox") }
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for TimeBoxProxy {
|
||||
fn type_name(&self) -> &'static str { "TimeBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_time_box() { Ok(new_box) => new_box, Err(_) => Box::new(TimeBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("TimeBox") }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox { other.as_any().downcast_ref::<TimeBoxProxy>().is_some().into() }
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TimeBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
|
||||
|
||||
// ================== DateTimeBoxProxy ==================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DateTimeBoxProxy { pub(crate) handle: Arc<DateTimeBoxHandle>, pub(crate) base: BoxBase }
|
||||
|
||||
unsafe impl Send for DateTimeBoxProxy {}
|
||||
unsafe impl Sync for DateTimeBoxProxy {}
|
||||
|
||||
impl DateTimeBoxProxy { pub fn new(handle: *mut c_void) -> Self { DateTimeBoxProxy { handle: Arc::new(DateTimeBoxHandle { ptr: handle }), base: BoxBase::new() } } }
|
||||
|
||||
impl BoxCore for DateTimeBoxProxy {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "DateTimeBox") }
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
|
||||
}
|
||||
|
||||
impl NyashBox for DateTimeBoxProxy {
|
||||
fn type_name(&self) -> &'static str { "DateTimeBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { match PluginLoader::create_datetime_now() { Ok(new_box) => new_box, Err(_) => Box::new(DateTimeBoxProxy { handle: Arc::clone(&self.handle), base: BoxBase::new() }) } }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("DateTimeBox") }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_datetime) = other.as_any().downcast_ref::<DateTimeBoxProxy>() {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
if let Ok(timestamp_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void) -> i64>>(b"nyash_datetime_timestamp\0") {
|
||||
let this_ts = timestamp_fn(self.handle.ptr);
|
||||
let other_ts = timestamp_fn(other_datetime.handle.ptr);
|
||||
return BoolBox::new(this_ts == other_ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BoolBox::new(false)
|
||||
} else { BoolBox::new(false) }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DateTimeBoxProxy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } }
|
||||
|
||||
159
src/interpreter/plugin_loader/types.rs
Normal file
159
src/interpreter/plugin_loader/types.rs
Normal file
@ -0,0 +1,159 @@
|
||||
//! Types and globals for interpreter plugin loader
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::ffi::c_void;
|
||||
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
use libloading::Library;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
/// Global cache for loaded plugins (keyed by simple name like "file" or "math")
|
||||
pub(crate) static ref PLUGIN_CACHE: RwLock<HashMap<String, LoadedPlugin>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
/// Loaded plugin handle + basic info
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
pub(crate) struct LoadedPlugin {
|
||||
pub(crate) library: Library,
|
||||
pub(crate) info: PluginInfo,
|
||||
}
|
||||
|
||||
/// Minimal plugin info (simplified)
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct PluginInfo {
|
||||
pub(crate) name: String,
|
||||
pub(crate) version: u32,
|
||||
pub(crate) api_version: u32,
|
||||
}
|
||||
|
||||
/// FileBox native handle wrapper
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FileBoxHandle { pub(crate) ptr: *mut c_void }
|
||||
|
||||
impl Drop for FileBoxHandle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
if !self.ptr.is_null() {
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("file") {
|
||||
unsafe {
|
||||
use libloading::Symbol;
|
||||
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_file_free\0") {
|
||||
free_fn(self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for FileBoxHandle {}
|
||||
unsafe impl Sync for FileBoxHandle {}
|
||||
|
||||
/// MathBox native handle wrapper
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MathBoxHandle { pub(crate) ptr: *mut c_void }
|
||||
|
||||
impl Drop for MathBoxHandle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
if !self.ptr.is_null() {
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
use libloading::Symbol;
|
||||
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_math_free\0") {
|
||||
free_fn(self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for MathBoxHandle {}
|
||||
unsafe impl Sync for MathBoxHandle {}
|
||||
|
||||
/// RandomBox native handle wrapper
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RandomBoxHandle { pub(crate) ptr: *mut c_void }
|
||||
|
||||
impl Drop for RandomBoxHandle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
if !self.ptr.is_null() {
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
use libloading::Symbol;
|
||||
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_random_free\0") {
|
||||
free_fn(self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for RandomBoxHandle {}
|
||||
unsafe impl Sync for RandomBoxHandle {}
|
||||
|
||||
/// TimeBox native handle wrapper
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TimeBoxHandle { pub(crate) ptr: *mut c_void }
|
||||
|
||||
impl Drop for TimeBoxHandle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
if !self.ptr.is_null() {
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
use libloading::Symbol;
|
||||
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_time_free\0") {
|
||||
free_fn(self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for TimeBoxHandle {}
|
||||
unsafe impl Sync for TimeBoxHandle {}
|
||||
|
||||
/// DateTimeBox native handle wrapper
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DateTimeBoxHandle { pub(crate) ptr: *mut c_void }
|
||||
|
||||
impl Drop for DateTimeBoxHandle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "dynamic-file")]
|
||||
{
|
||||
if !self.ptr.is_null() {
|
||||
let cache = PLUGIN_CACHE.read().unwrap();
|
||||
if let Some(plugin) = cache.get("math") {
|
||||
unsafe {
|
||||
use libloading::Symbol;
|
||||
if let Ok(free_fn) = plugin.library.get::<Symbol<unsafe extern "C" fn(*mut c_void)>>(b"nyash_datetime_free\0") {
|
||||
free_fn(self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for DateTimeBoxHandle {}
|
||||
unsafe impl Sync for DateTimeBoxHandle {}
|
||||
|
||||
@ -19,6 +19,7 @@ mod builder_calls;
|
||||
mod stmts;
|
||||
mod ops;
|
||||
mod utils;
|
||||
mod exprs; // expression lowering split
|
||||
|
||||
// moved helpers to builder/utils.rs
|
||||
|
||||
@ -230,6 +231,12 @@ impl MirBuilder {
|
||||
|
||||
/// Build an expression and return its value ID
|
||||
pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
|
||||
// Delegated to exprs.rs to keep this file lean
|
||||
self.build_expression_impl(ast)
|
||||
}
|
||||
|
||||
// Moved implementation to exprs.rs; keeping a small shim here improves readability
|
||||
pub(super) fn build_expression_impl_legacy(&mut self, ast: ASTNode) -> Result<ValueId, String> {
|
||||
match ast {
|
||||
ASTNode::Literal { value, .. } => {
|
||||
self.build_literal(value)
|
||||
@ -616,7 +623,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Build a literal value
|
||||
fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
|
||||
pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
|
||||
// Determine type without moving literal
|
||||
let ty_for_dst = match &literal {
|
||||
LiteralValue::Integer(_) => Some(super::MirType::Integer),
|
||||
@ -650,7 +657,7 @@ impl MirBuilder {
|
||||
// build_unary_op moved to builder/ops.rs
|
||||
|
||||
/// Build variable access
|
||||
fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
|
||||
pub(super) fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
|
||||
if let Some(&value_id) = self.variable_map.get(&name) {
|
||||
Ok(value_id)
|
||||
} else {
|
||||
@ -659,7 +666,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Build assignment
|
||||
fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
pub(super) fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
let value_id = self.build_expression(value)?;
|
||||
|
||||
// In SSA form, each assignment creates a new value
|
||||
@ -738,7 +745,7 @@ impl MirBuilder {
|
||||
|
||||
/// Build static box (e.g., Main) - extracts main() method body and converts to Program
|
||||
/// Also lowers other static methods into standalone MIR functions: BoxName.method/N
|
||||
fn build_static_main_box(&mut self, box_name: String, methods: std::collections::HashMap<String, ASTNode>) -> Result<ValueId, String> {
|
||||
pub(super) fn build_static_main_box(&mut self, box_name: String, methods: std::collections::HashMap<String, ASTNode>) -> Result<ValueId, String> {
|
||||
// Lower other static methods (except main) to standalone MIR functions so JIT can see them
|
||||
for (mname, mast) in methods.iter() {
|
||||
if mname == "main" { continue; }
|
||||
@ -792,7 +799,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Build field access: object.field
|
||||
fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> {
|
||||
pub(super) fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> {
|
||||
// Clone the object before building expression if we need to check it later
|
||||
let object_clone = object.clone();
|
||||
|
||||
@ -841,7 +848,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Build new expression: new ClassName(arguments)
|
||||
fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
pub(super) fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Phase 9.78a: Unified Box creation using NewBox instruction
|
||||
|
||||
// Optimization: Primitive wrappers → emit Const directly when possible
|
||||
@ -900,7 +907,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Build field assignment: object.field = value
|
||||
fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
pub(super) fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
// Build the object and value expressions
|
||||
let object_value = self.build_expression(object)?;
|
||||
let mut value_result = self.build_expression(value)?;
|
||||
@ -978,7 +985,7 @@ impl MirBuilder {
|
||||
// lower_static_method_as_function_legacy removed (use builder_calls::lower_static_method_as_function)
|
||||
|
||||
/// Build box declaration: box Name { fields... methods... }
|
||||
fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap<String, ASTNode>, fields: Vec<String>, weak_fields: Vec<String>) -> Result<(), String> {
|
||||
pub(super) fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap<String, ASTNode>, fields: Vec<String>, weak_fields: Vec<String>) -> Result<(), String> {
|
||||
// For Phase 8.4, we'll emit metadata instructions to register the box type
|
||||
// In a full implementation, this would register type information for later use
|
||||
|
||||
|
||||
244
src/mir/builder/exprs.rs
Normal file
244
src/mir/builder/exprs.rs
Normal file
@ -0,0 +1,244 @@
|
||||
// Expression lowering split from builder.rs to keep files lean
|
||||
use super::{MirInstruction, ConstValue, BasicBlockId, ValueId};
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
|
||||
impl super::MirBuilder {
|
||||
// Main expression dispatcher
|
||||
pub(super) fn build_expression_impl(&mut self, ast: ASTNode) -> Result<ValueId, String> {
|
||||
match ast {
|
||||
ASTNode::Literal { value, .. } => self.build_literal(value),
|
||||
|
||||
ASTNode::BinaryOp { left, operator, right, .. } =>
|
||||
self.build_binary_op(*left, operator, *right),
|
||||
|
||||
ASTNode::UnaryOp { operator, operand, .. } => {
|
||||
let op_string = match operator {
|
||||
crate::ast::UnaryOperator::Minus => "-".to_string(),
|
||||
crate::ast::UnaryOperator::Not => "not".to_string(),
|
||||
};
|
||||
self.build_unary_op(op_string, *operand)
|
||||
}
|
||||
|
||||
ASTNode::Variable { name, .. } => self.build_variable_access(name.clone()),
|
||||
|
||||
ASTNode::Me { .. } => self.build_me_expression(),
|
||||
|
||||
ASTNode::MethodCall { object, method, arguments, .. } => {
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||
let obj_val = self.build_expression_impl(*object.clone())?;
|
||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if method == "is" { crate::mir::TypeOpKind::Check } else { crate::mir::TypeOpKind::Cast };
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
self.build_method_call(*object.clone(), method.clone(), arguments.clone())
|
||||
}
|
||||
|
||||
ASTNode::FromCall { parent, method, arguments, .. } =>
|
||||
self.build_from_expression(parent.clone(), method.clone(), arguments.clone()),
|
||||
|
||||
ASTNode::Assignment { target, value, .. } => {
|
||||
if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() {
|
||||
self.build_field_assignment(*object.clone(), field.clone(), *value.clone())
|
||||
} else if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
self.build_assignment(name.clone(), *value.clone())
|
||||
} else {
|
||||
Err("Complex assignment targets not yet supported".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
ASTNode::FunctionCall { name, arguments, .. } =>
|
||||
self.build_function_call(name.clone(), arguments.clone()),
|
||||
|
||||
ASTNode::Call { callee, arguments, .. } => {
|
||||
let mut arg_ids: Vec<ValueId> = Vec::new();
|
||||
let callee_id = self.build_expression_impl(*callee.clone())?;
|
||||
for a in arguments { arg_ids.push(self.build_expression_impl(a)?); }
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Call { dst: Some(dst), func: callee_id, args: arg_ids, effects: crate::mir::EffectMask::PURE })?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
ASTNode::QMarkPropagate { expression, .. } => {
|
||||
let res_val = self.build_expression_impl(*expression.clone())?;
|
||||
let ok_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::PluginInvoke { dst: Some(ok_id), box_val: res_val, method: "isOk".to_string(), args: vec![], effects: crate::mir::EffectMask::PURE })?;
|
||||
let then_block = self.block_gen.next();
|
||||
let else_block = self.block_gen.next();
|
||||
self.emit_instruction(MirInstruction::Branch { condition: ok_id, then_bb: then_block, else_bb: else_block })?;
|
||||
self.start_new_block(then_block)?;
|
||||
self.emit_instruction(MirInstruction::Return { value: Some(res_val) })?;
|
||||
self.start_new_block(else_block)?;
|
||||
let val_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::PluginInvoke { dst: Some(val_id), box_val: res_val, method: "getValue".to_string(), args: vec![], effects: crate::mir::EffectMask::PURE })?;
|
||||
Ok(val_id)
|
||||
}
|
||||
|
||||
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => {
|
||||
let scr_val = self.build_expression_impl(*scrutinee.clone())?;
|
||||
let merge_block: BasicBlockId = self.block_gen.next();
|
||||
let result_val = self.value_gen.next();
|
||||
let mut phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
let mut next_block = self.block_gen.next();
|
||||
self.start_new_block(next_block)?;
|
||||
for (i, (label, arm_expr)) in arms.iter().enumerate() {
|
||||
let then_block = self.block_gen.next();
|
||||
if let LiteralValue::String(ref s) = label {
|
||||
let lit_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: lit_id, value: ConstValue::String(s.clone()) })?;
|
||||
let cond_id = self.value_gen.next();
|
||||
self.emit_instruction(crate::mir::MirInstruction::Compare { dst: cond_id, op: crate::mir::CompareOp::Eq, lhs: scr_val, rhs: lit_id })?;
|
||||
self.emit_instruction(crate::mir::MirInstruction::Branch { condition: cond_id, then_bb: then_block, else_bb: next_block })?;
|
||||
self.start_new_block(then_block)?;
|
||||
let then_val = self.build_expression_impl(arm_expr.clone())?;
|
||||
phi_inputs.push((then_block, then_val));
|
||||
self.emit_instruction(crate::mir::MirInstruction::Jump { target: merge_block })?;
|
||||
if i < arms.len() - 1 { let b = self.block_gen.next(); self.start_new_block(b)?; next_block = b; }
|
||||
}
|
||||
}
|
||||
let else_block_id = next_block; self.start_new_block(else_block_id)?;
|
||||
let else_val = self.build_expression_impl(*else_expr.clone())?;
|
||||
phi_inputs.push((else_block_id, else_val));
|
||||
self.emit_instruction(crate::mir::MirInstruction::Jump { target: merge_block })?;
|
||||
self.start_new_block(merge_block)?;
|
||||
self.emit_instruction(crate::mir::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
|
||||
Ok(result_val)
|
||||
}
|
||||
|
||||
ASTNode::Lambda { params, body, .. } => {
|
||||
use std::collections::HashSet;
|
||||
let mut used: HashSet<String> = HashSet::new();
|
||||
let mut locals: HashSet<String> = HashSet::new(); for p in ¶ms { locals.insert(p.clone()); }
|
||||
fn collect_vars(ast: &ASTNode, used: &mut std::collections::HashSet<String>, locals: &mut std::collections::HashSet<String>) {
|
||||
match ast {
|
||||
ASTNode::Variable { name, .. } => { if !locals.contains(name) { used.insert(name.clone()); } }
|
||||
ASTNode::Assignment { target, value, .. } => { collect_vars(target, used, locals); collect_vars(value, used, locals); }
|
||||
ASTNode::BinaryOp { left, right, .. } => { collect_vars(left, used, locals); collect_vars(right, used, locals); }
|
||||
ASTNode::UnaryOp { operand, .. } => { collect_vars(operand, used, locals); }
|
||||
ASTNode::MethodCall { object, arguments, .. } => { collect_vars(object, used, locals); for a in arguments { collect_vars(a, used, locals); } }
|
||||
ASTNode::FunctionCall { arguments, .. } => { for a in arguments { collect_vars(a, used, locals); } }
|
||||
ASTNode::Call { callee, arguments, .. } => { collect_vars(callee, used, locals); for a in arguments { collect_vars(a, used, locals); } }
|
||||
ASTNode::FieldAccess { object, .. } => { collect_vars(object, used, locals); }
|
||||
ASTNode::New { arguments, .. } => { for a in arguments { collect_vars(a, used, locals); } }
|
||||
ASTNode::If { condition, then_body, else_body, .. } => {
|
||||
collect_vars(condition, used, locals);
|
||||
for st in then_body { collect_vars(st, used, locals); }
|
||||
if let Some(eb) = else_body { for st in eb { collect_vars(st, used, locals); } }
|
||||
}
|
||||
ASTNode::Loop { condition, body, .. } => { collect_vars(condition, used, locals); for st in body { collect_vars(st, used, locals); } }
|
||||
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
|
||||
for st in try_body { collect_vars(st, used, locals); }
|
||||
for c in catch_clauses { for st in &c.body { collect_vars(st, used, locals); } }
|
||||
if let Some(fb) = finally_body { for st in fb { collect_vars(st, used, locals); } }
|
||||
}
|
||||
ASTNode::Throw { expression, .. } => { collect_vars(expression, used, locals); }
|
||||
ASTNode::Print { expression, .. } => { collect_vars(expression, used, locals); }
|
||||
ASTNode::Return { value, .. } => { if let Some(v) = value { collect_vars(v, used, locals); } }
|
||||
ASTNode::AwaitExpression { expression, .. } => { collect_vars(expression, used, locals); }
|
||||
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => {
|
||||
collect_vars(scrutinee, used, locals);
|
||||
for (_, e) in arms { collect_vars(e, used, locals); }
|
||||
collect_vars(else_expr, used, locals);
|
||||
}
|
||||
ASTNode::Program { statements, .. } => { for st in statements { collect_vars(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_vars(st, used, &mut inner); }
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for st in body.iter() { collect_vars(st, &mut used, &mut locals); }
|
||||
let mut captures: Vec<(String, ValueId)> = Vec::new();
|
||||
for name in used.into_iter() {
|
||||
if let Some(&vid) = self.variable_map.get(&name) { captures.push((name, vid)); }
|
||||
}
|
||||
let me = self.variable_map.get("me").copied();
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::FunctionNew { dst, params: params.clone(), body: body.clone(), captures, me })?;
|
||||
self.value_types.insert(dst, crate::mir::MirType::Box("FunctionBox".to_string()));
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
ASTNode::Return { value, .. } => self.build_return_statement(value.clone()),
|
||||
|
||||
ASTNode::Local { variables, initial_values, .. } =>
|
||||
self.build_local_statement(variables.clone(), initial_values.clone()),
|
||||
|
||||
ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } => {
|
||||
if is_static && name == "Main" {
|
||||
self.build_static_main_box(name.clone(), methods.clone())
|
||||
} else {
|
||||
self.user_defined_boxes.insert(name.clone());
|
||||
self.build_box_declaration(name.clone(), methods.clone(), fields.clone(), weak_fields.clone())?;
|
||||
for (ctor_key, ctor_ast) in constructors.clone() {
|
||||
if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast {
|
||||
let func_name = format!("{}.{}", name, ctor_key);
|
||||
self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?;
|
||||
}
|
||||
}
|
||||
for (method_name, method_ast) in methods.clone() {
|
||||
if let ASTNode::FunctionDeclaration { params, body, is_static, .. } = method_ast {
|
||||
if !is_static {
|
||||
let func_name = format!("{}.{}{}", name, method_name, format!("/{}", params.len()));
|
||||
self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?;
|
||||
Ok(void_val)
|
||||
}
|
||||
}
|
||||
|
||||
ASTNode::FieldAccess { object, field, .. } =>
|
||||
self.build_field_access(*object.clone(), field.clone()),
|
||||
|
||||
ASTNode::New { class, arguments, .. } =>
|
||||
self.build_new_expression(class.clone(), arguments.clone()),
|
||||
|
||||
ASTNode::Nowait { variable, expression, .. } =>
|
||||
self.build_nowait_statement(variable.clone(), *expression.clone()),
|
||||
|
||||
ASTNode::AwaitExpression { expression, .. } =>
|
||||
self.build_await_expression(*expression.clone()),
|
||||
|
||||
ASTNode::Include { filename, .. } => {
|
||||
let mut path = super::utils::resolve_include_path_builder(&filename);
|
||||
if std::path::Path::new(&path).is_dir() {
|
||||
path = format!("{}/index.nyash", path.trim_end_matches('/'));
|
||||
} else if std::path::Path::new(&path).extension().is_none() {
|
||||
path.push_str(".nyash");
|
||||
}
|
||||
if self.include_loading.contains(&path) {
|
||||
return Err(format!("Circular include detected: {}", path));
|
||||
}
|
||||
if let Some(name) = self.include_box_map.get(&path).cloned() {
|
||||
return self.build_new_expression(name, vec![]);
|
||||
}
|
||||
self.include_loading.insert(path.clone());
|
||||
let content = std::fs::read_to_string(&path)
|
||||
.map_err(|e| format!("Include read error '{}': {}", filename, e))?;
|
||||
let included_ast = crate::parser::NyashParser::parse_from_string(&content)
|
||||
.map_err(|e| format!("Include parse error '{}': {:?}", filename, e))?;
|
||||
let mut box_name: Option<String> = None;
|
||||
if let ASTNode::Program { statements, .. } = &included_ast {
|
||||
for st in statements {
|
||||
if let ASTNode::BoxDeclaration { name, is_static, .. } = st { if *is_static { box_name = Some(name.clone()); break; } }
|
||||
}
|
||||
}
|
||||
let bname = box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?;
|
||||
let _ = self.build_expression_impl(included_ast)?;
|
||||
self.include_loading.remove(&path);
|
||||
self.include_box_map.insert(path.clone(), bname.clone());
|
||||
self.build_new_expression(bname, vec![])
|
||||
}
|
||||
|
||||
_ => Err(format!("Unsupported AST node type: {:?}", ast)),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user