ksl 0.1.5

KSL core library and interpreter
Documentation
use crate::{
    Environment,
    builtin::{DEFAULT_KSL_PATH, FILE_EXT, KSL_PATH_ENV, MODULE_NAME_ENV, MODULE_PATH_ENV},
    eval::{apply::eval_apply, eval},
    expand_tilde,
    init_environment,
    value::Value,
};

pub(crate) fn builtin(args: &[Value], env: &Environment) -> Option<(Value, Environment)> {
    let mut local_environment = Environment::new();
    if let [Value::Identity(name), path_string] = &args[..] {
        if env.contains_key(name) {
            eprintln!(
                concat!(
                    "Error[ksl::builtin::load]: ",
                    "Symbol `{}` cannot be rebound."
                ),
                name
            );
            None
        } else {
            match eval_apply(path_string, env) {
                Some((Value::String(path), _)) => {
                    let path_with_ext = format!("{}.{}", path, FILE_EXT);
                    let real_path = if let Ok(true) = std::fs::exists(&path_with_ext) {
                        path_with_ext
                    } else {
                        let ksl_path = match std::env::var(KSL_PATH_ENV) {
                            Ok(p) => p,
                            Err(_) => String::from(DEFAULT_KSL_PATH),
                        };
                        if let Some(ksl_path) = expand_tilde(ksl_path) {
                            format!("{}/{}", ksl_path, path_with_ext)
                        } else {
                            return None;
                        }
                    };
                    if let Some(Value::List(module_paths)) = env.get(MODULE_PATH_ENV) {
                        if module_paths.contains(&Value::String(real_path.clone())) {
                            eprintln!(concat!(
                                "Error[ksl::builtin::load]: ",
                                "Module has already been loaded."
                            ));
                            None
                        } else {
                            let mut local_env = init_environment();
                            let mut currnet_paths = module_paths.clone();
                            currnet_paths.push(Value::String(real_path.clone()));
                            let _ = local_env.insert(
                                String::from(MODULE_PATH_ENV),
                                Value::List(currnet_paths.clone()),
                            );
                            let _ = local_environment.insert(
                                String::from(MODULE_PATH_ENV),
                                Value::List(currnet_paths),
                            );
                            match std::fs::File::options()
                                .read(true)
                                .open(&real_path)
                                .and_then(|f| {
                                    let mut buffer = String::new();
                                    std::io::Read::read_to_string(
                                        &mut std::io::BufReader::new(f),
                                        &mut buffer,
                                    )
                                    .map(|_| buffer)
                                }) {
                                Ok(load_file) => match eval(&load_file, &local_env) {
                                    Some((_, mut module_env)) => {
                                        if let Some(Value::String(module_name)) =
                                            module_env.remove(MODULE_NAME_ENV)
                                        {
                                            let _ = local_environment.insert(
                                                name.clone(),
                                                Value::Module(module_name, module_env),
                                            );
                                            Some((Value::Unit, local_environment))
                                        } else {
                                            eprintln!(
                                                concat!(
                                                    "Error[ksl::builtin::load]: ",
                                                    "`{}` is not a module."
                                                ),
                                                real_path
                                            );
                                            None
                                        }
                                    }
                                    None => {
                                        eprintln!(
                                            concat!(
                                                "Error[ksl::builtin::load]: ",
                                                "Failed to load module `{}`."
                                            ),
                                            name
                                        );
                                        None
                                    }
                                },
                                Err(_) => {
                                    eprintln!(
                                        concat!(
                                            "Error[ksl::builtin::load]: ",
                                            "Unable to read contents of file at `{}`."
                                        ),
                                        real_path
                                    );
                                    None
                                }
                            }
                        }
                    } else {
                        eprintln!(concat!(
                            "Error[ksl::builtin::load]: ",
                            "Uninitialized environment."
                        ));
                        None
                    }
                }
                Some((e, _)) => {
                    eprintln!(
                        concat!(
                            "Error[ksl::builtin::load]: ",
                            "Expected a string, but got `{:?}`."
                        ),
                        e
                    );
                    None
                }
                None => {
                    eprintln!(
                        concat!(
                            "Error[ksl::builtin::load]: ",
                            "Cannot evaluate expression {:?}."
                        ),
                        path_string
                    );
                    None
                }
            }
        }
    } else {
        eprintln!(
            concat!(
                "Error[ksl::builtin::load]: ",
                "Only accepts a symbol ",
                "and a file path without suffix as arguments, ",
                "but got {:?}.",
            ),
            args
        );
        None
    }
}