1use crate::code::Code;
6use crate::frame::Frame;
7use crate::instruction_table::InstructionTable;
8use crate::stack::Stack;
9use crate::table::Table;
10use std::fmt;
11
12pub struct Machine<'a, T: 'a + fmt::Debug> {
21 pub code: Code<T>,
22 pub instruction_table: &'a InstructionTable<T>,
23 pub ip: usize,
24 pub constants: &'a Table<Item = T>,
25 pub call_stack: Stack<Frame<T>>,
26 pub operand_stack: Stack<T>,
27}
28
29impl<'a, T: 'a + fmt::Debug> Machine<'a, T> {
30 pub fn new(
35 code: Code<T>,
36 constants: &'a Table<Item = T>,
37 instruction_table: &'a InstructionTable<T>,
38 ) -> Machine<'a, T> {
39 let frame: Frame<T> = Frame::new(code.code.len());
40 let mut call_stack = Stack::new();
41 call_stack.push(frame);
42
43 Machine {
44 code,
45 instruction_table,
46 ip: 0,
47 constants,
48 call_stack,
49 operand_stack: Stack::new(),
50 }
51 }
52
53 pub fn run(&mut self) {
64 loop {
65 if self.ip == self.code.code.len() {
66 break;
67 }
68
69 let op_code = self.next_code();
70 let arity = self.next_code();
71
72 let instr = self
73 .instruction_table
74 .by_op_code(op_code)
75 .unwrap_or_else(|| panic!("Unable to find instruction with op code {}", op_code));
76
77 let mut args: Vec<usize> = vec![];
78
79 for _i in 0..arity {
80 args.push(self.next_code());
81 }
82
83 let fun = instr.fun;
84 fun(self, args.as_slice());
85 }
86 }
87
88 #[inline]
91 fn next_code(&mut self) -> usize {
92 let code = self.code.code[self.ip];
93 self.ip += 1;
94 code
95 }
96
97 pub fn get_local(&self, name: &str) -> Option<&T> {
102 self.call_stack.peek().get_local(name)
103 }
104
105 pub fn get_local_deep(&self, name: &str) -> Option<&T> {
111 for frame in self.call_stack.as_slice().iter().rev() {
112 let local = frame.get_local(name);
113 if local.is_some() {
114 return local;
115 }
116 }
117 None
118 }
119
120 pub fn set_local(&mut self, name: &str, value: T) {
124 self.call_stack.peek_mut().set_local(name, value)
125 }
126
127 pub fn operand_push(&mut self, value: T) {
129 self.operand_stack.push(value);
130 }
131
132 pub fn operand_pop(&mut self) -> T {
134 self.operand_stack.pop()
135 }
136
137 pub fn get_data(&self, idx: usize) -> &T {
139 self.code
140 .data
141 .get(idx)
142 .unwrap_or_else(|| panic!("Constant data is not present at index {}.", idx))
143 }
144
145 pub fn jump(&mut self, label: &str) {
153 self.ip = self
154 .code
155 .get_label_ip(label)
156 .unwrap_or_else(|| panic!("Attempted to jump to unknown label {}", label));
157 }
158
159 pub fn call(&mut self, label: &str) {
171 self.call_stack.push(Frame::new(self.ip));
172 self.jump(label);
173 }
174
175 pub fn ret(&mut self) {
189 let frame = self.call_stack.pop();
190 self.ip = frame.return_address;
191 }
192}
193
194#[cfg(test)]
195mod test {
196 use super::*;
197 use crate::builder::Builder;
198 use crate::instruction::Instruction;
199 use crate::instruction_table::InstructionTable;
200 use crate::write_many_table::WriteManyTable;
201
202 fn push(machine: &mut Machine<usize>, args: &[usize]) {
203 let arg = &machine.code.data[args[0]];
204 machine.operand_stack.push(*arg);
205 }
206
207 fn add(machine: &mut Machine<usize>, _args: &[usize]) {
208 let rhs = machine.operand_pop();
209 let lhs = machine.operand_pop();
210 machine.operand_stack.push(lhs + rhs);
211 }
212
213 fn instruction_table() -> InstructionTable<usize> {
214 let mut it = InstructionTable::new();
215 it.insert(Instruction::new(1, "push", 1, push));
216 it.insert(Instruction::new(2, "add", 0, add));
217 it
218 }
219
220 #[test]
221 fn new() {
222 let it = instruction_table();
223 let builder: Builder<usize> = Builder::new(&it);
224 let constants: WriteManyTable<usize> = WriteManyTable::new();
225 let machine = Machine::new(Code::from(builder), &constants, &it);
226 assert_eq!(machine.ip, 0);
227 assert!(!machine.call_stack.is_empty());
228 assert!(machine.operand_stack.is_empty());
229 }
230
231 #[test]
232 fn run() {
233 let it = instruction_table();
234 let mut builder: Builder<usize> = Builder::new(&it);
235 builder.push("push", vec![2]);
236 builder.push("push", vec![3]);
237 builder.push("add", vec![]);
238 let constants: WriteManyTable<usize> = WriteManyTable::new();
239 let mut machine = Machine::new(Code::from(builder), &constants, &it);
240 machine.run();
241 let result = machine.operand_stack.pop();
242 assert_eq!(result, 5);
243 }
244
245 #[test]
246 fn get_local() {
247 let it = instruction_table();
248 let builder: Builder<usize> = Builder::new(&it);
249 let constants: WriteManyTable<usize> = WriteManyTable::new();
250 let mut machine = Machine::new(Code::from(builder), &constants, &it);
251 assert!(machine.get_local("example").is_none());
252 machine.set_local("example", 13);
253 assert!(machine.get_local("example").is_some());
254 }
255
256 #[test]
257 fn get_local_deep() {
258 let it = instruction_table();
259 let mut builder: Builder<usize> = Builder::new(&it);
260 builder.label("next");
261
262 let constants: WriteManyTable<usize> = WriteManyTable::new();
263 let mut machine = Machine::new(Code::from(builder), &constants, &it);
264 machine.set_local("outer", 13);
265 assert_eq!(*machine.get_local_deep("outer").unwrap(), 13);
266 machine.call("next");
267 machine.set_local("outer", 14);
268 machine.set_local("inner", 15);
269 assert_eq!(*machine.get_local_deep("outer").unwrap(), 14);
270 assert_eq!(*machine.get_local_deep("inner").unwrap(), 15);
271 machine.ret();
272 assert_eq!(*machine.get_local_deep("outer").unwrap(), 13);
273 assert!(machine.get_local_deep("inner").is_none());
274 }
275
276 #[test]
277 fn set_local() {
278 let it = instruction_table();
279 let builder: Builder<usize> = Builder::new(&it);
280 let constants: WriteManyTable<usize> = WriteManyTable::new();
281 let mut machine = Machine::new(Code::from(builder), &constants, &it);
282 assert!(machine.get_local("example").is_none());
283 machine.set_local("example", 13);
284 assert_eq!(*machine.get_local("example").unwrap(), 13);
285 }
286}