openscript 0.1.0

High-performance AI-powered scripting language runtime
Documentation
//! Value system for OpenScript
//!
//! This module defines the runtime value types and their operations.
//! Values are the fundamental data units that flow through OpenScript programs.

use crate::ast::{AssignmentOperator, BinaryOperator, Expression, Statement, Type};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;

/// Runtime values in OpenScript
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
    /// Null value
    Null,
    /// Boolean value
    Boolean(bool),
    /// Numeric value (64-bit float)
    Number(f64),
    /// String value
    String(String),
    /// Array of values
    Array(Vec<Value>),
    /// Object/map of key-value pairs
    Object(HashMap<String, Value>),
    /// Function value
    Function(Function),
    /// Native function (Rust closure)
    NativeFunction(NativeFunction),
}

/// User-defined function
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Function {
    /// The name of the function
    pub name: Option<String>,
    /// The parameters of the function
    pub parameters: Vec<String>,
    /// The body of the function
    pub body: Box<crate::ast::Statement>,
    /// The closure environment of the function
    pub closure: HashMap<String, Value>,
}

/// Native function wrapper
#[derive(Clone)]
pub struct NativeFunction {
    /// The name of the native function
    pub name: String,
    /// The arity of the function (None for variadic)
    pub arity: Option<usize>,
    /// The function pointer
    pub function: fn(&[Value]) -> Result<Value>,
}

impl fmt::Debug for NativeFunction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("NativeFunction")
            .field("name", &self.name)
            .field("arity", &self.arity)
            .finish()
    }
}

impl PartialEq for NativeFunction {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.arity == other.arity
    }
}

impl Serialize for NativeFunction {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(&format!("NativeFunction({})", self.name))
    }
}

impl<'de> Deserialize<'de> for NativeFunction {
    fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Err(serde::de::Error::custom(
            "Cannot deserialize native functions",
        ))
    }
}

impl Value {
    /// Create a new native function value
    pub fn native_function(
        name: impl Into<String>,
        arity: Option<usize>,
        function: fn(&[Value]) -> Result<Value>,
    ) -> Value {
        Value::NativeFunction(NativeFunction {
            name: name.into(),
            arity,
            function,
        })
    }

