use std::{any::Any, collections::HashMap};
mod errors;
mod frame;
mod gc;
mod io;
mod list;
mod run;
pub(crate) use errors::RuntimeError;
use {
crate::{Compiler, Error, Gc, UpvalueRef, Value},
frame::CallFrame,
list::List,
};
pub struct VM {
stdout: Option<Vec<u8>>,
stack: Vec<Value>,
frames: Vec<CallFrame>,
globals: HashMap<Box<str>, Value>,
open_upvalues: List<Gc<UpvalueRef>>,
objects: List<Gc<dyn Any>>,
compiler_roots: Vec<Value>,
total_allocations: usize,
next_gc: usize,
}
impl Default for VM {
fn default() -> Self {
VM {
stdout: None,
stack: Vec::with_capacity(256),
frames: Vec::with_capacity(256),
globals: HashMap::new(),
open_upvalues: List::new(),
objects: List::new(),
compiler_roots: Vec::new(),
total_allocations: 0,
next_gc: 1 << 20,
}
}
}
impl<'source> VM {
pub fn interpret<T: AsRef<str> + 'source>(&mut self, source: T) -> Result<(), Vec<Error>> {
self.compiler_roots.clear();
let fun = Compiler::compile(source.as_ref(), &mut |f| {
let val = Value::Fun(self.alloc(f));
self.compiler_roots.push(val.clone());
val
})
.map_err(|errs| errs.into_iter().map(Into::into).collect::<Vec<_>>())?;
let fun = Value::Fun(self.alloc(fun));
self.stack.push(fun);
self.compiler_roots.clear();
let _ = self.call_value_from_stack(0);
self.run().map_err(|err| {
let mut line_no = None;
let mut backtrace = format!("{}", err);
for frame in self.frames.iter().rev() {
let function = &(*frame.func).name;
let (line, _) = frame.chunk().find_line(frame.inst);
if line_no.is_none() {
line_no = Some(line);
}
backtrace += &format!("\n=>> line {} in `{}`", line, function);
}
log::error!("{}", backtrace);
vec![Error::from_runtime_error(err, line_no)]
})
}
pub fn define_global<T: ?Sized + ToString>(&mut self, name: &T, value: Value) {
self.globals
.insert(name.to_string().into_boxed_str(), value);
}
}
impl Drop for VM {
fn drop(&mut self) {
while let Some(obj) = self.objects.pop() {
obj.free();
}
}
}