use std::mem;
use crate::common::{
number::build_number,
data::Data,
opcode::Opcode,
lambda::{Captured, Lambda},
closure::Closure,
};
use crate::vm::{
trace::Trace,
stack::Stack,
};
#[derive(Debug)]
pub struct VM {
pub closure: Closure,
pub stack: Stack,
pub ip: usize,
}
impl VM {
pub fn init() -> VM {
VM {
closure: Closure::wrap(Lambda::empty()),
stack: Stack::init(),
ip: 0,
}
}
pub fn next(&mut self) { self.ip += 1; }
pub fn terminate(&mut self) -> Result<(), Trace> { self.ip = self.closure.lambda.code.len(); Ok(()) }
pub fn done(&mut self) -> Result<(), Trace> { self.next(); Ok(()) }
pub fn peek_byte(&mut self) -> u8 { self.closure.lambda.code[self.ip] }
pub fn next_byte(&mut self) -> u8 { self.next(); self.peek_byte() }
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;
}
pub fn step(&mut self) -> Result<(), Trace> {
let opcode = Opcode::from_byte(self.peek_byte());
match opcode {
Opcode::Con => self.con(),
Opcode::Del => self.del(),
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::UnLabel => self.un_label(),
Opcode::UnData => self.un_data(),
}
}
pub fn run(&mut self, closure: Closure) -> Result<(), Trace> {
let old_closure = mem::replace(&mut self.closure, closure);
let old_ip = mem::replace(&mut self.ip, 0);
let mut result = Ok(());
while self.ip < self.closure.lambda.code.len() {
if let error @ Err(_) = self.step() {
result = error;
break;
};
}
mem::drop(mem::replace(&mut self.closure, old_closure));
self.ip = old_ip;
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 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 data = self.stack.local_data(index);
self.stack.push_data(data);
self.done()
}
#[inline]
pub fn load_cap(&mut self) -> Result<(), Trace> {
let index = self.next_number();
self.stack.push_data(self.closure.captures[index].borrow().to_owned());
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()
}
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.closure.lambda.index_span(self.ip)],
)),
};
self.stack.push_data(*d);
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.closure.lambda.index_span(self.ip)],
));
}
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.closure.lambda.index_span(self.ip)],
)),
};
let arg = self.stack.pop_data();
self.stack.push_frame();
self.stack.push_data(arg);
match self.run(fun) {
Ok(()) => (),
Err(mut trace) => {
trace.add_context(self.closure.lambda.index_span(self.ip));
return Err(trace);
},
};
self.done()
}
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()?; }
self.stack.pop_frame();
self.stack.push_data(val);
self.terminate()
}
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()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::compiler::{
parse::parse,
desugar::desugar,
lex::lex,
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(gen)
.map_err(|e| println!("{}", e))
.unwrap();
let mut vm = VM::init();
match vm.run(Closure::wrap(lambda)) {
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\
");
}
}