mumu 0.10.0

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

use crate::parser::lexer::tokenize;
use crate::parser::core::driver::parse_tokens;
use crate::parser::interpreter::{Interpreter, DynamicFn, DynamicFnInfo};
use crate::parser::types::{FunctionValue, Value};
use std::sync::{Arc, Mutex};
use std::fs;

fn include_bridge_fn(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err("include(path) expects exactly one argument".to_string());
    }
    let path = match args.remove(0) {
        Value::SingleString(s) => s,
        Value::StrArray(ref arr) if arr.len() == 1 => arr[0].clone(),
        other => return Err(format!("include(path): path must be a string, got {:?}", other)),
    };

    let code = fs::read_to_string(&path)
        .map_err(|e| format!("include: failed to read '{}': {}", path, e))?;

    let tokens = tokenize(&code, interp.is_verbose())
        .map_err(|lex_err| format!("include: lex error: {}", lex_err))?;

    let ast = parse_tokens(&tokens, interp.is_verbose())
        .map_err(|parse_err| format!("include: parse error: {}", parse_err))?;

    let mut last_val = Value::Bool(true);
    for (stmt_index, stmt) in ast.iter().enumerate() {
        let val = interp.exec_statement(stmt)
            .map_err(|e| format!("include: statement {} error: {}", stmt_index + 1, e))?;
        last_val = val;
    }
    Ok(last_val)
}

pub fn register_include(interp: &mut Interpreter) {
    let func: DynamicFn = Arc::new(Mutex::new(include_bridge_fn));
    let info = DynamicFnInfo::new(func, false); // Not pure: it mutates state
    interp.register_dynamic_function_ex("include", info);
    interp.set_variable(
        "include",
        Value::Function(Box::new(FunctionValue::Named("include".to_string())))
    );
}