use std::{
mem,
rc::Rc,
cell::RefCell
};
use crate::common::data::Data;
use crate::vm::{
tag::Tagged,
slot::{Slot, Suspend},
};
#[derive(Debug)]
pub struct Stack {
pub frames: Vec<usize>,
pub stack: Vec<Tagged>
}
impl Stack {
pub fn init() -> Stack {
Stack {
frames: vec![0],
stack: vec![Tagged::frame()],
}
}
#[inline]
fn frame_index(&self) -> usize {
*self.frames.last().unwrap()
}
#[inline]
fn pop(&mut self) -> Tagged {
self.stack.pop()
.expect("VM tried to pop empty stack, stack should never be empty")
}
#[inline]
fn swap(&mut self, index: usize, tagged: Tagged) -> Tagged {
mem::replace(&mut self.stack[index], tagged)
}
#[inline]
pub fn push_data(&mut self, data: Data) {
self.stack.push(Tagged::new(Slot::Data(data)))
}
#[inline]
pub fn push_tagged(&mut self, tagged: Tagged) {
self.stack.push(tagged)
}
#[inline]
pub fn pop_data(&mut self) -> Data {
let value = self.stack.pop()
.expect("VM tried to pop empty stack, stack should never be empty");
match value.slot().data() {
Data::Heaped(h) => h.borrow().clone(),
d => d,
}
}
#[inline]
pub fn pop_frame(&mut self) -> Suspend {
if let Slot::Frame = self.pop().slot() {} else {
unreachable!("Expected frame on top of stack");
}
self.frames.pop();
let old_slot = self.swap(self.frame_index(), Tagged::frame()).slot();
if let Slot::Suspend(s) = old_slot {
return s;
} else {
unreachable!("Expected frame on top of stack");
}
}
#[inline]
pub fn push_frame(&mut self, suspend: Suspend) {
let frame_index = self.frame_index();
self.stack[frame_index] = Tagged::new(Slot::Suspend(suspend));
self.frames.push(self.stack.len());
self.stack.push(Tagged::frame());
}
#[inline]
pub fn push_not_init(&mut self) {
self.stack.push(Tagged::not_init());
}
#[inline]
pub fn declare(&mut self, decls: usize) {
for _ in 0..decls { self.push_not_init(); }
}
#[inline]
pub fn heapify(&mut self, index: usize) {
let local_index = self.frame_index() + index + 1;
let data = self.swap(local_index, Tagged::not_init()).slot().data();
let heaped = Slot::Data(Data::Heaped(Rc::new(RefCell::new(data))));
mem::drop(mem::replace(&mut self.stack[local_index], Tagged::new(heaped)));
}
#[inline]
pub fn unwind_frame(&mut self) -> bool {
self.stack.truncate(self.frame_index() + 1);
return self.frames.len() > 1;
}
pub fn local_slot(&mut self, index: usize) -> Slot {
let local_index = self.frame_index() + index + 1;
let slot = self.swap(local_index, Tagged::not_init()).slot();
let copy = slot.clone();
mem::drop(self.swap(local_index, Tagged::new(slot)));
return copy;
}
pub fn local_data(&mut self, index: usize) -> Data {
let local_index = self.frame_index() + index + 1;
let data = self.swap(local_index, Tagged::not_init()).slot().data();
let copy = data.clone();
mem::drop(self.swap(local_index, Tagged::new(Slot::Data(data))));
return copy;
}
pub fn set_local(&mut self, index: usize) {
let local_index = self.frame_index() + index + 1;
if (self.stack.len() - 1) == local_index {
return;
} else if (self.stack.len() - 1) < local_index {
unreachable!("Can not set local that is not yet on stack");
} else {
let slot = self.swap(local_index, Tagged::not_init()).slot();
let tagged = match slot {
Slot::Frame => unreachable!("Expected data, found frame"),
Slot::Data(Data::Heaped(ref cell)) => {
mem::drop(cell.replace(self.pop_data()));
Tagged::new(slot)
}
_ => self.stack.pop().unwrap(),
};
mem::drop(self.swap(local_index, tagged))
}
}
}