glyph-runtime 0.0.1

Runtime execution engine for the Glyph programming language
Documentation
use glyph_types::Value;
use std::collections::HashMap;

/// Represents a call frame in the VM's call stack.
///
/// Each function call creates a new frame that contains:
/// - The function being executed
/// - The instruction pointer within that function
/// - Local variables
/// - The stack base pointer for this frame
#[derive(Debug, Clone)]
pub struct Frame {
    /// Index of the function being executed
    pub function_idx: usize,
    /// Current instruction pointer within the function
    pub ip: usize,
    /// Base pointer in the stack for this frame
    pub bp: usize,
    /// Local variables for this frame
    pub locals: HashMap<String, Value>,
    /// Return address (instruction pointer to return to)
    pub return_ip: Option<usize>,
    /// Function index to return to
    pub return_function: Option<usize>,
}

impl Frame {
    /// Creates a new call frame for a function call
    pub fn new(function_idx: usize, bp: usize) -> Self {
        Frame {
            function_idx,
            ip: 0,
            bp,
            locals: HashMap::new(),
            return_ip: None,
            return_function: None,
        }
    }

    /// Creates a new call frame with return information
    pub fn with_return(
        function_idx: usize,
        bp: usize,
        return_ip: usize,
        return_function: usize,
    ) -> Self {
        Frame {
            function_idx,
            ip: 0,
            bp,
            locals: HashMap::new(),
            return_ip: Some(return_ip),
            return_function: Some(return_function),
        }
    }

    /// Sets a local variable in this frame
    pub fn set_local(&mut self, name: String, value: Value) {
        self.locals.insert(name, value);
    }

    /// Gets a local variable from this frame
    pub fn get_local(&self, name: &str) -> Option<&Value> {
        self.locals.get(name)
    }

    /// Checks if this is a tail call position
    /// (i.e., the next instruction would be a return)
    pub fn is_tail_position(
        &self,
        next_instruction: Option<&crate::instruction::Instruction>,
    ) -> bool {
        matches!(
            next_instruction,
            Some(crate::instruction::Instruction::Return)
        )
    }
}

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

    #[test]
    fn test_frame_creation() {
        let frame = Frame::new(0, 10);
        assert_eq!(frame.function_idx, 0);
        assert_eq!(frame.ip, 0);
        assert_eq!(frame.bp, 10);
        assert!(frame.locals.is_empty());
        assert!(frame.return_ip.is_none());
        assert!(frame.return_function.is_none());
    }

    #[test]
    fn test_frame_with_return() {
        let frame = Frame::with_return(1, 20, 15, 0);
        assert_eq!(frame.function_idx, 1);
        assert_eq!(frame.bp, 20);
        assert_eq!(frame.return_ip, Some(15));
        assert_eq!(frame.return_function, Some(0));
    }

    #[test]
    fn test_locals() {
        let mut frame = Frame::new(0, 0);

        frame.set_local("x".to_string(), Value::Int(42));
        frame.set_local("y".to_string(), Value::Str("hello".to_string()));

        assert_eq!(frame.get_local("x"), Some(&Value::Int(42)));
        assert_eq!(frame.get_local("y"), Some(&Value::Str("hello".to_string())));
        assert_eq!(frame.get_local("z"), None);
    }
}