ksl 0.1.8

KSL core library and interpreter
Documentation
//! # ksl::value
//!
//! Defines all the types and related functions for all the values
//! that are accepted in KSL.

use crate::{Environment, is_number_eq};

/// Values in KSL.
#[derive(Clone)]
pub enum Value {
    /// Unit value.
    Unit,
    /// Module name and environment of the module.
    Module(String, Environment),
    /// Symbol, which usually indicates the name of the variable.
    Identity(String),
    /// Atom value.
    Atom(String),
    /// String value.
    String(String),
    /// Number value.
    Number(f64),
    /// List value.
    List(Vec<Value>),
    /// Function parameter list, function body, and captured environment
    Lambda(Vec<String>, Box<Value>, Environment),
    /// Built-in function name.
    Builtin(&'static str),
    /// Plug-in function names and corresponding library names and function symbols.
    Plugin(String, String),
    /// Object type name and key-value pairs.
    Object(String, Environment),
    /// Raw object for plugin to store any object.
    ///
    /// # Warning
    ///
    /// RawObject instances must allocate data using the `std::alloc::alloc` method
    /// and release data using the `std::alloc::dealloc` method.
    /// After releasing the data, the original variable should be set to `Unit`.
    RawObject(String, *mut u8),
    /// Function argument list and function body,
    Apply(Vec<Value>, Box<Value>),
}

impl Value {
    /// Convert values into strings.
    pub fn into_string(&self, is_list: bool) -> String {
        match self {
            Value::Unit => String::from("()"),
            Value::Module(name, binds) => format!("Module[{}, {:?}]", name, binds.keys()),
            Value::Identity(id) => format!("{}", id),
            Value::Atom(a) => format!("#{}", a),
            Value::String(s) => {
                if is_list {
                    s.clone()
                } else {
                    format!("\"{}\"", s)
                }
            }
            Value::Number(n) => format!("{}", n),
            Value::List(elements) => format!(
                "{{{}}}",
                elements
                    .iter()
                    .map(|element| format!("{}", element.into_string(true)))
                    .collect::<Vec<String>>()
                    .join(", ")
            ),
            Value::Lambda(params, _, _) => {
                format!("λ({}) => <lambda>", params.join(", "),)
            }
            Value::Builtin(name) => format!("<<{}>>", name),
            Value::Plugin(lib, name) => format!("<${}:{}>", lib, name),
            Value::Object(type_name, env) => {
                format!(
                    "#({}){{{}}}",
                    type_name,
                    env.keys().cloned().collect::<Vec<String>>().join(", ")
                )
            }
            Value::RawObject(type_name, _) => format!("#({})<<Raw>>", type_name),
            Value::Apply(arguments, func) => format!(
                "({})[{}]",
                func.into_string(false),
                arguments
                    .iter()
                    .map(|val| format!("{}", val.into_string(false)))
                    .collect::<Vec<String>>()
                    .join(", ")
            ),
        }
    }
}

/// Check if two values are equal.
///
/// Modules and lambdas cannot compare to each other,
/// applies will never compare to other values.
impl std::cmp::PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Value::Unit, Value::Unit) => true,
            (Value::Identity(id1), Value::Identity(id2)) => id1 == id2,
            (Value::Atom(a1), Value::Atom(a2)) => a1 == a2,
            (Value::String(s1), Value::String(s2)) => s1 == s2,
            (Value::Number(n1), Value::Number(n2)) => is_number_eq(*n1, *n2),
            (Value::List(v1), Value::List(v2)) => v1.len() == v2.len() && v1.iter().zip(v2.iter()).all(|(e1, e2)| e1 == e2),
            (Value::Builtin(n1), Value::Builtin(n2)) => n1 == n2,
            _ => false,
        }
    }
}

impl std::fmt::Debug for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Value::List(elements) => write!(
                f,
                "{{{}}}",
                elements
                    .iter()
                    .map(|element| format!("{}", element.into_string(false)))
                    .collect::<Vec<String>>()
                    .join(", ")
            ),
            e => write!(f, "{}", e.into_string(false)),
        }
    }
}

impl std::fmt::Display for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Value::String(s) => write!(f, "{}", s),
            e => write!(f, "{:?}", e),
        }
    }
}