use std::mem;
use crate::common::{
number::build_number,
data::Data,
opcode::Opcode,
lambda::Captured,
closure::Closure,
span::Span,
};
use crate::vm::{
trace::Trace,
slot::Suspend,
stack::Stack,
};
#[derive(Debug)]
pub struct VM {
pub closure: Closure,
pub stack: Stack,
pub ip: usize,
}
impl VM {
pub fn init(closure: Closure) -> VM {
let mut vm = VM {
closure,
stack: Stack::init(),
ip: 0,
};
vm.stack.declare(vm.closure.lambda.decls);
return vm;
}
#[inline]
pub fn next(&mut self) { self.ip += 1; }
#[inline]
pub fn done(&mut self) -> Result<(), Trace> { self.next(); Ok(()) }
#[inline]
pub fn peek_byte(&mut self) -> u8 { self.closure.lambda.code[self.ip] }
#[inline]
pub fn next_byte(&mut self) -> u8 { self.next(); self.peek_byte() }
#[inline]
pub fn is_terminated(&mut self) -> bool {
self.ip >= self.closure.lambda.code.len()
}
#[inline]
pub fn next_number(&mut self) -> usize {
self.next();
let remaining = &self.closure.lambda.code[self.ip..];
let (index, eaten) = build_number(remaining);
self.ip += eaten - 1;
return index;
}
#[inline]
pub fn current_span(&self) -> Span {
self.closure.lambda.index_span(self.ip)
}
pub fn step(&mut self) -> Result<(), Trace> {
let opcode = Opcode::from_byte(self.peek_byte());
match opcode {
Opcode::Con => self.con(),
Opcode::NotInit => self.not_init(),
Opcode::Del => self.del(),
Opcode::FFICall => self.ffi_call(),
Opcode::Copy => self.copy_val(),
Opcode::Capture => self.capture(),
Opcode::Save => self.save(),
Opcode::SaveCap => self.save_cap(),
Opcode::Load => self.load(),
Opcode::LoadCap => self.load_cap(),
Opcode::Call => self.call(),
Opcode::Return => self.return_val(),
Opcode::Closure => self.closure(),
Opcode::Print => self.print(),
Opcode::Label => self.label(),
Opcode::Tuple => self.tuple(),
Opcode::UnData => self.un_data(),
Opcode::UnLabel => self.un_label(),
Opcode::UnTuple => self.un_tuple(),
}
}
pub fn unwind(&mut self) {
let suspend = self.stack.pop_frame();
self.ip = suspend.ip;
self.closure = suspend.closure;
self.stack.push_not_init();
}
pub fn run(&mut self) -> Result<(), Trace> {
let mut result = Ok(());
while !self.is_terminated() {
result = self.step();
if result.is_err() { break; }
}
if let Err(mut trace) = result {
while self.stack.unwind_frame() {
self.unwind();
self.ip -= 1;
trace.add_context(self.current_span());
}
result = Err(trace);
};
return result;
}
#[inline]
pub fn con(&mut self) -> Result<(), Trace> {
let index = self.next_number();
self.stack.push_data(self.closure.lambda.constants[index].clone());
self.done()
}
#[inline]
pub fn not_init(&mut self) -> Result<(), Trace> {
self.stack.push_not_init();
self.done()
}
#[inline]
pub fn capture(&mut self) -> Result<(), Trace> {
let index = self.next_number();
self.stack.heapify(index);
self.done()
}
#[inline]
pub fn save(&mut self) -> Result<(), Trace> {
let index = self.next_number();
self.stack.set_local(index);
self.done()
}
#[inline]
pub fn save_cap(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let data = self.stack.pop_data();
mem::drop(self.closure.captures[index].replace(data));
self.done()
}
#[inline]
pub fn load(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let mut data = self.stack.local_data(index);
if let Data::Heaped(d) = data { data = d.borrow().to_owned() };
if let Data::NotInit = data {
return Err(Trace::error(
"Reference",
&format!("This local variable was referenced before assignment"),
vec![self.current_span()],
));
};
self.stack.push_data(data);
self.done()
}
#[inline]
pub fn load_cap(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let data = self.closure.captures[index].borrow().to_owned();
if let Data::NotInit = data {
return Err(Trace::error(
"Reference",
&format!("This captured variable was referenced before assignment"),
vec![self.current_span()],
));
};
self.stack.push_data(data);
self.done()
}
#[inline]
pub fn del(&mut self) -> Result<(), Trace> {
mem::drop(self.stack.pop_data());
self.done()
}
#[inline]
pub fn copy_val(&mut self) -> Result<(), Trace> {
let data = self.stack.pop_data();
self.stack.push_data(data.clone());
self.stack.push_data(data);
self.done()
}
#[inline]
pub fn print(&mut self) -> Result<(), Trace> {
let data = self.stack.pop_data();
println!("{}", data);
self.stack.push_data(data);
self.done()
}
#[inline]
pub fn label(&mut self) -> Result<(), Trace> {
let kind = match self.stack.pop_data() {
Data::Kind(n) => n,
_ => unreachable!(),
};
let data = self.stack.pop_data();
self.stack.push_data(Data::Label(Box::new(kind), Box::new(data)));
self.done()
}
#[inline]
pub fn tuple(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let mut items = vec![];
for _ in 0..index {
items.push(self.stack.pop_data())
}
items.reverse();
self.stack.push_data(Data::Tuple(items));
self.done()
}
fn un_data(&mut self) -> Result<(), Trace> {
let expected = self.stack.pop_data();
let data = self.stack.pop_data();
if data != expected {
return Err(Trace::error(
"Pattern Matching",
&format!("The data '{}' does not match the expected data '{}'", data, expected),
vec![self.current_span()],
));
}
self.done()
}
fn un_label(&mut self) -> Result<(), Trace> {
let kind = match self.stack.pop_data() {
Data::Kind(n) => n,
_ => unreachable!(),
};
let d = match self.stack.pop_data() {
Data::Label(n, d) if *n == kind => d,
other => return Err(Trace::error(
"Pattern Matching",
&format!("The data '{}' does not match the Label '{}'", other, kind),
vec![self.current_span()],
)),
};
self.stack.push_data(*d);
self.done()
}
fn un_tuple(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let t = match self.stack.pop_data() {
Data::Tuple(t) => t,
other => return Err(Trace::error(
"Pattern Matching",
&format!("The data '{}' is not a tuple", other),
vec![self.current_span()],
)),
};
let length = t.len();
if index >= length {
return Err(Trace::error(
"Indexing",
&format!(
"The tuple '{}' is of length {}, so the index {} is out-of-bounds",
Data::Tuple(t), length, index
),
vec![self.current_span()],
));
}
let data = t[index].clone();
self.stack.push_data(Data::Tuple(t));
self.stack.push_data(data);
self.done()
}
pub fn call(&mut self) -> Result<(), Trace> {
let fun = match self.stack.pop_data() {
Data::Closure(c) => *c,
o => return Err(Trace::error(
"Call",
&format!("The data '{}' is not a function and can not be called", o),
vec![self.current_span()],
)),
};
let arg = self.stack.pop_data();
self.next();
let tail_call = !self.is_terminated()
&& Opcode::Return
== Opcode::from_byte(self.peek_byte());
if tail_call {
let locals = self.next_number();
for _ in 0..locals { self.del()?; }
}
let old_closure = mem::replace(&mut self.closure, fun);
let old_ip = mem::replace(&mut self.ip, 0);
let suspend = Suspend {
ip: old_ip,
closure: old_closure,
};
if !tail_call {
self.stack.push_frame(suspend);
}
self.stack.declare(self.closure.lambda.decls);
self.stack.push_data(arg);
Ok(())
}
pub fn return_val(&mut self) -> Result<(), Trace> {
let val = self.stack.pop_data();
let locals = self.next_number();
for _ in 0..locals { self.del()?; }
let suspend = self.stack.pop_frame();
self.ip = suspend.ip;
self.closure = suspend.closure;
self.stack.push_data(val);
Ok(())
}
pub fn closure(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let lambda = match self.closure.lambda.constants[index].clone() {
Data::Lambda(lambda) => *lambda,
_ => unreachable!("Expected a lambda to be wrapped with a closure"),
};
let mut closure = Closure::wrap(lambda);
for captured in closure.lambda.captures.iter() {
let reference = match captured {
Captured::Local(index) => {
match self.stack.local_data(*index) {
Data::Heaped(h) => h,
_ => unreachable!("Expected data to be on the heap"),
}
},
Captured::Nonlocal(upvalue) => self.closure.captures[*upvalue].clone(),
};
closure.captures.push(reference)
}
self.stack.push_data(Data::Closure(Box::new(closure)));
self.done()
}
pub fn ffi_call(&mut self) -> Result<(), Trace> {
let index = self.next_number();
let ffi_function = &self.closure.lambda.ffi[index];
let argument = self.stack.pop_data();
let returned = match ffi_function.call(argument) {
Ok(d) => d,
Err(e) => return Err(Trace::error(
"FFI Call", &e, vec![self.current_span()],
)),
};
self.stack.push_data(returned);
self.done()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::compiler::{
lex::lex,
parse::parse,
desugar::desugar,
hoist::hoist,
gen::gen,
};
use crate::common::source::Source;
fn inspect(source: &str) -> VM {
let lambda = lex(Source::source(source))
.and_then(parse)
.and_then(desugar)
.and_then(hoist)
.and_then(gen)
.map_err(|e| println!("{}", e))
.unwrap();
let mut vm = VM::init(Closure::wrap(lambda));
match vm.run() {
Ok(()) => vm,
Err(e) => {
println!("{}", e);
panic!();
},
}
}
#[test]
fn init_run() {
inspect("x = 0.0");
}
#[test]
fn block_expression() {
inspect("x = false; boop = true; heck = { x = boop; x }; heck");
}
#[test]
fn functions() {
let mut vm = inspect("iden = x -> x; y = true; iden ({ y = false; iden iden } (iden y))");
let identity = vm.stack.pop_data();
assert_eq!(identity, Data::Boolean(true));
}
#[test]
fn fun_scope() {
let mut vm = inspect("one = 1.0\npi = 3.14\ne = 2.72\n\nx = w -> pi\nx 37.6");
let pi = vm.stack.pop_data();
assert_eq!(pi, Data::Real(3.14));
}
#[test]
fn mutate_capture() {
inspect("odd = (); even = x -> odd; odd = 1.0; even (); odd");
}
#[test]
fn mutate_capture_fn() {
inspect("\
pi = 3.14\n\
printpi = x -> print pi\n\
\n\
redef = ()\n\
redef = w -> {\n \
w (printpi ())\n\
}\n\
\n\
redef printpi\n\
");
}
#[test]
fn hoist_later() {
inspect("\
w = 0.5
later = n -> thing 10.0 - w\n\
thing = x -> x + 20.0\n\
-- later 5.0\n\
");
}
}