1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
use crate::{Ref, Value}; // We need BTreeMap to implement the 'Heap' (registers) use alloc::collections::BTreeMap; // For ToString generics use alloc::string::{String, ToString}; // We need Vec for the dynamically allocated stack use alloc::vec::Vec; // For implementing Display and Debug use core::fmt::{Display, Error, Formatter}; #[derive(Default, Clone, PartialEq, PartialOrd)] pub struct Machine { /// A dynamically allocated stack to push and pop values onto and off of pub stack: Vec<Ref<Value>>, /// The place to store named values (variables) pub registers: BTreeMap<String, Ref<Value>>, } impl Machine { /// Return a new instance of a Machine with empty stack and registers pub fn new() -> Self { Machine { stack: Vec::new(), registers: BTreeMap::new(), } } /// #################################################### /// The following functions are meant to be used to /// interface and interact with the virtual machine /// #################################################### /// FOR FOREIGN FUNCTIONS /// This gets an argument from the call to this foreign /// function by popping a value off the stack, and removing /// the reference pub fn get_arg(&mut self) -> Value { (*self.pop()).clone() } /// FOR FOREIGN FUNCTIONS /// This pushes a return value onto the stack pub fn return_value(&mut self, value: Value) { self.push(Ref::new(value)) } /// #################################################### /// The following functions represent instructions that /// are natively supported by the virtual machine. These /// are not meant to be used by foreign functions, but /// they CAN be used without worry. /// #################################################### /// This function duplicates the current machine. This is /// VERY IMPORTANT. It iterates through the stack and copies /// each item into a new machine. /// /// This is ONLY used to create the context for functions. /// If we don't do this, the context Machine never goes out /// of scope, and never lets the Refs die. This causes a /// memory leak. /// /// The addition of this method fixes the memory leak. pub fn duplicate(self) -> Self { let mut new = Self::new(); // Copy the stack for the new machine for item in self.stack { new.push((*item).clone().copy()); } // Copy the registers for the new machine for (key, value) in self.registers { new.registers.insert(key.to_string(), value.copy()); } // Return new machine new } /// Push an item onto the stack pub fn push(&mut self, value: Ref<Value>) { self.stack.push(value); } /// Pop an item off of the stack, and return it /// If the stack is empty, return an Error pub fn pop(&mut self) -> Ref<Value> { match self.stack.pop() { Some(v) => v, None => Value::error("Popped from empty stack, called function with too few arguments"), } } /// 1) Pop off a REFERENCE value from the stack /// 2) Push a copy the object and remove the reference pub fn copy(&mut self) { let value = self.pop(); self.push(value.copy()); } /// 1) Pop off a REFERENCE value from the stack /// 2) Pop off a VALUE value from the stack /// 3) Assign the value of VALUE to the memory location of REFERENCE /// /// This can be used to assign to an indexed value from a list or table pub fn assign(&mut self) { let reference = self.pop(); let value = self.pop(); // We cant 'safely' modify a shared reference to a value, // so we need to convert to a mutable pointer in an unsafe block unsafe { // Take the Ref and convert it to a mutable pointer let ptr = Ref::into_raw(reference) as *mut Value; // Assign to the contents of the mutable pointer *ptr = (*value).clone(); // Re-wrap the pointer in a Ref value to properly manage it again Ref::from_raw(ptr as *const Value); } } /// 1) Pop off the INDEX value from the stack /// 2) Pop off a TABLE value from the stack /// 3) Push the TABLE[INDEX] reference onto the stack pub fn index(&mut self) { let index = self.pop(); let table = self.pop(); let result; // We cant 'safely' modify a shared reference to a value, // so we need to convert to a mutable pointer in an unsafe block unsafe { // Take the Ref and convert it to a mutable pointer let ptr = Ref::into_raw(table) as *mut Value; // Get the indexed value from the pointer to the table in memory result = (*ptr).index(index); // Re-wrap the pointer in a Ref value to properly manage it again Ref::from_raw(ptr as *const Value); } self.push(result); } /// 1) Pop off the INDEX value from the stack /// 2) Pop off a TABLE value from the stack /// 3) Push the TABLE onto the stack /// 4) Call the value at TABLE[INDEX] as a function pub fn method_call(&mut self) { let index = self.pop(); let table = self.pop(); // This is the `self` value to be passed to the function // The `self` value cannot be directly assigned to, // HOWEVER, its members / attributes can be assigned to self.push(Ref::clone(&table)); self.push(table); self.push(index); self.index(); self.call(); } /// 1) Pop off function from the stack /// 2) Call it with this Machine instance pub fn call(&mut self) { let function = self.pop(); function.call(self); } /// 1) Pop off a CONDITION function from the stack /// 2) Pop off a BODY function from the stack /// 3) Call the CONDITION function with the context of this instance /// 4) If the return value is true, run the BODY function with the context of this instance /// 5) Goto step 3 pub fn while_loop(&mut self) { let condition = self.pop(); let body = self.pop(); // This will take the top item of the stack and convert it to a bool let get_condition = |machine: &mut Machine| -> bool { (*machine.pop()).clone().into() }; // First, get the condition condition.call_global(self); while get_condition(self) { // If the condition is true, run the body of the while loop body.call_global(self); // Push the condition again to test on the next iteration condition.call_global(self); } } /// 1) Pop off a CONDITION function from the stack /// 2) Pop off a THEN function from the stack /// 3) Pop off an ELSE function from the stack /// 4) Call the CONDITION function with the context of this instance /// 5) If the return value is true, run the THEN function with the context of this instance /// 6) If the return value is false, run the ELSE function with the context of this instance pub fn if_then_else(&mut self) { let condition = self.pop(); let then_fn = self.pop(); let else_fn = self.pop(); // This will take the top item of the stack and convert it to a bool let get_condition = |machine: &mut Machine| -> bool { (*machine.pop()).clone().into() }; // First, get the condition condition.call_global(self); if get_condition(self) { // If the condition is true, run the body of the while loop then_fn.call_global(self); } else { // Push the condition again to test on the next iteration else_fn.call_global(self); } } /// 1) Pop off a KEY value from the stack /// 2) Pop off a VALUE value from the stack /// 3) Assign the value of VALUE to the register named KEY pub fn store(&mut self) { // The register to assign to let key = self.pop(); // The value to assign to it let value = self.pop(); // registers[key] = value self.registers.insert(key.to_string(), value); } /// 1) Pop off a KEY value from the stack /// 2) Push the value in the register named KEY to the stack pub fn load(&mut self) { let key = &self.pop().to_string(); // The reason we don't do an if-let expression here is the fact // that we can't borrow self as both mutable and immutable at once if self.registers.contains_key(key) { self.push(Ref::clone(self.registers.get(key).unwrap())); } else { self.push(Value::error(format!("No register named {}", key))); } } } /// How to print Machine / convert Machine to string /// This is for debugging code and seeing the current instance of the machine impl Display for Machine { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { write!( f, "Machine {{\n\tstack: {:?}\n\theap: {:?}\n}}", self.stack, self.registers ) } }