glyph-runtime 0.0.1

Runtime execution engine for the Glyph programming language
Documentation
//! Intermediate Representation (IR) for Glyph programs
//!
//! This module defines the IR that sits between the AST and bytecode.
//! The IR is designed to be:
//! - Easy to generate from the AST
//! - Easy to optimize
//! - Easy to convert to bytecode

use glyph_types::Value;
use std::collections::HashMap;

/// A complete IR module representing a Glyph program
#[derive(Debug, Clone)]
pub struct IRModule {
    /// Program metadata
    pub program: IRProgram,
    /// Global functions
    pub functions: HashMap<String, IRFunction>,
    /// Entry point (main function name)
    pub entry_point: Option<String>,
}

/// Program metadata from @program decorator
#[derive(Debug, Clone)]
pub struct IRProgram {
    pub name: String,
    pub version: String,
    pub requires: Vec<String>,
}

/// An IR function
#[derive(Debug, Clone)]
pub struct IRFunction {
    /// Function name
    pub name: String,
    /// Parameter names
    pub params: Vec<String>,
    /// Local variable count (for stack allocation)
    pub locals_count: usize,
    /// Function body as basic blocks
    pub blocks: Vec<IRBlock>,
    /// Is this an async function?
    pub is_async: bool,
}

/// A basic block - sequence of instructions with single entry/exit
#[derive(Debug, Clone)]
pub struct IRBlock {
    /// Block label
    pub label: String,
    /// Instructions in this block
    pub instructions: Vec<IRInstruction>,
    /// Terminal instruction (jump, return, etc.)
    pub terminator: IRTerminator,
}

/// IR instructions - higher level than VM bytecode
#[derive(Debug, Clone)]
pub enum IRInstruction {
    /// Load a constant value
    LoadConst(Value),
    /// Load a variable
    LoadVar(String),
    /// Store to a variable (creates new binding)
    StoreVar(String),
    /// Binary operation
    BinaryOp(BinaryOp),
    /// Unary operation
    UnaryOp(UnaryOp),
    /// Call a function
    Call { func: String, args_count: usize },
    /// Call an intrinsic
    CallIntrinsic { name: String, args_count: usize },
    /// Build a list
    MakeList(usize),
    /// Build a dict
    MakeDict(usize),
    /// Get attribute
    GetAttr(String),
    /// Get item by index
    GetItem,
    /// Call method on object
    CallMethod { name: String, argc: usize },
    /// Await a promise
    Await,
    /// Duplicate top of stack
    Dup,
    /// Pop top of stack
    Pop,
}

/// Block terminators
#[derive(Debug, Clone)]
pub enum IRTerminator {
    /// Return from function
    Return,
    /// Unconditional jump
    Jump(String),
    /// Conditional jump
    JumpIf {
        then_block: String,
        else_block: String,
    },
    /// Pattern match
    Match {
        cases: Vec<(IRPattern, String)>,
        default: Option<String>,
    },
}

/// IR patterns for matching
#[derive(Debug, Clone)]
pub enum IRPattern {
    /// Match a literal value
    Literal(Value),
    /// Bind to a variable
    Variable(String),
    /// Constructor pattern
    Constructor { name: String, args: Vec<IRPattern> },
    /// Wildcard pattern
    Wildcard,
}

/// Binary operations
#[derive(Debug, Clone, Copy)]
pub enum BinaryOp {
    // Arithmetic
    Add,
    Sub,
    Mul,
    Div,
    Mod,
    Pow,
    // Comparison
    Eq,
    Ne,
    Lt,
    Le,
    Gt,
    Ge,
    // Logical
    And,
    Or,
}

/// Unary operations
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
    Neg,
    Not,
}

/// IR builder for constructing IR from AST
pub struct IRBuilder {
    /// Current module being built
    pub module: IRModule,
    /// Current function being built
    current_function: Option<String>,
    /// Current block being built
    current_block: Option<String>,
    /// Variable name generator
    var_counter: usize,
    /// Block label generator
    block_counter: usize,
    /// Track if current block has been terminated
    block_terminated: bool,
}

impl IRBuilder {
    pub fn new(name: String, version: String, requires: Vec<String>) -> Self {
        Self {
            module: IRModule {
                program: IRProgram {
                    name,
                    version,
                    requires,
                },
                functions: HashMap::new(),
                entry_point: None,
            },
            current_function: None,
            current_block: None,
            var_counter: 0,
            block_counter: 0,
            block_terminated: false,
        }
    }

    /// Generate a fresh variable name
    pub fn fresh_var(&mut self) -> String {
        let var = format!("_t{}", self.var_counter);
        self.var_counter += 1;
        var
    }

    /// Generate a fresh block label
    pub fn fresh_label(&mut self) -> String {
        let label = format!("L{}", self.block_counter);
        self.block_counter += 1;
        label
    }

    /// Start building a new function
    pub fn start_function(&mut self, name: String, params: Vec<String>, is_async: bool) {
        let entry_label = self.fresh_label();
        let func = IRFunction {
            name: name.clone(),
            params,
            locals_count: 0,
            blocks: vec![IRBlock {
                label: entry_label.clone(),
                instructions: vec![],
                terminator: IRTerminator::Return,
            }],
            is_async,
        };
        self.module.functions.insert(name.clone(), func);
        self.current_function = Some(name);
        self.current_block = Some(entry_label);
        self.block_terminated = false;
    }

    /// Add an instruction to the current block
    pub fn emit(&mut self, inst: IRInstruction) {
        if let (Some(func_name), Some(block_label)) = (&self.current_function, &self.current_block)
        {
            if let Some(func) = self.module.functions.get_mut(func_name) {
                if let Some(block) = func.blocks.iter_mut().find(|b| b.label == *block_label) {
                    block.instructions.push(inst);
                }
            }
        }
    }

    /// Set the terminator for the current block
    pub fn terminate(&mut self, term: IRTerminator) {
        if let (Some(func_name), Some(block_label)) = (&self.current_function, &self.current_block)
        {
            if let Some(func) = self.module.functions.get_mut(func_name) {
                if let Some(block) = func.blocks.iter_mut().find(|b| b.label == *block_label) {
                    block.terminator = term;
                    self.block_terminated = true;
                }
            }
        }
    }

    /// Create a new block in the current function
    pub fn new_block(&mut self, label: String) {
        if let Some(func_name) = &self.current_function {
            if let Some(func) = self.module.functions.get_mut(func_name) {
                func.blocks.push(IRBlock {
                    label: label.clone(),
                    instructions: vec![],
                    terminator: IRTerminator::Return,
                });
                self.current_block = Some(label);
                self.block_terminated = false; // Reset termination status for new block
            }
        }
    }

    /// Check if the current block is already terminated
    pub fn is_terminated(&self) -> bool {
        self.block_terminated
    }

    /// Finish building and return the module
    pub fn finish(mut self) -> IRModule {
        // Set entry point if main function exists
        if self.module.functions.contains_key("main") {
            self.module.entry_point = Some("main".to_string());
        }
        self.module
    }
}