ksl 0.1.30

KSL core library and interpreter
Documentation
//! # KSL core library
//!
//! [![github]](https://github.com/kands-code/rswk)
//! [![crates-io]](https://crates.io/crates/ksl)
//! [![docs-rs]](https://docs.rs/ksl)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//! # Description
//!
//! This library provides a comprehensive set of functions
//! for tokenizing and executing KSL code.

mod builtin;
pub mod eval;
pub mod token;
pub mod value;

/// List of built-in functions.
pub use builtin::BUILTIN_FUNCTIONS;
/// Literal value corresponding to the error tag in Atom.
pub use builtin::ERR_SYMBOL;
/// Literal value corresponding to the boolean false in Atom.
pub use builtin::FALSE_SYMBOL;
/// Module name in environment as key's literal value.
pub use builtin::MODULE_NAME_ENV;
/// Imported module path list in environment as key's literal value.
pub use builtin::MODULE_PATH_ENV;
/// Literal value corresponding to the ok tag in Atom.
pub use builtin::OK_SYMBOL;
/// Literal value corresponding to the boolean true in Atom.
pub use builtin::TRUE_SYMBOL;

use crate::{builtin::ENV_ARGS_ENV, value::Value};

/// Dict type in KSL
///
/// Currently using `FxHashMap` as the underlying implementation.
pub type Dict = rustc_hash::FxHashMap<std::sync::Arc<str>, std::sync::Arc<Value>>;

/// Scope represents a single level in the environment chain.
#[derive(Debug)]
pub struct Scope {
    /// The variable store for this scope.
    ///
    /// Mutable and thread-safe.
    pub store: parking_lot::RwLock<Dict>,
    /// The parent scope.
    ///
    /// Immutable reference.
    pub parent: Option<Environment>,
}

/// Environment type in KSL
pub type Environment = std::sync::Arc<Scope>;

/// Initial thread stack size.
pub const INIT_STACK_SIZE: usize = 8 * 1024 * 1024;

/// Initial Environment in KSL
///
/// Includes built-in functions and various constants in KSL.
///
/// # Example
///
/// ```rust
/// let _ = ksl::init_environment(None); 
/// ```
pub fn init_environment(env_args: Option<Value>) -> Environment {
    let mut store = Dict::default();
    // bifs
    for &func in BUILTIN_FUNCTIONS.iter() {
        store.insert(
            std::sync::Arc::from(func),
            std::sync::Arc::new(Value::Builtin(func)),
        );
    }
    // module loading paths
    store.insert(
        MODULE_PATH_ENV.clone(),
        std::sync::Arc::new(Value::Object(MODULE_PATH_ENV.clone(), Box::default())),
    );
    // environment variables
    if let Some(val) = env_args
        && let Value::List(_) = val
    {
        store.insert(ENV_ARGS_ENV.clone(), std::sync::Arc::new(val));
    }
    // construct environment
    std::sync::Arc::new(Scope {
        store: parking_lot::RwLock::new(store),
        parent: None,
    })
}

/// Find a variable in the environment chain.
pub fn read_from_environment(env: &Environment, key: &str) -> Option<std::sync::Arc<Value>> {
    let mut current = env;
    loop {
        // try currnet scope
        if let Some(val) = current.store.upgradable_read().get(key) {
            return Some(val.clone());
        }
        // find in parent scope
        match &current.parent {
            Some(parent) => {
                current = parent;
            }
            None => {
                // otherwise
                return None;
            }
        }
    }
}

/// Find the environment where the variable is defined.
pub fn find_define_environment(env: &Environment, key: &str) -> Option<Environment> {
    let mut current = env;
    loop {
        // try currnet scope
        if current.store.upgradable_read().contains_key(key) {
            return Some(current.clone());
        }
        // find in parent scope
        match current.parent.as_ref() {
            Some(parent) => {
                current = parent;
            }
            None => {
                // otherwise
                return None;
            }
        }
    }
}

/// Check if two numbers are equal.
pub fn is_number_eq(n1: f64, n2: f64) -> bool { (n1 == n2) || (n1 - n2).abs() <= n1.abs().max(n2.abs()) * f64::EPSILON }

/// Validate symbols.
pub fn is_invalid_symbol(sym: &std::sync::Arc<str>) -> bool {
    sym.as_ref() == ENV_ARGS_ENV.as_ref()
        || sym.as_ref() == MODULE_NAME_ENV.as_ref()
        || sym.as_ref() == MODULE_PATH_ENV.as_ref()
        || BUILTIN_FUNCTIONS.contains(&sym.as_ref())
}

/// Expand the tilde at the start of a path.
pub fn expand_tilde(path: std::sync::Arc<str>) -> Result<std::path::PathBuf, std::sync::Arc<str>> {
    match path.strip_prefix("~/") {
        Some(rest) => std::env::home_dir()
            .ok_or_else(|| {
                std::sync::Arc::from(
                    concat!(
                        "Error[ksl::expand_tilde]: ",
                        "Failed to get user home directory path."
                    )
                    .to_string(),
                )
            })
            .map(|p| p.join(rest)),
        None => Ok(std::path::PathBuf::from(path.as_ref())),
    }
}