glyph_runtime/
ir.rs

1//! Intermediate Representation (IR) for Glyph programs
2//!
3//! This module defines the IR that sits between the AST and bytecode.
4//! The IR is designed to be:
5//! - Easy to generate from the AST
6//! - Easy to optimize
7//! - Easy to convert to bytecode
8
9use glyph_types::Value;
10use std::collections::HashMap;
11
12/// A complete IR module representing a Glyph program
13#[derive(Debug, Clone)]
14pub struct IRModule {
15    /// Program metadata
16    pub program: IRProgram,
17    /// Global functions
18    pub functions: HashMap<String, IRFunction>,
19    /// Entry point (main function name)
20    pub entry_point: Option<String>,
21}
22
23/// Program metadata from @program decorator
24#[derive(Debug, Clone)]
25pub struct IRProgram {
26    pub name: String,
27    pub version: String,
28    pub requires: Vec<String>,
29}
30
31/// An IR function
32#[derive(Debug, Clone)]
33pub struct IRFunction {
34    /// Function name
35    pub name: String,
36    /// Parameter names
37    pub params: Vec<String>,
38    /// Local variable count (for stack allocation)
39    pub locals_count: usize,
40    /// Function body as basic blocks
41    pub blocks: Vec<IRBlock>,
42    /// Is this an async function?
43    pub is_async: bool,
44}
45
46/// A basic block - sequence of instructions with single entry/exit
47#[derive(Debug, Clone)]
48pub struct IRBlock {
49    /// Block label
50    pub label: String,
51    /// Instructions in this block
52    pub instructions: Vec<IRInstruction>,
53    /// Terminal instruction (jump, return, etc.)
54    pub terminator: IRTerminator,
55}
56
57/// IR instructions - higher level than VM bytecode
58#[derive(Debug, Clone)]
59pub enum IRInstruction {
60    /// Load a constant value
61    LoadConst(Value),
62    /// Load a variable
63    LoadVar(String),
64    /// Store to a variable (creates new binding)
65    StoreVar(String),
66    /// Binary operation
67    BinaryOp(BinaryOp),
68    /// Unary operation
69    UnaryOp(UnaryOp),
70    /// Call a function
71    Call { func: String, args_count: usize },
72    /// Call an intrinsic
73    CallIntrinsic { name: String, args_count: usize },
74    /// Build a list
75    MakeList(usize),
76    /// Build a dict
77    MakeDict(usize),
78    /// Get attribute
79    GetAttr(String),
80    /// Get item by index
81    GetItem,
82    /// Call method on object
83    CallMethod { name: String, argc: usize },
84    /// Await a promise
85    Await,
86    /// Duplicate top of stack
87    Dup,
88    /// Pop top of stack
89    Pop,
90}
91
92/// Block terminators
93#[derive(Debug, Clone)]
94pub enum IRTerminator {
95    /// Return from function
96    Return,
97    /// Unconditional jump
98    Jump(String),
99    /// Conditional jump
100    JumpIf {
101        then_block: String,
102        else_block: String,
103    },
104    /// Pattern match
105    Match {
106        cases: Vec<(IRPattern, String)>,
107        default: Option<String>,
108    },
109}
110
111/// IR patterns for matching
112#[derive(Debug, Clone)]
113pub enum IRPattern {
114    /// Match a literal value
115    Literal(Value),
116    /// Bind to a variable
117    Variable(String),
118    /// Constructor pattern
119    Constructor { name: String, args: Vec<IRPattern> },
120    /// Wildcard pattern
121    Wildcard,
122}
123
124/// Binary operations
125#[derive(Debug, Clone, Copy)]
126pub enum BinaryOp {
127    // Arithmetic
128    Add,
129    Sub,
130    Mul,
131    Div,
132    Mod,
133    Pow,
134    // Comparison
135    Eq,
136    Ne,
137    Lt,
138    Le,
139    Gt,
140    Ge,
141    // Logical
142    And,
143    Or,
144}
145
146/// Unary operations
147#[derive(Debug, Clone, Copy)]
148pub enum UnaryOp {
149    Neg,
150    Not,
151}
152
153/// IR builder for constructing IR from AST
154pub struct IRBuilder {
155    /// Current module being built
156    pub module: IRModule,
157    /// Current function being built
158    current_function: Option<String>,
159    /// Current block being built
160    current_block: Option<String>,
161    /// Variable name generator
162    var_counter: usize,
163    /// Block label generator
164    block_counter: usize,
165    /// Track if current block has been terminated
166    block_terminated: bool,
167}
168
169impl IRBuilder {
170    pub fn new(name: String, version: String, requires: Vec<String>) -> Self {
171        Self {
172            module: IRModule {
173                program: IRProgram {
174                    name,
175                    version,
176                    requires,
177                },
178                functions: HashMap::new(),
179                entry_point: None,
180            },
181            current_function: None,
182            current_block: None,
183            var_counter: 0,
184            block_counter: 0,
185            block_terminated: false,
186        }
187    }
188
189    /// Generate a fresh variable name
190    pub fn fresh_var(&mut self) -> String {
191        let var = format!("_t{}", self.var_counter);
192        self.var_counter += 1;
193        var
194    }
195
196    /// Generate a fresh block label
197    pub fn fresh_label(&mut self) -> String {
198        let label = format!("L{}", self.block_counter);
199        self.block_counter += 1;
200        label
201    }
202
203    /// Start building a new function
204    pub fn start_function(&mut self, name: String, params: Vec<String>, is_async: bool) {
205        let entry_label = self.fresh_label();
206        let func = IRFunction {
207            name: name.clone(),
208            params,
209            locals_count: 0,
210            blocks: vec![IRBlock {
211                label: entry_label.clone(),
212                instructions: vec![],
213                terminator: IRTerminator::Return,
214            }],
215            is_async,
216        };
217        self.module.functions.insert(name.clone(), func);
218        self.current_function = Some(name);
219        self.current_block = Some(entry_label);
220        self.block_terminated = false;
221    }
222
223    /// Add an instruction to the current block
224    pub fn emit(&mut self, inst: IRInstruction) {
225        if let (Some(func_name), Some(block_label)) = (&self.current_function, &self.current_block)
226        {
227            if let Some(func) = self.module.functions.get_mut(func_name) {
228                if let Some(block) = func.blocks.iter_mut().find(|b| b.label == *block_label) {
229                    block.instructions.push(inst);
230                }
231            }
232        }
233    }
234
235    /// Set the terminator for the current block
236    pub fn terminate(&mut self, term: IRTerminator) {
237        if let (Some(func_name), Some(block_label)) = (&self.current_function, &self.current_block)
238        {
239            if let Some(func) = self.module.functions.get_mut(func_name) {
240                if let Some(block) = func.blocks.iter_mut().find(|b| b.label == *block_label) {
241                    block.terminator = term;
242                    self.block_terminated = true;
243                }
244            }
245        }
246    }
247
248    /// Create a new block in the current function
249    pub fn new_block(&mut self, label: String) {
250        if let Some(func_name) = &self.current_function {
251            if let Some(func) = self.module.functions.get_mut(func_name) {
252                func.blocks.push(IRBlock {
253                    label: label.clone(),
254                    instructions: vec![],
255                    terminator: IRTerminator::Return,
256                });
257                self.current_block = Some(label);
258                self.block_terminated = false; // Reset termination status for new block
259            }
260        }
261    }
262
263    /// Check if the current block is already terminated
264    pub fn is_terminated(&self) -> bool {
265        self.block_terminated
266    }
267
268    /// Finish building and return the module
269    pub fn finish(mut self) -> IRModule {
270        // Set entry point if main function exists
271        if self.module.functions.contains_key("main") {
272            self.module.entry_point = Some("main".to_string());
273        }
274        self.module
275    }
276}