lox_lang/vm/
mod.rs

1use std::{any::Any, collections::HashMap};
2
3mod errors;
4mod frame;
5mod gc;
6mod io;
7mod list;
8mod run;
9
10pub(crate) use errors::RuntimeError;
11
12use {
13    crate::{Compiler, Error, Gc, UpvalueRef, Value},
14    frame::CallFrame,
15    list::List,
16};
17
18/// The Lox virtual machine.
19///
20/// ### Example
21///
22/// ```
23/// # use lox_lang::VM;
24/// let mut vm = VM::default();
25/// vm.interpret("3 + 3 == 6"); // true
26/// ```
27pub struct VM {
28    stdout: Option<Vec<u8>>,
29
30    stack: Vec<Value>,
31    frames: Vec<CallFrame>,
32    globals: HashMap<Box<str>, Value>,
33    open_upvalues: List<Gc<UpvalueRef>>,
34
35    objects: List<Gc<dyn Any>>,
36    compiler_roots: Vec<Value>,
37    total_allocations: usize,
38    next_gc: usize,
39}
40
41impl Default for VM {
42    /// The `VM` constructor.
43    ///
44    /// Program output defaults to [`Stdout`]. To customize
45    /// this behavior, see the [`buffer_output`] method.
46    ///
47    /// [`buffer_output`]: #method.buffer_output
48    /// [`Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html
49    fn default() -> Self {
50        VM {
51            stdout: None,
52            stack: Vec::with_capacity(256),
53            frames: Vec::with_capacity(256),
54            globals: HashMap::new(),
55            open_upvalues: List::new(),
56            objects: List::new(),
57            compiler_roots: Vec::new(),
58            total_allocations: 0,
59            next_gc: 1 << 20,
60        }
61    }
62}
63
64impl<'source> VM {
65    /// Compile and run Lox code from a source string.
66    ///
67    /// ### Errors
68    ///
69    /// Errors are returned in a `Vec`. This is because compilation (not runtime) is able to
70    /// continue after error(s) are encountered.
71    pub fn interpret<T: AsRef<str> + 'source>(&mut self, source: T) -> Result<(), Vec<Error>> {
72        self.compiler_roots.clear();
73
74        let fun = Compiler::compile(source.as_ref(), &mut |f| {
75            let val = Value::Fun(self.alloc(f));
76            self.compiler_roots.push(val.clone());
77            val
78        })
79        .map_err(|errs| errs.into_iter().map(Into::into).collect::<Vec<_>>())?;
80
81        let fun = Value::Fun(self.alloc(fun));
82
83        self.stack.push(fun);
84        self.compiler_roots.clear();
85
86        let _ = self.call_value_from_stack(0);
87
88        self.run().map_err(|err| {
89            let mut line_no = None;
90            let mut backtrace = format!("{}", err);
91
92            for frame in self.frames.iter().rev() {
93                let function = &(*frame.func).name;
94                let (line, _) = frame.chunk().find_line(frame.inst);
95
96                if line_no.is_none() {
97                    line_no = Some(line);
98                }
99
100                backtrace += &format!("\n=>> line {} in `{}`", line, function);
101            }
102
103            log::error!("{}", backtrace);
104
105            vec![Error::from_runtime_error(err, line_no)]
106        })
107    }
108
109    /// Define a global variable inside the runtime.
110    ///
111    /// Since globals are late bound in Lox, functions that reference the provided name will see
112    /// the provided value, **even if they were declared in the runtime _before_ calling this
113    /// method**.
114    ///
115    /// ### Example
116    ///
117    /// ```
118    /// use std::io::Read;
119    ///
120    /// let mut vm = lox_lang::VM::default();
121    /// vm.buffer_output(true);
122    ///
123    /// vm.interpret("fun hello() { print world; }");
124    /// vm.define_global("world", "greetings, Earthling.".into());
125    /// vm.interpret("hello();");
126    ///
127    /// let mut buffer = String::new();
128    /// vm.read_to_string(&mut buffer).unwrap();
129    /// assert_eq!(buffer, "greetings, Earthling.\n");
130    /// ```
131    pub fn define_global<T: ?Sized + ToString>(&mut self, name: &T, value: Value) {
132        self.globals
133            .insert(name.to_string().into_boxed_str(), value);
134    }
135}
136
137impl Drop for VM {
138    fn drop(&mut self) {
139        // we free all objects here, as the VM is ultimately an arena for them. all other
140        // references to those objects are weak ones.
141        //
142        // note that we do not touch the `open_upvalues` list - they are actually owned by
143        // `objects`.
144        while let Some(obj) = self.objects.pop() {
145            obj.free();
146        }
147    }
148}