glyph-runtime 0.0.1

Runtime execution engine for the Glyph programming language
Documentation
//! Stack implementation for the Glyph VM

use glyph_types::Value;

/// Stack for the VM with configurable size limit
#[derive(Debug, Clone)]
pub struct Stack {
    items: Vec<Value>,
    max_size: usize,
}

impl Stack {
    /// Create a new stack with the given maximum size
    pub fn new(max_size: usize) -> Self {
        Stack {
            items: Vec::with_capacity(1024),
            max_size,
        }
    }

    /// Push a value onto the stack
    pub fn push(&mut self, value: Value) -> Result<(), StackError> {
        if self.items.len() >= self.max_size {
            return Err(StackError::Overflow);
        }
        self.items.push(value);
        Ok(())
    }

    /// Pop a value from the stack
    pub fn pop(&mut self) -> Result<Value, StackError> {
        self.items.pop().ok_or(StackError::Underflow)
    }

    /// Peek at the top value without removing it
    pub fn peek(&self) -> Result<&Value, StackError> {
        self.items.last().ok_or(StackError::Underflow)
    }

    /// Peek at the nth value from the top (0 = top)
    pub fn peek_n(&self, n: usize) -> Result<&Value, StackError> {
        let len = self.items.len();
        if n >= len {
            return Err(StackError::Underflow);
        }
        Ok(&self.items[len - n - 1])
    }

    /// Duplicate the top value
    pub fn dup(&mut self) -> Result<(), StackError> {
        let value = self.peek()?.clone();
        self.push(value)
    }

    /// Swap the top two values
    pub fn swap(&mut self) -> Result<(), StackError> {
        let len = self.items.len();
        if len < 2 {
            return Err(StackError::Underflow);
        }
        self.items.swap(len - 1, len - 2);
        Ok(())
    }

    /// Get the current stack depth
    pub fn depth(&self) -> usize {
        self.items.len()
    }

    /// Clear the stack
    pub fn clear(&mut self) {
        self.items.clear();
    }

    /// Get a slice of the stack from a base pointer
    pub fn slice_from(&self, bp: usize) -> &[Value] {
        &self.items[bp..]
    }

    /// Remove multiple items from the top
    pub fn pop_n(&mut self, n: usize) -> Result<Vec<Value>, StackError> {
        if self.items.len() < n {
            return Err(StackError::Underflow);
        }
        let split_at = self.items.len() - n;
        Ok(self.items.split_off(split_at))
    }

    /// Truncate stack to a specific depth
    pub fn truncate(&mut self, depth: usize) {
        self.items.truncate(depth);
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum StackError {
    Overflow,
    Underflow,
}

impl std::fmt::Display for StackError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            StackError::Overflow => write!(f, "Stack overflow"),
            StackError::Underflow => write!(f, "Stack underflow"),
        }
    }
}

impl std::error::Error for StackError {}

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

    #[test]
    fn test_stack_operations() {
        let mut stack = Stack::new(100);

        // Test push and pop
        stack.push(Value::Int(42)).unwrap();
        stack.push(Value::Str("hello".to_string())).unwrap();

        assert_eq!(stack.depth(), 2);
        assert_eq!(stack.pop().unwrap(), Value::Str("hello".to_string()));
        assert_eq!(stack.pop().unwrap(), Value::Int(42));

        // Test underflow
        assert!(matches!(stack.pop(), Err(StackError::Underflow)));
    }

    #[test]
    fn test_stack_overflow() {
        let mut stack = Stack::new(2);

        stack.push(Value::Int(1)).unwrap();
        stack.push(Value::Int(2)).unwrap();

        // Should overflow
        assert!(matches!(
            stack.push(Value::Int(3)),
            Err(StackError::Overflow)
        ));
    }

    #[test]
    fn test_dup_swap() {
        let mut stack = Stack::new(100);

        stack.push(Value::Int(1)).unwrap();
        stack.push(Value::Int(2)).unwrap();

        // Test dup
        stack.dup().unwrap();
        assert_eq!(stack.depth(), 3);
        assert_eq!(stack.peek().unwrap(), &Value::Int(2));

        // Test swap
        stack.pop().unwrap(); // Remove duplicated value
        stack.swap().unwrap();
        assert_eq!(stack.pop().unwrap(), Value::Int(1));
        assert_eq!(stack.pop().unwrap(), Value::Int(2));
    }
}