mumu 0.11.1

Lava Mumu is a language for those in the now and that know
Documentation
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use libloading::Library;
use crate::parser::types::Value;

/// A dynamic function pointer type. This Arc<Mutex<dyn Fn(...)>> allows
/// storing a Rust closure or bridging callback.
pub type DynamicFn = Arc<
    Mutex<
        dyn for<'a> Fn(&'a mut Interpreter, Vec<Value>)
            -> Result<Value, String>
            + Send
            + Sync
            + 'static
    >
>;

pub struct DynamicFnInfo {
    pub func: DynamicFn,
    pub is_pure: bool,
}

impl DynamicFnInfo {
    pub fn new(func: DynamicFn, is_pure: bool) -> Self {
        Self { func, is_pure }
    }
}

pub type PollerFn = Arc<Mutex<dyn Fn(&mut Interpreter) -> usize + Send + Sync + 'static>>;

pub struct Interpreter {
    // Variable scopes: stack of scopes, top is current
    pub scopes: Vec<HashMap<String, Arc<Mutex<Value>>>>,
    pub prints: Vec<String>,
    pub verbose: bool,
    pub allow_extend: bool,
    pub dynamic_funcs: HashMap<String, DynamicFnInfo>,
    pub libraries: Vec<Library>,
    pub pollers: Vec<PollerFn>,
    pub is_repl_mode: bool, // <--- new flag
}

impl Interpreter {
    pub fn new() -> Self {
        Self {
            scopes: vec![HashMap::new()],
            prints: Vec::new(),
            verbose: false,
            allow_extend: true,
            dynamic_funcs: HashMap::new(),
            libraries: Vec::new(),
            pollers: Vec::new(),
            is_repl_mode: false,
        }
    }

    pub fn push_scope(&mut self) {
        self.scopes.push(HashMap::new());
    }
    pub fn pop_scope(&mut self) {
        if self.scopes.len() > 1 {
            self.scopes.pop();
        }
    }

    pub fn add_library_handle(&mut self, lib: Library) {
        self.libraries.push(lib);
    }
    pub fn register_dynamic_function_ex(&mut self, name: &str, info: DynamicFnInfo) {
        self.dynamic_funcs.insert(name.to_string(), info);
    }
    pub fn register_dynamic_function(&mut self, name: &str, func: DynamicFn) {
        let info = DynamicFnInfo::new(func, false);
        self.register_dynamic_function_ex(name, info);
    }
    pub fn get_dynamic_function(&self, name: &str) -> Option<DynamicFn> {
        self.dynamic_funcs.get(name).map(|inf| inf.func.clone())
    }
    pub fn is_function_pure(&self, name: &str) -> bool {
        self.dynamic_funcs.get(name).map(|inf| inf.is_pure).unwrap_or(false)
    }
    pub fn get_dynamic_functions_for_clone(&self) -> &std::collections::HashMap<String, DynamicFnInfo> {
        &self.dynamic_funcs
    }
    pub fn set_verbose(&mut self, yes: bool) { self.verbose = yes; }
    pub fn is_verbose(&self) -> bool { self.verbose }
    pub fn disallow_extend(&mut self) { self.allow_extend = false; }
    pub fn can_extend(&self) -> bool { self.allow_extend }

    // --------- REPL mode handling ----------
    pub fn set_repl_mode(&mut self, flag: bool) {
        self.is_repl_mode = flag;
    }
    pub fn is_repl_mode(&self) -> bool {
        self.is_repl_mode
    }

    /// Returns a merged "view" of all variables, topmost shadowing
    pub fn get_variables(&self) -> HashMap<String, Value> {
        let mut map = HashMap::new();
        for scope in &self.scopes {
            for (k, v) in scope.iter() {
                map.insert(k.clone(), v.lock().unwrap().clone());
            }
        }
        map
    }

    /// Returns the "top" (current) scope mutably
    pub fn get_variables_mut(&mut self) -> &mut HashMap<String, Arc<Mutex<Value>>> {
        self.scopes.last_mut().unwrap()
    }

    /// Set variable in the top (current) scope
    pub fn set_variable(&mut self, name: &str, val: Value) {
        self.scopes.last_mut().unwrap().insert(name.to_string(), Arc::new(Mutex::new(val)));
    }

    /// Set variable in the global (bottom) scope
    pub fn set_global_variable(&mut self, name: &str, val: Value) {
        self.scopes[0].insert(name.to_string(), Arc::new(Mutex::new(val)));
    }

    /// Lookup a variable and return a reference Value::Ref to its storage
    pub fn lookup_variable(&self, name: &str) -> Option<Value> {
        for scope in self.scopes.iter().rev() {
            if let Some(v) = scope.get(name) {
                return Some(Value::Ref(v.clone()));
            }
        }
        None
    }

    /// Get direct access to variable storage (for reference creation)
    pub fn lookup_variable_storage(&self, name: &str) -> Option<Arc<Mutex<Value>>> {
        for scope in self.scopes.iter().rev() {
            if let Some(v) = scope.get(name) {
                return Some(v.clone());
            }
        }
        None
    }

