use std::ops::Deref;
use gc::{Gc, GcCell, Finalize, Trace};
use crate::{
fmt::{self, Display},
symbol,
};
use super::{program, Value};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Trace, Finalize)]
pub struct SlotIx(pub u32);
impl SlotIx {
pub fn copy(&self) -> Self {
Self(self.0)
}
}
impl From<program::mem::SlotIx> for SlotIx {
fn from(slot_ix: program::mem::SlotIx) -> Self {
Self(slot_ix.0)
}
}
impl From<&program::mem::SlotIx> for SlotIx {
fn from(slot_ix: &program::mem::SlotIx) -> Self {
Self(slot_ix.0)
}
}
#[derive(Debug)]
enum Slot {
Regular(Value),
Closed(Gc<GcCell<Value>>),
}
impl Slot {
fn fetch(&self) -> Value {
match self {
Slot::Regular(value) => value.copy(),
Slot::Closed(value) => value.deref().borrow().copy(),
}
}
fn store(&mut self, value: Value) {
match self {
Self::Closed(val) => *val.borrow_mut() = value,
Self::Regular(val) => *val = value,
}
}
fn capture(&mut self) -> Gc<GcCell<Value>> {
match self {
Slot::Closed(value) => value.clone(),
Slot::Regular(value) => {
let value = Gc::new(GcCell::new(std::mem::take(value)));
*self = Slot::Closed(value.clone());
value
}
}
}
fn place(&mut self, value: Gc<GcCell<Value>>) {
*self = Slot::Closed(value);
}
}
impl Default for Slot {
fn default() -> Self {
Self::Regular(Value::default())
}
}
#[derive(Debug)]
pub struct Stack {
slots: Vec<Slot>,
max_size: usize,
}
impl Stack {
pub fn new(max_size: usize) -> Self {
Self {
slots: Vec::new(),
max_size,
}
}
pub fn extend(&mut self, slots: SlotIx) -> Result<(), StackOverflow> {
let new_size = self.len() + slots.0 as usize;
if new_size > self.max_size {
Err(StackOverflow)
} else {
self.slots.resize_with(new_size, Slot::default);
Ok(())
}
}
pub fn shrink(&mut self, slots: SlotIx) {
self.slots.truncate(self.len() - slots.0 as usize);
}
pub fn fetch(&self, slot_ix: SlotIx) -> Value {
let offset = slot_ix.0 as usize;
self.slots[self.len() - 1 - offset].fetch()
}
pub fn capture(&mut self, slot_ix: SlotIx) -> Gc<GcCell<Value>> {
let len = self.len();
let offset = slot_ix.0 as usize;
self.slots[len - 1 - offset].capture()
}
pub fn place(&mut self, slot_ix: SlotIx, value: Gc<GcCell<Value>>) {
let len = self.len();
let offset = slot_ix.0 as usize;
self.slots[len - 1 - offset].place(value)
}
pub fn store(&mut self, slot_ix: SlotIx, value: Value) {
let len = self.len();
let offset = slot_ix.0 as usize;
self.slots[len - 1 - offset].store(value)
}
pub fn is_empty(&self) -> bool {
self.slots.is_empty()
}
pub fn len(&self) -> usize {
self.slots.len()
}
}
impl Default for Stack {
fn default() -> Self {
const STACK_SIZE: usize = (8 * 1024 * 1024) / std::mem::size_of::<Slot>();
Self::new(STACK_SIZE)
}
}
impl<'a> Display<'a> for Stack {
type Context = &'a symbol::Interner;
fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
for (ix, val) in self.slots.iter().rev().enumerate() {
writeln!(f, "{}: {}", ix, fmt::Show(val.fetch(), context))?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct StackOverflow;
impl std::fmt::Display for StackOverflow {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "stack overflow")
}
}
impl std::error::Error for StackOverflow { }