use std::{cell::RefCell, mem, rc::Rc};
use crate::gc::{self, Finalize, Trace};
use crate::lang::{Identifier, Sequence};
use crate::{CapturedVar, RantyValue};
#[derive(Debug, Clone, Trace, Finalize)]
#[rust_cc(unsafe_no_drop)]
pub struct LazyThunk {
#[rust_cc(ignore)]
pub(crate) name: Option<Identifier>,
#[rust_cc(ignore)]
pub(crate) expr: Rc<Sequence>,
pub(crate) captured_vars: Vec<CapturedVar>,
pub(crate) captured_pipeval: Option<RantyValue>,
}
#[derive(Debug, Clone, Trace, Finalize)]
#[rust_cc(unsafe_no_drop)]
pub enum LazyState {
Pending(LazyThunk),
Evaluating(LazyThunk),
Ready(RantyValue),
}
impl LazyState {
#[inline]
pub fn display_name(&self) -> Option<&Identifier> {
match self {
Self::Pending(thunk) => thunk.name.as_ref(),
Self::Evaluating(thunk) => thunk.name.as_ref(),
Self::Ready(_) => None,
}
}
}
#[derive(Debug, Trace, Finalize)]
#[rust_cc(unsafe_no_drop)]
pub enum RantyVar {
ByVal(RantyValue),
ByValConst(RantyValue),
ByRef(crate::gc::Cc<RefCell<RantyValue>>),
ByRefConst(crate::gc::Cc<RantyValue>),
Lazy(crate::gc::Cc<RefCell<LazyState>>),
LazyConst(crate::gc::Cc<RefCell<LazyState>>),
}
impl Default for RantyVar {
fn default() -> Self {
Self::ByVal(RantyValue::Nothing)
}
}
impl Clone for RantyVar {
fn clone(&self) -> Self {
match self {
Self::ByVal(val) => Self::ByVal(val.clone()),
Self::ByRef(val_ref) => Self::ByRef(val_ref.clone()),
Self::ByValConst(val) => Self::ByValConst(val.clone()),
Self::ByRefConst(val_ref) => Self::ByRefConst(val_ref.clone()),
Self::Lazy(state) => Self::Lazy(state.clone()),
Self::LazyConst(state) => Self::LazyConst(state.clone()),
}
}
}
impl RantyVar {
#[inline]
pub fn new_lazy(thunk: LazyThunk, is_const: bool) -> Self {
let state = gc::alloc(RefCell::new(LazyState::Pending(thunk)));
if is_const {
Self::LazyConst(state)
} else {
Self::Lazy(state)
}
}
#[inline]
pub fn is_const(&self) -> bool {
matches!(
self,
Self::ByValConst(_) | Self::ByRefConst(_) | Self::LazyConst(_)
)
}
#[inline]
pub fn is_by_val(&self) -> bool {
matches!(self, Self::ByVal(_) | Self::ByValConst(_))
}
#[inline]
pub fn is_by_ref(&self) -> bool {
matches!(
self,
Self::ByRef(_) | Self::ByRefConst(_) | Self::Lazy(_) | Self::LazyConst(_)
)
}
#[inline]
pub fn make_by_ref(&mut self) {
if self.is_by_ref() {
return;
}
match mem::take(self) {
Self::ByVal(val) => *self = Self::ByRef(gc::alloc(RefCell::new(val))),
Self::ByValConst(val) => *self = Self::ByRefConst(gc::alloc(val)),
Self::Lazy(state) => *self = Self::Lazy(state),
Self::LazyConst(state) => *self = Self::LazyConst(state),
_ => unreachable!(),
}
}
#[inline]
pub fn write(&mut self, value: RantyValue) -> bool {
match self {
Self::ByVal(val) => *val = value,
Self::ByRef(val_ref) => {
val_ref.replace(value);
}
Self::Lazy(state) => {
state.replace(LazyState::Ready(value));
}
Self::ByRefConst(_) | Self::ByValConst(_) | Self::LazyConst(_) => return false,
}
true
}
#[inline]
pub fn as_lazy_state(&self) -> Option<crate::gc::Cc<RefCell<LazyState>>> {
match self {
Self::Lazy(state) | Self::LazyConst(state) => Some(state.clone()),
_ => None,
}
}
#[inline]
pub fn value_ref(&self) -> RantyValue {
match self {
Self::ByVal(val) | Self::ByValConst(val) => val.clone(),
Self::ByRef(val_ref) => val_ref.borrow().clone(),
Self::ByRefConst(val_ref) => val_ref.as_ref().clone(),
Self::Lazy(state) | Self::LazyConst(state) => match &*state.borrow() {
LazyState::Ready(val) => val.clone(),
_ => panic!("attempted to read an unresolved lazy variable directly"),
},
}
}
#[inline]
pub fn value_cloned(&self) -> RantyValue {
match self {
RantyVar::ByVal(val) | RantyVar::ByValConst(val) => val.clone(),
RantyVar::ByRef(val_ref) => val_ref.borrow().clone(),
RantyVar::ByRefConst(val_ref) => val_ref.as_ref().clone(),
RantyVar::Lazy(state) | RantyVar::LazyConst(state) => match &*state.borrow() {
LazyState::Ready(val) => val.clone(),
_ => panic!("attempted to clone an unresolved lazy variable directly"),
},
}
}
}