mumu 0.11.1

Lava Mumu is a language for those in the now and that know
Documentation
use mumu::parser::interpreter::Interpreter;
use mumu::modules::compose::register_compose_and_pipe;
use mumu::modules::slog::register_slog;
use mumu::modules::sput::register_sput;
use mumu::modules::r#type::register_type;
use mumu::modules::ink::register_ink;
use mumu::modules::check::register_check;
use mumu::modules::include::register_include;

use mumu::parser::lexer::tokenize;
use mumu::parser::core::driver::parse_tokens;
use mumu::parser::ast::Stmt;

// FIX: Import run_repl_mode directly from main_loop
use mumu::modules::repl::main_loop::run_repl_mode;

fn print_help() {
    println!("af [OPTIONS] <path-to-file>");
    println!("  -h, --help       Print this help message");
    println!("  --version        Print version & debug info");
    println!("Examples:");
    println!("  af script.af");
}

fn main() {
    if let Err(e) = try_main() {
        println!("Error: {}", e);
    }
}

fn try_main() -> Result<(), String> {
    let args: Vec<String> = std::env::args().collect();

    let mut debug_verbose = false;
    let mut file_path: Option<String> = None;

    let mut i = 1;
    while i < args.len() {
        match args[i].as_str() {
            "-h" | "--help" => {
                print_help();
                return Ok(());
            }
            "--version" => {
                println!("mumu version 0.9.1");
                println!("Environment: REPL + file-run example");
                return Ok(());
            }
            "-v" | "--verbose" => {
                debug_verbose = true;
                i += 1;
                continue;
            }
            other => {
                file_path = Some(other.to_string());
            }
        }
        i += 1;
    }

    // -------------------- REPL PATH --------------------
    if file_path.is_none() {
        // Spawn a REPL-mode interpreter, and set is_repl_mode=true
        return run_repl_mode(debug_verbose);
    }

    // -------------------- FILE EXECUTION PATH --------------------
    let file_path = file_path.unwrap();
    let code = std::fs::read_to_string(&file_path)
        .map_err(|e| format!("Failed to read '{}': {}", file_path, e))?;

    let tokens = tokenize(&code, debug_verbose)
        .map_err(|lex_err| format!("Lex error: {}", lex_err))?;

    let ast = parse_tokens(&tokens, debug_verbose)
        .map_err(|parse_err| format!("Parse error: {}", parse_err))?;

    let mut interp = Interpreter::new();
    interp.set_verbose(debug_verbose);
    interp.set_repl_mode(false); // This run is *not* REPL

    // Register all built-in modules
    register_compose_and_pipe(&mut interp);
    register_sput(&mut interp);
    register_slog(&mut interp);
    register_type(&mut interp);
    register_ink(&mut interp);
    register_check(&mut interp);
    register_include(&mut interp);

    let mut _last_val = mumu::parser::types::Value::Int(0);

    // NEW: For correct plugin loading, execute each ImportSo immediately as you encounter it.
    let mut idx = 0;
    while idx < ast.len() {
        let stmt = &ast[idx];
        match stmt {
            Stmt::ImportSo { .. } => {
                // Execute extend/import statement immediately
                let val = interp
                    .exec_statement(stmt)
                    .map_err(|e| format!("Statement {} error: {}", idx + 1, e))?;
                _last_val = val;
                idx += 1;
                // continue to next statement
            }
            _ => {
                // For all subsequent non-ImportSo statements, execute as usual
                break;
            }
        }
    }
    // After all leading ImportSo/extend statements have been executed, execute the rest
    for (stmt_index, stmt) in ast[idx..].iter().enumerate() {
        let val = interp
            .exec_statement(stmt)
            .map_err(|e| format!("Statement {} error: {}", idx + stmt_index + 1, e))?;
        _last_val = val;
    }

    loop {
        if let Some(poll_fn) = interp.get_dynamic_function("poll_events") {
            let _ = poll_fn.lock().unwrap()(&mut interp, vec![]);
        }
        let total_tasks = interp.poll_all();
        if total_tasks == 0 {
            break;
        }
    }

    interp.remove_all_pollers();

    Ok(())
}