slang_backend/
vm.rs

1use crate::bytecode::{Chunk, NativeFunction, OpCode};
2use crate::value::{Value, ArithmeticOps, LogicalOps, ComparisonOps};
3use crate::native;
4use std::collections::HashMap;
5
6/// Represents a single scope with its variables
7struct Scope {
8    variables: HashMap<String, Value>,
9}
10
11/// Call frame to track function calls
12struct CallFrame {
13    /// Parameter names for the function (for local variable checking)
14    param_names: Vec<String>,
15    /// Address to return to after function completes
16    return_address: usize,
17    /// Stack position before function call
18    stack_offset: usize,
19    /// Local variables for the function
20    locals: HashMap<String, Value>,
21}
22
23/// Virtual Machine that executes bytecode
24pub struct VM {
25    /// Instruction pointer
26    ip: usize,
27    /// Stack for values
28    stack: Vec<Value>,
29    /// Stack of scopes, with global scope at index 0
30    scopes: Vec<Scope>,
31    /// Call frames for function calls
32    frames: Vec<CallFrame>,
33    /// Index of the current call frame
34    current_frame: Option<usize>,
35}
36
37
38/// Execute a bytecode chunk in the VM
39///
40/// ### Arguments
41///
42/// * `chunk` - The bytecode chunk to run
43///
44/// ### Returns
45///
46/// Ok(()) if successful, or an error message
47pub fn execute_bytecode(chunk: &Chunk) -> Result<(), String> {
48    let mut vm = VM::new();
49    vm.interpret(chunk)
50}
51
52
53impl Default for VM {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl VM {
60    /// Creates a new virtual machine
61    pub fn new() -> Self {
62        let mut vm = VM {
63            ip: 0,
64            stack: Vec::new(),
65            scopes: vec![Scope { variables: HashMap::new() }], // Global scope
66            frames: Vec::new(),
67            current_frame: None,
68        };
69        vm.register_native_functions();
70        vm
71    }
72
73    /// Registers built-in functions
74    fn register_native_functions(&mut self) {
75        self.define_native("print_value", 1, native::print_value);
76    }
77
78    /// Defines a native (built-in) function
79    ///
80    /// ### Arguments
81    ///
82    /// * `name` - Name of the native function
83    /// * `arity` - Number of parameters
84    /// * `function` - The Rust function implementing this native function
85    fn define_native(
86        &mut self,
87        name: &str,
88        arity: u8,
89        function: fn(&[Value]) -> Result<Value, String>,
90    ) {
91        let native_fn = Value::NativeFunction(Box::new(NativeFunction {
92            name: name.to_string(),
93            arity,
94            function,
95        }));
96
97        self.set_variable(name.to_string(), native_fn);
98    }
99
100
101    /// Interprets and executes a bytecode chunk
102    ///
103    /// ### Arguments
104    ///
105    /// * `chunk` - The bytecode chunk to execute
106    ///
107    /// ### Returns
108    ///
109    /// Ok(()) on success, or an error message on failure
110    pub fn interpret(&mut self, chunk: &Chunk) -> Result<(), String> {
111        self.ip = 0;
112        while self.ip < chunk.code.len() {
113            self.execute_instruction(chunk)?;
114        }
115
116        #[cfg(feature = "trace-execution")]
117        {
118            if !self.stack.is_empty() {
119                println!("\n=== Values on stack at end of execution ===");
120                for value in &self.stack {
121                    println!("{}", value);
122                }
123            }
124        }
125
126        Ok(())
127    }
128
129    /// Executes a single instruction
130    ///
131    /// ### Arguments
132    ///
133    /// * `chunk` - The bytecode chunk containing the instruction
134    ///
135    /// ### Returns
136    ///
137    /// Ok(()) on success, or an error message on failure
138    fn execute_instruction(&mut self, chunk: &Chunk) -> Result<(), String> {
139        let instruction = self.read_byte(chunk);
140        let op = OpCode::from_int(instruction)
141            .ok_or_else(|| format!("Unknown opcode: {}", instruction))?;
142
143        match op {
144            OpCode::Constant => {
145                let constant_idx = self.read_byte(chunk) as usize;
146                if constant_idx >= chunk.constants.len() {
147                    return Err("Invalid constant index".to_string());
148                }
149                let constant = &chunk.constants[constant_idx];
150                self.stack.push(constant.clone());
151            }
152            OpCode::Add => {
153                self.binary_op(|a, b| a.add(b))?;
154            }
155            OpCode::Subtract => {
156                self.binary_op(|a, b| a.subtract(b))?;
157            }
158            OpCode::Multiply => {
159                self.binary_op(|a, b| a.multiply(b))?;
160            }
161            OpCode::Divide => {
162                self.binary_op(|a, b| a.divide(b))?;
163            }
164            OpCode::Negate => {
165                let value = self.pop()?;
166                self.stack.push(value.negate()?);
167            }
168            OpCode::Return => {
169                if let Some(frame_index) = self.current_frame {
170                    let return_value = if self.stack.is_empty() {
171                        Value::Unit(()) 
172                    } else {
173                        self.pop()?
174                    };
175
176                    let frame = &self.frames[frame_index];
177                    let return_address = frame.return_address;
178                    let stack_offset = frame.stack_offset;
179
180                    while self.stack.len() > stack_offset {
181                        self.pop()?;
182                    }
183
184                    self.stack.push(return_value);
185
186                    self.ip = return_address;
187
188                    self.frames.pop();
189                    self.current_frame = if self.frames.is_empty() {
190                        None
191                    } else {
192                        Some(self.frames.len() - 1)
193                    };
194                } else {
195                    self.ip = chunk.code.len();
196                }
197            }
198            OpCode::Print => {
199                let value = self.pop()?;
200                println!("{}", value);
201            }
202            OpCode::GetVariable => {
203                let var_index = self.read_byte(chunk) as usize;
204                if var_index >= chunk.identifiers.len() {
205                    return Err("Invalid variable index".to_string());
206                }
207                let var_name = &chunk.identifiers[var_index];
208
209                let value = if let Some(frame_idx) = self.current_frame {
210                    if let Some(value) = self.frames[frame_idx].locals.get(var_name) {
211                        value.clone()
212                    } else if let Some(value) = self.get_variable(var_name) {
213                        value.clone()
214                    } else {
215                        return Err(format!("Undefined variable '{}'", var_name));
216                    }
217                } else if let Some(value) = self.get_variable(var_name) {
218                    value.clone()
219                } else {
220                    return Err(format!("Undefined variable '{}'", var_name));
221                };
222
223                self.stack.push(value);
224            }
225            OpCode::SetVariable => {
226                if self.stack.is_empty() {
227                    return Err("Stack underflow".to_string());
228                }
229                let var_index = self.read_byte(chunk) as usize;
230                if var_index >= chunk.identifiers.len() {
231                    return Err("Invalid variable index".to_string());
232                }
233                let var_name = chunk.identifiers[var_index].clone();
234                let value = self.stack.last().unwrap().clone();
235
236                if let Some(frame_idx) = self.current_frame {
237                    if self.frames[frame_idx].param_names.contains(&var_name) {
238                        self.frames[frame_idx].locals.insert(var_name, value);
239                    } else {
240                        self.set_variable(var_name, value);
241                    }
242                } else {
243                    self.set_variable(var_name, value);
244                }
245            }
246            OpCode::Pop => {
247                self.pop()?;
248            }
249            OpCode::DefineFunction => {
250                let var_index = self.read_byte(chunk) as usize;
251                let constant_index = self.read_byte(chunk) as usize;
252
253                if var_index >= chunk.identifiers.len() || constant_index >= chunk.constants.len() {
254                    return Err("Invalid index for function definition".to_string());
255                }
256
257                let var_name = chunk.identifiers[var_index].clone();
258                let value = &chunk.constants[constant_index];
259
260                self.set_variable(var_name, value.clone());
261            }
262            OpCode::Call => {
263                let arg_count = self.read_byte(chunk) as usize;
264
265                if self.stack.len() < arg_count + 1 {
266                    return Err("Stack underflow during function call".to_string());
267                }
268
269                let function_pos = self.stack.len() - 1;
270                let function_value = self.stack[function_pos].clone();
271
272                match function_value {
273                    Value::Function(func) => {
274                        if arg_count != func.arity as usize {
275                            return Err(format!(
276                                "Expected {} arguments but got {}",
277                                func.arity, arg_count
278                            ));
279                        }
280
281                        let mut locals = HashMap::new();
282
283                        for i in 0..arg_count {
284                            if i < func.locals.len() {
285                                let param_name = &func.locals[i];
286                                let arg_value = self.stack[function_pos - arg_count + i].clone();
287                                locals.insert(param_name.clone(), arg_value);
288                            }
289                        }
290
291                        let frame = CallFrame {
292                            param_names: func.locals.clone(),
293                            return_address: self.ip,
294                            stack_offset: function_pos - arg_count,
295                            locals,
296                        };
297
298                        // Remove function and arguments from stack
299                        for _ in 0..=arg_count {
300                            self.pop()?;
301                        }
302
303                        self.frames.push(frame);
304                        self.current_frame = Some(self.frames.len() - 1);
305
306                        self.ip = func.code_offset;
307                    }
308                    Value::NativeFunction(native_fn) => {
309                        if arg_count != native_fn.arity as usize {
310                            return Err(format!(
311                                "Expected {} arguments but got {}",
312                                native_fn.arity, arg_count
313                            ));
314                        }
315
316                        let mut args = Vec::with_capacity(arg_count);
317                        for i in 0..arg_count {
318                            args.push(self.stack[function_pos - 1 - i].clone());
319                        }
320
321                        let result = (native_fn.function)(&args)?;
322                        for _ in 0..=arg_count {
323                            self.pop()?;
324                        }
325
326                        self.stack.push(result);
327                    }
328                    _ => return Err("Can only call functions".to_string()),
329                }
330            }
331            OpCode::Jump => {
332                let offset =
333                    ((self.read_byte(chunk) as usize) << 8) | self.read_byte(chunk) as usize;
334                self.ip += offset;
335            }
336            OpCode::JumpIfFalse => {
337                let offset =
338                    ((self.read_byte(chunk) as usize) << 8) | self.read_byte(chunk) as usize;
339                let condition = self.peek(0)?;
340
341                let is_truthy = match condition {
342                    Value::Boolean(b) => *b,
343                    _ => return Err("Condition must be a boolean".to_string()),
344                };
345
346                if !is_truthy {
347                    self.ip += offset;
348                }
349            }
350            OpCode::BoolNot => {
351                let value = self.pop()?;
352                self.stack.push(value.not()?);
353            }
354            OpCode::BoolAnd => {
355                self.binary_op(|a, b| a.and(b))?;
356            }
357            OpCode::BoolOr => {
358                self.binary_op(|a, b| a.or(b))?;
359            }
360            OpCode::Greater => {
361                self.binary_op(|a, b| a.greater_than(b))?;
362            }
363            OpCode::Less => {
364                self.binary_op(|a, b| a.less_than(b))?;
365            }
366            OpCode::GreaterEqual => {
367                self.binary_op(|a, b| a.greater_than_equal(b))?;
368            }
369            OpCode::LessEqual => {
370                self.binary_op(|a, b| a.less_than_equal(b))?;
371            }
372            OpCode::Equal => {
373                self.binary_op(|a, b| a.equal(b))?;
374            }
375            OpCode::NotEqual => {
376                self.binary_op(|a, b| a.not_equal(b))?;
377            }
378            OpCode::BeginScope => {
379                self.scopes.push(Scope {
380                    variables: HashMap::new(),
381                });
382            }
383            OpCode::EndScope => {
384                if self.scopes.len() <= 1 {
385                    return Err("Cannot end global scope".to_string());
386                }
387                self.scopes.pop();
388            }
389        }
390
391        Ok(())
392    }
393
394    /// Reads the next byte from the chunk and advances the instruction pointer
395    ///
396    /// ### Arguments
397    ///
398    /// * `chunk` - The bytecode chunk to read from
399    ///
400    /// ### Returns
401    ///
402    /// The byte read from the chunk
403    fn read_byte(&mut self, chunk: &Chunk) -> u8 {
404        let byte = chunk.code[self.ip];
405        self.ip += 1;
406        byte
407    }
408
409    /// Pops a value off the stack
410    ///
411    /// ### Returns
412    ///
413    /// The popped value, or an error if the stack is empty
414    fn pop(&mut self) -> Result<Value, String> {
415        self.stack
416            .pop()
417            .ok_or_else(|| "Stack underflow".to_string())
418    }
419
420    /// Looks at a value on the stack without removing it
421    ///
422    /// ### Arguments
423    ///
424    /// * `distance` - How far from the top of the stack to look
425    ///
426    /// ### Returns
427    ///
428    /// Reference to the value, or an error if the stack isn't deep enough
429    fn peek(&self, distance: usize) -> Result<&Value, String> {
430        if distance >= self.stack.len() {
431            return Err("Stack underflow".to_string());
432        }
433
434        Ok(&self.stack[self.stack.len() - 1 - distance])
435    }
436
437    /// Performs a binary operation on the top two values of the stack
438    ///
439    /// ### Arguments
440    ///
441    /// * `op` - Function that implements the binary operation
442    ///
443    /// ### Returns
444    ///
445    /// Ok(()) if successful, or an error message
446    fn binary_op<F>(&mut self, op: F) -> Result<(), String>
447    where
448        F: FnOnce(&Value, &Value) -> Result<Value, String>,
449    {
450        if self.stack.len() < 2 {
451            return Err("Stack underflow".to_string());
452        }
453        let b = self.pop()?;
454        let a = self.pop()?;
455        let result = op(&a, &b)?;
456        self.stack.push(result);
457        Ok(())
458    }
459
460    /// Helper method to find a variable in any scope (from innermost to outermost)
461    fn get_variable(&self, name: &str) -> Option<&Value> {
462        for scope in self.scopes.iter().rev() {
463            if let Some(value) = scope.variables.get(name) {
464                return Some(value);
465            }
466        }
467        None
468    }
469
470    /// Helper method to set a variable (creates in current scope or updates existing)
471    fn set_variable(&mut self, name: String, value: Value) {
472        if let Some(current_scope) = self.scopes.last_mut() {
473            current_scope.variables.insert(name, value);
474        }
475    }
476}