    /// Get the type name of this value
    pub fn type_name(&self) -> &'static str {
        match self {
            Value::Null => "null",
            Value::Boolean(_) => "boolean",
            Value::Number(_) => "number",
            Value::String(_) => "string",
            Value::Array(_) => "array",
            Value::Object(_) => "object",
            Value::Function(_) => "function",
            Value::NativeFunction(_) => "native_function",
        }
    }

    /// Check if this value is truthy
    pub fn is_truthy(&self) -> bool {
        match self {
            Value::Null => false,
            Value::Boolean(b) => *b,
            Value::Number(n) => *n != 0.0 && !n.is_nan(),
            Value::String(s) => !s.is_empty(),
            Value::Array(arr) => !arr.is_empty(),
            Value::Object(obj) => !obj.is_empty(),
            Value::Function(_) | Value::NativeFunction(_) => true,
        }
    }

    /// Check if this value is falsy
    pub fn is_falsy(&self) -> bool {
        !self.is_truthy()
    }

    /// Convert this value to a boolean
    pub fn to_boolean(&self) -> bool {
        self.is_truthy()
    }

    /// Convert this value to a number
    pub fn to_number(&self) -> Result<f64> {
        match self {
            Value::Number(n) => Ok(*n),
            Value::Boolean(b) => Ok(if *b { 1.0 } else { 0.0 }),
            Value::String(s) => {
                s.parse::<f64>()
                    .map_err(|_| Error::type_error("number", &format!("string '{}'", s)))
            }
            Value::Null => Ok(0.0),
            _ => Err(Error::type_error("number", self.type_name())),
        }
    }

    /// Convert this value to a string
    pub fn to_string(&self) -> String {
        match self {
            Value::Null => "null".to_string(),
            Value::Boolean(b) => b.to_string(),
            Value::Number(n) => {
                if n.fract() == 0.0 && n.is_finite() {
                    format!("{}", *n as i64)
                } else {
                    n.to_string()
                }
            }
            Value::String(s) => s.clone(),
            Value::Array(arr) => {
                let elements: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
                format!("[{}]", elements.join(", "))
            }
            Value::Object(obj) => {
                let pairs: Vec<String> = obj
                    .iter()
                    .map(|(k, v)| format!("{}: {}", k, v.to_string()))
                    .collect();
                format!("{{{}}}", pairs.join(", "))
            }
            Value::Function(f) => {
                format!("function {}({})", 
                    f.name.as_deref().unwrap_or("anonymous"),
                    f.parameters.join(", ")
                )
            }
            Value::NativeFunction(f) => format!("native function {}", f.name),
        }
    }

    /// Get array length (returns error if not an array)
    pub fn len(&self) -> Result<usize> {
        match self {
            Value::Array(arr) => Ok(arr.len()),
            Value::String(s) => Ok(s.chars().count()),
            Value::Object(obj) => Ok(obj.len()),
            _ => Err(Error::type_error("array, string, or object", self.type_name())),
        }
    }

    /// Check if array/object is empty
    pub fn is_empty(&self) -> Result<bool> {
        self.len().map(|len| len == 0)
    }

    /// Get element at index (for arrays and strings)
    pub fn get_index(&self, index: &Value) -> Result<Value> {
        match (self, index) {
            (Value::Array(arr), Value::Number(n)) => {
                let idx = *n as i64;
                if idx < 0 {
                    let positive_idx = arr.len() as i64 + idx;
                    if positive_idx >= 0 {
                        arr.get(positive_idx as usize)
                            .cloned()
                            .ok_or_else(|| Error::runtime_error("Array index out of bounds"))
                    } else {
                        Err(Error::runtime_error("Array index out of bounds"))
                    }
                } else {
                    arr.get(idx as usize)
                        .cloned()
                        .ok_or_else(|| Error::runtime_error("Array index out of bounds"))
                }
            }
            (Value::String(s), Value::Number(n)) => {
                let chars: Vec<char> = s.chars().collect();
                let idx = *n as i64;
                if idx < 0 {
                    let positive_idx = chars.len() as i64 + idx;
                    if positive_idx >= 0 {
                        chars
                            .get(positive_idx as usize)
                            .map(|&c| Value::String(c.to_string()))
                            .ok_or_else(|| Error::runtime_error("String index out of bounds"))
                    } else {
                        Err(Error::runtime_error("String index out of bounds"))
                    }
                } else {
                    chars
                        .get(idx as usize)
                        .map(|&c| Value::String(c.to_string()))
                        .ok_or_else(|| Error::runtime_error("String index out of bounds"))
                }
            }
            (Value::Object(obj), Value::String(key)) => {
                Ok(obj.get(key).cloned().unwrap_or(Value::Null))
            }
            _ => Err(Error::type_error(
                "array, string, or object with appropriate index type",
                &format!("{}[{}]", self.type_name(), index.type_name()),
            )),
        }
    }

    /// Set element at index (for arrays and objects)
    pub fn set_index(&mut self, index: &Value, value: Value) -> Result<()> {
        let self_type_name = self.type_name();
        match (self, index) {
            (Value::Array(arr), Value::Number(n)) => {
                let idx = *n as usize;
                if idx >= arr.len() {
                    arr.resize(idx + 1, Value::Null);
                }
                arr[idx] = value;
                Ok(())
            }
            (Value::Object(obj), Value::String(key)) => {
                obj.insert(key.clone(), value);
                Ok(())
            }
            _ => Err(Error::type_error(
                "array or object with appropriate index type",
                &format!("{}[{}]", self_type_name, index.type_name()),
            )),
        }
    }
}

