mumu 0.9.1

Lava Mumu is a language for those in the now and that know
Documentation
// src/parser/parse_utils.rs

use crate::parser::interpreter::eval::eval_expression;  // <-- Changed here
use crate::parser::interpreter::Interpreter;
use crate::parser::types::Value;
use crate::parser::tokens::tokenize_bracket_contents;
use indexmap::IndexMap;

/// parse_bracketed_array(...) => for expressions like `[1,2,3]` or `[x:1,y:2]`.
/// - If it sees `:`, it treats it as a keyed array (IndexMap).
/// - Otherwise it tries int[] or str[].
/// - For keyed arrays, it will `eval_expression` on each value-part to allow
///   expressions inside the brackets.
pub fn parse_bracketed_array(interp: &mut Interpreter, arr_str: &str) -> Result<Value, String> {
    // Remove surrounding brackets
    let inside = &arr_str[1..arr_str.len() - 1];
    let tokens = tokenize_bracket_contents(inside, interp.verbose)?;

    // If any token has a colon, assume it's a keyed array
    let has_colon = tokens.iter().any(|t| t.contains(':'));
    if has_colon {
        let mut map = IndexMap::new();
        for tok in tokens {
            if let Some(idx) = tok.find(':') {
                let key_part = tok[..idx].trim();
                let val_part = tok[idx + 1..].trim();

                // If the value is itself bracketed, parse recursively
                if val_part.starts_with('[') && val_part.ends_with(']') {
                    let subval = parse_bracketed_array(interp, val_part)?;
                    map.insert(key_part.to_string(), subval);
                } else {
                    // Evaluate the expression for the value-part
                    let subval = eval_expression(interp, val_part)?;
                    map.insert(key_part.to_string(), subval);
                }
            } else {
                return Err(format!("Expected ':' in '{}'", tok));
            }
        }
        Ok(Value::KeyedArray(map))
    } else {
        // Non-keyed => either int[] or str[]
        let mut all_ints = true;
        let mut int_vals = Vec::new();
        let mut str_vals = Vec::new();

        for t in tokens {
            // Try parse as i32
            if let Ok(n) = t.parse::<i32>() {
                if !str_vals.is_empty() {
                    return Err("Mixed int/string array not supported.".to_string());
                }
                int_vals.push(n);
            } else {
                // Fallback to string array
                all_ints = false;
                let unquoted = t.trim_matches('"').to_string();
                if !int_vals.is_empty() {
                    return Err("Mixed int/string array not supported.".to_string());
                }
                str_vals.push(unquoted);
            }
        }

        if all_ints {
            Ok(Value::IntArray(int_vals))
        } else {
            Ok(Value::StrArray(str_vals))
        }
    }
}

/// find_lambda_arrow(...) => scans an expression string for a top-level `=>`.
/// Returns Some(index) if found, ignoring parentheses/quotes, otherwise None.
pub fn find_lambda_arrow(expr: &str) -> Option<usize> {
    let mut in_quotes = false;
    let mut depth_paren = 0;
    let chars: Vec<char> = expr.chars().collect();
    let mut i = 0;

    while i + 1 < chars.len() {
        let c = chars[i];
        if c == '"' {
            in_quotes = !in_quotes;
        } else if !in_quotes {
            if c == '(' {
                depth_paren += 1;
            } else if c == ')' && depth_paren > 0 {
                depth_paren -= 1;
            } else if depth_paren == 0 && c == '=' && chars[i + 1] == '>' {
                return Some(i);
            }
        }
        i += 1;
    }
    None
}

/// parse_param_list(...) => splits something like `(acc, x)` into ["acc","x"].
/// It will strip parentheses if present, then split by commas.
pub fn parse_param_list(param_str: &str) -> Result<Vec<String>, String> {
    let trimmed = param_str.trim();
    let without_parens = if trimmed.starts_with('(') && trimmed.ends_with(')') {
        &trimmed[1..trimmed.len() - 1]
    } else {
        trimmed
    };
    if without_parens.is_empty() {
        return Ok(vec![]);
    }
    Ok(without_parens
        .split(',')
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty())
        .collect())
}