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}