impl AssignmentOperator {
    /// Convert to corresponding binary operator
    pub fn to_binary_op(&self) -> Option<BinaryOperator> {
        match self {
            AssignmentOperator::Assign => None,
            AssignmentOperator::PlusAssign => Some(BinaryOperator::Add),
            AssignmentOperator::MinusAssign => Some(BinaryOperator::Subtract),
            AssignmentOperator::MultiplyAssign => Some(BinaryOperator::Multiply),
            AssignmentOperator::DivideAssign => Some(BinaryOperator::Divide),
            AssignmentOperator::ModuloAssign => Some(BinaryOperator::Modulo),
        }
    }
}

impl Type {
    /// Check if this type is compatible with another type
    pub fn is_compatible_with(&self, other: &Type) -> bool {
        match (self, other) {
            (Type::Any, _) | (_, Type::Any) => true,
            (Type::String, Type::String) => true,
            (Type::Number, Type::Number) => true,
            (Type::Boolean, Type::Boolean) => true,
            (Type::Null, Type::Null) => true,
            (Type::Array(a), Type::Array(b)) => a.is_compatible_with(b),
            (Type::Object(a), Type::Object(b)) => {
                a.iter().all(|(key, type_a)| {
                    b.get(key).map_or(false, |type_b| type_a.is_compatible_with(type_b))
                })
            }
            (
                Type::Function {
                    parameters: params_a,
                    return_type: ret_a,
                },
                Type::Function {
                    parameters: params_b,
                    return_type: ret_b,
                },
            ) => {
                params_a.len() == params_b.len()
                    && params_a
                        .iter()
                        .zip(params_b.iter())
                        .all(|(a, b)| a.is_compatible_with(b))
                    && ret_a.is_compatible_with(ret_b)
            }
            _ => false,
        }
    }

    /// Get the default value for this type
    pub fn default_value(&self) -> Value {
        match self {
            Type::String => Value::String(String::new()),
            Type::Number => Value::Number(0.0),
            Type::Boolean => Value::Boolean(false),
            Type::Array(_) => Value::Array(Vec::new()),
            Type::Object(_) => Value::Object(HashMap::new()),
            Type::Null | Type::Any => Value::Null,
            Type::Function { .. } => Value::Null, // Functions don't have default values
        }
    }
}

/// AST visitor trait for traversing the syntax tree
pub trait Visitor<T> {
    /// Visit a statement
    fn visit_statement(&mut self, stmt: &Statement) -> T;
    /// Visit an expression
    fn visit_expression(&mut self, expr: &Expression) -> T;
}

/// Mutable AST visitor trait
pub trait VisitorMut<T> {
    /// Visit a statement with mutable access
    fn visit_statement(&mut self, stmt: &mut Statement) -> T;
    /// Visit an expression with mutable access
    fn visit_expression(&mut self, expr: &mut Expression) -> T;
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ast::BinaryOperator;

    #[test]
    fn test_operator_precedence() {
        assert!(BinaryOperator::Multiply.precedence() > BinaryOperator::Add.precedence());
        assert!(BinaryOperator::Power.precedence() > BinaryOperator::Multiply.precedence());
        assert!(BinaryOperator::And.precedence() < BinaryOperator::Equal.precedence());
    }

    #[test]
    fn test_type_compatibility() {
        assert!(Type::Any.is_compatible_with(&Type::String));
        assert!(Type::String.is_compatible_with(&Type::Any));
        assert!(!Type::String.is_compatible_with(&Type::Number));
        
        let array_str = Type::Array(Box::new(Type::String));
        let array_num = Type::Array(Box::new(Type::Number));
        assert!(!array_str.is_compatible_with(&array_num));
    }

    #[test]
    fn test_expression_properties() {
        let literal = Expression::Literal(Value::Number(42.0));
        assert!(literal.is_literal());
        assert!(literal.is_constant());

        let var = Expression::Variable("x".to_string());
        assert!(!var.is_literal());
        assert!(!var.is_constant());
    }
}