Skip to main content

sim_lib_binding/
cell.rs

1//! Shared mutable cells for closed-over lexical bindings.
2
3use std::sync::{Arc, Mutex};
4
5use sim_kernel::{Error, Result, Symbol, Value};
6
7/// A reference-shared mutable binding slot captured from a lexical scope.
8///
9/// Cloned cells point at the same slot, so writes through one handle are visible
10/// through every other handle for the same lexical binding. Closure languages use
11/// this shape for boxed upvalues and closed-over mutable locals.
12#[derive(Clone, Debug)]
13pub struct BindingCell {
14    name: Symbol,
15    slot: Arc<Mutex<BindingCellState>>,
16}
17
18/// The explicit lifecycle and mutation state of a [`BindingCell`].
19#[derive(Clone, Debug)]
20pub enum BindingCellState {
21    /// The binding exists but has not received a value.
22    Uninitialized,
23    /// A mutable binding holding a value.
24    Initialized(Value),
25    /// A binding which has been removed and cannot be read.
26    Deleted,
27    /// A read-only binding holding a value.
28    Immutable(Value),
29    /// A binding which forwards reads and writes to another live cell.
30    LiveAlias(BindingCell),
31}
32
33impl BindingCell {
34    pub(crate) fn from_slot(name: Symbol, slot: Arc<Mutex<BindingCellState>>) -> Self {
35        Self { name, slot }
36    }
37
38    /// Creates an uninitialized cell.
39    pub fn uninitialized(name: Symbol) -> Self {
40        Self::from_slot(name, Arc::new(Mutex::new(BindingCellState::Uninitialized)))
41    }
42
43    /// Creates an initialized mutable cell.
44    pub fn initialized(name: Symbol, value: Value) -> Self {
45        Self::from_slot(
46            name,
47            Arc::new(Mutex::new(BindingCellState::Initialized(value))),
48        )
49    }
50
51    /// Creates an immutable initialized cell.
52    pub fn immutable(name: Symbol, value: Value) -> Self {
53        Self::from_slot(
54            name,
55            Arc::new(Mutex::new(BindingCellState::Immutable(value))),
56        )
57    }
58
59    /// Creates a live alias which follows reads and writes to `target`.
60    pub fn live_alias(name: Symbol, target: BindingCell) -> Self {
61        Self::from_slot(
62            name,
63            Arc::new(Mutex::new(BindingCellState::LiveAlias(target))),
64        )
65    }
66
67    /// Returns the binding name associated with this cell.
68    pub fn name(&self) -> &Symbol {
69        &self.name
70    }
71
72    /// Reads the cell's current value.
73    ///
74    /// Errors if the captured slot is still uninitialized.
75    pub fn get(&self) -> Result<Value> {
76        let state = self
77            .slot
78            .lock()
79            .map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))?
80            .clone();
81        match state {
82            BindingCellState::Initialized(value) | BindingCellState::Immutable(value) => Ok(value),
83            BindingCellState::LiveAlias(target) => target.get(),
84            BindingCellState::Uninitialized => Err(Error::Eval(format!(
85                "binding cell {} is not initialized",
86                self.name
87            ))),
88            BindingCellState::Deleted => Err(Error::Eval(format!(
89                "binding cell {} is deleted",
90                self.name
91            ))),
92        }
93    }
94
95    /// Replaces the cell's current value.
96    pub fn set(&self, value: Value) -> Result<()> {
97        let mut state = self
98            .slot
99            .lock()
100            .map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))?;
101        match &mut *state {
102            BindingCellState::LiveAlias(target) => target.set(value),
103            BindingCellState::Immutable(_) => Err(Error::Eval(format!(
104                "binding cell {} is immutable",
105                self.name
106            ))),
107            BindingCellState::Deleted => Err(Error::Eval(format!(
108                "binding cell {} is deleted",
109                self.name
110            ))),
111            BindingCellState::Uninitialized | BindingCellState::Initialized(_) => {
112                *state = BindingCellState::Initialized(value);
113                Ok(())
114            }
115        }
116    }
117
118    /// Deletes this cell. A deleted cell cannot be read or reinitialized.
119    pub fn delete(&self) -> Result<()> {
120        let mut state = self
121            .slot
122            .lock()
123            .map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))?;
124        if matches!(*state, BindingCellState::Immutable(_)) {
125            return Err(Error::Eval(format!(
126                "binding cell {} is immutable",
127                self.name
128            )));
129        }
130        *state = BindingCellState::Deleted;
131        Ok(())
132    }
133
134    /// Returns a snapshot of the cell's current state.
135    pub fn state(&self) -> Result<BindingCellState> {
136        self.slot
137            .lock()
138            .map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))
139            .map(|state| state.clone())
140    }
141}