use std::{
mem,
rc::Rc,
cell::RefCell
};
use crate::common::data::Data;
use crate::vm::{
tag::Tagged,
linked::Linked,
};
#[derive(Debug)]
pub struct Stack {
pub frames: Linked,
pub stack: Vec<Tagged>
}
impl Stack {
pub fn init() -> Stack {
Stack {
frames: Linked::new(0),
stack: vec![Tagged::frame()],
}
}
#[inline]
pub fn push_data(&mut self, data: Data) {
self.stack.push(Tagged::new(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.data() {
Data::Frame => unreachable!("tried to pop data, Frame is not data"),
Data::Heaped(r) => r.borrow().clone(),
data => data,
}
}
#[inline]
pub fn pop_frame(&mut self) {
let index = self.frames.prepop();
if self.stack.len() - 1 == index {
self.stack.pop();
} else {
unreachable!("Expected frame on top of stack, found data")
}
}
#[inline]
pub fn push_frame(&mut self) {
self.frames.prepend(self.stack.len());
self.stack.push(Tagged::frame());
}
#[inline]
pub fn heapify(&mut self, index: usize) -> Rc<RefCell<Data>> {
let local_index = self.frames.peek() + index + 1;
let data = mem::replace(&mut self.stack[local_index], Tagged::frame()).data();
let reference = Rc::new(RefCell::new(data));
let heaped = Data::Heaped(reference.clone());
mem::drop(mem::replace(&mut self.stack[local_index], Tagged::new(heaped)));
return reference;
}
pub fn get_local(&mut self, index: usize) {
let local_index = self.frames.peek() + index + 1;
let data = mem::replace(&mut self.stack[local_index], Tagged::frame()).data();
let copy = data.clone();
mem::drop(mem::replace(&mut self.stack[local_index], Tagged::new(data)));
self.push_data(copy);
}
pub fn set_local(&mut self, index: usize) {
let local_index = self.frames.peek() + index + 1;
if self.stack.len() - 1 == local_index {
return;
} else if self.stack.len() <= local_index {
unreachable!("Can not set local that is not yet on stack");
} else {
let data = mem::replace(&mut self.stack[local_index], Tagged::frame()).data();
let tagged = match data {
Data::Frame => unreachable!(),
Data::Heaped(ref cell) => {
mem::drop(cell.replace(self.pop_data()));
Tagged::new(data)
}
_ => self.stack.pop().unwrap(),
};
mem::drop(mem::replace(&mut self.stack[local_index], tagged))
}
}
}