litex-lang 0.9.88-beta

The Formal Way to Write Math as It Looks
Documentation
use crate::pipeline::run_source_code;
use crate::prelude::*;
use std::fs;
use std::path::{Path, PathBuf};

fn resolve_run_file_path(user_path: &str, current_lit_file_path: &str) -> String {
    let user = Path::new(user_path);
    if user.is_absolute() {
        return user_path.to_string();
    }
    let current = Path::new(current_lit_file_path);
    let base_dir = current.parent().unwrap_or_else(|| Path::new(""));
    base_dir.join(user).to_string_lossy().into_owned()
}

pub fn run_stmt_at_global_env(
    stmt: &Stmt,
    runtime: &mut Runtime,
) -> Result<StmtResult, RuntimeError> {
    match stmt {
        Stmt::RunFileStmt(run_file_stmt) => {
            return run_file(run_file_stmt, runtime);
        }
        Stmt::RunFileInStd(run_file_in_std) => {
            return run_file_in_std_folder(run_file_in_std, runtime);
        }
        Stmt::ImportStmt(import_stmt) => {
            return run_import_stmt(import_stmt, runtime);
        }
        _ => {
            return runtime.exec_stmt(stmt);
        }
    }
}

fn run_file(
    _run_file_stmt: &RunFileStmt,
    _runtime: &mut Runtime,
) -> Result<StmtResult, RuntimeError> {
    let current_lit_path = _runtime.module_manager.current_file_path_rc();
    let path = resolve_run_file_path(_run_file_stmt.file_path.as_str(), current_lit_path.as_ref());
    run_file_at_resolved_path(_run_file_stmt.clone().into(), path, _runtime)
}

fn run_file_in_std_folder(
    run_file_in_std: &RunFileInStd,
    runtime: &mut Runtime,
) -> Result<StmtResult, RuntimeError> {
    let path = resolve_run_file_in_std_path(run_file_in_std)?;
    run_file_at_resolved_path(run_file_in_std.clone().into(), path, runtime)
}

fn run_file_at_resolved_path(
    stmt: Stmt,
    path: String,
    runtime: &mut Runtime,
) -> Result<StmtResult, RuntimeError> {
    let content = fs::read_to_string(path.as_str()).map_err(|_| {
        RuntimeError::ExecStmtError({
            let lf = stmt.line_file();
            RuntimeErrorStruct::new(
                Some(stmt.clone()),
                format!("Failed to read file: {}", path.as_str()),
                lf,
                None,
                vec![],
            )
        })
    })?;

    let current_file_index = runtime.module_manager.current_file_index;
    runtime.new_file_and_update_runtime_with_file_content(path.as_str());

    let result = run_source_code(content.as_str(), runtime);

    runtime.change_file_index_to(current_file_index);

    if let Some(error) = result.1 {
        return Err(error);
    };

    return Ok((NonFactualStmtSuccess::new(stmt, InferResult::new(), result.0)).into());
}

fn resolve_run_file_in_std_path(run_file_in_std: &RunFileInStd) -> Result<String, RuntimeError> {
    let relative_main_lit_path = Path::new(run_file_in_std.file_path.as_str()).join("main.lit");
    let std_roots = candidate_std_roots();
    for std_root in std_roots.iter() {
        let candidate = std_root.join(&relative_main_lit_path);
        if candidate.is_file() {
            return Ok(candidate.to_string_lossy().into_owned());
        }
    }

    let attempted_paths = std_roots
        .iter()
        .map(|root| {
            root.join(&relative_main_lit_path)
                .to_string_lossy()
                .into_owned()
        })
        .collect::<Vec<_>>()
        .join(", ");
    let st: Stmt = run_file_in_std.clone().into();
    let lf = st.line_file();
    Err(RuntimeError::ExecStmtError(RuntimeErrorStruct::new(
        Some(st),
        format!(
            "Failed to find std run_file target `{}`. Tried: {}",
            run_file_in_std.file_path, attempted_paths
        ),
        lf,
        None,
        vec![],
    )))
}

fn candidate_std_roots() -> Vec<PathBuf> {
    let mut roots: Vec<PathBuf> = Vec::new();
    push_std_root_if_new(&mut roots, PathBuf::from("std"));

    if let Some(env_std_path) = std::env::var_os("LITEX_STD_PATH") {
        push_std_root_if_new(&mut roots, PathBuf::from(env_std_path));
    }

    push_std_root_if_new(
        &mut roots,
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("std"),
    );

    if let Ok(current_exe) = std::env::current_exe() {
        for ancestor in current_exe.ancestors() {
            push_std_root_if_new(&mut roots, ancestor.join("std"));
            push_std_root_if_new(&mut roots, ancestor.join("share").join("litex").join("std"));
        }
    }

    push_std_root_if_new(&mut roots, PathBuf::from("/opt/homebrew/share/litex/std"));
    push_std_root_if_new(&mut roots, PathBuf::from("/usr/local/share/litex/std"));
    push_std_root_if_new(&mut roots, PathBuf::from("/usr/share/litex/std"));

    roots
}

fn push_std_root_if_new(roots: &mut Vec<PathBuf>, candidate: PathBuf) {
    let candidate_string = candidate.to_string_lossy().into_owned();
    if roots
        .iter()
        .all(|existing| existing.to_string_lossy() != candidate_string)
    {
        roots.push(candidate);
    }
}

fn run_import_stmt(
    _import_stmt: &ImportStmt,
    _runtime: &mut Runtime,
) -> Result<StmtResult, RuntimeError> {
    unimplemented!();
}