    pub fn get_prints(&self) -> &Vec<String> { &self.prints }
    pub fn push_print(&mut self, text: &str) { self.prints.push(text.to_string()); }

    pub fn print_value(&mut self, val: &Value) {
        let text = self.value_to_bracketed_string(val);
        println!("{}", text);
        self.push_print(&text);
    }

    pub fn value_to_bracketed_string(&self, val: &Value) -> String {
        use Value::*;
        match val {
            Int(n) => format!("{}", n),
            IntArray(xs) => {
                if xs.is_empty() {
                    "[]".to_string()
                } else {
                    let joined = xs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
                    format!("[{}]", joined)
                }
            }
            Int2DArray(rows) => {
                let mut outer = String::new();
                outer.push('[');
                for (i, row) in rows.iter().enumerate() {
                    if i > 0 { outer.push_str(", "); }
                    let joined = row.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
                    outer.push('['); outer.push_str(&joined); outer.push(']');
                }
                outer.push(']');
                outer
            }
            StrArray(ss) => {
                if ss.is_empty() { "[]".to_string() }
                else { let joined = ss.join("\", \""); format!("[\"{}\"]", joined) }
            }
            KeyedArray(map) => {
                if map.is_empty() { "{}".to_string() }
                else {
                    let mut parts = Vec::new();
                    for (k, v) in map.iter() {
                        parts.push(format!("{}: {}", k, self.value_to_bracketed_string(v)));
                    }
                    format!("{{ {} }}", parts.join(", "))
                }
            }
            Function(_) => "[Function]".to_string(),
            Bool(b) => b.to_string(),
            BoolArray(bb) => {
                if bb.is_empty() { "[]".to_string() }
                else {
                    let joined = bb.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
                    format!("[{}]", joined)
                }
            }
            Placeholder => "_".to_string(),
            SingleString(s) => s.clone(),
            Long(l) => format!("{}", l),
            Float(ff) => ff.to_string(),
            FloatArray(ffs) => {
                if ffs.is_empty() { "[]".to_string() }
                else {
                    let joined = ffs.iter().map(|f| f.to_string()).collect::<Vec<_>>().join(", ");
                    format!("[{}]", joined)
                }
            }
            Stream(sh) => format!("<Stream id={}, label={}>", sh.stream_id, sh.label),
            Float2DArray(rows) => {
                let mut outer = String::new();
                outer.push('[');
                for (i, row) in rows.iter().enumerate() {
                    if i > 0 { outer.push_str(", "); }
                    let joined = row.iter().map(|f| f.to_string()).collect::<Vec<_>>().join(", ");
                    outer.push('['); outer.push_str(&joined); outer.push(']');
                }
                outer.push(']');
                outer
            }
            InkIterator(_) => "[InkIterator]".to_string(),
            InkTransform(_) => "[InkTransform]".to_string(),
            Tensor(_) => "[Tensor]".to_string(),
            MixedArray(items) => {
                if items.is_empty() { "[]".to_string() }
                else {
                    let joined = items.iter().map(|x| self.value_to_bracketed_string(x)).collect::<Vec<_>>().join(", ");
                    format!("[{}]", joined)
                }
            }
            Ref(arc_mutex) => self.value_to_bracketed_string(&arc_mutex.lock().unwrap()),
            Regex(rx) => format!("Regex(/{}{}/)", rx.pattern, rx.flags),
        }
    }

    pub fn poll_all(&mut self) -> usize {
        let pollers_clone = self.pollers.clone();
        let mut total = 0;
        for p in pollers_clone {
            total += p.lock().unwrap()(self);
        }
        total
    }

    pub fn remove_all_pollers(&mut self) { self.pollers.clear(); }
    pub fn add_poller(&mut self, poller: PollerFn) { self.pollers.push(poller); }

    // ---------------- PLUGIN LOADER ----------------
    pub fn load_plugin(&mut self, path: &str) -> Result<(), String> {
        use std::path::Path;
        use std::ffi::c_void;
        use libloading::{Library, Symbol};

        if !Path::new(path).exists() {
            return Err(format!("Plugin file not found: {}", path));
        }
        unsafe {
            let lib = Library::new(path)
                .map_err(|e| format!("Failed to load library '{}': {}", path, e))?;
            // Try to locate the Cargo_lock symbol for registration
            let func: Symbol<unsafe extern "C" fn(*mut c_void, *const c_void) -> i32> =
                lib.get(b"Cargo_lock")
                    .map_err(|e| format!("Failed to load symbol 'Cargo_lock': {}", e))?;
            // SAFETY: Call Cargo_lock with self as *mut c_void, second arg unused
            let self_ptr = self as *mut Interpreter as *mut c_void;
            let _ = func(self_ptr, std::ptr::null());
            self.add_library_handle(lib);
        }
        Ok(())
    }
}