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
        )
    }
}