passerine/vm/
mod.rs

1//! This module contains the core VM implementation.
2//! Note that these modules are public for documentation visiblility,
3//! But should never be used outside of the module by `common` or `compiler`.
4
5pub mod tag;
6pub mod stack;
7pub mod trace;
8pub mod slot;
9
10use std::mem;
11
12use crate::common::{
13    number::build_number,
14    data::Data,
15    opcode::Opcode,
16    lambda::Captured,
17    closure::Closure,
18    span::Span,
19};
20
21use crate::vm::{
22    trace::Trace,
23    slot::Suspend,
24    stack::Stack,
25};
26
27/// A `VM` executes bytecode lambda closures.
28/// (That's a mouthful - think bytecode + some context).
29/// VM initialization overhead is tiny,
30/// and each VM's state is self-contained,
31/// so more than one can be spawned if needed.
32#[derive(Debug)]
33pub struct VM {
34    pub closure: Closure,
35    pub stack:   Stack,
36    pub ip:      usize,
37}
38
39// NOTE: use Opcode::same and Opcode.to_byte() rather than actual bytes
40// Don't worry, the compiler *should* get rid of this overhead and just use bytes
41
42// this impl contains initialization, helper functions, and the core interpreter loop
43// the next impl contains opcode implementations
44impl VM {
45    /// Initialize a new VM.
46    /// To run the VM, a lambda must be passed to it through `run`.
47    pub fn init(closure: Closure) -> VM {
48        let mut vm = VM {
49            closure,
50            stack: Stack::init(),
51            ip:    0,
52        };
53        vm.stack.declare(vm.closure.lambda.decls);
54        vm
55    }
56
57    /// Advances to the next instruction.
58    #[inline]
59    pub fn next(&mut self)                           { self.ip += 1; }
60    /// Advances IP, returns `Ok`. Used in Bytecode implementations.
61    #[inline]
62    pub fn done(&mut self)      -> Result<(), Trace> { self.next(); Ok(()) }
63    /// Returns the current instruction as a byte.
64    #[inline]
65    pub fn peek_byte(&mut self) -> u8                { self.closure.lambda.code[self.ip] }
66    /// Advances IP and returns the current instruction as a byte.
67    #[inline]
68    pub fn next_byte(&mut self) -> u8                { self.next(); self.peek_byte() }
69
70    /// Returns whether the program has terminated
71    #[inline]
72    pub fn is_terminated(&mut self) -> bool {
73        self.ip >= self.closure.lambda.code.len()
74    }
75
76    /// Builds the next number in the bytecode stream.
77    /// See `utils::number` for more.
78    #[inline]
79    pub fn next_number(&mut self) -> usize {
80        self.next();
81        let remaining      = &self.closure.lambda.code[self.ip..];
82        let (index, eaten) = build_number(remaining);
83        self.ip += eaten - 1; // ip left on next op
84        index
85    }
86
87    #[inline]
88    pub fn current_span(&self) -> Span {
89        self.closure.lambda.index_span(self.ip)
90    }
91
92    // core interpreter loop
93
94    /// Dissasembles and interprets a single (potentially fallible) bytecode op.
95    /// The op definitions follow in the next `impl` block.
96    /// To see what each op does, check `common::opcode::Opcode`.
97    pub fn step(&mut self) -> Result<(), Trace> {
98        let opcode = Opcode::from_byte(self.peek_byte());
99
100        match opcode {
101            Opcode::Con     => self.con(),
102            Opcode::NotInit => self.not_init(),
103            Opcode::Del     => self.del(),
104            Opcode::FFICall => self.ffi_call(),
105            Opcode::Copy    => self.copy_val(),
106            Opcode::Capture => self.capture(),
107            Opcode::Save    => self.save(),
108            Opcode::SaveCap => self.save_cap(),
109            Opcode::Load    => self.load(),
110            Opcode::LoadCap => self.load_cap(),
111            Opcode::Call    => self.call(),
112            Opcode::Return  => self.return_val(),
113            Opcode::Closure => self.closure(),
114            Opcode::Print   => self.print(),
115            Opcode::Label   => self.label(),
116            Opcode::Tuple   => self.tuple(),
117            Opcode::UnData  => self.un_data(),
118            Opcode::UnLabel => self.un_label(),
119            Opcode::UnTuple => self.un_tuple(),
120        }
121    }
122
123    pub fn unwind(&mut self) {
124        // restore suspended callee
125        let suspend = self.stack.pop_frame(); // remove the frame
126        self.ip      = suspend.ip;
127        self.closure = suspend.closure;
128
129        // indicate failure
130        self.stack.push_not_init();
131    }
132
133    /// Suspends the current lambda and runs a new one on the VM.
134    /// Runs until either success, in which it restores the state of the previous lambda,
135    /// Or failure, in which it returns the runtime error.
136    /// In the future, fibers will allow for error handling -
137    /// right now, error in Passerine are practically panics.
138    pub fn run(&mut self) -> Result<(), Trace> {
139        // println!("Starting\n{}", self.closure.lambda);
140        let mut result = Ok(());
141
142        while !self.is_terminated() {
143            // println!("before: {:#?}", self.stack.stack);
144            // println!("executing: {:?}", Opcode::from_byte(self.peek_byte()));
145            result = self.step();
146            if result.is_err() { break; }
147            // println!("---");
148        }
149        // println!("after: {:?}", self.stack.stack);
150        // println!("---");
151
152        if let Err(mut trace) = result {
153            while self.stack.unwind_frame() {
154                self.unwind();
155                self.ip -= 1;
156                trace.add_context(self.current_span());
157            }
158
159            result = Err(trace);
160        };
161
162        result
163    }
164
165    // TODO: there are a lot of optimizations that can be made
166    // I'll list a few here:
167    // - searching the stack for variables
168    //   A global Hash-table has significantly less overhead for function calls
169    // - cloning the heck out of everything - useless copies
170    //   instead, lifetime analysis during compilation
171    // - replace some panics with Result<()>s
172
173    /// Load a constant and push it onto the stack.
174    #[inline]
175    pub fn con(&mut self) -> Result<(), Trace> {
176        // get the constant index
177        let index = self.next_number();
178
179        self.stack.push_data(self.closure.lambda.constants[index].clone());
180        self.done()
181    }
182
183    #[inline]
184    pub fn not_init(&mut self) -> Result<(), Trace> {
185        self.stack.push_not_init();
186        self.done()
187    }
188
189    /// Moves the top value on the stack to the heap,
190    /// replacing it with a reference to the heapified value.
191    #[inline]
192    pub fn capture(&mut self) -> Result<(), Trace> {
193        let index = self.next_number();
194        self.stack.heapify(index);   // move value to the heap
195        self.done()
196    }
197
198    /// Save the topmost value on the stack into a variable.
199    #[inline]
200    pub fn save(&mut self) -> Result<(), Trace> {
201        let index = self.next_number();
202        self.stack.set_local(index);
203        self.done()
204    }
205
206    /// Save the topmost value on the stack into a captured variable.
207    #[inline]
208    pub fn save_cap(&mut self) -> Result<(), Trace> {
209        let index = self.next_number();
210        let data  = self.stack.pop_data();
211        mem::drop(self.closure.captures[index].replace(data));
212        self.done()
213    }
214
215    /// Push a copy of a variable's value onto the stack.
216    #[inline]
217    pub fn load(&mut self) -> Result<(), Trace> {
218        let index = self.next_number();
219        let mut data = self.stack.local_data(index);
220
221        if let Data::Heaped(d) = data { data = d.borrow().to_owned() };
222        if let Data::NotInit = data {
223            return Err(Trace::error(
224                "Reference",
225                "This local variable was referenced before assignment",
226                vec![self.current_span()],
227            ));
228        };
229
230        self.stack.push_data(data);
231        self.done()
232    }
233
234    /// Load a captured variable from the current closure.
235    #[inline]
236    pub fn load_cap(&mut self) -> Result<(), Trace> {
237        let index = self.next_number();
238        let data = self.closure.captures[index].borrow().to_owned();
239
240        if let Data::NotInit = data {
241            return Err(Trace::error(
242                "Reference",
243                "This captured variable was referenced before assignment",
244                vec![self.current_span()],
245            ));
246        };
247
248        self.stack.push_data(data);
249        self.done()
250    }
251
252    /// Delete the top item of the stack.
253    #[inline]
254    pub fn del(&mut self) -> Result<(), Trace> {
255        mem::drop(self.stack.pop_data());
256        self.done()
257    }
258
259    /// Copy the top data of the stack, i.e.
260    /// `[F, D]` becomes `[F, D, D]`.
261    #[inline]
262    pub fn copy_val(&mut self) -> Result<(), Trace> {
263        let data = self.stack.pop_data();
264        self.stack.push_data(data.clone());
265        self.stack.push_data(data);
266        self.done()
267    }
268
269    #[inline]
270    pub fn print(&mut self) -> Result<(), Trace> {
271        let data = self.stack.pop_data();
272        println!("{}", data);
273        self.stack.push_data(data);
274        self.done()
275    }
276
277    #[inline]
278    pub fn label(&mut self) -> Result<(), Trace> {
279        let kind = match self.stack.pop_data() {
280            Data::Kind(n) => n,
281            _ => unreachable!(),
282        };
283        let data = self.stack.pop_data();
284        self.stack.push_data(Data::Label(Box::new(kind), Box::new(data)));
285        self.done()
286    }
287
288    #[inline]
289    pub fn tuple(&mut self) -> Result<(), Trace> {
290        let index = self.next_number();
291        let mut items = vec![];
292        for _ in 0..index {
293            items.push(self.stack.pop_data())
294        }
295
296        items.reverse();
297        self.stack.push_data(Data::Tuple(items));
298        self.done()
299    }
300
301    fn un_data(&mut self) -> Result<(), Trace> {
302        let expected = self.stack.pop_data();
303        let data = self.stack.pop_data();
304
305        if data != expected {
306            return Err(Trace::error(
307                "Pattern Matching",
308                &format!("The data '{}' does not match the expected data '{}'", data, expected),
309                vec![self.current_span()],
310            ));
311        }
312
313        self.done()
314    }
315
316    fn un_label(&mut self) -> Result<(), Trace> {
317        let kind = match self.stack.pop_data() {
318            Data::Kind(n) => n,
319            _ => unreachable!(),
320        };
321
322        let d = match self.stack.pop_data() {
323            Data::Label(n, d) if *n == kind => d,
324            other => return Err(Trace::error(
325                "Pattern Matching",
326                &format!("The data '{}' does not match the Label '{}'", other, kind),
327                vec![self.current_span()],
328            )),
329        };
330
331        self.stack.push_data(*d);
332        self.done()
333    }
334
335    fn un_tuple(&mut self) -> Result<(), Trace> {
336        let index = self.next_number();
337        let t = match self.stack.pop_data() {
338            Data::Tuple(t) => t,
339            other => return Err(Trace::error(
340                "Pattern Matching",
341                &format!("The data '{}' is not a tuple", other),
342                vec![self.current_span()],
343            )),
344        };
345
346        let length = t.len();
347        if index >= length {
348            return Err(Trace::error(
349                "Indexing",
350                &format!(
351                    "The tuple '{}' is of length {}, so the index {} is out-of-bounds",
352                    Data::Tuple(t), length, index
353                ),
354                vec![self.current_span()],
355            ));
356        }
357
358        let data = t[index].clone();
359        self.stack.push_data(Data::Tuple(t));
360        self.stack.push_data(data);
361        self.done()
362    }
363
364    /// Call a function on the top of the stack, passing the next value as an argument.
365    pub fn call(&mut self) -> Result<(), Trace> {
366        // get the function and argument to run
367        let fun = match self.stack.pop_data() {
368            Data::Closure(c) => *c,
369            o => return Err(Trace::error(
370                "Call",
371                &format!("The data '{}' is not a function and can not be called", o),
372                vec![self.current_span()],
373            )),
374        };
375        let arg = self.stack.pop_data();
376
377        // TODO: make all programs end in return,
378        // so bounds check (i.e. is_terminated) is never required
379        self.next();
380        let tail_call = !self.is_terminated()
381                     && Opcode::Return
382                     == Opcode::from_byte(self.peek_byte());
383
384        // clear the stack if there's a tail call
385        // we must do this before we suspend the calling context
386        if tail_call {
387            let locals = self.next_number();
388            for _ in 0..locals { self.del()?; }
389        }
390
391        // suspend the calling context
392        let old_closure = mem::replace(&mut self.closure, fun);
393        let old_ip      = mem::replace(&mut self.ip,      0);
394        let suspend = Suspend {
395            ip: old_ip,
396            closure: old_closure,
397        };
398
399        // if there's a tail call, we don't bother pushing a new frame
400        // the topmost frame doesn't carry any context;
401        // that context is intrinsic to the VM itself.
402        if !tail_call {
403            self.stack.push_frame(suspend);
404        }
405
406        // set up the stack for the function call
407        // self.stack.push_frame(suspend);
408        self.stack.declare(self.closure.lambda.decls);
409        self.stack.push_data(arg);
410
411        // println!("{}", self.closure.lambda);
412
413        Ok(())
414    }
415
416    /// Return a value from a function.
417    /// End the execution of the current lambda.
418    /// Takes the number of locals on the stack
419    /// Relpaces the last frame with the value on the top of the stack.
420    /// Expects the stack to be a `[..., Frame, Local 1, ..., Local N, Data]`
421    pub fn return_val(&mut self) -> Result<(), Trace> {
422        // the value to be returned
423        let val = self.stack.pop_data();
424
425        // clear all locals
426        let locals = self.next_number();
427        for _ in 0..locals { self.del()?; }
428
429        // restore suspended callee
430        let suspend = self.stack.pop_frame(); // remove the frame
431        self.ip      = suspend.ip;
432        self.closure = suspend.closure;
433
434        // push return value
435        self.stack.push_data(val); // push the return value
436        Ok(())
437    }
438
439    pub fn closure(&mut self) -> Result<(), Trace> {
440        let index = self.next_number();
441
442        let lambda = match self.closure.lambda.constants[index].clone() {
443            Data::Lambda(lambda) => lambda,
444            _ => unreachable!("Expected a lambda to be wrapped with a closure"),
445        };
446
447        let mut closure = Closure::wrap(lambda);
448
449        for captured in closure.lambda.captures.iter() /* .rev */ {
450            let reference = match captured {
451                Captured::Local(index) => {
452                    match self.stack.local_data(*index) {
453                        Data::Heaped(h) => h,
454                        _ => unreachable!("Expected data to be on the heap"),
455                    }
456                },
457                Captured::Nonlocal(upvalue) => self.closure.captures[*upvalue].clone(),
458            };
459            closure.captures.push(reference)
460        }
461
462        self.stack.push_data(Data::Closure(Box::new(closure)));
463        self.done()
464    }
465
466    pub fn ffi_call(&mut self) -> Result<(), Trace> {
467        let index = self.next_number();
468        let ffi_function = &self.closure.lambda.ffi[index];
469
470        let argument = self.stack.pop_data();
471        let returned = match ffi_function.call(argument) {
472            Ok(d) => d,
473            Err(e) => return Err(Trace::error(
474                "FFI Call", &e, vec![self.current_span()],
475            )),
476        };
477
478        self.stack.push_data(returned);
479        self.done()
480    }
481}
482
483#[cfg(test)]
484mod test {
485    use super::*;
486    use crate::compiler::{
487        lex::lex,
488        parse::parse,
489        desugar::desugar,
490        hoist::hoist,
491        gen::gen,
492    };
493    use crate::common::source::Source;
494
495    fn inspect(source: &str) -> VM {
496        let lambda = lex(Source::source(source))
497            .and_then(parse)
498            .and_then(desugar)
499            .and_then(hoist)
500            .and_then(gen)
501            .map_err(|e| println!("{}", e))
502            .unwrap();
503
504        // println!("{:?}", lambda);
505        let mut vm = VM::init(Closure::wrap(lambda));
506
507        match vm.run() {
508            Ok(()) => vm,
509            Err(e) => {
510                println!("{}", e);
511                panic!();
512            },
513        }
514    }
515
516    #[test]
517    fn init_run() {
518        inspect("x = 0.0");
519    }
520
521    #[test]
522    fn block_expression() {
523        inspect("x = false; boop = true; heck = { x = boop; x }; heck");
524    }
525
526    #[test]
527    fn functions() {
528        let mut vm = inspect("iden = x -> x; y = true; iden ({ y = false; iden iden } (iden y))");
529        let identity = vm.stack.pop_data();
530        assert_eq!(identity, Data::Boolean(true));
531    }
532
533    #[test]
534    fn fun_scope() {
535        // y = (x -> { y = x; y ) 7.0; y
536        let mut vm = inspect("one = 1.0\npi = 3.14\ne = 2.72\n\nx = w -> pi\nx 37.6");
537        let pi = vm.stack.pop_data();
538        assert_eq!(pi, Data::Real(3.14));
539    }
540
541    #[test]
542    fn mutate_capture() {
543        inspect("odd = (); even = x -> odd; odd = 1.0; even (); odd");
544    }
545
546    #[test]
547    fn mutate_capture_fn() {
548        inspect("\
549            pi = 3.14\n\
550            printpi = x -> println pi\n\
551            \n\
552            redef = ()\n\
553            redef = w -> {\n    \
554                w (printpi ())\n\
555            }\n\
556            \n\
557            redef printpi\n\
558        ");
559    }
560
561    #[test]
562    fn hoist_later() {
563        inspect("\
564            w = 0.5
565            later = n -> thing 10.0 - w\n\
566            thing = x -> x + 20.0\n\
567            -- later 5.0\n\
568        ");
569    }
570
571    // TODO: figure out how to make the following passerine code into a test
572    // without entering into an infinite loop (which is the intended behaviour)
573    // maybe try running it a large number of times,
574    // and check the size of the stack?
575    // loop = ()
576    // loop = y -> x -> {
577    //     print y
578    //     print x
579    //     loop x y
580    // }
581    //
582    // loop true false
583}