Skip to main content

sim_lib_binding/
lexical.rs

1use std::{
2    collections::BTreeMap,
3    sync::{Arc, Mutex, MutexGuard},
4};
5
6use sim_kernel::{
7    Args, Callable, ClassRef, Cx, Error, Object, ObjectCompat, Result, Symbol, Value,
8};
9
10use crate::BindingCell;
11
12type BindingSlot = Arc<Mutex<Option<Value>>>;
13
14/// Computes a binding's initial value within a (possibly partial) scope.
15///
16/// Used by `let*` and `letrec` so each initializer can observe the bindings
17/// already established in the same frame.
18pub type BindingInitializer =
19    Box<dyn Fn(&mut Cx, &LexicalEnv) -> Result<Value> + Send + Sync + 'static>;
20
21type LexicalBody =
22    Arc<dyn Fn(&mut Cx, &LexicalEnv, Vec<Value>) -> Result<Value> + Send + Sync + 'static>;
23
24/// A lexical scope: a frame of name-to-value slots chained to its parent.
25///
26/// Cloning shares the same frame; [`child`](LexicalEnv::child) opens a nested
27/// scope. Slots support deferred initialization so `letrec` can predefine names
28/// before computing their values.
29#[derive(Clone, Debug)]
30pub struct LexicalEnv {
31    frame: Arc<LexicalFrame>,
32}
33
34#[derive(Debug)]
35struct LexicalFrame {
36    parent: Option<LexicalEnv>,
37    slots: Mutex<BTreeMap<Symbol, BindingSlot>>,
38}
39
40impl Default for LexicalEnv {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl LexicalEnv {
47    /// Creates a fresh root scope with no parent and no bindings.
48    pub fn new() -> Self {
49        Self {
50            frame: Arc::new(LexicalFrame {
51                parent: None,
52                slots: Mutex::new(BTreeMap::new()),
53            }),
54        }
55    }
56
57    /// Opens a nested scope whose lookups fall through to this one.
58    pub fn child(&self) -> Self {
59        Self {
60            frame: Arc::new(LexicalFrame {
61                parent: Some(self.clone()),
62                slots: Mutex::new(BTreeMap::new()),
63            }),
64        }
65    }
66
67    /// Binds `name` to `value` in this frame.
68    ///
69    /// Errors if `name` is already bound in the same frame (shadowing requires
70    /// a [`child`](LexicalEnv::child) scope).
71    pub fn define(&self, name: Symbol, value: Value) -> Result<()> {
72        self.define_slot(name, Some(value))
73    }
74
75    /// Resolves `name` through this frame and its parents.
76    ///
77    /// Errors if the name is undefined or was predefined but never initialized.
78    pub fn lookup(&self, name: &Symbol) -> Result<Value> {
79        let Some(slot) = self.lookup_slot(name)? else {
80            return Err(Error::Eval(format!(
81                "lexical binding {name} is not defined"
82            )));
83        };
84        slot.lock()
85            .map_err(|_| Error::Eval(format!("lexical binding {name} lock is poisoned")))?
86            .clone()
87            .ok_or_else(|| Error::Eval(format!("lexical binding {name} is not initialized")))
88    }
89
90    /// Captures `name` as a shared cell for closure formation.
91    ///
92    /// Mutating the returned cell updates the lexical slot itself, so every
93    /// closure that captures the same binding observes the same value.
94    pub fn capture_cell(&self, name: &Symbol) -> Result<BindingCell> {
95        let Some(slot) = self.lookup_slot(name)? else {
96            return Err(Error::Eval(format!(
97                "lexical binding {name} is not defined"
98            )));
99        };
100        Ok(BindingCell::from_slot(name.clone(), slot))
101    }
102
103    fn predefine(&self, name: Symbol) -> Result<()> {
104        self.define_slot(name, None)
105    }
106
107    fn set(&self, name: &Symbol, value: Value) -> Result<()> {
108        let Some(slot) = self.lookup_slot(name)? else {
109            return Err(Error::Eval(format!(
110                "lexical binding {name} is not defined"
111            )));
112        };
113        *slot
114            .lock()
115            .map_err(|_| Error::Eval(format!("lexical binding {name} lock is poisoned")))? =
116            Some(value);
117        Ok(())
118    }
119
120    fn define_slot(&self, name: Symbol, value: Option<Value>) -> Result<()> {
121        let mut slots = self.slots()?;
122        if slots.contains_key(&name) {
123            return Err(Error::Eval(format!(
124                "lexical binding {name} is already defined in this frame"
125            )));
126        }
127        slots.insert(name, Arc::new(Mutex::new(value)));
128        Ok(())
129    }
130
131    fn lookup_slot(&self, name: &Symbol) -> Result<Option<BindingSlot>> {
132        if let Some(slot) = self.slots()?.get(name).cloned() {
133            return Ok(Some(slot));
134        }
135        match &self.frame.parent {
136            Some(parent) => parent.lookup_slot(name),
137            None => Ok(None),
138        }
139    }
140
141    fn slots(&self) -> Result<MutexGuard<'_, BTreeMap<Symbol, BindingSlot>>> {
142        self.frame
143            .slots
144            .lock()
145            .map_err(|_| Error::Eval("lexical binding frame lock is poisoned".to_owned()))
146    }
147}
148
149/// Evaluates a `let` form: parallel bindings in a fresh child scope.
150///
151/// All values are supplied up front, so no binding can observe another; `body`
152/// then runs in the child scope.
153pub fn eval_let(
154    cx: &mut Cx,
155    outer: &LexicalEnv,
156    bindings: Vec<(Symbol, Value)>,
157    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
158) -> Result<Value> {
159    let env = outer.child();
160    for (name, value) in bindings {
161        env.define(name, value)?;
162    }
163    body(cx, &env)
164}
165
166/// Evaluates a `let*` form: sequential bindings in a fresh child scope.
167///
168/// Each [`BindingInitializer`] runs in order and sees the bindings established
169/// before it.
170pub fn eval_let_star(
171    cx: &mut Cx,
172    outer: &LexicalEnv,
173    bindings: Vec<(Symbol, BindingInitializer)>,
174    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
175) -> Result<Value> {
176    let env = outer.child();
177    for (name, initializer) in bindings {
178        let value = initializer(cx, &env)?;
179        env.define(name, value)?;
180    }
181    body(cx, &env)
182}
183
184/// Evaluates a `letrec` form: mutually recursive bindings in a child scope.
185///
186/// All names are predefined before any initializer runs, so each
187/// [`BindingInitializer`] may reference every binding in the frame (including
188/// itself and later ones), enabling mutual recursion.
189pub fn eval_letrec(
190    cx: &mut Cx,
191    outer: &LexicalEnv,
192    bindings: Vec<(Symbol, BindingInitializer)>,
193    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
194) -> Result<Value> {
195    let env = outer.child();
196    let names = bindings
197        .iter()
198        .map(|(name, _)| name.clone())
199        .collect::<Vec<_>>();
200    for name in &names {
201        env.predefine(name.clone())?;
202    }
203    for ((_, initializer), name) in bindings.into_iter().zip(names.iter()) {
204        let value = initializer(cx, &env)?;
205        env.set(name, value)?;
206    }
207    body(cx, &env)
208}
209
210/// A closure that captures a [`LexicalEnv`] and is callable as a runtime object.
211///
212/// The kernel defines the `Object`/`Callable` contracts; this type realizes
213/// them for a body that closes over its defining lexical scope. It is the
214/// binding organ's representation of a lexically scoped function value.
215#[derive(Clone)]
216pub struct LexicalFunction {
217    name: Symbol,
218    env: LexicalEnv,
219    body: LexicalBody,
220}
221
222impl LexicalFunction {
223    /// Creates a function closing over `env`, identified by `name`.
224    pub fn new(name: Symbol, env: LexicalEnv, body: LexicalBody) -> Self {
225        Self { name, env, body }
226    }
227
228    /// Returns the function's name.
229    pub fn name(&self) -> &Symbol {
230        &self.name
231    }
232}
233
234impl Object for LexicalFunction {
235    fn display(&self, _cx: &mut Cx) -> Result<String> {
236        Ok(format!("#<binding-function {}>", self.name))
237    }
238
239    fn as_any(&self) -> &dyn std::any::Any {
240        self
241    }
242}
243
244impl ObjectCompat for LexicalFunction {
245    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
246        cx.resolve_class(&Symbol::qualified("core", "Function"))
247    }
248
249    fn as_callable(&self) -> Option<&dyn Callable> {
250        Some(self)
251    }
252}
253
254impl Callable for LexicalFunction {
255    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
256        (self.body)(cx, &self.env, args.into_vec())
257    }
258}
259
260/// Wraps a [`LexicalFunction`] as an opaque, callable runtime [`Value`].
261///
262/// The kernel factory defines opaque-object construction; this helper packages
263/// a name, captured scope, and body into a callable value for the host eval.
264pub fn lexical_function_value(
265    cx: &mut Cx,
266    name: Symbol,
267    env: LexicalEnv,
268    body: LexicalBody,
269) -> Result<Value> {
270    cx.factory()
271        .opaque(Arc::new(LexicalFunction::new(name, env, body)))
272}