Skip to main content

sim_lib_lang_lua/
env.rs

1use sim_kernel::{Result, Symbol, Value};
2use std::{
3    collections::BTreeMap,
4    sync::{Arc, Mutex},
5};
6
7use sim_lib_binding::{BindingCell, LexicalEnv};
8use sim_lib_function::CapturedBinding;
9use sim_lib_mutation::{HardCappedRetainPolicy, ManagedArena, ManagedHandle, ManagedNode};
10
11const LUA_BINDING_LIMIT: usize = 4096;
12
13/// Lexical local environment used by the Lua core eval policy.
14#[derive(Clone)]
15pub struct LuaEnv {
16    lexical: LexicalEnv,
17    managed: Arc<Mutex<ManagedArena<ManagedNode<()>>>>,
18    handles: Arc<ManagedBindingFrame>,
19}
20
21struct ManagedBindingFrame {
22    parent: Option<Arc<ManagedBindingFrame>>,
23    slots: Mutex<BTreeMap<Symbol, ManagedHandle>>,
24}
25
26impl Default for LuaEnv {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl LuaEnv {
33    /// Build an empty Lua local environment.
34    pub fn new() -> Self {
35        Self {
36            lexical: LexicalEnv::new(),
37            managed: Arc::new(Mutex::new(ManagedArena::new(
38                HardCappedRetainPolicy::new(LUA_BINDING_LIMIT)
39                    .expect("the Lua binding limit is nonzero"),
40            ))),
41            handles: Arc::new(ManagedBindingFrame {
42                parent: None,
43                slots: Mutex::new(BTreeMap::new()),
44            }),
45        }
46    }
47
48    /// Open a nested scope whose lookups fall through to this one.
49    pub fn child(&self) -> Self {
50        Self {
51            lexical: self.lexical.child(),
52            managed: Arc::clone(&self.managed),
53            handles: Arc::new(ManagedBindingFrame {
54                parent: Some(Arc::clone(&self.handles)),
55                slots: Mutex::new(BTreeMap::new()),
56            }),
57        }
58    }
59
60    /// Bind a Lua local value in the current frame.
61    pub fn define(&mut self, name: Symbol, value: Value) -> Result<()> {
62        self.lexical.define(name.clone(), value)?;
63        let handle = self
64            .managed
65            .lock()
66            .map_err(|_| sim_kernel::Error::PoisonedLock("lua managed bindings"))?
67            .allocate(ManagedNode::new(()))
68            .map_err(|error| {
69                sim_kernel::Error::Eval(format!("cannot allocate Lua binding: {error}"))
70            })?;
71        self.handles
72            .slots
73            .lock()
74            .map_err(|_| sim_kernel::Error::PoisonedLock("lua managed binding frame"))?
75            .insert(name, handle);
76        Ok(())
77    }
78
79    /// Return whether a Lua local is bound.
80    pub fn contains(&self, name: &Symbol) -> bool {
81        self.lexical.lookup(name).is_ok()
82    }
83
84    /// Assign an existing Lua local.
85    pub fn assign(&mut self, name: &Symbol, value: Value) -> Result<Value> {
86        self.capture(name)?.set(value.clone())?;
87        Ok(value)
88    }
89
90    /// Look up a Lua local value.
91    pub fn get(&self, name: &Symbol) -> Result<Value> {
92        self.lexical.lookup(name)
93    }
94
95    /// Capture an existing Lua local as a shared upvalue cell.
96    pub fn capture(&self, name: &Symbol) -> Result<BindingCell> {
97        self.lexical.capture_cell(name)
98    }
99
100    /// Capture an existing local as one shared cell with its managed identity.
101    pub fn capture_managed(&self, name: &Symbol) -> Result<CapturedBinding> {
102        let cell = self.capture(name)?;
103        let mut frame = Some(Arc::clone(&self.handles));
104        while let Some(current) = frame {
105            if let Some(handle) = current
106                .slots
107                .lock()
108                .map_err(|_| sim_kernel::Error::PoisonedLock("lua managed binding frame"))?
109                .get(name)
110                .copied()
111            {
112                return Ok(CapturedBinding::new(cell, handle));
113            }
114            frame = current.parent.as_ref().map(Arc::clone);
115        }
116        Err(sim_kernel::Error::Eval(format!(
117            "lua binding {name} has no managed identity"
118        )))
119    }
120}