phoenix_lang/
vm.rs

1use crate::chunk::OpCode::*;
2use crate::chunk::{ClassChunk, FunctionChunk, Instr, ModuleChunk};
3use crate::compiler::CompilationResult;
4use crate::debug::*;
5use crate::gc::GC;
6use crate::native::native_functions::*;
7use crate::native::native_methods::{NativeMethod, NATIVE_METHODS};
8use crate::resolver::UpValue;
9use crate::value::{
10    is_falsey, values_equal, HeapObj, HeapObjType, HeapObjVal, ObjBoundMethod, ObjClosure,
11    ObjHashMap, ObjInstance, ObjList, ObjString, Value,
12};
13use crate::{error, phoenix_error, warn, InterpretResult, VERSION};
14use std::collections::HashMap;
15use std::io::{stdin, stdout, Write};
16use std::thread::{sleep};
17use std::time::Duration;
18
19const FRAMES_MAX: usize = 255;
20
21#[derive(Debug)]
22pub enum ExecutionMode {
23    Default,
24    Trace,
25}
26
27/// This ended up not being very useful since we usually don't care what kind of deref error we get, they usually mean the same thing, that we tried to use a value in a way it wasn't supposed to be used
28#[derive(Debug, Clone, Copy)]
29pub enum DerefError {
30    NotPointer,
31    WrongType,
32}
33
34#[derive(Debug, Clone, PartialEq)]
35pub struct CallFrame {
36    /// Index into the VM.modules Vec for which module is being called
37    pub module: usize,
38    /// Index into the VM.functions Vec for which function is being called
39    function: usize,
40
41    pub ip: usize,
42    frame_start: usize,
43}
44
45#[derive(Debug, PartialEq, Clone)]
46pub enum Global {
47    Init(Value),
48    Uninit,
49}
50
51// Is it good rust to split these into two very coupled but separate structs or is there a way to keep them together while not angering the borrow checker?
52//
53// I think this setup worked quite well, but I'm sure there's a better way to do it
54/// Manages all the state involved with the VM's execution, namely the stack, global variables, the heap, and the call frames
55#[derive(PartialEq, Debug, Clone)]
56pub struct VMState {
57    pub current_frame: CallFrame,
58    stack: Vec<Value>,
59    frames: Vec<CallFrame>,
60    // we have a Vec of Vecs because each module has its own set of globals
61    pub(crate) globals: Vec<Vec<Global>>,
62    gc: GC,
63    // Not implemented due to it destroying my code => multiple upvalues pointing to the same original value in a function will NOT affect each other. This is a small enough edge case that I'm willing to just let it go
64    // upvalues: Vec<Value>,
65    /// stack of called modules for returning home after entering a new module for a function call
66    module_stack: Vec<usize>,
67}
68
69impl VMState {
70    fn pop(&mut self) -> Value {
71        match self.stack.pop() {
72            Some(x) => x,
73            None => {
74                phoenix_error!("VM panic! Attempted to pop a value when the value stack was empty");
75            }
76        }
77    }
78
79    // Note to future self: peek_mut SHOULD NEVER BE IMPLEMENTED!
80    // Values on the stack are always implicit copy/cloned, any persistent values must be allocated with the Gc and represented with PhoenixPointers instead
81
82    fn peek(&self) -> &Value {
83        self.peek_at(0)
84    }
85
86    fn peek_at(&self, dist: usize) -> &Value {
87        match self.stack.get(self.stack.len() - dist - 1) {
88            Some(x) => x,
89            None => {
90                phoenix_error!(
91                    "VM panic! Attempted to peek a value when the value stack was empty"
92                );
93            }
94        }
95    }
96
97    fn alloc(&mut self, val: HeapObj) -> Value {
98        self.gc
99            .alloc(val, &self.stack, &self.globals[self.current_frame.module])
100    }
101
102    fn alloc_string(&mut self, string: String) -> Value {
103        self.alloc(HeapObj::new_string(ObjString::new(string)))
104    }
105
106    // Fixme: Figure out how to not copy paste this code for mut and immut
107    pub fn deref(&self, pointer: usize) -> &HeapObj {
108        match self.gc.instances.get(pointer) {
109            Some(x) => x,
110            None => {
111                phoenix_error!("VM panic! Invalid pointer");
112            }
113        }
114    }
115
116    pub fn deref_mut(&mut self, pointer: usize) -> &mut HeapObj {
117        match self.gc.instances.get_mut(pointer) {
118            Some(x) => x,
119            None => {
120                phoenix_error!("VM panic! Invalid pointer");
121            }
122        }
123    }
124
125    pub fn deref_list(&self, pointer: usize) -> &ObjList {
126        let obj = self.deref(pointer);
127        if let HeapObjType::PhoenixList = obj.obj_type {
128            obj.obj.as_list()
129        } else {
130            phoenix_error!("VM panic! Attempted to deref a non-list object as a list");
131        }
132    }
133
134    pub fn deref_list_mut(&mut self, pointer: usize) -> &ObjList {
135        let obj = self.deref_mut(pointer);
136        if let HeapObjType::PhoenixList = obj.obj_type {
137            obj.obj.as_list_mut()
138        } else {
139            phoenix_error!("VM panic! Attempted to deref a non-list object as a list");
140        }
141    }
142
143    pub fn deref_string(&self, pointer: usize) -> &ObjString {
144        let obj = self.deref(pointer);
145        if let HeapObjType::PhoenixString = obj.obj_type {
146            obj.obj.as_string()
147        } else {
148            phoenix_error!("VM panic! Attempted to deref a non-string object as a string");
149        }
150    }
151
152    pub fn deref_string_mut(&mut self, pointer: usize) -> &mut ObjString {
153        let obj = self.deref_mut(pointer);
154        if let HeapObjType::PhoenixString = obj.obj_type {
155            obj.obj.as_string_mut()
156        } else {
157            phoenix_error!("VM panic! Attempted to deref a non-string object as a string");
158        }
159    }
160
161    pub fn create_string(&mut self, s: String) {
162        let string_obj = ObjString::new(s);
163        let string_ptr = self.alloc(HeapObj::new_string(string_obj));
164        self.stack.push(string_ptr);
165    }
166
167    /// Attempts to
168    /// 1. Take the given Value as a PhoenixPointer
169    /// 2. Deref it into a HeapObj
170    /// 3. Match the obj_types
171    fn deref_into(
172        &self,
173        pointer_val: &Value,
174        obj_type: HeapObjType,
175    ) -> Result<&HeapObjVal, DerefError> {
176        if let Value::PhoenixPointer(pointer) = pointer_val {
177            let obj = self.deref(*pointer);
178            if obj.obj_type == obj_type {
179                Ok(&obj.obj)
180            } else {
181                Err(DerefError::WrongType)
182            }
183        } else {
184            Err(DerefError::NotPointer)
185        }
186    }
187
188    fn deref_into_mut(
189        &mut self,
190        pointer_val: &Value,
191        obj_type: HeapObjType,
192    ) -> Result<&mut HeapObjVal, DerefError> {
193        if let Value::PhoenixPointer(pointer) = pointer_val {
194            let obj = self.deref_mut(*pointer);
195            if obj.obj_type == obj_type {
196                Ok(&mut obj.obj)
197            } else {
198                Err(DerefError::WrongType)
199            }
200        } else {
201            Err(DerefError::NotPointer)
202        }
203    }
204
205    fn current_closure(&self) -> &ObjClosure {
206        let pointer_val = match self.stack.get(self.current_frame.frame_start) {
207            Some(x) => x,
208            None => {
209                phoenix_error!("VM panic! Unable to get current closure?");
210            }
211        };
212        match self.deref_into(pointer_val, HeapObjType::PhoenixClosure) {
213            Ok(closure_obj) => closure_obj.as_closure(),
214            Err(x) => {
215                phoenix_error!("VM panic! Unable to get current closure? {:?}", x);
216            }
217        }
218    }
219
220    fn current_closure_mut(&mut self) -> &mut ObjClosure {
221        let pointer_val = match self.stack.get(self.current_frame.frame_start) {
222            Some(x) => x,
223            None => {
224                phoenix_error!("VM panic! Unable to get current closure?");
225            }
226        }
227        .clone();
228        match self.deref_into_mut(&pointer_val, HeapObjType::PhoenixClosure) {
229            Ok(closure_obj) => closure_obj.as_closure_mut(),
230            Err(x) => {
231                phoenix_error!("VM panic! Unable to get current closure? {:?}", x);
232            }
233        }
234    }
235
236    fn increment_ip(&mut self) {
237        self.current_frame.ip += 1;
238    }
239
240    fn jump(&mut self, offset: usize) {
241        self.current_frame.ip += offset - 1;
242    }
243
244    fn jump_back(&mut self, neg_offset: usize) {
245        self.current_frame.ip -= neg_offset + 1;
246    }
247
248    fn capture_upvalue(&self, upvalue: &UpValue) -> Value {
249        if upvalue.is_local {
250            // Just copy the value from the current stack frame (which is the parents)
251            self.stack[self.current_frame.frame_start + upvalue.index].clone()
252        } else {
253            // Look at the current frame's closure's upvalue vec and copy it from there
254            let parent_closure = self.current_closure();
255            parent_closure.values[upvalue.index].clone()
256        }
257    }
258
259    /// Push an upvalue onto the stack
260    fn push_upvalue(&mut self, index: usize) {
261        let closure = self.current_closure();
262        let val = match closure.values.get(index) {
263            Some(x) => x,
264            None => {
265                phoenix_error!("VM panic! Attempted to push an upvalue that doesn't exist");
266            }
267        }
268        .clone();
269        self.stack.push(val);
270    }
271
272    /// Set an upvalue with the top value of the stack
273    fn set_upvalue(&mut self, index: usize) {
274        let val = self.peek().clone();
275        let closure = self.current_closure_mut();
276        closure.values[index] = val;
277    }
278
279    /// Checks if the targeted Value is callable {PhoenixPointer to a PhoenixClosure, NativeFn, PhoenixClass, PhoenixBoundMethod}, passes it to call() to continue attempting the call if necessary.
280    ///
281    /// Note: This function or call() must fulfill the promise made in Resolver about what value sits in slot 0 of the local variables.
282    /// Whether that's 'this' or a placeholder
283    ///
284    /// Returns a String containing an error message or None
285    fn call_value(
286        &mut self,
287        arg_count: usize,
288        function_defs: &[FunctionChunk],
289        class_defs: &[ClassChunk],
290        _module: &ModuleChunk,
291        vm: &VM,
292        modules: &[ModuleChunk],
293    ) -> Option<String> {
294        let init_slot = vm.init_slot;
295        let callee = self.peek_at(arg_count);
296        if let Value::PhoenixPointer(_) = callee {
297            match self.deref_into(callee, HeapObjType::PhoenixClosure) {
298                Ok(closure) => {
299                    let closure = closure.as_closure();
300                    let fn_index = closure.function;
301                    self.call(fn_index, arg_count, function_defs)
302                }
303                Err(_) => Some(String::from("Can only call functions and classes")),
304            }
305        } else if let Value::PhoenixFunction(fn_index) = callee {
306            let index = *fn_index;
307            self.call(index, arg_count, function_defs)
308        } else if let Value::PhoenixBoundMethod(method) = callee {
309            let fn_index = method.method;
310            let index = self.stack.len() - arg_count - 1; // Index to put the PhoenixPointer to represent the "this" variable
311            self.stack[index] = Value::PhoenixPointer(method.pointer);
312            self.call(fn_index, arg_count, function_defs)
313        } else if let Value::PhoenixClass(class) = callee {
314            let instance_obj = ObjInstance::new(*class);
315            let class_def = &class_defs[*class];
316            let ptr = self.alloc(HeapObj::new_instance(instance_obj));
317            let index = self.stack.len() - arg_count - 1;
318            self.stack[index] = ptr; // Replace the PhoenixClass with the pointer
319
320            // Call the initializer if it exists
321            // If the PhoenixClass was called with arguments the stack will look like this: PhoenixClass | arg1 | arg2
322            // So we want to call with the stack as: PhoenixPointer => PhoenixInstance | arg1 | arg2
323            // And we need the init() fn to return the PhoenixInstance
324            if class_def.has_init {
325                if init_slot.is_none() {
326                    phoenix_error!("VM panic! Attempted to call a custom initializer without it existing as a method identifier?");
327                }
328                self.call(
329                    *class_def.methods.get(&init_slot.unwrap()).unwrap(),
330                    arg_count,
331                    function_defs,
332                )
333            } else if arg_count != 0 {
334                Some(format!(
335                    "Expected 0 arguments but got {} instead",
336                    arg_count
337                ))
338            } else {
339                // hacky fix because class definitions dont return with a classic return opcode
340                Some("back".to_string())
341            }
342        } else if let Value::NativeFunction(native_arg_count, native_fn) = callee {
343            let native_fn = *native_fn;
344            self.call_native(&native_fn, arg_count, *native_arg_count, vm, modules)
345        } else {
346            Some(String::from("Can only call functions and classes"))
347        }
348    }
349
350    /// Attempts to call a function with the values on the stack, with the given # of arguments
351    fn call(
352        &mut self,
353        fn_index: usize,
354        arg_count: usize,
355        function_defs: &[FunctionChunk],
356    ) -> Option<String> {
357        let target_fn = match function_defs.get(fn_index) {
358            Some(x) => x,
359            None => return Some(format!("Function with index {} does not exist", fn_index)),
360        };
361        if arg_count != target_fn.arity {
362            return Some(format!(
363                "Expected {} arguments but got {} instead",
364                target_fn.arity, arg_count
365            ));
366        }
367        if self.frames.len() == FRAMES_MAX {
368            return Some(String::from("Stack overflow"));
369        }
370
371        let mut frame = CallFrame {
372            module: self.current_frame.module,
373            function: fn_index,
374            ip: 0,
375            frame_start: self.stack.len() - arg_count - 1,
376        };
377
378        // Swap on the new call frame for the old one
379        std::mem::swap(&mut self.current_frame, &mut frame);
380
381        // Put the old one onto the stack
382        self.frames.push(frame);
383        None
384    }
385
386    /// Attempts to call a native (rust) function
387    fn call_native(
388        &mut self,
389        native_fn: &NativeFn,
390        arg_count: usize,
391        native_arg_count: Option<usize>,
392        vm: &VM,
393        modules: &[ModuleChunk],
394    ) -> Option<String> {
395        let mut args: Vec<Value> = Vec::new();
396        for _ in 0..arg_count {
397            args.push(self.pop());
398        }
399        self.pop(); // Pop off the Value::NativeFunction
400        args.reverse();
401        if let Some(n_a_count) = native_arg_count {
402            if arg_count != n_a_count {
403                return Some(format!(
404                    "Expected {} arguments but got {} instead",
405                    n_a_count, arg_count
406                ));
407            }
408        }
409        let result = match native_fn(args, vm, self, modules) {
410            Ok(x) => x,
411            Err(e) => return Some(e),
412        };
413        // check if the value is a PhoneixString and if yes call state.create_string
414        if let Value::PhoenixString(s) = result {
415            self.create_string(s);
416        } else {
417            self.stack.push(result);
418        }
419        // self.stack.push(result);
420        None
421    }
422
423    /// Attempts to call a native (rust) method
424    /// returns: If first Option is None, the method doesnt support this type, if the second Option is None, the function was successfully
425    fn call_method(
426        &mut self,
427        native_method: &NativeMethod,
428        arg_count: usize,
429        native_arg_count: Option<usize>,
430        vm: &VM,
431        modules: &[ModuleChunk],
432    ) -> Option<Option<String>> {
433        // println!(
434        //     "calling method, arg_count: {}, stack: {:?}",
435        //     arg_count, self.stack
436        // );
437        // we need know the stack looks like this: self | arg1 | arg2 | ... | argn
438        let mut args: Vec<Value> = Vec::new();
439        for _ in 0..arg_count {
440            args.push(self.pop());
441        }
442        // self.pop(); // Pop off the Value::NativeFunction
443        args.reverse();
444        let this = self.pop();
445        if let Some(n_a_count) = native_arg_count {
446            if arg_count != n_a_count {
447                return Some(Some(format!(
448                    "Expected {} arguments but got {} instead",
449                    n_a_count, arg_count
450                )));
451            }
452        }
453        let result = match native_method(this, args, vm, self, modules) {
454            Some(x) => match x {
455                Ok(x) => x,
456                Err(e) => return Some(Some(e)),
457            },
458            None => return None,
459        };
460        // check if the value is a PhoenixString and if yes call state.create_string
461        if let Value::PhoenixString(s) = result {
462            self.create_string(s);
463        } else {
464            self.stack.push(result);
465        }
466        // self.stack.push(result);
467        Some(None)
468    }
469
470    /// Defines all native functions
471    ///
472    /// Searches for references to native functions and adds them in if they're used in the program
473    fn define_std_lib(&mut self, identifiers: &[String]) {
474        for (str, nf) in match NATIVE_FUNCTIONS.lock() {
475            Ok(x) => x,
476            Err(_) => phoenix_error!("Failed to lock native functions mutex"),
477        }
478        .iter()
479        {
480            if let Some(index) = identifiers.iter().position(|x| x == str) {
481                self.globals[self.current_frame.module][index] =
482                    Global::Init(Value::NativeFunction(nf.0, nf.1));
483            }
484        }
485    }
486
487    /// Initializes the VMState with:
488    ///
489    /// - A CallFrame for function #0 and module #0 (should be the main module)
490    /// - Defined global variables for the native functions
491    /// - A Value::PhoenixFunction for function #0 pushed onto the stack => Satisfies the resolver assumption that the first locals slot is filled with something
492    /// update the globals each time a new module is loaded
493    fn new(identifiers: &Vec<String>) -> VMState {
494        let first_fn = CallFrame {
495            module: 0,
496            function: 0,
497            ip: 0,
498            frame_start: 0,
499        };
500
501        let first_val = Value::PhoenixFunction(0);
502        let stack = vec![first_val];
503
504        let mut state = VMState {
505            current_frame: first_fn,
506            stack,
507            frames: Vec::new(),
508            // globals: vec![Global::Uninit; identifiers.len()],
509            globals: vec![vec![Global::Uninit; identifiers.len()]],
510            gc: GC::new(),
511            module_stack: vec![0],
512        };
513
514        // todo: make this work with modules
515        state.define_std_lib(identifiers);
516        state
517    }
518}
519
520/// Contains all the information outputted by the compiler
521/// ie: All function and class definitions
522pub struct VM {
523    quiet_mode: bool,
524    mode: ExecutionMode,
525    pub modules_cache: Vec<ModuleChunk>,
526    pub modules_table: HashMap<String, usize>,
527    init_slot: Option<usize>,
528}
529
530impl VM {
531    pub fn new(mode: ExecutionMode, result: CompilationResult, quiet: bool) -> VM {
532        // compare version of the compilationResult and the VM
533        if result.version != *VERSION {
534            warn!(
535                "Version mismatch! Expected {} but got {}",
536                VERSION, result.version
537            );
538        }
539        let init_slot = result
540            .modules
541            .get(0)
542            .and_then(|x| x.clone().identifiers.iter().position(|x| x == "init"));
543        let modules = result.modules;
544        VM {
545            quiet_mode: quiet,
546            mode,
547            modules_cache: modules,
548            init_slot,
549            modules_table: result.modules_table,
550        }
551    }
552
553    pub fn append(&mut self, result: CompilationResult) {
554        let init_slot = result.modules[0]
555            .clone()
556            .identifiers
557            .iter()
558            .position(|x| x == "init");
559        if !self.modules_cache.is_empty() {
560            self.modules_cache[0].functions[0].chunk.code.pop();
561        }
562        self.modules_cache.extend(result.modules);
563        self.modules_table.extend(result.modules_table);
564        self.init_slot = init_slot;
565    }
566
567    fn runtime_error(&self, msg: &str, state: &VMState, modules: &[ModuleChunk]) {
568        if self.quiet_mode {
569            return;
570        }
571
572        error!("{}", msg);
573        for call_frame in [state.current_frame.clone()]
574            .iter()
575            .chain(state.frames.iter().rev())
576        {
577            let function = &modules[state.current_frame.module]
578                .functions
579                .get(call_frame.function)
580                .unwrap();
581            eprint!(
582                "[{}:{}] in ",
583                modules[state.current_frame.module].file,
584                function.chunk.code.get(call_frame.ip).unwrap().line_num + 1
585            );
586            match &function.name {
587                Some(name) => eprintln!("{}", name),
588                None => eprintln!("script"),
589            }
590        }
591    }
592
593    // /// Returns the current module
594    // pub(crate) fn current_module(state: &VMState, modules: &Vec<ModuleChunk>) -> &ModuleChunk {
595    //     &modules[state.current_frame.module]
596    // }
597
598    /// Should only be used for getting debugging and error reporting
599    ///
600    /// * For the global instructions, just the index should suffice
601    /// * For instance properties and fields, the hashmaps are keyed on the usize corresponding to the identifier string
602    /// * Local variable names are erased completely by the resolver at compile time
603    fn get_variable_name<'a>(
604        index: usize,
605        state: &VMState,
606        modules: &'a [ModuleChunk],
607    ) -> &'a String {
608        let name_val = &modules[state.current_frame.module].identifiers.get(index);
609        if let Some(var_name) = name_val {
610            var_name
611        } else {
612            panic!("VM panic: Found a non PhoenixString value for a variable name");
613        }
614    }
615
616    // fn get_current_code(&self, state: &VMState, modules: &Vec<ModuleChunk>) -> &Vec<Instr> {
617    //     &modules[state.current_frame.module]
618    //         .functions
619    //         .get(state.current_frame.function)
620    //         .unwrap()
621    //         .chunk
622    //         .code
623    // }
624
625    pub fn run(&mut self) -> InterpretResult {
626        self.run_state(None, Vec::new())
627    }
628
629    pub fn run_state(&mut self, state: Option<VMState>, m: Vec<ModuleChunk>) -> InterpretResult {
630        if let ExecutionMode::Trace = self.mode {
631            eprintln!("== Starting execution | Mode: {:?} ==", self.mode);
632            debug_print_constants(&self.modules_cache);
633        }
634
635        // look at the functions in the first module
636        let mut state = if let Some(s) = state {
637            s
638        } else {
639            VMState::new(&self.modules_cache[0].identifiers)
640        };
641        state.define_std_lib(&self.modules_cache[0].identifiers);
642        // todo: maybe move this to VMState
643        let modules = self.modules_cache.clone();
644        self.modules_cache = m;
645        // todo: make this work with modules
646        // modules[0].define_std_lib(&modules[0].identifiers, &modules[0]);
647        // let mut states = vec![&mut state];
648        // let mut parent_states = Vec::new();
649
650        // Makes getting new instructions faster
651        // Update this vec whenever
652        let mut current_code = &modules[state.current_frame.module]
653            .functions
654            .get(state.current_frame.function)
655            .unwrap()
656            .chunk
657            .code[..];
658
659        // Move this into a match arm that matches all the binary ops, and then matches on the individual opcodes?
660        macro_rules! op_binary {
661            ($val_type: path, $oper: tt) => {
662                {
663                    //if let ($val_type(a), $val_type(b)) = (self.pop(), self.pop()) {
664                    let var_a = state.pop();
665                    let var_b = state.pop();
666                    if let (Value::Float(a), Value::Float(b)) = (var_a.clone(), var_b.clone()) {
667                        state.stack.push($val_type(b $oper a))
668                    } else if let (Value::Long(a), Value::Long(b)) = (var_a, var_b) {
669                        state.stack.push($val_type(b $oper a))
670                    } else {
671                        self.runtime_error("Operands must be numbers", &state, &modules);
672                        return InterpretResult::InterpretRuntimeError;
673                    }
674                }
675            };
676            ($val_type: path, $val_type2: path, $oper: tt) => {
677                {
678                    //if let ($val_type(a), $val_type(b)) = (self.pop(), self.pop()) {
679                    let var_a = state.pop();
680                    let var_b = state.pop();
681                    if let (Value::Float(a), Value::Float(b)) = (var_a.clone(), var_b.clone()) {
682                        state.stack.push($val_type(b $oper a))
683                    } else if let (Value::Long(a), Value::Long(b)) = (var_a.clone(), var_b.clone()) {
684                        state.stack.push($val_type2(b $oper a))
685                    } else if let (Value::Long(_a), Value::Float(_b)) = (var_a.clone(), var_b.clone()) {
686                        self.runtime_error(concat!("Operands must have the same type for the ", stringify!($oper), " operation. (long and float)"), &state, &modules);
687                        return InterpretResult::InterpretRuntimeError;
688                    } else if let (Value::Float(_a), Value::Long(_b)) = (var_a.clone(), var_b.clone()) {
689                        self.runtime_error(concat!("Operands must have the same type for the ", stringify!($oper), " operation. (float and long)"), &state, &modules);
690                        return InterpretResult::InterpretRuntimeError;
691                    } else {
692                        self.runtime_error(concat!("Operands must be numbers for the ", stringify!($oper), " operation"), &state, &modules);
693                        return InterpretResult::InterpretRuntimeError;
694                    }
695                }
696            }
697        }
698
699        loop {
700            let instr = match current_code.get(state.current_frame.ip) {
701                Some(instr) => instr,
702                None => {
703                    phoenix_error!(
704                        "Tried to access an invalid instruction, index: {}, length: {}",
705                        state.current_frame.ip,
706                        current_code.len()
707                    );
708                }
709            };
710            state.increment_ip(); // Preincrement the ip so OpLoops to 0 are possible
711
712            if let ExecutionMode::Trace = self.mode {
713                debug_trace(self, instr, &state, &modules);
714            }
715
716            match instr.op_code {
717                OpImport(module_index) => {
718                    // println!(
719                    //     "Importing module: {}, stack len: {}",
720                    //     module_index,
721                    //     state.stack.len()
722                    // );
723                    if state.frames.len() == FRAMES_MAX {
724                        self.runtime_error("Stack overflow", &state, &modules);
725                        return InterpretResult::InterpretRuntimeError;
726                    }
727                    let mut frame = CallFrame {
728                        module: module_index,
729                        function: 0,
730                        ip: 0,
731                        frame_start: 0,
732                    };
733
734                    // Swap on the new call frame for the old one
735                    std::mem::swap(&mut state.current_frame, &mut frame);
736
737                    // Put the old one onto the stack
738                    state.frames.push(frame);
739                    current_code = &modules[state.current_frame.module]
740                        .functions
741                        .get(state.current_frame.function)
742                        .unwrap()
743                        .chunk
744                        .code[..];
745                    if state.globals.len() < module_index + 1 {
746                        // println!("Pushing new globals for module: {:?}", module_index);
747                        let len = modules[module_index].identifiers.len();
748                        state.globals.push(vec![Global::Uninit; len]);
749                    }
750                }
751                OpReturn => {
752                    let result = state.pop(); // Save the result (the value on the top of the stack)
753                    for _ in 0..(state.stack.len() - state.current_frame.frame_start) {
754                        // Clean up the call frame part of that stack
755                        state.pop();
756                    }
757
758                    if state.frames.is_empty() {
759                        return InterpretResult::InterpretOK(state, modules);
760                    } else {
761                        state.current_frame = state.frames.pop().unwrap(); // Update the current frame
762                        if state.module_stack != vec![0] {
763                            state.current_frame.module = state.module_stack.pop().unwrap();
764                        }
765                        current_code = &modules[state.current_frame.module]
766                            .functions
767                            .get(state.current_frame.function)
768                            .unwrap()
769                            .chunk
770                            .code[..]; // Update the current code
771                        state.stack.push(result); // Push the result back
772                    }
773                }
774                OpPop => {
775                    state.pop();
776                }
777                OpDefineGlobal(index) => {
778                    let var_val = state.pop();
779                    state.globals[state.current_frame.module][index] = Global::Init(var_val);
780                }
781                OpCallGlobal(module_index, index, arity) => {
782                    let cur_module = state.current_frame.module;
783                    state.module_stack.push(cur_module);
784                    state.current_frame.module = module_index;
785                    let var_val = &state.globals[state.current_frame.module][index];
786                    match var_val {
787                        Global::Init(x) => {
788                            let new = x.clone();
789                            let index = state.stack.len() - arity;
790                            state.stack.insert(index, new);
791                            let result = state.call_value(
792                                arity,
793                                &modules[state.current_frame.module].functions,
794                                &modules[state.current_frame.module].classes,
795                                &modules[state.current_frame.module],
796                                self,
797                                &modules,
798                            );
799
800                            if let Some(msg) = result {
801                                if msg == "back" {
802                                    // when classes are initialized, there is no return opcode emitted, so we have to return to the old module by ourself
803                                    state.current_frame.module = state.module_stack.pop().unwrap();
804                                } else {
805                                    self.runtime_error(&msg[..], &state, &modules);
806                                    return InterpretResult::InterpretRuntimeError;
807                                }
808                            }
809                            current_code = &modules[state.current_frame.module]
810                                .functions
811                                .get(state.current_frame.function)
812                                .unwrap()
813                                .chunk
814                                .code[..]; // Update the current code
815                        }
816                        _ => {
817                            self.runtime_error(
818                                format!(
819                                    "Undefined variable '{}'",
820                                    VM::get_variable_name(index, &state, &modules)
821                                )
822                                .as_str(),
823                                &state,
824                                &modules,
825                            );
826                            return InterpretResult::InterpretRuntimeError;
827                        }
828                    }
829                }
830                OpGetGlobal(index) => {
831                    let var_val = &state.globals[state.current_frame.module][index];
832                    match var_val {
833                        Global::Init(x) => {
834                            let new = x.clone();
835                            state.stack.push(new)
836                        }
837                        _ => {
838                            self.runtime_error(
839                                format!(
840                                    "Undefined variable '{}'",
841                                    VM::get_variable_name(index, &state, &modules)
842                                )
843                                .as_str(),
844                                &state,
845                                &modules,
846                            );
847                            return InterpretResult::InterpretRuntimeError;
848                        }
849                    }
850                }
851                OpGetModuleVar(module_index, index) => {
852                    // println!("globals: {:?}", state.globals);
853                    let var_val = &state.globals[module_index][index];
854                    match var_val {
855                        Global::Init(x) => {
856                            let new = x.clone();
857                            state.stack.push(new)
858                        }
859                        _ => {
860                            self.runtime_error(
861                                format!(
862                                    "Undefined variable '{}'",
863                                    VM::get_variable_name(index, &state, &modules)
864                                )
865                                .as_str(),
866                                &state,
867                                &modules,
868                            );
869                            return InterpretResult::InterpretRuntimeError;
870                        }
871                    }
872                }
873                OpSetGlobal(index) => {
874                    // We don't want assignment to pop the value since this is an expression
875                    // this will almost always be in a expression statement, which will pop the value
876                    let var_val = state.peek().clone();
877                    match &state.globals[state.current_frame.module][index] {
878                        Global::Init(_) => {
879                            state.globals[state.current_frame.module][index] = Global::Init(var_val)
880                        } // We require it to be initialized (ie defined earlier by OpDefineGlobal)
881                        _ => {
882                            self.runtime_error(
883                                format!(
884                                    "Undefined variable '{}'",
885                                    VM::get_variable_name(index, &state, &modules)
886                                )
887                                .as_str(),
888                                &state,
889                                &modules,
890                            );
891                            return InterpretResult::InterpretRuntimeError;
892                        }
893                    }
894                }
895                OpGetLocal(index) => state
896                    .stack
897                    .push(state.stack[state.current_frame.frame_start + index].clone()), // Note: We gotta clone these values around the stack because our operators pop off the top and we also don't want to modify the variable value
898                OpSetLocal(index) => {
899                    let dest = state.current_frame.frame_start + index;
900                    state.stack[dest] = state.peek().clone(); // Same idea as OpSetGlobal, don't pop value since it's an expression
901                }
902                OpInvoke(name_index, arg_count, module_index) => {
903                    let cur_module = state.current_frame.module;
904                    state.module_stack.push(cur_module);
905                    state.current_frame.module = module_index;
906                    let pointer_val = state.peek_at(arg_count).clone();
907                    let obj = if let Value::PhoenixPointer(pointer) = pointer_val {
908                        state.deref(pointer)
909                    } else {
910                        self.runtime_error(
911                            "Can only invoke methods on instances and lists",
912                            &state,
913                            &modules,
914                        );
915                        return InterpretResult::InterpretRuntimeError;
916                    };
917
918                    let result =
919                        // match state.deref_into(&pointer_val.clone(), HeapObjType::PhoenixInstance) {
920                        match &obj.obj {
921                            HeapObjVal::PhoenixInstance(_) => {
922                                let instance = obj.obj.as_instance();
923                                let class_def =
924                                    &&modules[state.current_frame.module].classes[instance.class];
925                                if instance.fields.contains_key(&name_index) {
926                                    // Guard against the weird edge case where instance.thing() is actually calling a closure instance.thing, not a method invocation
927                                    let value = instance.fields.get(&name_index).unwrap().clone();
928                                    let index = state.stack.len() - 1 - arg_count;
929                                    state.stack[index] = value; // Remove the instance and replace with the value
930                                    state.call_value(
931                                        arg_count,
932                                        &modules[state.current_frame.module].functions,
933                                        &modules[state.current_frame.module].classes,
934                                        &modules[state.current_frame.module],
935                                        self,
936                                        &modules,
937                                    )
938                                    // Perform the call
939                                } else if class_def.methods.contains_key(&name_index) {
940                                    // We know that the top of the stack is PhoenixPointer | arg1 | arg2
941                                    // So we can go ahead and call
942                                    let fn_index = class_def.methods.get(&name_index).unwrap();
943                                    state.call(
944                                        *fn_index,
945                                        arg_count,
946                                        &modules[state.current_frame.module].functions,
947                                    )
948                                } else {
949                                    Some(format!(
950                                        "Undefined property '{}' in {:?}",
951                                        VM::get_variable_name(name_index, &state, &modules),
952                                        instance
953                                    ))
954                                }
955                            }
956                            /*HeapObjVal::PhoenixList(_) => {
957                                // check if its a list
958                                let curr_module = state.current_frame.module;
959                                // we need to deref the pointer to get the actual list
960                                match state.deref_into_mut(&pointer_val, HeapObjType::PhoenixList) {
961                                    Ok(&mut ref mut list) => {
962                                        match list {
963                                            HeapObjVal::PhoenixList(ref mut list) => {
964                                                match &*modules[curr_module].identifiers[name_index] {
965                                                    // "push" => {
966                                                    //     list.values.push(value);
967                                                    // }
968                                                    "pop" => {
969                                                        if let Some(val) = list.values.pop() {
970                                                            state.stack.push(val);
971                                                        } else {
972                                                            self.runtime_error(
973                                                                "Attempted to pop from an empty list",
974                                                                &state,
975                                                                &modules,
976                                                            );
977                                                            return InterpretResult::InterpretRuntimeError;
978                                                        }
979                                                        None
980                                                    }
981                                                    "len" => {
982                                                        let len = list.values.len() as i64;
983                                                        state.stack.push(Value::Long(len));
984                                                        None
985                                                    }
986                                                    "sort" => {
987                                                        list.values.sort_by(|a, b| {
988                                                            if let Value::Float(a) = a {
989                                                                if let Value::Float(b) = b {
990                                                                    return a.partial_cmp(b).unwrap();
991                                                                }
992                                                            }
993                                                            if let Value::Long(a) = a {
994                                                                if let Value::Long(b) = b {
995                                                                    return a.partial_cmp(b).unwrap();
996                                                                }
997                                                            }
998                                                            panic!("Attempted to sort a list with non-numeric values");
999                                                        });
1000                                                        None
1001                                                    }
1002                                                    _ => {
1003                                                        // self.runtime_error(
1004                                                        //     format!("Function {} not found on list", &*modules[curr_module].identifiers[name_index]).as_str(),
1005                                                        //     &state,
1006                                                        //     &modules,
1007                                                        // );
1008                                                        // return InterpretResult::InterpretRuntimeError;
1009                                                        Some(format!("Function \"{}\" not found on list", &*modules[curr_module].identifiers[name_index]))
1010                                                    }
1011                                                }
1012                                            }
1013                                            _ => {
1014                                                self.runtime_error(
1015                                                    "Attempted to index a non-indexable value",
1016                                                    &state,
1017                                                    &modules,
1018                                                );
1019                                                return InterpretResult::InterpretRuntimeError;
1020                                            }
1021                                        }
1022                                    }
1023                                    Err(_) => {
1024                                        Some(String::from(
1025                                            "Can only invoke methods on class instances and lists",
1026                                        ))
1027                                    }
1028                                }
1029                            }*/
1030                            _ => Some(format!(
1031                                "Can only invoke methods on instances and lists, not {:?}",
1032                                obj
1033                            )),
1034                        };
1035
1036                    if let Some(error) = result {
1037                        let result = if NATIVE_METHODS.lock().unwrap().contains_key(
1038                            modules[state.current_frame.module].identifiers[name_index].as_str(),
1039                        ) {
1040                            // println!("found method in native methods");
1041                            let native_methods = NATIVE_METHODS.lock().unwrap();
1042                            let native_method = native_methods
1043                                .get(
1044                                    modules[state.current_frame.module].identifiers[name_index]
1045                                        .as_str(),
1046                                )
1047                                .unwrap();
1048                            if native_method.0.is_some() && native_method.0.unwrap() != arg_count {
1049                                Some(format!(
1050                                    "Expected {} arguments but got {} instead",
1051                                    native_method.0.unwrap(),
1052                                    arg_count
1053                                ))
1054                            } else {
1055                                let result = state.call_method(
1056                                    &native_method.1,
1057                                    arg_count,
1058                                    native_method.0,
1059                                    self,
1060                                    &modules,
1061                                );
1062                                if result.is_none() {
1063                                    Some(error)
1064                                } else if let Some(Some(msg)) = result {
1065                                    self.runtime_error(&msg[..], &state, &modules);
1066                                    return InterpretResult::InterpretRuntimeError;
1067                                } else {
1068                                    None
1069                                }
1070                            }
1071                        } else {
1072                            Some(error)
1073                        };
1074                        if let Some(error) = result {
1075                            self.runtime_error(error.as_str(), &state, &modules);
1076                            return InterpretResult::InterpretRuntimeError;
1077                        }
1078                    }
1079                    current_code = &modules[state.current_frame.module]
1080                        .functions
1081                        .get(state.current_frame.function)
1082                        .unwrap()
1083                        .chunk
1084                        .code[..]; // Update the current code
1085                }
1086                OpGetProperty(name_index) => {
1087                    let pointer_val = state.peek();
1088
1089                    // Todo: Combine this and SetProperty into a macro so it doesn't hurt me everytime i have to read this
1090                    match state.deref_into(pointer_val, HeapObjType::PhoenixInstance) {
1091                        Ok(instance) => {
1092                            let instance = instance.as_instance();
1093                            if instance.fields.contains_key(&name_index) {
1094                                // See if we tried to get a field
1095                                let value = instance.fields.get(&name_index).unwrap().clone();
1096                                state.pop(); // Remove the instance
1097                                state.stack.push(value); // Replace with the value
1098                            } else {
1099                                let class_chunk =
1100                                    &&modules[state.current_frame.module].classes[instance.class]; // if not a field, then we must be getting a function. Create a PhoenixBoundMethod for it
1101                                if class_chunk.methods.contains_key(&name_index) {
1102                                    let bound_value = ObjBoundMethod {
1103                                        method: *class_chunk.methods.get(&name_index).unwrap(),
1104                                        pointer: pointer_val.as_pointer(),
1105                                    };
1106                                    state.pop(); // Remove the instance
1107                                    state.stack.push(Value::PhoenixBoundMethod(bound_value));
1108                                    // Replace with bound method
1109                                } else {
1110                                    self.runtime_error(
1111                                        format!(
1112                                            "Undefined property '{}' in {:?}",
1113                                            VM::get_variable_name(name_index, &state, &modules),
1114                                            instance
1115                                        )
1116                                        .as_str(),
1117                                        &state,
1118                                        &modules,
1119                                    );
1120                                    return InterpretResult::InterpretRuntimeError;
1121                                }
1122                            }
1123                        }
1124                        Err(_) => {
1125                            let msg = format!("Only class instances can access properties with '.' Found {} instead", pointer_val.to_string(self, &state, &modules));
1126                            self.runtime_error(msg.as_str(), &state, &modules);
1127                            return InterpretResult::InterpretRuntimeError;
1128                        }
1129                    }
1130                }
1131                OpSetProperty(name_index) => {
1132                    // Fixme: this is nearly identical to OpGetProperty, is there any way to combine them nicely?
1133                    let val = state.pop();
1134                    let pointer_val = state.peek().clone();
1135
1136                    match state.deref_into_mut(&pointer_val, HeapObjType::PhoenixInstance) {
1137                        Ok(instance) => {
1138                            let instance = instance.as_instance_mut();
1139                            instance.fields.insert(name_index, val.clone());
1140                        }
1141                        Err(_) => {
1142                            let msg = format!("Only class instances can access properties with '.' Found {} instead", pointer_val.to_string(self, &state, &modules));
1143                            self.runtime_error(msg.as_str(), &state, &modules);
1144                            return InterpretResult::InterpretRuntimeError;
1145                        }
1146                    }
1147
1148                    // We return on an error, so we can clean up the stack now
1149                    state.pop(); // Instance
1150                    state.stack.push(val); // Return the value to the stack
1151                }
1152                // This is almost identical to OpGetProperty, but it goes one extra jump to get the method from the superclass, and binds it to itself
1153                OpGetSuper(name_index) => {
1154                    let pointer_val = state.peek();
1155                    let superclass_val = state.peek_at(1);
1156                    if let Value::PhoenixClass(superclass) = superclass_val {
1157                        // Todo: Combine this and SetProperty into a macro so it doesn't hurt me everytime i have to read this
1158                        match state.deref_into(pointer_val, HeapObjType::PhoenixInstance) {
1159                            Ok(instance) => {
1160                                let instance = instance.as_instance();
1161                                let superclass_chunk =
1162                                    &&modules[state.current_frame.module].classes[*superclass];
1163                                if superclass_chunk.methods.contains_key(&name_index) {
1164                                    let bound_value = ObjBoundMethod {
1165                                        method: *superclass_chunk.methods.get(&name_index).unwrap(),
1166                                        pointer: pointer_val.as_pointer(),
1167                                    };
1168                                    // println!("Superclass get method found method {:?} ", bound_value);
1169                                    // println!("Superclass methods {:?}", superclass_chunk.methods);
1170                                    // println!("Superclass for {:?} is {:?}", instance, class_chunk.superclass);
1171                                    state.pop(); // Remove the instance
1172                                    state.stack.push(Value::PhoenixBoundMethod(bound_value));
1173                                    // Replace with bound method
1174                                } else {
1175                                    self.runtime_error(
1176                                        format!(
1177                                            "Undefined superclass method '{}' for {}",
1178                                            VM::get_variable_name(name_index, &state, &modules),
1179                                            &modules[state.current_frame.module]
1180                                                .classes
1181                                                .get(instance.class)
1182                                                .unwrap()
1183                                                .name,
1184                                        )
1185                                        .as_str(),
1186                                        &state,
1187                                        &modules,
1188                                    );
1189                                    return InterpretResult::InterpretRuntimeError;
1190                                }
1191                            }
1192                            Err(_) => {
1193                                panic!(
1194                                    "VM panic! Failed to obtain instance PhoenixPointer for super"
1195                                );
1196                            }
1197                        }
1198                    } else {
1199                        panic!("VM panic! Failed to obtain superclass index for super, got {:?} instead", superclass_val);
1200                    }
1201                }
1202
1203                OpGetUpvalue(index) => {
1204                    state.push_upvalue(index);
1205                }
1206                OpSetUpvalue(index) => {
1207                    state.set_upvalue(index);
1208                }
1209
1210                OpClosure => {
1211                    if let Value::PhoenixFunction(function) = state.pop() {
1212                        let mut closure = ObjClosure::new(function); // Capture values into the closure here
1213
1214                        let fn_chunk = &modules[state.current_frame.module]
1215                            .functions
1216                            .get(function)
1217                            .unwrap();
1218                        for upvalue in fn_chunk.upvalues.as_ref().unwrap().iter() {
1219                            closure.values.push(state.capture_upvalue(upvalue))
1220                        }
1221                        let ptr = state.alloc(HeapObj::new_closure(closure));
1222                        state.stack.push(ptr);
1223                    } else {
1224                        panic!("VM panic! Attempted to wrap a non-function value in a closure");
1225                    }
1226                }
1227
1228                OpJump(offset) => state.jump(offset),
1229                OpJumpIfFalse(offset) => {
1230                    if is_falsey(state.peek()) {
1231                        // Does not pop the value off the top of the stack because we need them for logical operators
1232                        state.jump(offset);
1233                    }
1234                }
1235                OpLoop(neg_offset) => state.jump_back(neg_offset),
1236                OpCall(arity, module_index) => {
1237                    let callee = state.peek_at(arity);
1238                    // do we need to switch modules?
1239                    if let Value::PhoenixModule(module) = callee {
1240                        state.current_frame.module = *module;
1241                        // pop the module off the stack
1242                        state.pop();
1243                    }
1244                    let cur_module = state.current_frame.module;
1245                    state.module_stack.push(cur_module);
1246                    state.current_frame.module = module_index;
1247                    let result = state.call_value(
1248                        arity,
1249                        &modules[state.current_frame.module].functions,
1250                        &modules[state.current_frame.module].classes,
1251                        &modules[state.current_frame.module],
1252                        self,
1253                        &modules,
1254                    );
1255                    current_code = &modules[state.current_frame.module]
1256                        .functions
1257                        .get(state.current_frame.function)
1258                        .unwrap()
1259                        .chunk
1260                        .code[..]; // Update the current code
1261                    if let Some(msg) = result {
1262                        self.runtime_error(&msg[..], &state, &modules);
1263                        return InterpretResult::InterpretRuntimeError;
1264                    }
1265                    // state.current_frame.module = cur_module;
1266                }
1267
1268                OpClass(index) => state.stack.push(Value::PhoenixClass(index)),
1269                OpConstant(index) => state
1270                    .stack
1271                    .push(modules[state.current_frame.module].constants[index].clone()),
1272                OpTrue => state.stack.push(Value::Bool(true)),
1273                OpFalse => state.stack.push(Value::Bool(false)),
1274                OpNil => state.stack.push(Value::Nil),
1275                OpAdd => {
1276                    let t = (state.pop(), state.pop());
1277                    if let (Value::PhoenixPointer(a), Value::PhoenixPointer(b)) = t {
1278                        let ptr = state.alloc_string(format!(
1279                            "{}{}",
1280                            state.deref_string(b).value,
1281                            state.deref_string(a).value
1282                        ));
1283                        state.stack.push(ptr)
1284                    } else if let (Value::Float(a), Value::Float(b)) = t {
1285                        state.stack.push(Value::Float(a + b))
1286                    } else if let (Value::Long(a), Value::Long(b)) = t {
1287                        state.stack.push(Value::Long(a + b))
1288                    } else {
1289                        self.runtime_error(
1290                            "Operands must be numbers or strings and must have the same type",
1291                            &state,
1292                            &modules,
1293                        );
1294                        return InterpretResult::InterpretRuntimeError;
1295                    }
1296                }
1297                OpDivide => op_binary!(Value::Float, Value::Long, /),
1298                OpSubtract => op_binary!(Value::Float, Value::Long, -),
1299                OpMultiply => op_binary!(Value::Float, Value::Long, *),
1300                OpGreater => op_binary!(Value::Bool, >),
1301                OpLess => op_binary!(Value::Bool, <),
1302                OpEqual => {
1303                    let t = (&state.pop(), &state.pop());
1304                    state.stack.push(Value::Bool(values_equal(t)));
1305                }
1306
1307                OpNot => {
1308                    let val = Value::Bool(is_falsey(&state.pop()));
1309                    state.stack.push(val);
1310                }
1311                OpNegate => {
1312                    let value = state.pop().as_float();
1313                    match value {
1314                        Some(x) => state.stack.push(Value::Float(x * -1.0)),
1315                        None => {
1316                            let value = state.pop().as_long();
1317                            match value {
1318                                Some(x) => state.stack.push(Value::Long(-x)),
1319                                None => {
1320                                    self.runtime_error(
1321                                        "Attempted to negate a non-number value",
1322                                        &state,
1323                                        &modules,
1324                                    );
1325                                    return InterpretResult::InterpretRuntimeError;
1326                                }
1327                            }
1328                        }
1329                    }
1330                }
1331                OpPrint => {
1332                    println!("{}", state.pop().to_string(self, &state, &modules));
1333                }
1334                OpGetIndex => {
1335                    let index = match state.pop() {
1336                        Value::Long(i) => i as usize,
1337                        Value::Float(i) => i as usize,
1338                        _ => {
1339                            self.runtime_error(
1340                                "Attempted to index a non-iterable value",
1341                                &state,
1342                                &modules,
1343                            );
1344                            return InterpretResult::InterpretRuntimeError;
1345                        }
1346                    };
1347                    let value = state.pop();
1348                    match value {
1349                        Value::PhoenixPointer(list_index) => {
1350                            // get the list from the allocated lists
1351                            let v = state.deref_mut(list_index);
1352                            let value = match v.obj {
1353                                HeapObjVal::PhoenixList(ref mut list) => {
1354                                    if list.values.len() <= index {
1355                                        self.runtime_error(
1356                                            format!("Attempted to index a list with an out-of-bounds index (index: {}, length: {})", index, list.values.len()).as_str(),
1357                                            &state,
1358                                            &modules,
1359                                        );
1360                                        return InterpretResult::InterpretRuntimeError;
1361                                    }
1362                                    list.values[index].clone()
1363                                }
1364                                HeapObjVal::PhoenixString(ref s) => {
1365                                    let val = match s.value.chars().nth(index) {
1366                                        Some(c) => c,
1367                                        None => {
1368                                            self.runtime_error(
1369                                                format!("Attempted to index a string with an out-of-bounds index (index: {}, length: {})", index, s.value.len()).as_str(),
1370                                                &state,
1371                                                &modules,
1372                                            );
1373                                            return InterpretResult::InterpretRuntimeError;
1374                                        }
1375                                    };
1376                                    // todo: think about creating an extra type for strings that only have a short life time and put them on the stackk
1377                                    let string_obj = ObjString::new(val.to_string());
1378                                    state.alloc(HeapObj::new_string(string_obj))
1379                                }
1380                                _ => {
1381                                    self.runtime_error(
1382                                        "Attempted to index a non-indexable value",
1383                                        &state,
1384                                        &modules,
1385                                    );
1386                                    return InterpretResult::InterpretRuntimeError;
1387                                }
1388                            };
1389                            state.stack.push(value);
1390                        }
1391                        _ => {
1392                            self.runtime_error(
1393                                "Attempted to index a non-indexable value",
1394                                &state,
1395                                &modules,
1396                            );
1397                            return InterpretResult::InterpretRuntimeError;
1398                        }
1399                    }
1400                }
1401                OpSetIndex => {
1402                    // the new value
1403                    let value = state.pop();
1404                    // the index
1405                    let index = match state.pop() {
1406                        Value::Long(i) => i as usize,
1407                        Value::Float(i) => i as usize,
1408                        _ => {
1409                            self.runtime_error(
1410                                "Attempted to index a non-iterable value",
1411                                &state,
1412                                &modules,
1413                            );
1414                            return InterpretResult::InterpretRuntimeError;
1415                        }
1416                    };
1417                    // the target (list or string)
1418                    let target = state.pop();
1419                    match target {
1420                        Value::PhoenixPointer(heap_index) => {
1421                            // get the heapObject from the allocated lists
1422                            let v = state.deref(heap_index);
1423                            match &v.obj {
1424                                HeapObjVal::PhoenixList(_) => {
1425                                    let o = state.deref_mut(heap_index);
1426                                    let list = if let HeapObjVal::PhoenixList(list) = &mut o.obj {
1427                                        list
1428                                    } else {
1429                                        unreachable!(
1430                                            "We just checked that the heap object is a list"
1431                                        )
1432                                    };
1433                                    if list.values.len() <= index {
1434                                        self.runtime_error(
1435                                            format!("Attempted to index a list with an out-of-bounds index (index: {}, length: {})", index, list.values.len()).as_str(),
1436                                            &state,
1437                                            &modules,
1438                                        );
1439                                        return InterpretResult::InterpretRuntimeError;
1440                                    }
1441                                    list.values[index] = value;
1442                                }
1443                                HeapObjVal::PhoenixString(_) => {
1444                                    if let Value::PhoenixPointer(new_val) = value {
1445                                        let new_val = state.deref_string(new_val).clone();
1446                                        let o = state.deref_mut(heap_index);
1447                                        let s = if let HeapObjVal::PhoenixString(s) = &mut o.obj {
1448                                            s
1449                                        } else {
1450                                            unreachable!(
1451                                                "We just checked that the heap object is a string"
1452                                            )
1453                                        };
1454                                        if s.value.len() <= index {
1455                                            self.runtime_error(
1456                                                format!("Attempted to index a string with an out-of-bounds index (index: {}, length: {})", index, s.value.len()).as_str(),
1457                                                &state,
1458                                                &modules,
1459                                            );
1460                                            return InterpretResult::InterpretRuntimeError;
1461                                        }
1462                                        let mut new_string = String::new();
1463                                        for (i, c) in s.value.chars().enumerate() {
1464                                            if i == index {
1465                                                new_string.push(match new_val.value.parse() {
1466                                                    Ok(v) => v,
1467                                                    Err(_) => {
1468                                                        self.runtime_error(
1469                                                            "Attempted to set a string index to a non-string value",
1470                                                            &state,
1471                                                            &modules,
1472                                                        );
1473                                                        return InterpretResult::InterpretRuntimeError;
1474                                                    }
1475                                                });
1476                                            } else {
1477                                                new_string.push(c);
1478                                            }
1479                                        }
1480                                        s.value = new_string;
1481                                    } else {
1482                                        self.runtime_error(
1483                                            "Attempted to set a string index to a non-string value",
1484                                            &state,
1485                                            &modules,
1486                                        );
1487                                        return InterpretResult::InterpretRuntimeError;
1488                                    }
1489                                }
1490                                _ => {
1491                                    self.runtime_error(
1492                                        "Attempted to index a non-indexable value",
1493                                        &state,
1494                                        &modules,
1495                                    );
1496                                    return InterpretResult::InterpretRuntimeError;
1497                                }
1498                            };
1499                        }
1500                        _ => {
1501                            self.runtime_error(
1502                                "Attempted to index a non-indexable value",
1503                                &state,
1504                                &modules,
1505                            );
1506                            return InterpretResult::InterpretRuntimeError;
1507                        }
1508                    }
1509                    // todo: find out if this is needed
1510                    // state.stack.push(target);
1511                    state.stack.push(Value::Nil);
1512                }
1513                OpCreateList(size) => {
1514                    let mut list = Vec::new();
1515                    for _ in 0..size {
1516                        list.push(state.pop());
1517                    }
1518                    list.reverse();
1519                    // allocate the list
1520                    let list_obj = ObjList::new(list);
1521                    let ptr = state.alloc(HeapObj::new_list(list_obj));
1522                    state.stack.push(ptr);
1523                }
1524                OpCreateString => {
1525                    let s = state.pop();
1526                    if let Value::PhoenixString(s) = s {
1527                        let ptr = state.alloc(HeapObj::new_string(ObjString::new(s)));
1528                        state.stack.push(ptr);
1529                    } else {
1530                        self.runtime_error(
1531                            "Attempted to create a string from a non-string value",
1532                            &state,
1533                            &modules,
1534                        );
1535                        return InterpretResult::InterpretRuntimeError;
1536                    }
1537                }
1538                OpCreateHashMap(n) => {
1539                    let mut map = HashMap::new();
1540                    for _ in 0..n {
1541                        let value = state.pop();
1542                        let key = state.pop();
1543                        map.insert(key, value);
1544                    }
1545                    let map_obj = ObjHashMap::new(map);
1546                    let ptr = state.alloc(HeapObj::new_hashmap(map_obj));
1547                    state.stack.push(ptr);
1548                }
1549                OpWait => {
1550                    let mut stdout = stdout();
1551                    // we know that the last value on the stack is the string we want to print
1552                    let s = state.pop().to_string(self, &state, &modules);
1553                    stdout.write(format!("{}", s).as_bytes()).unwrap();
1554                    stdout.flush().unwrap();
1555                    let mut buffer = String::new();
1556
1557                    stdin()
1558                        .read_line(&mut buffer)
1559                        .expect("Failed to read line");
1560                    sleep(Duration::from_millis(100));
1561                }
1562            }
1563        }
1564    }
1565}
1566
1567fn debug_state_trace(state: &VMState, _vm: &VM, modules: &[ModuleChunk]) {
1568    eprintln!("> Frame: {:?}", state.current_frame);
1569    eprintln!("> Stack: ");
1570    eprint!(">>");
1571    for value in state.stack.iter() {
1572        eprint!(" [ {:?} ] ", value);
1573    }
1574    eprintln!();
1575    eprintln!("> Frames: ");
1576    eprint!(">>");
1577    for value in state.frames.iter() {
1578        eprint!(" [ {:?} ] ", value);
1579    }
1580    eprintln!();
1581    eprintln!("> Globals: ");
1582    for (index, val) in state.globals[state.current_frame.module].iter().enumerate() {
1583        if let Global::Init(global) = val {
1584            eprintln!(
1585                ">> {} => {:?}",
1586                VM::get_variable_name(index, state, modules),
1587                global
1588            );
1589        }
1590    }
1591    debug_instances(state);
1592}
1593
1594fn debug_instances(state: &VMState) {
1595    eprintln!("> Instances: ");
1596    for (i, instance) in state.gc.instances.iter().enumerate() {
1597        eprintln!(">> [{}] {:?}", i, instance)
1598    }
1599}
1600
1601fn debug_trace(vm: &VM, instr: &Instr, state: &VMState, modules: &[ModuleChunk]) {
1602    eprintln!("---");
1603    eprint!("> Next instr (#{}): ", state.current_frame.ip - 1);
1604    disassemble_instruction(
1605        instr,
1606        state.current_frame.ip - 1,
1607        &modules[state.current_frame.module].constants,
1608        &modules[state.current_frame.module].identifiers,
1609    );
1610    debug_state_trace(state, vm, modules);
1611    eprintln!("---\n");
1612}
1613
1614pub fn debug_print_constants(modules: &[ModuleChunk]) {
1615    eprintln!("---");
1616    eprintln!("> Constants");
1617    for m in modules.iter() {
1618        eprintln!("--- [ module: {:?} ] ---", m.name);
1619        for val in m.constants.iter() {
1620            eprintln!("\t>> [ {:?} ]", val);
1621        }
1622    }
1623    eprintln!("---\n");
1624    // debug all m.identifiers
1625    eprintln!("---");
1626    eprintln!("> Identifiers");
1627    for m in modules.iter() {
1628        eprintln!("--- [ module: {:?} ] ---", m.name);
1629        for (i, val) in m.identifiers.iter().enumerate() {
1630            eprintln!("\t>> [ {} ] => {:?}", i, val);
1631        }
1632    }
1633    eprintln!("---\n");
1634}