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, BindingCellState};
11
12type BindingSlot = Arc<Mutex<BindingCellState>>;
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        BindingCell::from_slot(name.clone(), slot)
85            .get()
86            .map_err(|error| match error {
87                Error::Eval(message) if message.contains("is not initialized") => {
88                    Error::Eval(format!("lexical binding {name} is not initialized"))
89                }
90                other => other,
91            })
92    }
93
94    /// Captures `name` as a shared cell for closure formation.
95    ///
96    /// Mutating the returned cell updates the lexical slot itself, so every
97    /// closure that captures the same binding observes the same value.
98    pub fn capture_cell(&self, name: &Symbol) -> Result<BindingCell> {
99        let Some(slot) = self.lookup_slot(name)? else {
100            return Err(Error::Eval(format!(
101                "lexical binding {name} is not defined"
102            )));
103        };
104        Ok(BindingCell::from_slot(name.clone(), slot))
105    }
106
107    fn predefine(&self, name: Symbol) -> Result<()> {
108        self.define_slot(name, None)
109    }
110
111    fn set(&self, name: &Symbol, value: Value) -> Result<()> {
112        let Some(slot) = self.lookup_slot(name)? else {
113            return Err(Error::Eval(format!(
114                "lexical binding {name} is not defined"
115            )));
116        };
117        BindingCell::from_slot(name.clone(), slot).set(value)
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        let state = value.map_or(
128            BindingCellState::Uninitialized,
129            BindingCellState::Initialized,
130        );
131        slots.insert(name, Arc::new(Mutex::new(state)));
132        Ok(())
133    }
134
135    fn lookup_slot(&self, name: &Symbol) -> Result<Option<BindingSlot>> {
136        if let Some(slot) = self.slots()?.get(name).cloned() {
137            return Ok(Some(slot));
138        }
139        match &self.frame.parent {
140            Some(parent) => parent.lookup_slot(name),
141            None => Ok(None),
142        }
143    }
144
145    fn slots(&self) -> Result<MutexGuard<'_, BTreeMap<Symbol, BindingSlot>>> {
146        self.frame
147            .slots
148            .lock()
149            .map_err(|_| Error::Eval("lexical binding frame lock is poisoned".to_owned()))
150    }
151}
152
153/// Evaluates a `let` form: parallel bindings in a fresh child scope.
154///
155/// All values are supplied up front, so no binding can observe another; `body`
156/// then runs in the child scope.
157pub fn eval_let(
158    cx: &mut Cx,
159    outer: &LexicalEnv,
160    bindings: Vec<(Symbol, Value)>,
161    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
162) -> Result<Value> {
163    let env = outer.child();
164    for (name, value) in bindings {
165        env.define(name, value)?;
166    }
167    body(cx, &env)
168}
169
170/// Evaluates a `let*` form: sequential bindings in a fresh child scope.
171///
172/// Each [`BindingInitializer`] runs in order and sees the bindings established
173/// before it.
174pub fn eval_let_star(
175    cx: &mut Cx,
176    outer: &LexicalEnv,
177    bindings: Vec<(Symbol, BindingInitializer)>,
178    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
179) -> Result<Value> {
180    let env = outer.child();
181    for (name, initializer) in bindings {
182        let value = initializer(cx, &env)?;
183        env.define(name, value)?;
184    }
185    body(cx, &env)
186}
187
188/// Evaluates a `letrec` form: mutually recursive bindings in a child scope.
189///
190/// All names are predefined before any initializer runs, so each
191/// [`BindingInitializer`] may reference every binding in the frame (including
192/// itself and later ones), enabling mutual recursion.
193pub fn eval_letrec(
194    cx: &mut Cx,
195    outer: &LexicalEnv,
196    bindings: Vec<(Symbol, BindingInitializer)>,
197    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
198) -> Result<Value> {
199    let env = outer.child();
200    let names = bindings
201        .iter()
202        .map(|(name, _)| name.clone())
203        .collect::<Vec<_>>();
204    for name in &names {
205        env.predefine(name.clone())?;
206    }
207    for ((_, initializer), name) in bindings.into_iter().zip(names.iter()) {
208        let value = initializer(cx, &env)?;
209        env.set(name, value)?;
210    }
211    body(cx, &env)
212}
213
214/// A closure that captures a [`LexicalEnv`] and is callable as a runtime object.
215///
216/// The kernel defines the `Object`/`Callable` contracts; this type realizes
217/// them for a body that closes over its defining lexical scope. It is the
218/// binding organ's representation of a lexically scoped function value.
219#[derive(Clone)]
220pub struct LexicalFunction {
221    name: Symbol,
222    env: LexicalEnv,
223    body: LexicalBody,
224}
225
226impl LexicalFunction {
227    /// Creates a function closing over `env`, identified by `name`.
228    pub fn new(name: Symbol, env: LexicalEnv, body: LexicalBody) -> Self {
229        Self { name, env, body }
230    }
231
232    /// Returns the function's name.
233    pub fn name(&self) -> &Symbol {
234        &self.name
235    }
236}
237
238impl Object for LexicalFunction {
239    fn display(&self, _cx: &mut Cx) -> Result<String> {
240        Ok(format!("#<binding-function {}>", self.name))
241    }
242
243    fn as_any(&self) -> &dyn std::any::Any {
244        self
245    }
246}
247
248impl ObjectCompat for LexicalFunction {
249    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
250        cx.resolve_class(&Symbol::qualified("core", "Function"))
251    }
252
253    fn as_callable(&self) -> Option<&dyn Callable> {
254        Some(self)
255    }
256}
257
258impl Callable for LexicalFunction {
259    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
260        (self.body)(cx, &self.env, args.into_vec())
261    }
262}
263
264/// Wraps a [`LexicalFunction`] as an opaque, callable runtime [`Value`].
265///
266/// The kernel factory defines opaque-object construction; this helper packages
267/// a name, captured scope, and body into a callable value for the host eval.
268pub fn lexical_function_value(
269    cx: &mut Cx,
270    name: Symbol,
271    env: LexicalEnv,
272    body: LexicalBody,
273) -> Result<Value> {
274    cx.factory()
275        .opaque(Arc::new(LexicalFunction::new(name, env, body)))
276}