use sim_kernel::{Result, Symbol, Value};
use std::{
collections::BTreeMap,
sync::{Arc, Mutex},
};
use sim_lib_binding::{BindingCell, LexicalEnv};
use sim_lib_function::CapturedBinding;
use sim_lib_mutation::{HardCappedRetainPolicy, ManagedArena, ManagedHandle, ManagedNode};
const LUA_BINDING_LIMIT: usize = 4096;
#[derive(Clone)]
pub struct LuaEnv {
lexical: LexicalEnv,
managed: Arc<Mutex<ManagedArena<ManagedNode<()>>>>,
handles: Arc<ManagedBindingFrame>,
}
struct ManagedBindingFrame {
parent: Option<Arc<ManagedBindingFrame>>,
slots: Mutex<BTreeMap<Symbol, ManagedHandle>>,
}
impl Default for LuaEnv {
fn default() -> Self {
Self::new()
}
}
impl LuaEnv {
pub fn new() -> Self {
Self {
lexical: LexicalEnv::new(),
managed: Arc::new(Mutex::new(ManagedArena::new(
HardCappedRetainPolicy::new(LUA_BINDING_LIMIT)
.expect("the Lua binding limit is nonzero"),
))),
handles: Arc::new(ManagedBindingFrame {
parent: None,
slots: Mutex::new(BTreeMap::new()),
}),
}
}
pub fn child(&self) -> Self {
Self {
lexical: self.lexical.child(),
managed: Arc::clone(&self.managed),
handles: Arc::new(ManagedBindingFrame {
parent: Some(Arc::clone(&self.handles)),
slots: Mutex::new(BTreeMap::new()),
}),
}
}
pub fn define(&mut self, name: Symbol, value: Value) -> Result<()> {
self.lexical.define(name.clone(), value)?;
let handle = self
.managed
.lock()
.map_err(|_| sim_kernel::Error::PoisonedLock("lua managed bindings"))?
.allocate(ManagedNode::new(()))
.map_err(|error| {
sim_kernel::Error::Eval(format!("cannot allocate Lua binding: {error}"))
})?;
self.handles
.slots
.lock()
.map_err(|_| sim_kernel::Error::PoisonedLock("lua managed binding frame"))?
.insert(name, handle);
Ok(())
}
pub fn contains(&self, name: &Symbol) -> bool {
self.lexical.lookup(name).is_ok()
}
pub fn assign(&mut self, name: &Symbol, value: Value) -> Result<Value> {
self.capture(name)?.set(value.clone())?;
Ok(value)
}
pub fn get(&self, name: &Symbol) -> Result<Value> {
self.lexical.lookup(name)
}
pub fn capture(&self, name: &Symbol) -> Result<BindingCell> {
self.lexical.capture_cell(name)
}
pub fn capture_managed(&self, name: &Symbol) -> Result<CapturedBinding> {
let cell = self.capture(name)?;
let mut frame = Some(Arc::clone(&self.handles));
while let Some(current) = frame {
if let Some(handle) = current
.slots
.lock()
.map_err(|_| sim_kernel::Error::PoisonedLock("lua managed binding frame"))?
.get(name)
.copied()
{
return Ok(CapturedBinding::new(cell, handle));
}
frame = current.parent.as_ref().map(Arc::clone);
}
Err(sim_kernel::Error::Eval(format!(
"lua binding {name} has no managed identity"
)))
}
}